Make internal/external names work

original commit: f895e3156f26d01a5c62c18d675514204440f9ff
This commit is contained in:
Asumu Takikawa 2013-05-20 18:10:19 -04:00
parent 21b9774043
commit f4c8fd57e0
3 changed files with 201 additions and 73 deletions

View File

@ -70,11 +70,16 @@
(quote-syntax inspect)))))
(begin-for-syntax
;; A Clause is a (clause Syntax Id Listof<Id>)
;; A Clause is a (clause Syntax Id Listof<Syntax>)
;;
;; interp. a class clause such as init or field.
(struct clause (stx type ids))
;; An InitClause is a (init-clause Syntax Id Listof<Syntax> Boolean)
;;
;; interp. an init class clause
(struct init-clause clause (optional?))
;; A NonClause is a (non-clause Syntax)
;;
;; interp. a top-level class expression that is not one of the special
@ -82,31 +87,29 @@
(struct non-clause (stx))
(define-syntax-class init-decl
(pattern id
#:with internal-id #'id
#:with external-id #'id)
(pattern id:id
#:attr optional? #f
#:with ids #'(id id))
(pattern (ren:renamed)
#:with internal-id #'ren.internal-id
#:with external-id #'ren.external-id)
#:attr optional? #f
#:with ids #'ren.ids)
(pattern (mren:maybe-renamed default-value:expr)
#:with internal-id #'mren.internal-id
#:with external-id #'mren.external-id))
#:attr optional? #t
#:with ids #'mren.ids))
(define-syntax-class field-decl
(pattern (mren:maybe-renamed default-value:expr)
#:with internal-id #'mren.internal-id
#:with external-id #'mren.external-id))
#:with ids #'mren.ids))
(define-syntax-class renamed
(pattern (internal-id:id external-id:id)))
(pattern (internal-id:id external-id:id)
#:with ids #'(internal-id external-id)))
(define-syntax-class maybe-renamed
(pattern id
#:with internal-id #'id
#:with external-id #'id)
(pattern id:id
#:with ids #'(id id))
(pattern ren:renamed
#:with internal-id #'ren.internal-id
#:with external-id #'ren.external-id))
#:with ids #'ren.ids))
(define-syntax-class class-clause
(pattern (~and ((~and clause-name (~or (~literal init)
@ -117,11 +120,12 @@
;; make this an attribute instead to represent
;; internal and external names
#:attr data
(clause #'form #'clause-name
(stx->list #'(names.external-id ...))))
(init-clause #'form #'clause-name
(stx->list #'(names.ids ...))
(attribute names.optional?)))
(pattern (~and ((~literal field) names:field-decl ...) form)
#:attr data (clause #'form #'field
(stx->list #'(names.external-id ...))))
(stx->list #'(names.ids ...))))
(pattern (~and ((~and clause-name (~or (~literal inherit-field)
(~literal public)
(~literal pubment)
@ -139,7 +143,7 @@
form)
#:attr data
(clause #'form #'clause-name
(stx->list #'(names.external-id ...))))
(stx->list #'(names.ids ...))))
(pattern (~and ((~and clause-name (~or (~literal private)
(~literal abstract)))
names:id ...)
@ -176,11 +180,13 @@
[_ stx]))
(module+ test
;; equal? check but considers identifier equality
;; equal? check but considers stx pair equality
(define (equal?/id x y)
(if (and (identifier? x) (identifier? y))
(free-identifier=? x y)
(equal?/recur x y equal?/id)))
(if (and (syntax? x) (syntax? y))
(and (free-identifier=? (stx-car x) (stx-car y))
(free-identifier=? (stx-car (stx-cdr x))
(stx-car (stx-cdr y))))
(equal?/recur x y equal?/id)))
;; utility macro for checking if a syntax matches a
;; given syntax class
@ -200,11 +206,15 @@
(check-true (syntax-parses? #'(public f g h) class-clause))
(check-true (syntax-parses? #'(public f) class-clause-or-other))
(check-equal?/id
(extract-names (list (clause #'(init x y z) #'init (list #'x #'y #'z))
(clause #'(public f g h) #'public (list #'f #'g #'h))))
(extract-names (list (clause #'(init x y z)
#'init
(list #'(x x) #'(y y) #'(z z)))
(clause #'(public f g h)
#'public
(list #'(f f) #'(g g) #'(h h)))))
(make-immutable-free-id-table
(hash #'public (list #'f #'g #'h)
#'init (list #'x #'y #'z))))))
(hash #'public (list #'(f f) #'(g g) #'(h h))
#'init (list #'(x x) #'(y y) #'(z z)))))))
(define-syntax (class: stx)
(syntax-parse stx
@ -269,8 +279,8 @@
;; FIXME: this needs to track privates, augments, etc.
[(define-values (id) . rst)
#:when (memf (λ (n) (free-identifier=? #'id n))
(append (dict-ref name-dict #'public '())
(dict-ref name-dict #'override '())
(append (stx-map stx-car (dict-ref name-dict #'public '()))
(stx-map stx-car (dict-ref name-dict #'override '()))
(dict-ref name-dict #'private '())))
(values (cons (non-clause (syntax-property stx
'tr:class:method
@ -293,12 +303,12 @@
;; us figure out the accessor identifiers.
(define (make-locals-table name-dict)
(define method-names
(append (dict-ref name-dict #'public '())
(dict-ref name-dict #'override '())))
(append (stx-map stx-car (dict-ref name-dict #'public '()))
(stx-map stx-car (dict-ref name-dict #'override '()))))
(define private-names (dict-ref name-dict #'private '()))
(define field-names
(append (dict-ref name-dict #'field '())
(dict-ref name-dict #'init-field '())))
(append (stx-map stx-car (dict-ref name-dict #'field '()))
(stx-map stx-car (dict-ref name-dict #'init-field '()))))
(syntax-property
#`(let-values ([(#,@method-names)
(values #,@(map (λ (stx) #`(λ () (#,stx)))

View File

@ -28,6 +28,34 @@
(import tc-if^ tc-lambda^ tc-app^ tc-let^ tc-expr^)
(export check-class^)
;; Syntax classes for use in functions below
(define-syntax-class name-pair
(pattern (internal:id external:id)))
(define-syntax-class internal-class-data
#:literals (#%plain-app quote-syntax class:-internal begin
values c:init c:init-field c:field
c:public c:override c:private)
(pattern (begin (quote-syntax
(class:-internal
(c:init init-names:name-pair ...)
(c:init-field init-field-names:name-pair ...)
(c:field field-names:name-pair ...)
(c:public public-names:name-pair ...)
(c:override override-names:name-pair ...)
(c:private private-names:id ...)))
(#%plain-app values))
#:with init-internals #'(init-names.internal ...)
#:with init-externals #'(init-names.external ...)
#:with init-field-internals #'(init-field-names.internal ...)
#:with init-field-externals #'(init-field-names.external ...)
#:with field-internals #'(field-names.internal ...)
#:with field-externals #'(field-names.external ...)
#:with public-internals #'(public-names.internal ...)
#:with public-externals #'(public-names.external ...)
#:with override-internals #'(override-names.internal ...)
#:with override-externals #'(override-names.external ...)))
;; Syntax TCResults -> Type
;; Type-check a class form by trawling its innards
;;
@ -48,10 +76,8 @@
;; Do the actual type-checking
(define (do-check form expected? self-class-type)
(syntax-parse form
#:literals (let-values #%plain-lambda quote-syntax begin
#%plain-app values class:-internal letrec-syntaxes+values
c:init c:init-field c:field c:public c:override
c:private)
#:literals (let-values #%plain-lambda begin
#%plain-app values letrec-syntaxes+values)
;; Inspect the expansion of the class macro for the pieces that
;; we need to type-check like superclass, methods, top-level
;; expressions and so on
@ -59,17 +85,7 @@
(letrec-syntaxes+values ()
((()
;; residual class: data
;; FIXME: put in syntax class
(begin
(quote-syntax
(class:-internal
(c:init internal-init-names ...)
(c:init-field internal-init-field-names ...)
(c:field internal-field-names ...)
(c:public internal-public-names ...)
(c:override internal-override-names ...)
(c:private internal-private-names ...)))
(#%plain-app values))))
data:internal-class-data))
(let-values (((superclass) superclass-expr)
((interfaces) interface-expr))
(?#%app compose-class
@ -98,34 +114,66 @@
(define super-init-names (list->set (dict-keys super-inits)))
(define super-field-names (list->set (dict-keys super-fields)))
(define super-method-names (list->set (dict-keys super-methods)))
(define this%-init-internals
(list->set (append (syntax->datum #'data.init-internals)
(syntax->datum #'data.init-internals))))
(define this%-public-internals
(list->set (syntax->datum #'data.public-internals)))
(define this%-override-internals
(list->set (syntax->datum #'data.override-internals)))
(define this%-method-internals
(set-union this%-public-internals this%-override-internals))
(define this%-field-internals
(list->set (syntax->datum #'data.field-internals)))
(define this%-init-names
(list->set
(append (syntax->datum #'(internal-init-names ...))
(syntax->datum #'(internal-init-field-names ...)))))
(append (syntax->datum #'data.init-externals)
(syntax->datum #'data.init-field-externals))))
(define this%-field-names
(list->set
(append (syntax->datum #'(internal-field-names ...))
(syntax->datum #'(internal-init-field-names ...)))))
(append (syntax->datum #'data.field-externals)
(syntax->datum #'data.init-field-externals))))
(define this%-public-names
(list->set (syntax->datum #'(internal-public-names ...))))
(list->set (syntax->datum #'data.public-externals)))
(define this%-override-names
(list->set (syntax->datum #'(internal-override-names ...))))
(list->set (syntax->datum #'data.override-externals)))
(define this%-private-names
(list->set (syntax->datum #'(internal-private-names ...))))
(list->set (syntax->datum #'(data.private-names ...))))
(define this%-method-names
(set-union this%-public-names this%-override-names))
(define all-internal
(apply append
(map (λ (stx) (syntax->datum stx))
(list #'data.init-internals
#'data.init-field-internals
#'data.field-internals
#'data.public-internals
#'data.override-internals))))
(define all-external
(apply append
(map (λ (stx) (syntax->datum stx))
(list #'data.init-externals
#'data.init-field-externals
#'data.field-externals
#'data.public-externals
#'data.override-externals))))
;; establish a mapping between internal and external names
(define internal-external-mapping
(for/hash ([internal all-internal]
[external all-external])
(values internal external)))
;; trawl the body for top-level expressions
(define top-level-exprs (trawl-for-property #'body 'tr:class:top-level))
(define internals-table
(register-internals top-level-exprs #'(internal-public-names ...)))
(define internals-table (register-internals top-level-exprs))
;; Type for self in method calls
(define self-type
(if self-class-type
(make-Instance self-class-type)
(infer-self-type internals-table
this%-init-names
this%-field-names
this%-public-names)))
internal-external-mapping
this%-init-internals
this%-field-internals
this%-public-internals)))
(match-define (Instance: (Class: _ inits fields methods))
self-type)
;; Use the internal class: information to check whether clauses
@ -161,8 +209,8 @@
(construct-local-mapping-tables (car locals)))
;; start type-checking elements in the body
(define-values (lexical-names lexical-types)
(local-tables->lexical-env local-method-table methods this%-method-names
local-field-table fields this%-field-names
(local-tables->lexical-env local-method-table methods this%-method-internals
local-field-table fields this%-field-internals
self-type))
(with-lexical-env/extend lexical-names lexical-types
(for ([stx top-level-exprs]
@ -175,7 +223,7 @@
(define meths (trawl-for-property #'body 'tr:class:method))
(define checked-method-types
(with-lexical-env/extend lexical-names lexical-types
(check-methods meths methods self-type)))
(check-methods internal-external-mapping meths methods self-type)))
(if expected?
self-class-type
(merge-types self-type checked-method-types))]))
@ -242,12 +290,15 @@
localized-field-get-names localized-field-set-names)
(append method-types field-get-types field-set-types)))
;; check-methods : Listof<Syntax> Dict Type -> Dict<Symbol, Type>
;; check-methods : Listof<Syntax> Dict<Symbol, Symbol> Dict Type
;; -> Dict<Symbol, Type>
;; Type-check the methods inside of a class
(define (check-methods meths methods self-type)
(define (check-methods internal-external-mapping
meths methods self-type)
(for/list ([meth meths])
(define method-name (syntax-property meth 'tr:class:method))
(define maybe-expected (dict-ref methods method-name #f))
(define external-name (dict-ref internal-external-mapping method-name))
(define maybe-expected (dict-ref methods external-name #f))
(cond [maybe-expected
(define pre-method-type (car maybe-expected))
(define method-type
@ -255,8 +306,8 @@
(define expected (ret method-type))
(define annotated (annotate-method meth self-type method-type))
(tc-expr/check annotated expected)
(list method-name pre-method-type)]
[else (list method-name
(list external-name pre-method-type)]
[else (list external-name
(unfixup-method-type (tc-expr/t meth)))])))
;; Syntax -> Dict<Symbol, Id> Dict<Symbol, (List Symbol Symbol)>
@ -350,7 +401,7 @@
;; register-internals : Listof<Syntax> -> Dict<Symbol, Type>
;; Find : annotations and register them
;; TODO: support `define-type`?
(define (register-internals stxs dummy)
(define (register-internals stxs)
(for/fold ([table '()])
([stx stxs])
(syntax-parse stx
@ -365,19 +416,22 @@
table)]
[_ table])))
;; infer-self-type : Dict<Symbol, Type> Set<Symbol> * 3 -> Type
;; infer-self-type : Dict<Symbol, Type> Dict<Symbol, Symbol>
;; Set<Symbol> * 3 -> Type
;; Construct a self object type based on the registered types
;; from : inside the class body.
(define (infer-self-type internals-table inits fields publics)
(define (infer-self-type internals-table internal-external-mapping
inits fields publics)
(define (make-type-dict names [inits? #f])
(for/fold ([type-dict '()])
([name names])
(define external (dict-ref internal-external-mapping name))
(cond [(dict-ref internals-table name #f) =>
(λ (type)
(define entry
;; FIXME: this should record the correct optional
;; boolean based on internal macro data
(if inits? (list name type #f) (list name type)))
(if inits? (list external type #f) (list external type)))
(cons entry type-dict))]
[else type-dict])))
(define init-types (make-type-dict inits #t))

View File

@ -317,6 +317,70 @@
(define/public (m y) 0)
(+ "foo" 5))))
;; test different internal/external names
(check-ok
(define c% (class: object% (super-new)
(public [m n])
(define m (lambda () 0))))
(send (new c%) n))
;; internal/external the same is ok
(check-ok
(define c% (class: object% (super-new)
(public [m m])
(define m (lambda () 0))))
(send (new c%) m))
;; fails, internal name not accessible
(check-err
(define c% (class: object% (super-new)
(public [m n])
(define m (lambda () 0))))
(send (new c%) m))
;; test internal/external with expected
(check-ok
(: c% (Class [n (-> Integer)]))
(define c% (class: object% (super-new)
(public [m n])
(define m (lambda () 0))))
(send (new c%) n))
;; test internal/external field
(check-ok
(define c% (class: object% (super-new)
(: f Integer)
(field ([f g] 0))))
(get-field g (new c%)))
;; fail, internal name not accessible
(check-err
(define c% (class: object% (super-new)
(: f Integer)
(field ([f g] 0))))
(get-field f (new c%)))
;; test internal/external init
(check-ok
(define c% (class: object% (super-new)
(: i Integer)
(init ([i j]))))
(new c% [j 5]))
;; fails, internal name not accessible
(check-err
(define c% (class: object% (super-new)
(: i Integer)
(init ([i j]))))
(new c% [i 5]))
;; test type-checking method with internal/external
(check-err
(: c% (Class [n (Integer -> Integer)]))
(define c% (class: object% (super-new)
(public [m n])
(define m (lambda () 0)))))
;; test type-checking without expected class type
(check-ok
(define c% (class: object% (super-new)