Racket: bad optimization in CS ?

Created on 5 Aug 2020  路  3Comments  路  Source: racket/racket

(Long story)

I made some changes to Typed Racket. Before the changes, the expanded code for a small program looked like this and did not raise an exception:

(define ctc 'FOUNDING)
....
(contract ctc x ....)

After the changes, the expanded code looks like this and errors on CS 7.8, but not BC 7.8:

(define ctc (lambda (x) (eq? x 'FOUNDING)))
....
(if (ctc x) 'ok (raise-user-error 'die))

If I change my contract to (lambda (x) (void) (eq? x 'FOUNDING)) then the CS error goes away.

So I guess there is a problem with a CS optimization.


Here's the code I started with:

#lang typed/racket/base

(module uuu racket/base
  (define FOUNDING 'FOUNDING)
  (provide FOUNDING))

(require/typed/provide 'uuu
  (FOUNDING 'FOUNDING))

And here is the full raco expand output. It's the same for CS 7.8 and BC 7.8 after adding my changes to Typed Racket.

I added newlines around the important parts, which are near the bottom.

(module aaa typed/racket/base
  (#%module-begin
   (module configure-runtime '#%kernel (#%module-begin (#%require racket/runtime-config) (#%app configure '#f)))
   (#%require (submod typed-racket/private/type-contract predicates))
   (#%require typed-racket/utils/utils)
   (#%require (for-meta 1 typed-racket/utils/utils))
   (#%require typed-racket/utils/any-wrap)
   (#%require typed-racket/utils/struct-type-c)
   (#%require typed-racket/utils/prefab-c)
   (#%require typed-racket/utils/opaque-object)
   (#%require typed-racket/utils/evt-contract)
   (#%require typed-racket/utils/hash-contract)
   (#%require typed-racket/utils/vector-contract)
   (#%require typed-racket/utils/sealing-contract)
   (#%require typed-racket/utils/promise-not-name-contract)
   (#%require typed-racket/utils/simple-result-arrow)
   (#%require racket/sequence)
   (#%require racket/contract/parametric)
   (begin-for-syntax
    (module*
     #%type-decl
     #f
     (#%plain-module-begin
      (#%declare #:empty-namespace)
      (#%require typed-racket/types/numeric-tower)
      (#%require typed-racket/env/type-name-env)
      (#%require typed-racket/env/global-env)
      (#%require typed-racket/env/type-alias-env)
      (#%require typed-racket/types/struct-table)
      (#%require typed-racket/types/abbrev)
      (#%require
       (just-meta 0 (rename racket/private/sort raw-sort sort))
       (just-meta 0 (rename racket/private/sort vector-sort! vector-sort!))
       (just-meta 0 (rename racket/private/sort vector-sort vector-sort))
       (only racket/private/sort))
      (#%app register-type (t-quote-syntax FOUNDING1) (#%app make-Value 'FOUNDING))
      (#%app register-type (t-quote-syntax lifted/1) -False))))
   (begin-for-syntax (#%app add-mod! (#%app variable-reference->module-path-index (#%variable-reference))))
   (define-values
    (blame2)
    (#%app module-name-fixup (#%app variable-reference->module-source/submod (#%variable-reference)) (#%app list)))
   (begin-for-syntax
    (#%require typed-racket/utils/redirect-contract)
    (module #%contract-defs-reference racket/base
      (#%module-begin
       (module configure-runtime '#%kernel (#%module-begin (#%require racket/runtime-config) (#%app configure '#f)))
       (#%require racket/runtime-path)
       (#%require (for-meta 1 racket/base))
       (define-values
        (contract-defs-submod)
        (let-values (((contract-defs-submod)
                      (let-values (((runtime?) '#t))
                        (#%app list 'module '(submod ".." #%contract-defs) (#%variable-reference)))))
          (let-values (((get-dir) void))
            (#%app
             apply
             values
             (#%app resolve-paths (#%variable-reference) get-dir (#%app list contract-defs-submod))))))
       (begin-for-syntax
        (#%app
         register-ext-files
         (#%variable-reference)
         (let-values (((contract-defs-submod)
                       (let-values (((runtime?) '#f))
                         (#%app list 'module '(submod ".." #%contract-defs) (#%variable-reference)))))
           (#%app list contract-defs-submod))))
       (#%provide contract-defs-submod)))
    (#%require (submod "." #%contract-defs-reference))
    (define-values (make-redirect3) (#%app make-make-redirect-to-contract contract-defs-submod)))
   (module*
    #%contract-defs
    #f
    (#%plain-module-begin
     (#%declare #:empty-namespace)
     (#%require (submod typed-racket/private/type-contract predicates))
     (#%require typed-racket/utils/utils)
     (#%require (for-meta 1 typed-racket/utils/utils))
     (#%require typed-racket/utils/any-wrap)
     (#%require typed-racket/utils/struct-type-c)
     (#%require typed-racket/utils/prefab-c)
     (#%require typed-racket/utils/opaque-object)
     (#%require typed-racket/utils/evt-contract)
     (#%require typed-racket/utils/hash-contract)
     (#%require typed-racket/utils/vector-contract)
     (#%require typed-racket/utils/sealing-contract)
     (#%require typed-racket/utils/promise-not-name-contract)
     (#%require typed-racket/utils/simple-result-arrow)
     (#%require racket/sequence)
     (#%require racket/contract/parametric)))
   (module uuu racket/base
     (#%module-begin
      (module configure-runtime '#%kernel (#%module-begin (#%require racket/runtime-config) (#%app configure '#f)))
      (define-values (FOUNDING) 'FOUNDING)
      (#%provide FOUNDING)))

   (define-values (g5) (lambda (x) (#%app eq? x 'FOUNDING)))
   (define-values (lifted/1) g5)

   (define-values () (begin (quote-syntax (require/typed-internal FOUNDING1 'FOUNDING) #:local) (#%plain-app values)))
   (#%require (just-meta 0 (rename 'uuu FOUNDING FOUNDING)) (only 'uuu))
   (define-syntaxes
    (FOUNDING)
    (#%app
     make-rename-transformer
     (#%app
      syntax-property
      (#%app syntax-property (quote-syntax FOUNDING1) 'not-free-identifier=? '#t)
      'not-provide-all-defined
      '#t)))

   (define-values (FOUNDING1) (if (#%plain-app lifted/1 FOUNDING) FOUNDING (#%plain-app raise-user-error 'die)))

   (#%provide FOUNDING)
   (#%provide)
   (#%app void)))

Lastly, here are my changes to Typed Racket

diff --git a/typed-racket-lib/typed-racket/private/type-contract.rkt b/typed-racket-lib/typed-racket/private/type-contract.rkt
index a773a59a..4b452a17 100644
--- a/typed-racket-lib/typed-racket/private/type-contract.rkt
+++ b/typed-racket-lib/typed-racket/private/type-contract.rkt
@@ -400,6 +400,9 @@
        [(Listof: elem-ty) (listof/sc (t->sc elem-ty))]
        ;; This comes before Base-ctc to use the Value-style logic
        ;; for the singleton base types (e.g. -Null, 1, etc)
+       [(Val-able: v)
+        #:when (symbol? v)
+        (flat/sc #`(lambda (x) (eq? x '#,v)))]
        [(Val-able: v)
         (if (and (c:flat-contract? v)
                  ;; numbers used as contracts compare with =, but TR
diff --git a/typed-racket-lib/typed-racket/utils/require-contract.rkt b/typed-racket-lib/typed-racket/utils/require-contract.rkt
index 05219dbf..d3306ca2 100644
--- a/typed-racket-lib/typed-racket/utils/require-contract.rkt
+++ b/typed-racket-lib/typed-racket/utils/require-contract.rkt
@@ -56,12 +56,9 @@
               (rename-without-provide nm.nm hidden)

               (define-ignored hidden
-                (contract cnt
-                          #,(get-alternate #'nm.orig-nm-r)
-                          '(interface for #,(syntax->datum #'nm.nm))
-                          (current-contract-region)
-                          (quote nm.nm)
-                          (quote-srcloc nm.nm))))]))
+                     (if (#%plain-app cnt #,(get-alternate #'nm.orig-nm-r))
+                       #,(get-alternate #'nm.orig-nm-r)
+                       (#%plain-app raise-user-error 'die))))]))

(I will try again later to make an example without Typed Racket.)

bug racket-cs

All 3 comments

Running this with set PLT_LINKLET_SHOW_CP0=1 I get this (after a lot of minimization and a few small lies).

The first part is just the definition of the variable FOUNDING:

;; linklet ---------------------
(linklet (...)
  (define-values (FOUNDING) 'FOUNDING))
;; schemified ---------------------
(lambda (...)
  (define FOUNDING 'FOUNDING)
  (variable-set!/define FOUNDING3 FOUNDING 'consistent))
;; cp0 ---------------------
(lambda (...))
  (variable-set!/define FOUNDING3 'FOUNDING 'consistent))

The second part is more interesting:

;; linklet ---------------------
(linklet (...)
  (define-values (lifted/1.1) (lambda (x_2) (eq? x_2 'FOUNDING)))    ; there are a few renames that I'm simplifying.
  (if (lifted/1.1 FOUNDING) FOUNDING (raise-user-error 'die))) 
;; schemified ---------------------
(lambda (...)
  (call-with-module-prompt
    (lambda ()
      (if (eq? 'FOUNDING ''FOUNDING)                                 ; <-- the second FOUNDING has a double quote!
          'FOUNDING
          (raise-user-error 'die)))
      ...))
;; cp0 ---------------------
(lambda (...)
  (call-with-module-prompt
    (lambda () ((#3%$top-level-value '1/raise-user-error) 'die))
    ...))

Based on @gus-massa's report, here's a small program:

#lang racket/base

(module a racket/base
  (provide FOUNDING)
  (define-values (FOUNDING) 'FOUNDING))

(module b racket/base
  (require (submod ".." a))
  (define (f x_2) (eq? x_2 'FOUNDING))
  (if (f FOUNDING) FOUNDING (raise-user-error 'die)))

(require (submod "." b))

Seems likely to be a problem with cross-module inlining in schemify.

The problem is a missing quote pattern in schemify's optimize*. Things go wrong in this example because the quoted namee FOUNDING gets replaced by the value of the FOUNDING variable.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

MichaelMMacLeod picture MichaelMMacLeod  路  3Comments

mmoore96 picture mmoore96  路  5Comments

schackbrian2012 picture schackbrian2012  路  4Comments

schackbrian2012 picture schackbrian2012  路  6Comments

shhyou picture shhyou  路  7Comments