move internal undefined to unsafe-undefined

This commit is contained in:
Matthew Flatt 2014-04-07 11:10:03 -06:00
parent f8813474d4
commit 574b8a5d3b
24 changed files with 1796 additions and 1212 deletions

View File

@ -4496,7 +4496,6 @@
(if (null? v)
(send bug-icon show #f)
(send bug-icon show #t)))
(set-bug-label (preferences:get 'drracket:saved-bug-reports))
(define remove-bug-icon-callback
(preferences:add-callback
'drracket:saved-bug-reports
@ -4608,6 +4607,7 @@
(λ (l)
(cons btn (remq* (list btn) l))))
btn))
(set-bug-label (preferences:get 'drracket:saved-bug-reports))
(set! func-defs-canvas (new func-defs-canvas%
(parent name-panel)

View File

@ -1,6 +1,5 @@
#lang scribble/doc
@(require "mz.rkt"
(for-label racket/undefined))
@(require "mz.rkt")
@title[#:style 'toc #:tag "data"]{Datatypes}
@ -170,33 +169,5 @@ The @|void-const| value is always @racket[eq?] to itself.
@defproc[(void [v any/c] ...) void?]{Returns the constant @|void-const|. Each
@racket[v] argument is ignored.}
@; ----------------------------------------------------------------------
@section[#:tag "undefined"]{Undefined}
@note-lib[racket/undefined]
The constant @racket[undefined] is conceptually used as a placeholder
value for a binding, so that a reference to a binding before its
definition can be detected. Such references are normally protected
implicitly via @racket[check-not-undefined], so that an expression does
not normally produce an @racket[undefined] value.
The @racket[undefined] value is always @racket[eq?] to itself.
@history[#:added "6.0.0.6"]
@defproc[(undefined? [v any/c]) boolean?]{Returns @racket[#t] if @racket[v] is the
constant @racket[undefined], @racket[#f] otherwise.}
@defthing[undefined undefined?]{The ``undefined'' constant.}
@defproc[(check-not-undefined [v any/c] [sym symbol?]) (and/c any/c (not/c undefined?))]{
Checks whether @racket[v] is @racket[undefined], and raises
@racket[exn:fail:contract:variable] in that case with an error message
along the lines of ``@racket[sym]: variable used before its definition.''
If @racket[v] is not @racket[undefined], then @racket[v] is returned.
}
@include-section["undefined.scrbl"]

View File

@ -0,0 +1,23 @@
#lang scribble/doc
@(require "mz.rkt"
(for-label racket/undefined))
@title[#:tag "undefined"]{Undefined}
@note-lib[racket/undefined]
The constant @racket[undefined] can be used as a placeholder value for
a value to be installed later, especially for cases where premature
access of the value is either difficult or impossible to detect or
prevent.
The @racket[undefined] value is always @racket[eq?] to itself.
@history[#:added "6.0.0.6"]
@defproc[(undefined? [v any/c]) boolean?]{
Returns @racket[#t] if @racket[v] is the constant @racket[undefined],
@racket[#f] otherwise.}
@defthing[undefined undefined?]{The ``undefined'' constant.}

View File

@ -0,0 +1,55 @@
#lang scribble/doc
@(require "mz.rkt"
(for-label racket/unsafe/undefined))
@title[#:tag "unsafe-undefined"]{Unsafe Undefined}
@note-lib[racket/unsafe/undefined]
The constant @racket[unsafe-undefined] is used internally as a
placeholder value. For example, it is used by @racket[letrec] as a
value for a variable that has not yet been assigned a value. Unlike
the @racket[undefined] value exported by @racket[racket/undefined],
however, the @racket[unsafe-undefined] value should not leak as the
result of a safe expression. Expression results that potentially
produce @racket[unsafe-undefined] can be guarded by
@racket[check-not-unsafe-undefined], so that an exception can be
raised instead of producing an @racket[undefined] value.
The @racket[unsafe-undefined] value is always @racket[eq?] to itself.
@history[#:added "6.0.0.6"]
@defproc[(unsafe-undefined? [v any/c]) boolean?]{
Returns @racket[#t] if @racket[v] is the constant
@racket[unsafe-undefined], @racket[#f] otherwise.}
@defthing[unsafe-undefined unsafe-undefined?]{
The unsafe ``undefined'' constant.}
@defproc[(check-not-unsafe-undefined [v any/c] [sym symbol?])
(and/c any/c (not/c unsafe-undefined?))]{
Checks whether @racket[v] is @racket[unsafe-undefined], and raises
@racket[exn:fail:contract:variable] in that case with an error message
along the lines of ``@racket[sym]: variable used before its
definition.'' If @racket[v] is not @racket[unsafe-undefined], then
@racket[v] is returned.}
@defthing[prop:chaperone-unsafe-undefined struct-type-property?]{
A @tech{structure type property} that causes a structure type's
constructor to produce a @tech{chaperone} of an instance where every
access of a field in the structure is checked to prevent returning
@racket[unsafe-undefined].
The property value should be a list of symbols used as field names,
but the list should be in reverse order of the structure's fields.
When a field access would otherwise produce @racket[unsafe-undefined],
the @racket[exn:fail:contract:variable] exception is raised if a field
name is provided by the structure property's value, otherwise the
@racket[exn:fail:contract] exception is raised.}

View File

@ -418,3 +418,7 @@ Unchecked versions of @racket[extflvector-length], @racket[extflvector-ref], and
@racket[extflvector-set!]. A @tech{extflvector}'s size can never be larger than a
@tech{fixnum} (so even @racket[extflvector-length] always returns a
fixnum).}
@; ------------------------------------------------------------------------
@include-section["unsafe-undefined.scrbl"]

View File

@ -1922,6 +1922,122 @@
(test 5 'send-generic (send-generic (new c%) (generic c% m) 5))
(test 6 'send-generic-interface (send-generic (new c/i%) (generic i<%> m) 6))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; check error reporting for variable use before assignment
(let ()
(define c%
(class object%
(define x 7)
(field [h 10])
(super-new)))
(define d%
(class c%
(define z z)
(super-new)))
(define e%
(class d%
(define q 1)
(field [s 1])
(super-new)))
(define d2%
(class c%
(define z 1)
(field [f f])
(super-new)))
(define e2%
(class d2%
(define q 1)
(field [s 1])
(super-new)))
(err/rt-test (new d%) (lambda (exn)
(and (exn:fail:contract:variable? exn)
(eq? 'z (exn:fail:contract:variable-id exn)))))
(err/rt-test (new e%) (lambda (exn)
(and (exn:fail:contract:variable? exn)
(eq? 'z (exn:fail:contract:variable-id exn)))))
(err/rt-test (new d2%) (lambda (exn)
(and (exn:fail:contract:variable? exn)
(eq? 'f (exn:fail:contract:variable-id exn)))))
(err/rt-test (new e2%) (lambda (exn)
(and (exn:fail:contract:variable? exn)
(eq? 'f (exn:fail:contract:variable-id exn))))))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; check optimization to omit use-before-definition chaperone:
;; Relies on expansion where the next-to-last argument of `compose-class`
;; is #t when the chaperone is needed.
(let ()
(define (check-opt e opt?)
(test #t
`(,opt? ,e)
(let ([e (expand e)])
(let loop ([e e])
(cond
[(and (pair? e)
(pair? (cdr e))
(eq? (syntax-e (car e)) 'compose-class))
(eq? opt? (syntax-case (list-ref e (- (length e) 2)) (quote)
[(quote #f) #t]
[(quote #t) #f]
[_ 'unknown]))]
[(syntax? e)
(loop (syntax-e e))]
[(pair? e)
(and (loop (car e)) (loop (cdr e)))]
[else #t])))))
(check-opt '(class object%) #t)
(check-opt '(class object% (super-new)) #t)
(check-opt '(class object% (define x 1)) #t)
(check-opt '(class object% (define x 1) (super-new)) #t)
(check-opt '(class object% (field [x 1]) (super-new)) #t)
(check-opt '(class object% (init-field [x 1]) (super-new)) #t)
(check-opt '(class object% (define x (+ 1 2))) #t)
(check-opt '(class object% (define x (free-var 1))) #t)
(check-opt '(class object% (define x x)) #f)
(check-opt '(class object% (field [x x])) #f)
(check-opt '(class object% (init-field [x x])) #f)
(check-opt '(class object% (super-new) (define x 1)) #f)
(check-opt '(class object% (super-make-object) (define x 1)) #f)
(check-opt '(class object% (super-instantiate ()) (define x 1)) #f)
(check-opt '(class object% (displayln this) (define x 1)) #f)
(check-opt '(class object% (inherit m) (m) (define x 1)) #f)
(check-opt '(class object% (define/override (m) x) (super m) (define x 1)) #f)
(check-opt '(class object% (define x 1) (define y 1) (super-new)) #t)
(check-opt '(class object% (define x 1) (define y x) (super-new)) #t)
(check-opt '(class object% (field [x 1] [y x]) (super-new)) #t)
(check-opt '(class object% (field [x 1] [y x]) (super-make-object)) #t)
(check-opt '(class object% (init-field [x 1] [y x]) (super-new)) #t)
(check-opt '(class object% (init-field [x 1]) (define y x) (super-new)) #t)
(check-opt '(class object% (define x 1) (define y (list x)) (super-new)) #t)
(check-opt '(class object% (define/public (m) x) (define x 1) (super-new)) #t)
(check-opt '(class object% (define/override (m) x) (define x 1) (super-new)) #t)
(check-opt '(class object% (define x y) (define y 1) (super-new)) #f)
(check-opt '(class object% (init-field [x y] [y 1])) #f)
(check-opt '(class object% (inherit-field f) (super-new) (displayln f)) #t)
(check-opt '(class object% (inherit-field f) (displayln f) (super-new)) #f)
(check-opt '(class object% (inherit-field f) (set! f 10) (super-new)) #t)
;; Ok to use after explicit assignment that's before the decl:
(check-opt '(class object% (set! y 7) (define x y) (define y 1) (super-new)) #t)
;; But not in a branch
(check-opt '(class object% (when ? (set! y 7)) (define x y) (define y 1) (super-new)) #f)
(void))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(report-errs)

View File

@ -236,6 +236,7 @@
field-pub-width
field-ht
(class-field-ids cls)
(class-all-field-ids cls)
'struct:object 'object? 'make-object
'field-ref 'field-set!
@ -247,6 +248,8 @@
(class-orig-cls cls)
#f #f ; serializer is never set
(class-check-undef? cls)
#f)]
[obj-name (if name
(string->symbol (format "object:~a" name))
@ -500,6 +503,7 @@
field-pub-width
field-ht
(class-field-ids cls)
(class-all-field-ids cls)
'struct:object 'object? 'make-object
'field-ref 'field-set!
@ -511,6 +515,9 @@
(class-orig-cls cls)
#f #f ; serializer is never set
(class-check-undef? cls)
#f)]
[obj-name (if name
(string->symbol (format "object:~a" name))
@ -1249,6 +1256,7 @@
field-pub-width
field-ht
(class-field-ids cls)
(class-all-field-ids cls)
'struct:object 'object? 'make-object
'field-ref 'field-set!
@ -1259,6 +1267,8 @@
(class-orig-cls cls)
#f #f ; serializer is never set
(class-check-undef? cls)
#f)]
[obj-name (if name
(string->symbol (format "wrapper-object:~a" name))

View File

@ -9,7 +9,8 @@
(only-in "../contract/region.rkt" current-contract-region)
"../contract/base.rkt"
"../contract/combinator.rkt"
racket/undefined
racket/unsafe/undefined
"class-undef.rkt"
(for-syntax racket/stxparam
syntax/kerncase
syntax/stx
@ -1220,7 +1221,6 @@
(syntax-track-origin
(syntax/loc e
(begin
1 ; to ensure a non-empty body
(set! id (extract-arg 'class-name `idpos init-args defval))
...))
e
@ -1232,7 +1232,6 @@
(map normalize-init/field (syntax->list #'(idp ...)))])
(syntax-track-origin
(syntax/loc e (begin
1 ; to ensure a non-empty body
(set! iid expr)
...))
e
@ -1247,7 +1246,8 @@
#'id/rename
(stx-car #'id/rename))])
(syntax-track-origin
(syntax/loc e (set! id (extract-rest-args n init-args)))
(syntax/loc e
(set! id (extract-rest-args n init-args)))
e
#'-i-r))]
[(-i-r)
@ -1348,14 +1348,16 @@
public-final-name ...
pubment-name ...)
(values
(make-field-map (quote-syntax the-finder)
(make-field-map #t
(quote-syntax the-finder)
(quote the-obj)
(quote-syntax inherit-field-name)
(quote-syntax inherit-field-name-localized)
(quote-syntax inherit-field-accessor)
(quote-syntax inherit-field-mutator))
...
(make-field-map (quote-syntax the-finder)
(make-field-map #f
(quote-syntax the-finder)
(quote the-obj)
(quote-syntax local-field)
(quote-syntax local-field-localized)
@ -1486,18 +1488,22 @@
[inspector (if (pair? inspect-decls)
(stx-car (stx-cdr (car inspect-decls)))
#'(current-inspector))]
[deserialize-id-expr deserialize-id-expr])
[deserialize-id-expr deserialize-id-expr]
[private-field-names private-field-names])
(add-decl-props
(quasisyntax/loc stx
(let ([superclass super-expression]
[interfaces (list interface-expression ...)])
(compose-class
'name superclass interfaces inspector deserialize-id-expr #,any-localized?
(detect-field-unsafe-undefined
compose-class
'name
super-expression
(list interface-expression ...)
inspector deserialize-id-expr #,any-localized?
;; Field count:
num-fields
;; Field names:
`field-names
`inherit-field-names
`private-field-names ; for undefined-checking property
;; Method names:
`(rename-super-name ... rename-super-extra-name ...)
`(rename-inner-name ... rename-inner-extra-name ...)
@ -1620,42 +1626,54 @@
(syntax-case stx ()
[(_ (arg (... ...)) (kw kwarg) (... ...))
(with-syntax ([stx stx])
(syntax (-instantiate super-go stx #f (the-obj si_c si_inited?
(syntax
(begin
`(declare-super-new)
(-instantiate super-go stx #f (the-obj si_c si_inited?
si_leftovers)
(list arg (... ...))
(kw kwarg) (... ...))))]))]
(kw kwarg) (... ...)))))]))]
[super-new-param
(lambda (stx)
(syntax-case stx ()
[(_ (kw kwarg) (... ...))
(with-syntax ([stx stx])
(syntax (-instantiate super-go stx #f (the-obj si_c si_inited?
(syntax
(begin
`(declare-super-new)
(-instantiate super-go stx #f (the-obj si_c si_inited?
si_leftovers)
null
(kw kwarg) (... ...))))]))]
(kw kwarg) (... ...)))))]))]
[super-make-object-param
(lambda (stx)
(let ([code
(quote-syntax
(lambda args
(super-go the-obj si_c si_inited? si_leftovers args null)))])
(if (identifier? stx)
#`(begin
`(declare-super-new)
#,(if (identifier? stx)
code
(datum->syntax
code
(cons code
(cdr (syntax-e stx)))))))])
(cdr (syntax-e stx))))))))])
(letrec-syntaxes+values
([(plain-init-name) (make-init-redirect
(quote-syntax set!)
(quote-syntax #%plain-app)
(quote-syntax local-plain-init-name)
(quote-syntax plain-init-name-localized))] ...)
([(local-plain-init-name) undefined] ...)
([(local-plain-init-name) unsafe-undefined] ...)
(void) ; in case the body is empty
. exprs)))))))))))))
(begin
'(declare-field-use-start) ; see "class-undef.rkt"
. exprs))))))))))))))
;; Extra argument added here by `detect-field-unsafe-undefined`
#; check-undef?
;; Not primitive:
#f)))))))))))))))))
#f))))))))))))))))
;; The class* and class entry points:
(values
@ -1746,7 +1764,6 @@
()
defn-or-expr ...))]))
(define-syntaxes (private* public* pubment* override* overment* augride* augment*
public-final* override-final* augment-final*)
(let ([mk
@ -1969,6 +1986,7 @@
field-pub-width ; total number of public fields
field-ht ; maps public field names to field-infos (see make-field-info above)
field-ids ; list of public field names
all-field-ids ; list of field names in reverse order, used for `undefined` error reporting
[struct:object ; structure type for instances
#:mutable]
@ -2001,6 +2019,8 @@
[fixup ; for deserialization
#:mutable]
check-undef? ; objects need an unsafe-undefined guarding chaperone?
no-super-init?); #t => no super-init needed
#:inspector insp)
@ -2044,6 +2064,7 @@ last few projections.
num-fields ; total fields (public & private)
public-field-names ; list of symbols (shorter than num-fields)
inherit-field-names ; list of symbols (not included in num-fields)
private-field-names ; list of symbols (the rest of num-fields)
rename-super-names ; list of symbols
rename-inner-names
@ -2063,6 +2084,9 @@ last few projections.
init-mode ; 'normal, 'stop, or 'list
make-methods ; takes field and method accessors
check-undef?
make-struct:prim) ; see "primitive classes", below
(define (make-method proc meth-name)
(procedure-rename
@ -2288,11 +2312,15 @@ last few projections.
methods super-methods int-methods beta-methods meth-flags
inner-projs dynamic-idxs dynamic-projs
field-width field-pub-width field-ht field-names
(append (reverse private-field-names)
(reverse public-field-names)
(class-all-field-ids super))
'struct:object 'object? 'make-object 'field-ref 'field-set!
init-args
init-mode
'init
#f #f #f ; serializer is set later
(or check-undef? (class-check-undef? super))
(and make-struct:prim #t))]
[obj-name (if name
(string->symbol (format "object:~a" name))
@ -2347,10 +2375,14 @@ last few projections.
(add-properties (class-struct:object super) interfaces)
0 ;; No init fields
;; Fields for new slots:
num-fields undefined
num-fields unsafe-undefined
;; Map object property to class:
(append
(list (cons prop:object c))
(if (class-check-undef? c)
(list (cons prop:chaperone-unsafe-undefined
(class-all-field-ids c)))
null)
(if deserialize-id
(list
(cons prop:serializable
@ -3078,7 +3110,7 @@ An example
(vector) (vector) (vector)
0 0 (make-hasheq) null
0 0 (make-hasheq) null null
'struct:object object? 'make-object
'field-ref-not-needed 'field-set!-not-needed
@ -3095,6 +3127,8 @@ An example
(lambda (obj) #(())) ; serialize
(lambda (obj args) (void)) ; deserialize-fixup
#f ; no chaperone to guard against unsafe-undefined
#t)) ; no super-init
(vector-set! (class-supers object%) 0 object%)
@ -3242,6 +3276,7 @@ An example
field-pub-width
field-ht
(class-field-ids cls)
(class-all-field-ids cls)
'struct:object 'object? 'make-object
'field-ref 'field-set!
@ -3252,6 +3287,8 @@ An example
(class-orig-cls cls)
#f #f ; serializer is never set
(class-check-undef? cls)
#f)]
[obj-name (if name
(string->symbol (format "wrapper-object:~a" name))
@ -3265,7 +3302,7 @@ An example
(class-struct:object cls)
0 ;; No init fields
0 ;; No new fields in this class replacement
undefined
unsafe-undefined
;; Map object property to class:
(list (cons prop:object c)))])
(set-class-struct:object! c struct:object)
@ -4261,7 +4298,7 @@ An example
#f
#f
0 null null ; no fields
0 null null null ; no fields
null ; no rename-supers
null ; no rename-inners
@ -4290,6 +4327,8 @@ An example
(extract-primitive-args this name init-arg-names init-args)
init-args)))))
#f
make-struct:prim))
(define (extract-primitive-args this class-name init-arg-names init-args)

View File

@ -0,0 +1,201 @@
#lang racket/base
(require (for-syntax racket/base
syntax/private/boundmap
syntax/kerncase))
(provide declare-field-use-start
declare-field-assignment
declare-field-use
declare-inherit-use
declare-this-escapes
declare-super-new
detect-field-unsafe-undefined)
;; The `class` macros inject declarations into expansions
;; of the form `(begin (_declare-word _id ...) _expr ...)`
;; for each of the following `_declare-word`s:
(define-syntax declare-field-use-start #f) ; marks start of initialization
(define-syntax declare-field-assignment #f)
(define-syntax declare-field-use #f)
(define-syntax declare-inherit-use #f)
(define-syntax declare-this-escapes #f)
(define-syntax declare-super-new #f)
;; A wrapper macro that runs the `need-undeed-check?` analysis
;; and adds a boolean argument to a call to `compose-class`:
(define-syntax (detect-field-unsafe-undefined stx)
(syntax-case stx ()
[(_ compose-class arg ... proc final)
(let-values ([(exp exp-proc) (syntax-local-expand-expression #'proc)])
(with-syntax ([exp-proc exp-proc]
[need-undef? (need-undefined-check? exp)])
(syntax/loc stx
(compose-class arg ... proc need-undef? final))))]))
;; Analysis to detect whether any field can be referenced while
;; its value is `unsafe-undefined`, based on `declare-...` annotations
;; inserted by macros.
(define-for-syntax (need-undefined-check? exp)
;; All local fields need to be initialized (i.e., assigned)
;; before a method call or `super-new`
(define init-too-late? #f)
;; It's ok to use inherited fields only after `super-new` has
;; definitely been called:
(define super-new? #f)
;; cloop returns #t if access-before-definition looks possible:
(let cloop ([exp exp]
[ready #f] ; table of initializations, after start
[in-branch? #f])
(define (loop e) (cloop e ready in-branch?))
(kernel-syntax-case exp #f
[_
(identifier? exp)
#f]
;; ----------------------------------------
;; Handle annotations at start of `begin`:
[(begin) #f]
[(begin '(decl) . body)
(and (identifier? #'decl)
(free-identifier=? #'decl #'declare-field-use-start))
;; Beginning of the class body; start tracking initialization
;; creating the `ready` table:
(cloop #`(begin . body) (make-module-identifier-mapping) #f)]
[(begin '(decl id ...) . body)
(and (identifier? #'decl)
(free-identifier=? #'decl #'declare-field-use))
;; A field is used. If tracking has started, make sure the
;; field is definitely initalized:
(or (and ready
(ormap (lambda (id)
(not (module-identifier-mapping-get ready id (lambda () #f))))
(syntax->list #'(id ...)))
(report #'body)
#t)
(loop #'(begin . body)))]
[(begin '(decl id ...) . body)
(and (identifier? #'decl)
(free-identifier=? #'decl #'declare-field-assignment))
;; A field is assigned. If this is after an action that
;; might read a field externally, it's too late. Otherwise,
;; assuming that we're not in a branch, the field is after here
;; assigned (but not before the right-hand side is evaluated):
(let ([ids (syntax->list #'(id ...))])
(or (and ready
init-too-late?
(ormap (lambda (id)
(not (module-identifier-mapping-get ready id (lambda () #f))))
ids)
(report #'body)
#t)
;; field is ready after RHS is evaluated:
(begin0
(loop #'(begin . body))
(when ready
(unless in-branch?
(for-each (lambda (id)
(module-identifier-mapping-put! ready id #t))
ids))))))]
[(begin '(decl id ...) . body)
(and (identifier? #'decl)
(free-identifier=? #'decl #'declare-inherit-use))
;; It's ok to use an inherited field only if `super-new` has
;; definitely been called.
(or (and ready
(not super-new?)
(report #'body)
#t)
(loop #'(begin . body)))]
[(begin '(decl) . body)
(and (identifier? #'decl)
(free-identifier=? #'decl #'declare-this-escapes))
;; Any method call or explicit use of `this` means a field
;; might be accessed outside of the `class` declaration,
;; so any initialization afterward is too late:
(begin
(when ready (set! init-too-late? #t))
(loop #'(begin . body)))]
[(begin '(decl) . body)
(and (identifier? #'decl)
(free-identifier=? #'decl #'declare-super-new))
;; As long as we're not in a branch, `super-new` is definitely
;; called after here.
(begin
(when (and ready (not in-branch?)) (set! super-new? #t))
(loop #'(begin '(declare-this-escapes) . body)))]
;; ----------------------------------------
;; Abstract interpretation of core forms.
;; We model order by calling `cloop` in order, which can mutate
;; `init-too-late?`, `super-new?` and `ready`. (Those could
;; have been threaded through, but local mutation is easier.)
;; We model both branches and delayed computation (via closures)
;; by recurring with a true `in-branch?`. In a branch, we
;; pessimistically ignore initialization (via local-field
;; assignment) and super-new`, and pessimistcally assume all
;; references, method calls, and inherite-field assignemtnt.
;; [Room for improvement: use a functional table in place of
;; `ready`, etc., and suitably split and merge.]
[(begin exp . body)
(or (loop #'exp)
(loop #'(begin . body)))]
[(#%plain-lambda _ exp ...)
(cloop #'(begin exp ...) ready #t)]
[(case-lambda clause ...)
(ormap (lambda (clause)
(cloop #`(#%plain-lambda . #,clause) ready #t))
(syntax->list #'(clause ...)))]
[(if tst thn els)
(or (loop #'tst)
(cloop #'thn ready #t)
(cloop #'els ready #t))]
[(begin0 exp ...)
(loop #'(begin exp ...))]
[(let-values ([(id ...) exp] ...) body-exp ...)
(loop #'(begin exp ... body-exp ...))]
[(letrec-values ([(id ...) exp] ...) body-exp ...)
(loop #'(begin exp ... body-exp ...))]
[(letrec-syntaxes+values _ ([(id ...) exp] ...) body-exp ...)
(loop #'(begin exp ... body-exp ...))]
[(set! id exp)
(loop #'exp)]
[(quote . _) #f]
[(quote-syntax . _) #f]
[(with-continuation-mark key val exp)
(loop #'(begin key val exp))]
[(#%plain-app exp ...)
(loop #'(begin exp ...))]
[(#%top . _) #f]
[(#%variable-reference . _) #f]
[(#%expression expr) (loop #'expr)]
[_else (raise-syntax-error #f "unrecognized expression form" exp)])))
(define-for-syntax (report exprs)
(when (pair? (syntax->list exprs))
(define expr (car (syntax->list exprs)))
(define s (srcloc->string (srcloc (syntax-source expr)
(syntax-line expr)
(syntax-column expr)
(syntax-position expr)
(syntax-span expr))))
(log-message (make-logger 'optimizer (current-logger))
'debug
(format "chaperoning to prevent undefined access due to: ~.s~a~a"
(syntax->datum expr)
(if s " at: " "")
(or s ""))
#f)))

View File

@ -3,8 +3,9 @@
(require syntax/stx
(for-syntax racket/base)
(for-template racket/base
racket/undefined
"class-wrapped.rkt"))
racket/unsafe/undefined
"class-wrapped.rkt"
"class-undef.rkt"))
(define insp (variable-reference->module-declaration-inspector
(#%variable-reference)))
@ -35,6 +36,18 @@
(define (binding from to stx)
stx)
;; Declarations used to determine whether a chaperone is
;; needed to protect against unsafe-undefined access
(define (add-declare-this-escapes src-stx stx)
(quasisyntax/loc src-stx (begin '(declare-this-escapes) #,stx)))
(define (add-declare-field-use id inherited? src-stx stx)
(if inherited?
(quasisyntax/loc src-stx (begin '(declare-inherit-use #,id) #,stx))
(quasisyntax/loc src-stx (begin '(declare-field-use #,id) #,stx))))
(define (add-declare-field-assignment id inherited? src-stx stx)
(if inherited?
stx
(quasisyntax/loc src-stx (begin '(declare-field-assignment #,id) #,stx))))
(define (make-this-map orig-id the-finder the-obj)
(let ([set!-stx (datum->syntax the-finder 'set!)])
@ -46,11 +59,13 @@
(free-identifier=? (syntax set!) set!-stx)
(raise-syntax-error 'class "cannot mutate object identifier" stx)]
[(id . args)
(add-declare-this-escapes
stx
(datum->syntax
stx
(cons (find the-finder the-obj stx) (syntax args))
stx)]
[id (find the-finder the-obj stx)])))))
stx))]
[id (add-declare-this-escapes stx (find the-finder the-obj stx))])))))
(define (make-this%-map replace-stx the-finder)
(let ([set!-stx (datum->syntax the-finder 'set!)])
@ -66,9 +81,10 @@
[(f . args)
(quasisyntax/loc stx (#,replace-stx . args))])))))
(define (make-field-map the-finder the-obj the-binder the-binder-localized
(define (make-field-map inherited? the-finder the-obj the-binder the-binder-localized
field-accessor field-mutator)
(let ([set!-stx (datum->syntax the-finder 'set!)])
(define (choose-src a b) (if (syntax-source a) a b))
(mk-set!-trans
the-binder-localized
(lambda (stx)
@ -77,20 +93,32 @@
(syntax-case stx ()
[(set! id expr)
(free-identifier=? (syntax set!) set!-stx)
(add-declare-field-assignment
#'id
inherited?
#'id
(with-syntax ([bindings (syntax/loc stx ([obj obj-expr] [id expr]))]
[set (quasisyntax/loc stx
((unsyntax field-mutator) obj id))])
(syntax/loc stx (let* bindings set)))]
(syntax/loc (choose-src stx #'id) (let* bindings set))))]
[(id . args)
(add-declare-field-use
#'id
inherited?
#'id
(with-syntax ([bindings (syntax/loc stx ([obj obj-expr]))]
[call (quasisyntax/loc stx
((check-not-undefined ((unsyntax field-accessor) obj) 'id) . args))])
(syntax/loc stx (let* bindings call)))]
(((unsyntax field-accessor) obj) . args))])
(syntax/loc (choose-src stx #'id) (let* bindings call))))]
[id
(add-declare-field-use
#'id
inherited?
stx
(with-syntax ([bindings (syntax/loc stx ([obj obj-expr]))]
[get (quasisyntax/loc stx
(check-not-undefined ((unsyntax field-accessor) obj) 'id))])
(syntax/loc stx (let* bindings get)))])))))))
((unsyntax field-accessor) obj))])
(syntax/loc (choose-src stx #'id) (let* bindings get))))])))))))
(define (make-method-map the-finder the-obj the-binder the-binder-localized method-accessor)
(let ([set!-stx (datum->syntax the-finder 'set!)])
@ -103,6 +131,8 @@
(free-identifier=? (syntax set!) set!-stx)
(raise-syntax-error 'class "cannot mutate method" stx)]
[(id . args)
(add-declare-this-escapes
stx
(binding
the-binder (syntax id)
(datum->syntax
@ -111,7 +141,7 @@
(list method-accessor (find the-finder the-obj stx))
(find the-finder the-obj stx)
(syntax args))
stx))]
stx)))]
[_else
(raise-syntax-error
'class
@ -131,12 +161,14 @@
(free-identifier=? (syntax set!) set!-stx)
(raise-syntax-error 'class "cannot mutate method" stx)]
[(id . args)
(add-declare-this-escapes
stx
(binding
the-binder (syntax id)
(datum->syntax
the-finder
(make-method-apply (find the-finder new-name stx) (find the-finder the-obj stx) (syntax args))
stx))]
stx)))]
[_else
(raise-syntax-error
'class
@ -154,12 +186,14 @@
(free-identifier=? (syntax set!) set!-stx)
(raise-syntax-error 'class "cannot mutate super method" stx)]
[(id . args)
(add-declare-this-escapes
stx
(binding
the-binder (syntax id)
(datum->syntax
the-finder
(make-method-apply (find the-finder rename-temp stx) (find the-finder the-obj stx) (syntax args))
stx))]
stx)))]
[_else
(raise-syntax-error
'class
@ -180,13 +214,15 @@
[(id (lambda () default) . args)
(free-identifier=? (syntax lambda) lambda-stx)
(let ([target (find the-finder the-obj stx)])
(add-declare-this-escapes
stx
(binding
the-binder (syntax id)
(datum->syntax
the-finder
(make-method-apply (list (find the-finder rename-temp stx) target #'default)
target (syntax args))
stx)))]
stx))))]
[(id (lambda largs default) . args)
(free-identifier=? (syntax lambda) lambda-stx)
(raise-syntax-error
@ -211,15 +247,19 @@
stx)]))))))
(define (generate-super-call stx the-finder the-obj rename-temp args)
(add-declare-this-escapes
stx
(class-syntax-protect
(datum->syntax
the-finder
(make-method-apply (find the-finder rename-temp stx)
(find the-finder the-obj stx)
args)
stx)))
stx))))
(define (generate-inner-call stx the-finder the-obj default-expr rename-temp args)
(add-declare-this-escapes
stx
(class-syntax-protect
(datum->syntax
the-finder
@ -231,7 +271,7 @@
,(make-method-apply 'i target args)
,default-expr))
stx))
stx)))
stx))))
(define (make-init-error-map localized-id)
(mk-set!-trans
@ -253,14 +293,22 @@
(with-syntax ([local-id local-id])
(syntax/loc stx (set! local-id expr)))]
[(id . args)
(with-syntax ([local-id local-id]
(with-syntax ([local-id (datum->syntax
local-id
(syntax-e local-id)
#'id
#'id)]
[#%app #%app-stx])
(syntax/loc stx (#%app local-id . args)))]
[_else (datum->syntax
(syntax/loc stx (#%app (#%app check-not-unsafe-undefined local-id 'id) . args)))]
[id (quasisyntax/loc stx
(#,#%app-stx
check-not-unsafe-undefined
#,(datum->syntax
local-id
(syntax-e local-id)
stx
stx)])))))
stx)
'id))])))))
(define super-error-map
(lambda (stx)

View File

@ -179,8 +179,7 @@
procedure->method procedure-rename
chaperone-procedure impersonate-procedure
assq assv assoc
prop:incomplete-arity prop:method-arity-error
check-not-undefined undefined)
prop:incomplete-arity prop:method-arity-error)
(all-from "reqprov.rkt")
(all-from-except "for.rkt"
define-in-vector-like

View File

@ -1,8 +1,10 @@
#lang racket/base
(require '#%kernel)
(provide check-not-undefined
undefined
(provide undefined
undefined?)
(define-values (struct:undef make-undef undef? undef-ref undef-set!)
(make-struct-type 'undefined #f 0 0))
(define undefined (make-undef))
(define (undefined? v) (eq? v undefined))

View File

@ -17,7 +17,7 @@
"private/unit-syntax.rkt"))
(require racket/block
racket/undefined
racket/unsafe/undefined
racket/contract/base
racket/contract/region
racket/stxparam
@ -967,7 +967,7 @@
(list (cons 'dept depr) ...)
(syntax-parameterize ([current-contract-region (lambda (stx) #'(quote (unit name)))])
(lambda ()
(let ([eloc (box undefined)] ... ...)
(let ([eloc (box unsafe-undefined)] ... ...)
(values
(lambda (import-table)
(let-values ([(iloc ...)
@ -1005,7 +1005,10 @@
(define-values (e-post-id ...)
(letrec-syntaxes+values (post-renames ...) ()
e-post-rhs)) ... ...)))))
(unit-export ((export-key ...) (vector-immutable (λ () (unbox eloc)) ...)) ...)))))))
(unit-export ((export-key ...)
(vector-immutable (λ () (check-not-unsafe-undefined (unbox eloc) 'int-evar))
...))
...)))))))
import-tagged-sigids
export-tagged-sigids
dep-tagged-sigids))))))
@ -1187,7 +1190,7 @@
(lambda (int/ext-name index ctc)
(bound-identifier-mapping-put! def-table
(car int/ext-name)
#`(check-not-undefined (vector-ref #,v #,index)
#`(check-not-unsafe-undefined (vector-ref #,v #,index)
'#,(car int/ext-name)))
(bound-identifier-mapping-put! ctc-table
(car int/ext-name)

View File

@ -3,7 +3,10 @@
'#%flfxnum
'#%extfl)
(provide (all-from-out '#%unsafe)
(provide (except-out (all-from-out '#%unsafe)
unsafe-undefined
check-not-unsafe-undefined
prop:chaperone-unsafe-undefined)
(prefix-out unsafe-
(combine-out flsin flcos fltan
flasin flacos flatan

View File

@ -0,0 +1,9 @@
#lang racket/base
(require '#%unsafe)
(provide check-not-unsafe-undefined
unsafe-undefined
unsafe-undefined?
prop:chaperone-unsafe-undefined)
(define (unsafe-undefined? v) (eq? v unsafe-undefined))

File diff suppressed because it is too large Load Diff

View File

@ -328,6 +328,7 @@ static void init_unsafe(Scheme_Env *env)
scheme_init_unsafe_numcomp(unsafe_env);
scheme_init_unsafe_list(unsafe_env);
scheme_init_unsafe_vector(unsafe_env);
scheme_init_unsafe_fun(unsafe_env);
scheme_init_extfl_unsafe_number(unsafe_env);
scheme_init_extfl_unsafe_numarith(unsafe_env);

View File

@ -93,6 +93,7 @@ READ_ONLY Scheme_Object *scheme_reduced_procedure_struct;
READ_ONLY Scheme_Object *scheme_tail_call_waiting;
READ_ONLY Scheme_Object *scheme_inferred_name_symbol;
READ_ONLY Scheme_Object *scheme_default_prompt_tag;
READ_ONLY Scheme_Object *scheme_chaperone_undefined_property;
/* READ ONLY SHARABLE GLOBALS */
@ -190,6 +191,7 @@ static Scheme_Object *current_prompt_read(int, Scheme_Object **);
static Scheme_Object *current_read(int, Scheme_Object **);
static Scheme_Object *current_get_read_input_port(int, Scheme_Object **);
static Scheme_Object *chaperone_not_undefined (int argc, Scheme_Object *argv[]);
static Scheme_Object *chaperone_wrap_cc_guard(Scheme_Object *obj, Scheme_Object *proc);
static Scheme_Object *do_cc_guard(Scheme_Object *v, Scheme_Object *cc_guard, Scheme_Object *chaperone);
@ -491,16 +493,6 @@ scheme_init_fun (Scheme_Env *env)
| SCHEME_PRIM_IS_OMITABLE);
scheme_add_global_constant("void?", o, env);
/* adds the new primitive check-undefined to the kernel langauge
check-undefined has an arity of 1 and no flags */
REGISTER_SO(scheme_check_not_undefined_proc);
o = scheme_make_prim_w_arity(scheme_check_not_undefined, "check-not-undefined", 2, 2);
scheme_check_not_undefined_proc = o;
SCHEME_PRIM_PROC_FLAGS(o) |= scheme_intern_prim_opt_flags(SCHEME_PRIM_IS_BINARY_INLINED);
scheme_add_global_constant("check-not-undefined", o, env);
scheme_add_global_constant("undefined", scheme_undefined, env);
#ifdef TIME_SYNTAX
scheme_add_global_constant("time-apply",
scheme_make_prim_w_arity2(time_apply,
@ -677,6 +669,25 @@ scheme_init_fun (Scheme_Env *env)
original_default_prompt->tag = scheme_default_prompt_tag;
}
void
scheme_init_unsafe_fun (Scheme_Env *env)
{
Scheme_Object *o, *a[1];
REGISTER_SO(scheme_check_not_undefined_proc);
o = scheme_make_prim_w_arity(scheme_check_not_undefined, "check-not-unsafe-undefined", 2, 2);
scheme_check_not_undefined_proc = o;
SCHEME_PRIM_PROC_FLAGS(o) |= scheme_intern_prim_opt_flags(SCHEME_PRIM_IS_BINARY_INLINED);
scheme_add_global_constant("check-not-unsafe-undefined", o, env);
scheme_add_global_constant("unsafe-undefined", scheme_undefined, env);
REGISTER_SO(scheme_chaperone_undefined_property);
o = scheme_make_struct_type_property(scheme_intern_symbol("chaperone-unsafe-undefined"));
scheme_chaperone_undefined_property = o;
scheme_add_global_constant("prop:chaperone-unsafe-undefined", o, env);
}
void
scheme_init_fun_places()
{
@ -2538,7 +2549,7 @@ Scheme_Object *
scheme_check_not_undefined (int argc, Scheme_Object *argv[])
{
if (!SCHEME_SYMBOLP(argv[1]))
scheme_wrong_contract("check-not-undefined", "symbol?", 1, argc, argv);
scheme_wrong_contract("check-not-unsafe-undefined", "symbol?", 1, argc, argv);
if (SAME_OBJ(argv[0], scheme_undefined)) {
scheme_raise_exn(MZEXN_FAIL_CONTRACT_VARIABLE,
@ -2550,6 +2561,11 @@ scheme_check_not_undefined (int argc, Scheme_Object *argv[])
return argv[0];
}
Scheme_Object *
chaperone_not_undefined (int argc, Scheme_Object *argv[])
{
return scheme_chaperone_not_undefined(argv[0]);
}
static Scheme_Object *
procedure_p (int argc, Scheme_Object *argv[])

View File

@ -3527,11 +3527,11 @@ int scheme_generate_inlined_binary(mz_jit_state *jitter, Scheme_App3_Rec *app, i
jit_movr_p(dest, JIT_R0);
return 1;
} else if (IS_NAMED_PRIM(rator, "check-not-undefined")) {
} else if (IS_NAMED_PRIM(rator, "check-not-unsafe-undefined")) {
if (SCHEME_SYMBOLP(app->rand2)) {
GC_CAN_IGNORE jit_insn *ref, *ref2;
LOG_IT(("inlined check-not-undefined\n"));
LOG_IT(("inlined check-not-unsafe-undefined\n"));
mz_runstack_skipped(jitter, 2);
scheme_generate_non_tail(app->rand1, jitter, 0, 1, 0); /* no sync... */

View File

@ -14,8 +14,8 @@
#define USE_COMPILED_STARTUP 1
#define EXPECTED_PRIM_COUNT 1118
#define EXPECTED_UNSAFE_COUNT 101
#define EXPECTED_PRIM_COUNT 1116
#define EXPECTED_UNSAFE_COUNT 104
#define EXPECTED_FLFXNUM_COUNT 69
#define EXPECTED_EXTFL_COUNT 45
#define EXPECTED_FUTURES_COUNT 15

View File

@ -308,6 +308,7 @@ void scheme_init_promise(Scheme_Env *env);
void scheme_init_struct(Scheme_Env *env);
void scheme_init_reduced_proc_struct(Scheme_Env *env);
void scheme_init_fun(Scheme_Env *env);
void scheme_init_unsafe_fun(Scheme_Env *env);
void scheme_init_compile(Scheme_Env *env);
void scheme_init_symbol(Scheme_Env *env);
void scheme_init_char(Scheme_Env *env);
@ -527,6 +528,8 @@ extern Scheme_Object *scheme_app_mark_impersonator_property;
extern Scheme_Object *scheme_no_arity_property;
extern Scheme_Object *scheme_chaperone_undefined_property;
extern Scheme_Object *scheme_reduced_procedure_struct;
/* recycle some constants that can't appear in code: */
@ -1003,6 +1006,8 @@ Scheme_Object *scheme_chaperone_hash_get(Scheme_Object *table, Scheme_Object *ke
Scheme_Object *scheme_chaperone_hash_traversal_get(Scheme_Object *table, Scheme_Object *key, Scheme_Object **alt_key);
void scheme_chaperone_hash_set(Scheme_Object *table, Scheme_Object *key, Scheme_Object *val);
Scheme_Object *scheme_chaperone_not_undefined(Scheme_Object *orig_val);
/*========================================================================*/
/* syntax objects */
/*========================================================================*/

View File

@ -13,12 +13,12 @@
consistently.)
*/
#define MZSCHEME_VERSION "6.0.1.2"
#define MZSCHEME_VERSION "6.0.1.3"
#define MZSCHEME_VERSION_X 6
#define MZSCHEME_VERSION_Y 0
#define MZSCHEME_VERSION_Z 1
#define MZSCHEME_VERSION_W 2
#define MZSCHEME_VERSION_W 3
#define MZSCHEME_VERSION_MAJOR ((MZSCHEME_VERSION_X * 100) + MZSCHEME_VERSION_Y)
#define MZSCHEME_VERSION_MINOR ((MZSCHEME_VERSION_Z * 1000) + MZSCHEME_VERSION_W)

View File

@ -2002,7 +2002,38 @@ static Scheme_Object *chaperone_struct_ref(const char *who, Scheme_Object *o, in
Scheme_Chaperone *px = (Scheme_Chaperone *)o;
Scheme_Object *a[2], *red, *orig;
if (!SCHEME_VECTORP(px->redirects)
if (SCHEME_VECTORP(px->redirects)
&& !(SCHEME_VEC_SIZE(px->redirects) & 1)
&& SAME_OBJ(SCHEME_VEC_ELS(px->redirects)[1], scheme_undefined)) {
/* chaperone on every field: check that result is not undefined */
o = px->prev;
if (!SCHEME_CHAPERONEP(o))
orig = ((Scheme_Structure *)o)->slots[i];
else
orig = chaperone_struct_ref(who, o, i);
if (SAME_OBJ(orig, scheme_undefined)) {
int len;
o = scheme_struct_type_property_ref(scheme_chaperone_undefined_property, px->val);
len = (o ? scheme_proper_list_length(o) : 0);
if (i < len) {
for (i = len - i; --i; ) {
o = SCHEME_CDR(o);
}
o = SCHEME_CAR(o);
scheme_raise_exn(MZEXN_FAIL_CONTRACT_VARIABLE,
o,
"%S: field used before its initialization",
o);
} else {
scheme_raise_exn(MZEXN_FAIL_CONTRACT,
"field used before its initialization");
}
}
return orig;
} else if (!SCHEME_VECTORP(px->redirects)
|| (SCHEME_VEC_SIZE(px->redirects) & 1)
|| SCHEME_FALSEP(SCHEME_VEC_ELS(px->redirects)[PRE_REDIRECTS + i])) {
o = px->prev;
@ -2065,7 +2096,8 @@ static void chaperone_struct_set(const char *who, Scheme_Object *o, int i, Schem
o = px->prev;
if (SCHEME_VECTORP(px->redirects)
&& !(SCHEME_VEC_SIZE(px->redirects) & 1)) {
&& !(SCHEME_VEC_SIZE(px->redirects) & 1)
&& !SAME_OBJ(SCHEME_VEC_ELS(px->redirects)[1], scheme_undefined)) {
half = (SCHEME_VEC_SIZE(px->redirects) - PRE_REDIRECTS) >> 1;
red = SCHEME_VEC_ELS(px->redirects)[PRE_REDIRECTS + half + i];
if (SCHEME_TRUEP(red)) {
@ -2102,7 +2134,8 @@ void scheme_struct_set(Scheme_Object *sv, int pos, Scheme_Object *v)
}
}
static Scheme_Object **apply_guards(Scheme_Struct_Type *stype, int argc, Scheme_Object **args)
static Scheme_Object **apply_guards(Scheme_Struct_Type *stype, int argc, Scheme_Object **args,
int *_chaperone_undefined)
{
Scheme_Object **guard_argv = NULL, *v, *prev_guards = NULL, *guard;
int p, gcount;
@ -2131,7 +2164,9 @@ static Scheme_Object **apply_guards(Scheme_Struct_Type *stype, int argc, Scheme_
guard = scheme_false;
}
if (!SCHEME_FALSEP(guard)) {
if (SAME_OBJ(guard, scheme_undefined))
*_chaperone_undefined = 1;
else if (!SCHEME_FALSEP(guard)) {
gcount = stype->parent_types[p]->num_islots;
guard_argv[argc] = guard_argv[gcount];
guard_argv[gcount] = stype->name;
@ -2173,6 +2208,7 @@ scheme_make_struct_instance(Scheme_Object *_stype, int argc, Scheme_Object **arg
Scheme_Structure *inst;
Scheme_Struct_Type *stype;
int p, i, j, nis, ns, c;
int chaperone_undefined = 0;
stype = (Scheme_Struct_Type *)_stype;
@ -2185,7 +2221,7 @@ scheme_make_struct_instance(Scheme_Object *_stype, int argc, Scheme_Object **arg
inst->stype = stype;
/* Apply guards, if any: */
args = apply_guards(stype, argc, args);
args = apply_guards(stype, argc, args, &chaperone_undefined);
/* Fill in fields: */
j = c;
@ -2213,6 +2249,9 @@ scheme_make_struct_instance(Scheme_Object *_stype, int argc, Scheme_Object **arg
}
}
if (chaperone_undefined)
return scheme_chaperone_not_undefined((Scheme_Object *)inst);
else
return (Scheme_Object *)inst;
}
@ -2609,7 +2648,7 @@ static Scheme_Object *struct_info_chaperone(Scheme_Object *o, Scheme_Object *si,
if (SCHEME_VECTORP(px->redirects)
&& !(SCHEME_VEC_SIZE(px->redirects) & 1)) {
proc = SCHEME_VEC_ELS(px->redirects)[1];
if (SCHEME_TRUEP(proc)) {
if (SCHEME_TRUEP(proc) && !SAME_OBJ(proc, scheme_undefined)) {
if (SCHEME_CHAPERONE_FLAGS(px) & SCHEME_CHAPERONE_IS_IMPERSONATOR)
proc = scheme_box(proc);
procs = scheme_make_pair(proc, procs);
@ -4289,7 +4328,7 @@ static Scheme_Object *_make_struct_type(Scheme_Object *base,
Scheme_Object *guard)
{
Scheme_Struct_Type *struct_type, *parent_type;
int j, depth, checked_proc = 0;
int j, depth, checked_proc = 0, chaperone_undefined = 0;
if (parent && SCHEME_NP_CHAPERONEP(parent))
parent_type = (Scheme_Struct_Type *)SCHEME_CHAPERONE_VAL(parent);
@ -4432,6 +4471,8 @@ static Scheme_Object *_make_struct_type(Scheme_Object *base,
if (SAME_OBJ(prop, scheme_checked_proc_property))
checked_proc = 1;
if (SAME_OBJ(prop, scheme_chaperone_undefined_property))
chaperone_undefined = 1;
propv = guard_property(prop, SCHEME_CDR(a), struct_type);
@ -4489,6 +4530,8 @@ static Scheme_Object *_make_struct_type(Scheme_Object *base,
if (SAME_OBJ(prop, scheme_checked_proc_property))
checked_proc = 1;
if (SAME_OBJ(prop, scheme_chaperone_undefined_property))
chaperone_undefined = 1;
propv = guard_property(prop, SCHEME_CDR(a), struct_type);
@ -4554,6 +4597,8 @@ static Scheme_Object *_make_struct_type(Scheme_Object *base,
}
struct_type->guard = guard;
} else if (chaperone_undefined) {
struct_type->guard = scheme_undefined;
}
if (parent && SCHEME_NP_CHAPERONEP(parent)) {
@ -5666,6 +5711,40 @@ static Scheme_Object *impersonate_struct(int argc, Scheme_Object **argv)
return do_chaperone_struct("impersonate-struct", 1, argc, argv);
}
Scheme_Object *scheme_chaperone_not_undefined (Scheme_Object *orig_val)
{
Scheme_Chaperone *px;
Scheme_Object *val, *redirects;
Scheme_Hash_Tree *props;
val = orig_val;
if (SCHEME_CHAPERONEP(val)) {
props = ((Scheme_Chaperone *)val)->props;
val = SCHEME_CHAPERONE_VAL(val);
} else
props = NULL;
redirects = scheme_make_vector(PRE_REDIRECTS, scheme_false);
SCHEME_VEC_ELS(redirects)[0] = scheme_false;
SCHEME_VEC_ELS(redirects)[1] = scheme_undefined; /* special handing in struct_ref */
px = MALLOC_ONE_TAGGED(Scheme_Chaperone);
if (SCHEME_PROCP(val))
px->iso.so.type = scheme_proc_chaperone_type;
else
px->iso.so.type = scheme_chaperone_type;
px->val = val;
px->prev = orig_val;
px->props = props;
px->redirects = redirects;
return (Scheme_Object *)px;
}
static Scheme_Object *do_chaperone_struct_type(const char *name, int is_impersonator, int argc, Scheme_Object **argv)
{
Scheme_Chaperone *px;

View File

@ -163,7 +163,7 @@ scheme_init_type ()
set_name(scheme_double_type, "<inexact-number>");
set_name(scheme_long_double_type, "<extflonum>");
set_name(scheme_float_type, "<inexact-number*>");
set_name(scheme_undefined_type, "<undefined>");
set_name(scheme_undefined_type, "<unsafe-undefined>");
set_name(scheme_eof_type, "<eof>");
set_name(scheme_input_port_type, "<input-port>");
set_name(scheme_output_port_type, "<output-port>");