The expr/c syntax class, as well as its underlying implementation
function, wrap-expr/c, previously produced misleading error messages.
The main purpose of these tools is to ensure a user-provided expression
conforms to a macro-provided contract. However, contract errors produced
by these forms were consistent with situations where both value and
contract were provided by the same party.
This fixes the discrepancy by changing how these forms assign blame to
emulate contract errors that arise from improper function arguments,
since most expressions provided to macros are semantically similar to
function arguments. All examples within the documentation itself
reflect this use case.
These changes alter the contents of error messages raised by expr/c and
wrap-expr/c, which could theoretically break some test suites, but it’s
extremely unlikely that any non-test code would depend on the precise
wording of contract error messages, and the interface is otherwise
completely backwards-compatible.
fixes#1412
When the second argument to `bytes-set!` is a reference to a
module-level variable that is definitely defined but not a known
constant, then an incorrect reordering was used that would cause
the third argument value to get overwritten before the call.
Closes#1601
The primitive `read` uses a shortcut --- a private "ungetc"
implementation --- that did not count position correctly for
non-ASCII characters.
Closes#1599
The documentation says that it should work on any output port,
although there's special treatment of ports that originate
from `pretty-print` itself.
Closes#1579.
Allow the directory for GUI executables to be specified as different
from console executables. The defaults for those two are different
on Mac OS, and configuring them differently might be useful to
address #1575.
Although there is probably no demand on Windows or Unix for splitting
the console and GUI bin directories, this patch tries to make things
work sensible there. On Windows, there's a corner case where a
launcher that starts GRacket (especially with `-z`) is intended to be
a console executable. The launcher creator can be told that via a
`subsystem` option, but a new `#:console?` argument was needed for
`make-gracket-launcher-path` lets the path selector know.
After some reductions, the new rator advance less the effect
clocks than the original rator. For example in
(equal? x 7) ==> (eq? x 7)
(my-struct? x) ==> #t or #f
The lambdas can be marked as single valued and/or mark preserving.
With this information is possible to remove unnecessary wrapping
like the `values` in
(let ([f (lambda () '(1))])
(display f f)
(values (f)))
or in reductions like
(car (list (f))) ==> (values (f)) ==> (f)
Moreover, this is useful to test that the optimizer has marked
correctly the function f as single valued and mark preserving.
If a module has any sort of complex bindings, such as a definition of
a macor-introduced identifiers, then `module->namespace` and variants
(like `variable-reference->namespace`) need to recreate suitable
bindings. Make sure that the module-path index for recreated bindings
is the run-time one, not the compile-time one.
Closes#1584
To avoid moving expressions that may have a side effect, the optimizer must
recognize that in this position this will cause an error and advance
the virtual clock.
Currently the only primitive that is flagged as SCHEME_PRIM_IS_OMITABLE and
may have multiple return values is `values`.
Thanks to Robby for finding the original version of the test.
When a hash table or other special value appears immediately on the
right-hand side of `define-values`, it needs to be protected by an
explicit quote when writing to bytecode.
Closes#1580
* byte-regexp? values should not be considered 3D syntax.
* hash? values are now allowed in serialized syntax properties with (template … #:properties (…))
* marshalling properties which were prefab structs called map on the result of struct->vector, changed it to struct->list as the struct "name" is always serializable.
Continuing the saga that includes 8190a7730d and d1ba9fbb6e, it turns
out that a 0-binding clause as the last one isn't so special after
all. A little later in the optimizer, now that we're sometimes moving
an error to the body, we can't assume that the body can be discard
if an error was detected.
Set up bindings and shift phases as needed to make
`variable-reference->namespace` work in a run-time position when the
enclosing module is instantiated at a phase other than 0.
Thanks to Rohin Shah for the bug report.
Support an external implementation of `read-syntax` by exposing
functionality that is currently internal to `read-syntax`: a srcloc
argument to a "special"-producing port function and wrapping special
results to reliably distinguish them from characters.
When Racket is run with stdout or stderr redirected to a file,
then it must be treated as a regular file, otherwise flushing
and position counting doesn't work right.
Merge to v6.8
In e59f888, new GCs no longer inherit the `avoid_collection` value set by
PLTDISABLEGC, and so that setting is lost as soon as the master place spawns
(due to `GC_switch_out_master_gc`).
When multiple-binding `let-values` form is split into a single-binding
form on the grounds that the right-hand side will definitely error,
the optimizer's effect clocks were advance incorrectly.
Closes#1552
Instead of duplicating a context line, show "[repeats <n> times]".
This improvement particularly helps avoid showing less context
now that `for/list` creates a non-tail recursion to build up
the result list.
Specifically, when it sees these contracts:
(and/c real? negative?)
(and/c real? positive?)
(and/c real? (not/c positive?))
(and/c real? (not/c negative?))
it generates the corresponding use of >=/c, <=/c, </c, or >/c, but
those contracts have also been adjusted to report their names as
(and/c real? ...).
This mostly is an improvement for contract-stronger, but also make it
so that (between/c -inf.0 +inf.0) just uses the real? predicate
directly, instead of a more complex function
Although "macOS" is the correct name for Apple's current desktop OS,
we've decided to go with "Mac OS" to cover all of Apple's Unix-like
desktop OS versions. The label "Mac OS" is more readable, clear in
context (i.e., unlikely to be confused with the Mac OSes that
proceeded Mac OS X), and as likely to match Apple's future OS names
as anything.
For example, an `unsafe-unbox` call should not be moved past the
call to an unknown function that might change a box's content.
Thanks to Sergey Pinaev for the report.
Using `--disable-jit` causes futures to be disabled, and places are
always disabled for CGC; in that case thread-local variables are
not needed. Meanwhile, the 3m build still has places, so a "gmp.c"
compiled without thread-local support is broken.
The objective of lookup_constant_proc and the first part of
optimize_for_inline was to find out if the value of an expression was a
procedure and get it to analyze its properties or try to inline it. Both
were called together in a few places, because each one had some special
cases that were missing in the other.
So, move the lookup and special cases from optimize_for_inline to
lookup_constant_proc, and keep only the code relevant to inlinig in
optimize_for_inline.
If an OS-level thread other than a Racket thread logs a message, then
the message needs to be queued instead of handled immediately.
If multiple places are running, then the right handler thread is not
clear, so just queue to the main place's thread.
Closesracket/gui#66Closesracket/drracket#77
Implement POSIX.1-2001/pax and GNU extensions for long paths and links
in `untar` and `tar`. Add a `#:format` argument to `tar` to select
among POSIX.1-2001/pax, GNU, or error encoding for long paths.
it doesn't apply it the second time (since we know that the
only difference for indy blame is in the negative position
and we know that flat contracts never assign negative blame)
This commit combined with the two previous (2b9d855 and 003e8c7) do
not seem to have a significant effect on the performance of ->i
contract checking. In particular, I see a 50% slowdown between the
version before and the version after these commits on the third `time`
expression below, but no significant difference on the first two.
(without the improvement to flat-contract?, these commits are
a significant slowdown to `g`)
#lang racket
(require profile)
(define f
(contract (->i ([y () integer?]
[x (y) integer?])
(values [a () integer?]
[b (a) integer?]))
values
'pos 'neg))
(define g
(contract (->i ([y () (<=/c 10)]
[x (y) (>=/c y)])
(values [a () (<=/c 10)]
[b (a) (>=/c a)]))
values
'pos 'neg))
(define (slow-predicate n)
(cond
[(zero? n) #t]
[else (slow-predicate (- n 1))]))
(define h
(contract (->i ([y () slow-predicate]
[x (y) slow-predicate])
(values [a () slow-predicate]
[b (a) slow-predicate]))
values
'pos 'neg))
(time
(for ([x (in-range 100000)])
(f 1 2) (f 1 2) (f 1 2)
(f 1 2) (f 1 2) (f 1 2)
(f 1 2) (f 1 2) (f 1 2)))
(time
(for ([x (in-range 100000)])
(g 1 2) (g 1 2) (g 1 2)
(g 1 2) (g 1 2) (g 1 2)
(g 1 2) (g 1 2) (g 1 2)))
(time
(for ([x (in-range 10000)])
(h 50000 50000)))
When the main interpreter loop is called for an application where the
argument array coincides with the current runstack pointer, then the
protocol is that the callee gets to modify that space --- and it
should modify that space as arguments become unused. The interpreter
was always copying arguments to a fresh space, though.
Both function have a similar purpose and implementation, so merge them to consider
all the special cases for both uses.
In particular, detect that:
(if x (error 'e) (void)) is single-valued
(with-continuation-mark <chaperone-key> <val> <omittable>) is not tail sensitive.
Also, as ensure_single_value was checking also that the expression was has not a
continuation mark in tail position, it added in some cases an unnecessary
wrapper. Now ensure_single_value checks only that the expression produces
a single vale and a new function ensure_single_value_noncm checks both
properties like the old function.
Adjust list and stream handling as sequences so that during the body
(for ([i (in-list l)])
....)
then `i` and its cons cell in `l` are not implicitly retained while
the body is evaluated. A `for .... in-stream` similarly avoids
retaining the stream whose head is being used in the loop body.
The `map`, `for-each`, `andmap`, and `ormap` functions are similarly
updated.
The `make-do-sequence` protocol allows an optional extra result so
that new sequence types could have the same properties. It's not clear
that using `make-do-sequence` is any more useful than creating the new
sequence as a stream, but it was easier to expose the new
functionality than to hide it.
Making this work required a repair to the optimizer, which would
incorrectly move an `if` expression in a way that could affect
space complexity, as well as a few repairs to the run-time system
(especially in the vicinity of the built-in `map`, which we should
just get rid of eventually, anyway).
Compile a `for[*]/list` form to behave more like `map` by `cons`ing
onto a recursive call, instead of accumulating a list to reverse.
This style of compilation requires a different strategy than before.
A form like
(for*/fold ([v 0]) ([i (in-range M)]
[j (in-range N)])
j)
compiles as nested loops, like
(let i-loop ([v 0] [i 0])
(if (unsafe-fx< i M)
(i-loop (let j-loop ([v v] [j 0])
(if (unsafe-fx< j N)
(j-loop (SEL v j) (unsafe-fx+ j 1))
v))
(unsafe-fx+ i 1))
v))
instead of mutually recursive loops, like
(let i-loop ([v 0] [i 0])
(if (unsafe-fx< i M)
(let j-loop ([v v] [j 0])
(if (unsafe-fx< j N)
(j-loop (SEL v j) (unsafe-fx+ j 1))
(i-loop v (unsafe-fx+ i 1))))
v))
The former runs slightly faster. It's difficult to say why, for
certain, but the reason may be that the JIT can generate more direct
jumps for self-recursion than mutual recursion. (In the case of mutual
recursion, the JIT has to generate one function or the other to get a
known address to jump to.)
Nested loops con't work for `for/list`, though, since each `cons`
needs to be wrapped around the whole continuation of the computation.
So, the `for` compiler adapts, depending on the initial form. (With a
base, CPS-like approach to support `for/list`, it's easy to use the
nested mode when it works by just not fully CPSing.)
Forms that use `#:break` or `#:final` use the mutual-recursion
approach, because `#:break` and #:final` are easier and faster that
way. Internallt, that simplies the imoplementation. Externally, a
`for` loop with `#:break` or `#:final` can be slightly faster than
before.
When parsing cannot fail, avoid allocating expectstacks and
failures (thanks samth for the idea). Allocation still happens
for progress and failuresets (conses of #t, now), though.
Compile with `PLTSTDERR="debug@syntax-parse"` to log cannot-fail
syntax-parse expressions and syntax class definitions.
The optimizer can detect that some expressions will escape through
an error, and it can discard surrounding code in that case. It should
not change the tailness of a `with-continuation-mark` form by
liftng it out of a nested position, however. Doing so can eliminate
stack frames that should be visible via errotrace, for example.
This change fixes the optimizer to wrap an extra `(begin ... (void))`
around an expression if it's lifted out of a nested context and
might have a `with-continuation-mark` form in tail position.
When caching the result of a stack traversal, adjust
the actual stack only after the traversal is complete
to avoid interfering with libunwind's decoding of the
stack.
In
(with-syntax ([x ....])
#'(x y))
and property on the source syntax object `(x y)` was lost in
constructing a new syntax object to substitute for `x`, while
properties on preserved literal syntax objects, such as `y`
were intact. Change `syntax` to preserve properties for
reconstructed parts of the template.
This change exposes a problem with 'transparent taint modes,
where the internal "is original?" property was preserved while
losing scopes that wuld cancel originalness. So, that's fixed
here, too.
The continuation of a future being evaluated concurrently was not
correctly attributed to the future's custodian (as inherited from
from the creating thread).
One interesting corner case is when a vector is allocated
in a way that would exceed a memory limit. The custodian
captured with a future is used to guard large allocations.
extend optimize_ignore to go inside expressions with
begin, begin0 and let.
Also, try to reuse begin's in the first argument of
make_discarding_sequence.
Make sure `#` is start at the start of the line using
the `IFDEF` function, although we'll probably never
again use a compiler old enough for this to matter.
If a mutable hash table changes while it's being printed,
various parts of the printing function could see a mismatch
between the current size and an old array size. To avoid this
problem, extract the size whenever extracting the array.
Track total bytes allocated for various tags and categories
in backtrace mode, and improve structure reporting to include
byte counts.
Strip away a more ad-hoc counting that was added recently.
This adds #:eager as an option for controlling this behavior.
Using `#:eager 10` is a 2x improvement in performance for configuration 010001
of the suffixtree benchmark from Takikawa et al, POPL 2016.
The default behavior is unchanged. This is configurable because some
programs are much faster when eager checking is performed. For example:
(require racket/contract)
(collect-garbage)
(time (for/sum ([_ 100000])
(vector-ref (contract (vectorof integer? #:eager #t) #(1) 'pos 'neg)
0)))
(collect-garbage)
(time (for/sum ([_ 100000])
(vector-ref (contract (vectorof integer? #:eager #f) #(1) 'pos 'neg)
0)))
The second loop is 3-4 times slower than the first. However, making
the vector much larger will make the difference go the other way.
An identifier that gets a module context via `module->namespace` plus
`namespace-syntax-introduce` should not count as having the module as
its source as reported by `syntax-source-module`.
The correct behavior happened for the wrong reason prior to commit
cb6af9664c.
Closes#1515
For a template expression that involevs ellipses, a wrapper is added
to catch failures an report as an "incompatible ellipsis match count"
error. The wrapper was only added when there are multiple pattern
variables with ellipses, but it turns out that it's possible to fail
with incompatible counts using a single pattern variable.
Besides handlign that case, the revised check avoids an unnecessary
wrapper in cases where multiple pattern variables have ellipses but
they are used independently in a template.
Closes#1511
Without this repair,
#lang racket/base
(require 2htdp/abstraction)
(for/list ((dropping-which-one (in-naturals)))
1)
fails to compile with a "optimizer clock tracking has gone wrong"
error. A variant of this test (that doesn't depend on `2htdp`)
is now in the "optimize.rktl"; a simpler and more direct test
should be possible, but I wasn't able to construct one.
The value of rator_flags was calculated in optimize_application and used in
finish_optimize_application, but it is possible to calculate it directly in
finish_optimize_application, and then remove some internal coupling.
This also simplifies other locations where the rator of an application was
changed and then it was necessary to recalculate the value of rator_flags
to complete the optimization steps.
When a thread that is blocked on a set of semaphores and channels
is suspended and resumed after one of the events becomes ready,
and if the event has a wrapper function, then the wrapper was
not applied and the event selection was not reported correctly.
Thanks to Philip McGrath for reporting the problem.
Comparing to daylight-saving time change was performed incorrectly, so
that days in the same month as a change and the hour within the day before
the switch hour were all treated as pre-switch (instead of counting
only days up to the switch as pre-switch).
Thanks to Jon Zeppieri for the repair and George Neuner for
the report.
Mac OS X header files for 10.12 include `_Nullable` and `_Nonnull`
annotations. When those appear in xform output, they're no longer
counted as being in system headers, and so nullability
completeness is enabled. Disable is explicitly when the flag is
supported.
This kind of reductions were applied only when x or y was a constant.
Classify the relevant predicates in 4 categories. In particular,
if <expr> satisfy pred? we can use this classification to apply
the correct reduction:
(equal? <expr> y) ==> [no reduction, unless y has a different type]
(equal? <expr> y) ==> (eqv? <expr> y)
(equal? <expr> y) ==> (eq? <expr> y)
(equal? <expr> y) ==> (begin <expr> (pred? y))
Also, add a new primitive interned-char? that is hidden, but it's
useful to track in the optimizer the the chars? with a value < 256
that are interned because they are treated specially, and if they
are equal? then they are eq?.
... + prefix-in + relative-path module. All of those ingredients
(or some similar alternatives) are necessary to trigger a slow
way of saving module context for interaction evaluation where
a module-path index shift was getting lost.
Previously the relevant predicates where disjoint, and until this commit
the only predicate that recognizes #f was `not`. So it's necessary to fix
two reductions to allow other predicates that recognize #f, like `boolean?`.
Add a hidden `true-object?` primitive that recognizes only #t, that is also
useful to calculate unions and complements with `boolean?` and `not`.
Also, extend a special case for expressions like
(or (symbol? x) (something))
where the optimizer is confused by the temporal variable that saves the
result of `(symbol? x)`, and the final expression is equivalent to
(let ([temp (symbol? x)])
(if temp #t (something)))
This extension detects that the temporal variable is a `boolean?` and
reduces the expression to
(if (symbol? x) #t (something))
It's worth noting that this hasn't caused me an issue, I came across it as I wanted to see what sys-type actually did. I couldn't say what the affect of this change would be and don't have a use case for it, it just looks wrong!
Provide `--enable-ubsan` to simplify `-fsanitize=undefined` builds,
where alignment and floating-point divide-by-zero need to be
disabled to get useful results.
Also, repair undefined behavior exposed by the test suite. Most of the
repairs are avoiding `memset(..., NULL, 0)` --- which is an unhelpful
requirement, IMO. The other two repairs are worthwhile but unlikely to
have caused trouble.
The optimizer assumed a fixnum result if either argument to
`bitwise-and` implies a fixnum result. That's not correct if the
fixnum agument is negative.
Thanks to Peter Samarin for a bug report.
Merge to v6.7
In case a write barrier happens between the set and use
of `errno`, make sure the barrier doesn't cause the
`errno` value to change in the process of making other
system calls.
When calling a procedure that is attached as a
`prop:rename-transformer` property value, make sure that
any available expansion context is accessible as reflected by
`(syntax-transforming?)`.
Syntax parameters as rename transformers particularly rely on that
information for local expansion.
Thanks to Jay for the "stxparam.rktl" test.
Closes#1479
In a pattern like
a*b
a naive attempt to match will take quadratic time on an input that
contains all "a"s an no "b". To improve that case, the regexp compiler
detects that a match will require a "b" and checks the input for a "b"
to enable linear-time failure.
That optimization mishandled `(?!...)` and `(?<!...)` patterns,
treating the must-not-match subpatterns as things that must match.
So,
(regexp-match "a*(?!b)" "aaaxy")
returned false, because the input doesn't contain "b".
Thie commit repairs the optimization.
Closes#1468
Fix a regression relative to v6.4 caused by a refactoring of the
compiler between v6.4 and v6.5. The refactoring lost information about
letrecs that are converted internally to let* when a mutable variable
is involved, and it ends up allocating a closure before the box of a
mutable variable that is referenced by the closure. Something like
`with-continuation-mark` is needed around the closure's `lambda` to
prevent other optimizations from hiding the bug.
Closes#1462
lookup-errno now returns #f when given an unknown symbol instead
of raising a contract error. It should not return #f for any
symbol that it previously accepted.
The `if` case of the compiler's space-safety pass abused its "last
non-tail call relative to the closest enclosing binding" state as
"last non-tail call relative to the enclosing run time", which could
cause it to not clear a stack position as needed to maintain space
safety.
* add lang-reader-module-paths to syntax/module-reader
to be used as the third argument to make-meta-reader in lang-extensions
like at-exp
* document lang-reader-module-paths
* use lang-reader-module-paths in at-exp
This makes two changes to the forms in racket/splicing to adjust how
syntax properties are propagated through expansion:
1. Uses of make-syntax-introducer are passed #t as the first argument,
which causes the introduced scope to be consider a use-site rather
than macro-introduction scope. This prevents syntax objects from
being unnecessarily marked as unoriginal, in the syntax-original?
sense.
2. Uses of syntax/loc have been adjusted to copy syntax properties
from original syntax objects, which were previously discared. Forms
that were spliced into the surrounding context, such as begin,
define-values, and define-syntaxes, recreated the top-level syntax
objects, which did not preserve syntax properties from the
originals.
This is not a perfect solution, mostly because it potentially elides
properties that may be associated with captured literals (that is,
properties attached directly to begin, define-values, or define-syntaxes
identifiers themselves). However, it seems to accommodate most of the
common use-cases: propagation of syntax-original?-ness and forms like
`struct`, which attach properties like 'sub-range-binders.
fixes#1410
A `struct-copy` form can generates a call for a constructor that
includes a sequence of `unsafe-struct-ref` arguments. Each
`unsafe-struct-ref` must still check for a chaperone. Make the JIT
recognize that pattern an turn it into a single test instead of
one test per `unsafe-struct-ref`.
http-proxy/ contains a suite of almost useful (but mostly useless) servers.
These can be used to test http-client, and url.rkt
git proxy is not tested yet -- I really wouldn’t know how
This patch adds https and git proxying through HTTP’s `CONNECT` method.
**Sanity Checks Needed:**
1. Is the git protocol proxying necessary?
It might be overkill, and I haven’t overly tested it since `raco pkg
install` uses https as its transport anyway
2. If anyone is better clued up on HTTP `CONNECT` best practice, then
please check the headers that I pass (in `http-client.rkt`)
3. Is HTTP `CONNECT` the only/best way to proxy HTTPS? It is what *curl*
uses (which might be a good indicator)
4. Will the ports be closed properly? (does anyone see a fid leak?)
- how do I test for that? Open (and allegedly close) 1024 tunnels?
5. The `abandon-p` definitions in `http-conn-CONNECT-tunnel` could
probably be reduced, but they’re defined as they are to allow me to
put debugging hooks in
6. No tests or documentation yet
7. I edited this with *vim*, and therefore the indentation is a la vim.
I looked at doing a global reindent (of git-checkout) and so much
changed that I abandoned that as an idea. It indentation is too
“off-style” then feel free to change it :-)
**git-checkout.rkt:**
- `initial-connect` now tries to use a git proxy (see `url.rkt`, below)
when *transport*=`git`
- (if *transport*=`https`, then `url.rkt`’s standard proxying will be
used)
**http-client.rkt:**
- `http-conn-open!` can now be passed a
`(list/c base-ssl?/c input-port? output-port? (-> port? void?))` to
describe:
- maybe a negotiated ssl context
- two tunnel (or other arbitrary) ports to use instead of newly
`...-connect`ed ports
- an abandon function for those ports
- `http-conn-send!` has a function `print-to` which curries
`(fprintf to)`, but allows a hook for an `eprintf` for debugging
- **added `http-conn-CONNECT-tunnel`:** this opens an new `http-conn`
and arranges for CONNECT tunneling to `target-host` and `target-port`
- factored contracts into `base-ssl?/c` and `base-ssl?-tnl/c`
- added contract for `http-conn-CONNECT-tunnel`
**url.rkt:**
- `proxiable-url-schemes`: now includes `https` and `git`
- `env->c-p-s-entries`: the environment variable “parser” now takes a
rest-list of lists of environment variables, and the scheme that these
variables proxy is garnered from the variables’ names. As before
there are:
- `plt_http_proxy` and `http_proxy`
- `plt_https_proxy` and `https_proxy`
- `plt_git_proxy` and `git_proxy`
during the previous iteration of obtaining the proxy variables at
startup, we discussed the appropriate naming conventions for these
variables. This doesn’t seem to deviate from that
- `env->c-p-s-entries`: having a proxy url that isn’t strictly:
`http://hostname:portno` (e.g. having a training slash) generates a
log warning, not an error. It was beginning to bug me
- `proxy-servers-guard`: accepts any one of the `proxiable-url-schemes`
(not just `http`)
- no proxy is agnostic to the URL scheme
- `proxy-tunneled?`: returns false for `http`, which is proxied using an
HTTP proxy. Returns true for other URL schemes -- which go through a
tunnel
- **`make-ports`:** tests whether a tunnel proxy is necessary. If so, it
creates a tunnel and plumbs the connections
- elsewhere, anywhere that tests for proxy, now tests for
`(and proxy (not proxy-tunneled? url))`, because tunneled HTTPS
connections are direct (once they’re through the tunnel, IYSWIM)
Make the optimizer recognize and track `make-struct-property-type`
values, and use that information to recognize `make-struct-type`
calls that will defnitely succeed because a property that hs no
guard is given a value in the list of properties.
Combined with the change to require-keyword expansion, this
change allows the optimizer to inline `f` in
(define (g y)
(f #:x y))
(define (f #:x x)
(list x))
because the `make-struct-type` that appears between `g` and `f`
is determined to have no side-effect that would prevent `f` from
having its expected value.
Make the definition of a function with a required keyword expand in a
way that allows the optimizer to recognize it as a form that has no
errors or externally visible side effects.
The old expansion of
(define (f #:x x) ...)
included
(define lifted-constructor (make-required ....))
(define f (lifted-constructor (lambda ....) ....))
where `make-required` calls `make-struct-type` and returns just the
constructor.
The new expansion instead has
(define-values (_ lifted-constructor _ _ _)
(make-struct-type ....))
(define f (lifted-constructor (lambda ....) ....))
In other words, `make-required` is inlined by macro expansion,
so that the optimizer will be able to see it and eventually
conclude that no side effects have taken place.
When a module defines and exports an identifier at two phases,
and when another module imports both of them at the same phase,
an error was not reported as it should have been.
With this option, FFI calls always block until scheme_check_foreign_work
is called by the program embedding Racket.
This is needed for embedding Racket into contexts where you do not
control the event loop, need Racket to make FFI calls, and those FFI
calls must occur on a thread within the event loop. A good example of
this is with OpenGL FFI calls that require the current thread to hold
the OpenGL/EGL context.
An important point about this is that scheme_check_foreign_work will
only execute a single FFI call. So if this is used for OpenGL rendering,
you'll want to run it a lot.
Some expressions are omittable only when the arguments have certain types.
In this case the application is marked with APPN_FLAG_OMITTABLE instead of relaying on the flags of the primitive.
The optimizer can't use this flag to move the expression inside a lamba or across a potential continuation capture, unlike other omittable expressions. They can be moved
only in more restricted conditions.
For example, in this program
#lang racket/base
(define n 10000)
(define m 10000)
(time
(define xs (build-list n (lambda (x) 0)))
(length xs)
(define ws (list->vector xs)) ; <-- omittable
(for ([i (in-range m)])
(vector-ref ws 0))) ; <-- ws is used once
If the optimizer moves the expression in the definition of ws inside the recursive
lambda that is created by the for, then the code is equivalent to:
#lang racket/base
(define n 10000)
(define m 10000)
(time
(define xs (build-list n (lambda (x) 0)))
(length xs)
(for ([i (in-range m)])
(vector-ref (list->vector xs) 0))) ; <-- moved here
And the new code is O(n*m) instead of O(n+m). This example is a minimized version
of the function kde from the plot package, where n=m and the bug changed the run
time from linear to quadratic.