cisco / ChezScheme

Chez Scheme
Apache License 2.0
6.98k stars 988 forks source link

Compiled code can't evaluate variables in a different environment. #620

Open swatson555 opened 2 years ago

swatson555 commented 2 years ago

Evaluating variables in a different environment only works in interpreter mode. Compiled code can't evaluate variables in a different environment using eval.

burgerrg commented 2 years ago

Can you give an example of what you mean? See https://cisco.github.io/ChezScheme/csug9.5/system.html#./system:s38 for details on Chez Scheme's eval function that takes an optional environment argument.

swatson555 commented 2 years ago

Screen Shot 2022-04-26 at 9 07 20 AM

This is what happens when trying to build a boot file:

Screen Shot 2022-04-26 at 9 12 25 AM

This isn't the same error I've had working with Chez. The error I've encountered is eval will throw an unbound variable exception. This is how I'm building:

(generate-wpo-files #t)
(compile-imported-libraries #t)

(compile-program "hello.ss")
(compile-whole-program "hello.wpo" "hello.cwp")
(strip-fasl-file "hello.cwp" "hello.cwp"
         (fasl-strip-options inspector-source
                     source-annotations
                     profile-source))
(make-boot-file "hello.boot" '("scheme" "petite") "hello.cwp")
burgerrg commented 2 years ago

Try adding (expand '(import (n))) to hello.ss before calling eval. This will load the compile-time information for library (n).

burgerrg commented 2 years ago

If you do (eval '(import (n)), then the variable binding will be available in the interaction environment, and you can use (eval 'var).

swatson555 commented 2 years ago

I tried expand but eval still can't find the library. I'm not sure under what conditions eval will throw an exception but, I think I still have code that will produce this in Chez in my archive and, if I can find it I'll try to see how I did that.

There's also Chez specific procedures for working with env which also share similar problems. https://cisco.github.io/ChezScheme/csug9.5/binding.html#./binding:h5

swatson555 commented 2 years ago

I had a look at the code that produced the unbound variable exception and, it doesn't do this in newer versions of Chez. It gives a similar "library not found" exception.

burgerrg commented 2 years ago

Would you please zip up all the files you've used to demonstrate the trouble and attach to this issue so that I can reproduce the behavior?

swatson555 commented 2 years ago

hello.zip

This zip file contains the build script, test program source, and library source.

akeep commented 2 years ago

Thanks for sharing the zip @swatson555, there are actually a few issues you're running into here.

  1. compile-whole-program does not preserve the library entry points like (n) unless you specify libs-visible? as #t in the (compile-whole-program input-filename output-filename libs-visible?) version of the call. Generally, this information is not needed at run time, unless you want to use the library explicitly in eval (as in your example) or environment. You can read more about that in Section 12.4 of the Chez Scheme User's Guide.
  2. When hello.ss is compiled, (n) is not identified as a dependency because it is not imported and nothing in the library is referenced. You can see this actually, when you run build.ss because it never even tries to compile (n) when compile-program is invoked, even though the compile-imported-libraries parameter is set to #t. When the output is executed the eval will cause scheme to look for your library (and compile it if necessary, which in this case it is). You can see this by enabling import-notify and using load-program to load the hello.cwp program file:
    
    % scheme
    Chez Scheme Version 9.5.5
    Copyright 1984-2020 Cisco Systems, Inc.

(import-notify #t) (load-program "hello.cwp") import: did not find source file "n.chezscheme.sls" import: did not find source file "n.ss" import: did not find source file "n.sls" import: did not find source file "n.scm" import: did not find source file "n.sch" import: did not find object file "n.chezscheme.so" import: did not find object file "n.so" Exception in environment: library (n) not found Type (debug) to enter the debugger.

If we extend the `library-directories` to include `lib`, it will find the file, and run the program correctly (though not the boot file---perhaps @burgerrg has some insight on that part).
```scheme
[akeep@hawkeye hello-2]% scheme
Chez Scheme Version 9.5.5
Copyright 1984-2020 Cisco Systems, Inc.

(import-notify #t)
(library-directories (cons "lib" (library-directories)))
(load-program "hello.cwp")
import: did not find source file "lib/n.chezscheme.sls"
import: did not find source file "lib/n.ss"
import: found source file "lib/n.sls"
import: did not find corresponding object file "lib/n.so"
import: loading source file "lib/n.sls"
Hello, World!
  1. Even if (n) were imported, the hello.ss program would not record that (n) is necessary for it building, because only code that is compiled is used to determine the imports, and the code in eval is not evaluated until run time.

So, here are a few changes that can build a hello.boot that will contain (n) (recognizing that this is a little different from your original intention).

Changes to hello.ss (comments inline):

(import (chezscheme) (n)) ;; 1. add the import for (n)
(define foo var)                    ;; 2. reference something from (n)
(display
 (eval 'var (environment '(n))))
(newline)

and changes to build.ss (comments inline):

#!/usr/bin/env scheme --script

(generate-wpo-files #t)
(compile-imported-libraries #t)
(library-directories (cons "lib" (library-directories))) ;; 1. add the "lib" directory to library-directories

(compile-program "hello.ss")
(compile-whole-program "hello.wpo" "hello.cwp" #t) ;; 2. add the _libs-visible?_ #t argument.
(strip-fasl-file "hello.cwp" "hello.cwp"
     (fasl-strip-options inspector-source
             source-annotations
             profile-source))
(make-boot-file "hello.boot" '("scheme" "petite") "hello.cwp")

With these changes, I was able to get the following to work:

% ./build.ss 
compiling hello.ss with output to hello.so
compiling lib/n.sls with output to lib/n.so
[akeep@hawkeye hello]% scheme --boot hello.boot 
Hello, World!
Chez Scheme Version 9.5.5
Copyright 1984-2020 Cisco Systems, Inc.

> 

Incidentally, Chez cannot compile the arguments to eval or environment in general because they can be constructed at run time.

swatson555 commented 2 years ago

This isn't viable work around because in the case where the name var exists in the current environment the compiler will throw an exception that there are multiple definitions of the same name.

akeep commented 2 years ago

Fortunately, naming is an easy problem to work around because R6RS allows you to rename and limit imports, so you can do something like:

(import (chezscheme) (rename (only (n) var) (var something-else)))
(define foo something-else)

And if this is being generated as the output of a macro something-else could just be a gensym (actually you could even generate a gensym and use that directly:

% scheme
Chez Scheme Version 9.5.5
Copyright 1984-2020 Cisco Systems, Inc.

> (gensym)
#{g0 mpf482drvcnazscbdytjja3wj-0}

Then rewrite to:

#!chezscheme
(import (chezscheme) (rename (only (n) var) (var #{g0 mpf482drvcnazscbdytjja3wj-
(define foo #{g0 mpf482drvcnazscbdytjja3wj-0})

The #!chezscheme is necessary to inform the reader to accept extended Chez Scheme syntax.

However, it is worth noting that you are not really getting any benefit from compile-whole-program with regards to the (n) library, since, compile-whole-program cannot inline a library you are not really using. (In your use case, if you have libraries you are using directly, you will still get the benefits of those of course.)

Since you are building a boot file, you can simply include other libraries with the program. The way to do this is to compile lib/n.sls separately (since compiling hello.ss will not cause it to be compiled otherwise), and then include it in the boot file call:

#!/usr/bin/env scheme --script

(generate-wpo-files #t)
(compile-imported-libraries #t)

(compile-program "hello.ss")
(compile-whole-program "hello.wpo" "hello.cwp")
(compile-library "lib/n.sls" "lib/n.so") ;; 1. compile any additional libraries

(strip-fasl-file "hello.cwp" "hello.cwp"
     (fasl-strip-options inspector-source
             source-annotations
             profile-source))
;; 2. add the libraries before the program, so that they will be loaded and available in the boot file.
(make-boot-file "hello.boot" '("scheme" "petite") "lib/n.so" "hello.cwp")

This approach will work without needing to change the hello.ss code.