Support private fields

This commit is contained in:
Asumu Takikawa 2013-05-24 10:54:15 -04:00
parent 4559981212
commit bbd5d97a23
3 changed files with 122 additions and 26 deletions

View File

@ -25,7 +25,8 @@
class:
;; for use in ~literal clauses
class:-internal
optional-init)
optional-init
private-field)
;; give it a binding, but it shouldn't be used directly
(define-syntax (class:-internal stx)
@ -34,6 +35,9 @@
(define-syntax (optional-init stx)
(raise-syntax-error "should only be used internally"))
(define-syntax (private-field stx)
(raise-syntax-error "should only be used internally"))
(begin-for-syntax
(module+ test (require rackunit))
@ -239,7 +243,7 @@
clause?
non-clause?))
(define name-dict (extract-names clauses))
(define-values (annotated-methods other-top-level)
(define-values (annotated-methods other-top-level private-fields)
(process-class-contents others name-dict))
(define annotated-super
(syntax-property #'super 'tr:class:super #t))
@ -258,6 +262,7 @@
(public #,@(dict-ref name-dict #'public '()))
(override #,@(dict-ref name-dict #'override '()))
(private #,@(dict-ref name-dict #'private '()))
(private-field #,@private-fields)
(inherit #,@(dict-ref name-dict #'inherit '()))))
(class #,annotated-super
#,@(map clause-stx clauses)
@ -265,18 +270,19 @@
#,(syntax-property
#`(begin #,@(map non-clause-stx other-top-level))
'tr:class:top-level #t)
#,(make-locals-table name-dict)))
#,(make-locals-table name-dict private-fields)))
'tr:class #t)
'typechecker:ignore #t)])]))
(begin-for-syntax
;; process-class-contents : Listof<Syntax> Dict<Id, Listof<Id>>
;; -> Listof<Syntax> Listof<Syntax>
;; -> Listof<Syntax> Listof<Syntax> Listof<Syntax>
;; Process methods and other top-level expressions and definitions
;; that aren't class clauses like `init` or `public`
(define (process-class-contents contents name-dict)
(for/fold ([methods '()]
[rest-top '()])
[rest-top '()]
[private-fields '()])
([content contents])
(define stx (non-clause-stx content))
(syntax-parse stx
@ -293,13 +299,21 @@
'tr:class:method
(syntax-e #'id)))
methods)
rest-top)]
rest-top private-fields)]
;; private field definition
[(define-values (id ...) . rst)
(values methods
(append rest-top (list content))
(append (syntax->list #'(id ...))
private-fields))]
;; Identify super-new for the benefit of the type checker
[(super-new [init-id init-expr] ...)
(define new-non-clause
(non-clause (syntax-property stx 'tr:class:super-new #t)))
(values methods (append rest-top (list new-non-clause)))]
[_ (values methods (append rest-top (list content)))])))
(values methods (append rest-top (list new-non-clause))
private-fields)]
[_ (values methods (append rest-top (list content))
private-fields)])))
;; get-optional-inits : Listof<Clause> -> Listof<Id>
;; Get a list of the internal names of mandatory inits
@ -327,7 +341,7 @@
;; The identifiers inside the lambdas below will expand via
;; set!-transformers to the appropriate accessors, which lets
;; us figure out the accessor identifiers.
(define (make-locals-table name-dict)
(define (make-locals-table name-dict private-field-names)
(define public-names (stx-map stx-car (dict-ref name-dict #'public '())))
(define override-names
(stx-map stx-car (dict-ref name-dict #'override '())))
@ -349,6 +363,9 @@
[(#,@field-names)
(values #,@(map (λ (stx) #`(λ () #,stx (set! #,stx 0)))
field-names))]
[(#,@private-field-names)
(values #,@(map (λ (stx) #`(λ () #,stx (set! #,stx 0)))
private-field-names))]
[(#,@init-names)
(values #,@(map (λ (stx) #`(λ () #,stx))
init-names))]

View File

@ -35,7 +35,7 @@
(define-syntax-class internal-class-data
#:literals (#%plain-app quote-syntax class:-internal begin
values c:init c:init-field optional-init c:field
c:public c:override c:private c:inherit)
c:public c:override c:private c:inherit private-field)
(pattern (begin (quote-syntax
(class:-internal
(c:init init-names:name-pair ...)
@ -45,6 +45,7 @@
(c:public public-names:name-pair ...)
(c:override override-names:name-pair ...)
(c:private privates:id ...)
(private-field private-fields:id ...)
(c:inherit inherit-names:name-pair ...)))
(#%plain-app values))
#:with init-internals #'(init-names.internal ...)
@ -60,7 +61,8 @@
#:with override-externals #'(override-names.external ...)
#:with inherit-externals #'(inherit-names.external ...)
#:with inherit-internals #'(inherit-names.internal ...)
#:with private-names #'(privates ...)))
#:with private-names #'(privates ...)
#:with private-field-names #'(private-fields ...)))
(define-syntax-class initializer-body
#:literals (letrec-syntaxes+values)
@ -117,7 +119,7 @@
public-internals public-externals
override-internals override-externals
inherit-internals inherit-externals
private-names
private-names private-field-names
make-methods
initializer-body
initializer-self-id
@ -208,6 +210,8 @@
(list->set (syntax->datum #'cls.inherit-externals)))
(define this%-private-names
(list->set (syntax->datum #'cls.private-names)))
(define this%-private-fields
(list->set (syntax->datum #'cls.private-field-names)))
(define this%-method-names
(set-union this%-public-names this%-override-names))
(define all-internal
@ -274,13 +278,18 @@
;; trawl the body for the local name table
(define locals (trawl-for-property #'cls.make-methods 'tr:class:local-table))
(define-values (local-method-table local-private-table local-field-table
local-init-table local-inherit-table local-super-table)
local-private-field-table local-init-table
local-inherit-table local-super-table)
(construct-local-mapping-tables (car locals)))
;; types for private elements
(define private-method-types
(for/hash ([(name type) (in-dict internals-table)]
#:when (set-member? this%-private-names name))
(values name type)))
(define private-field-types
(for/hash ([(name type) (in-dict internals-table)]
#:when (set-member? this%-private-fields name))
(values name (list type))))
;; start type-checking elements in the body
(define-values (lexical-names lexical-types
lexical-names/top-level lexical-types/top-level)
@ -289,6 +298,8 @@
this%-method-internals
local-field-table fields
this%-field-internals
local-private-field-table private-field-types
this%-private-fields
local-init-table inits
;; omit init-fields here since they don't have
;; init accessors, only field accessors
@ -393,6 +404,8 @@
(define (local-tables->lexical-env internal-external-mapping
local-method-table methods method-names
local-field-table fields field-names
local-private-field-table
private-field-types private-field-names
local-init-table inits init-names
local-inherit-table local-super-table
super-types
@ -408,6 +421,12 @@
(define localized-field-pairs (localize local-field-table field-names))
(define localized-field-get-names (map car localized-field-pairs))
(define localized-field-set-names (map cadr localized-field-pairs))
(define localized-private-field-pairs
(localize local-private-field-table private-field-names))
(define localized-private-field-get-names
(map car localized-private-field-pairs))
(define localized-private-field-set-names
(map cadr localized-private-field-pairs))
(define localized-inherit-names (localize local-inherit-table inherit-names))
(define localized-private-methods
(localize local-private-table private-methods))
@ -429,19 +448,26 @@
(define method-types (make-method-types method-names methods))
(define inherit-types (make-method-types inherit-names super-types))
(define field-get-types
(for/list ([f (in-set field-names)])
(define external (dict-ref internal-external-mapping f))
(define maybe-type (dict-ref fields external #f))
;; construct field accessor types
(define (make-field-types field-names type-map #:private? [private? #f])
(for/lists (_1 _2) ([f (in-set field-names)])
(define external
(if private?
f
(dict-ref internal-external-mapping f)))
(define maybe-type (dict-ref type-map external #f))
(values
(->* (list (make-Univ)) (or (and maybe-type (car maybe-type))
(make-Univ)))))
(define field-set-types
(for/list ([f (in-set field-names)])
(define external (dict-ref internal-external-mapping f))
(define maybe-type (dict-ref fields external #f))
(make-Univ)))
(->* (list (make-Univ) (or (and maybe-type (car maybe-type))
-Bottom))
-Void)))
-Void))))
(define-values (field-get-types field-set-types)
(make-field-types field-names fields))
(define-values (private-field-get-types private-field-set-types)
(make-field-types private-field-names private-field-types
#:private? #t))
;; types for privates and super calls
(define (make-private-like-types names type-map)
@ -465,10 +491,13 @@
localized-private-methods
localized-field-get-names
localized-field-set-names
localized-private-field-get-names
localized-private-field-set-names
localized-inherit-names
localized-override-names))
(define all-types (append method-types private-method-types
field-get-types field-set-types
private-field-get-types private-field-set-types
inherit-types super-call-types))
(values all-names all-types
;; FIXME: consider removing method names and types
@ -628,6 +657,14 @@
(let-values (((_) _))
(let-values (((_) _)) (#%plain-app local-field-set:id _ _))))
...)]
[(private-field:id ...)
(#%plain-app
values
(#%plain-lambda ()
(let-values (((_) _)) (#%plain-app local-private-get:id _))
(let-values (((_) _))
(let-values (((_) _)) (#%plain-app local-private-set:id _ _))))
...)]
[(init:id ...)
(#%plain-app values (#%plain-lambda () local-init:id) ...)]
[(inherit:id ...)
@ -656,6 +693,10 @@
(syntax->datum #'(field ...))
(syntax->list #'(local-field-get ...))
(syntax->list #'(local-field-set ...)))
(map list
(syntax->datum #'(private-field ...))
(syntax->list #'(local-private-get ...))
(syntax->list #'(local-private-set ...)))
(map cons
(syntax->datum #'(init ...))
(syntax->list #'(local-init ...)))

View File

@ -382,6 +382,44 @@
(field [f "foo"])
(set! f 5))))
;; test private field
(check-ok
(class: object%
(super-new)
(: x Integer)
(define x 5)
(set! x 8)
(+ x 1))
(: d% (Class (field [y String])))
(define d%
(class: object%
(super-new)
(: x Integer)
(define x 5)
(: y String)
(field [y "foo"]))))
;; fails, bad private field set!
(check-err
(class: object%
(super-new)
(: x Integer)
(define x 5)
(set! x "foo")))
;; fails, bad private field default
(check-err
(class: object%
(super-new)
(: x Integer)
(define x "foo")))
;; fails, private field needs type annotation
(check-err
(class: object%
(super-new)
(define x "foo")))
;; test private method
(check-ok
(class: object% (super-new)