[R6RS] safe and unsafe; declarations

dyb at cs.indiana.edu dyb at cs.indiana.edu
Sat Mar 4 09:27:26 EST 2006


To bring the scoping rules for declarations in line with lexical scoping
and referential transparency, and also to limit unnecessary freedom to
ignore safe declarations in some cases where unsafe and safe declarations
are mixed, I would like propose the following treatement of declarations.

 * A declaration appearing in a <body> associates the specified safe,
   fast, small, and debug priorities with each free identifier within the
   <body>, except where the declaration is shadowed by a conflicting
   declaration.

 * If the declare keyword of a declaration is introduced into the output
   of a macro, the declaration affects only the identifiers introduced by
   the macro.  Similarly, an identifier introduced by the macro is
   affected only by such declarations and declarations whose scope
   includes the macro definition.

 * A standard syntactic form (recognized as such because its identifying
   keyword is an identifier reference resolving to a keyword imported from
   the r6rs core language or standard library) whose identifying keyword
   has safe priority 0 has unspecified behavior if a situation for which
   the implementation is allowed or required to raise an exception arises
   directly from the use of the syntactic form.

 * A standard procedure reference (recognized as such because it is an
   identifier reference resolving to a variable binding imported from the
   r6rs core language or standard library) may evaluate to different
   versions of the standard procedure depending on the priorities
   associated with the reference.  In particular, if the standard
   procedure reference is associated with safe priority 0, the standard
   procedure reference may evaluate to an unsafe version of the standard
   procedure.  An unsafe version has unspecified behavior if a situation
   for which the implementation is allowed or required to raise an
   exception arises directly from the use of that standard procedure.

 * Assuming unbound variables are possible in the standard, the behavior
   of an unbound reference to a variable associated with safe priority 0
   is unspecified.

This proposal differs from Will's proposal, as I understand it, in the
following ways:

1. The scope of a declaration is more precisely specified (and possibly
   different from what Will had in mind).

2. It requires that safe code embedded within or run from unsafe code
   must raise all required exceptions, as long as no potentially
   exception-causing situation arises directly from the use of the unsafe
   code.

Also, the notion that exceptions are always raised but are handled in an
unspecified manner in unsafe mode, which Mike, at least, found confusing,
is gone, but I believe that's an expository detail only.

The following illustrates difference 2.  Let's assume that all of the free
identifiers in the code below resolve to the corresponding standard
keyword or procedure bindings.  Let's also assume that the standard
procedure to which + is bound is required to raise a &nonnumeric exception
if any of its arguments are not numbers and that it returns normally
without raising an exception in all other cases.  Then the expression:

  (begin
    (declare unsafe)
    (let ([x (+ 3)])
      (declare safe)
      (+ 'b)))

has unspecified behavior with Will's proposal, even though the outer call
to (possibly unsafe) + returns normally, because the inner safe
declaration can be ignored.  It must raise a &nonnumeric exception with
mine.  Similarly,

  (begin
    (declare unsafe)
    (+ 'a (begin (declare safe) (+ 'b))))

has unspecified behavior with Will's proposal but must raise a &nonnumeric
exception with mine.  The first argument to the outer (possibly unsafe) +
is not a number, but the exception is raised by the inner call to + before
the outer call is made, due to applicative-order evaluation.

In both systems,

  (begin
    (declare unsafe)
    (let ([x (+ 'a)])
      (declare safe)
      (+ 'b)))

has unspecified behavior, because the first (possibly unsafe) + receives a
non-numeric argument, at which point all bets are off.

Note that an implementation is free to ignore all declarations with either
proposal, but with mine it is effectively required not to ignore a safe
declaration that appears within the scope of an unsafe declaration if it
does not also ignore the unsafe declaration.

A likely side implication of my proposal is that eqv? might return either
#t or #f when applied to two versions of a primitive that might differ due
to differing declarations.  For example,

  (eqv? (begin (declare safe) +)
        (begin (declare unsafe) +))

can return either #t or #f.  This doesn't bother me in the slightest,
but we could require that eqv? return #t in situations such as this.
In practice, this would likely cause most implementations to use the
same (default) version of the standard procedure whenever the value
might escape.

If we agree to this proposal, then I also intend to suggest that an
identifier-priority procedure be included in the standard syntax-case
system.  This procedure can be used to extract a priority associated with
an identifier, e.g.:

  (define-syntax frobitate
    (lambda (x)
      (syntax-case x ()
        [(k (x ...) e)
         (if (= (identifier-priority 'safe #'k) 0)
             #'<unsafe code>
             #'<safe code>)])))

This will allow user-defined syntactic forms to be sensitive to declared
priorities.  In conjunction with identifier macros, it will also allow a
similar effect for user-defined procedures.

Kent



More information about the R6RS mailing list