Rewrite TR class form to preserve abstractions

This uses a technique discovered by Ryan and Dan that
allows the typed class macro to function without invasively
local-expanding the entire class macro (using its private
context information).

Instead, it expands into many helper macros inside the
normal class body and communicates among them using
`syntax-local-value` and compile-time state within the
class body.

This rewrite didn't save that many lines, but it did
reduce the amount of magic that's used.
This commit is contained in:
Asumu Takikawa 2014-10-09 00:40:11 -04:00
parent d17fca3838
commit 725cb99f4a
13 changed files with 660 additions and 632 deletions

View File

@ -2,7 +2,7 @@
(require typed/mred/mred (require typed/mred/mred
typed/framework/framework typed/framework/framework
racket/class typed/racket/class
string-constants) string-constants)
(require/typed framework (require/typed framework

View File

@ -224,6 +224,23 @@ additional provides all other bindings from @racketmodname[racket/class].
@define/foo-content[d/pr-element] @define/foo-content[d/pr-element]
} }
@deftogether[(@defform[(init init-decl ...)]
@defform[(init-field init-decl ...)]
@defform[(field field-decl ...)]
@defform[(inherit-field field-decl ...)]
@defform[(init-rest id/type)]
@defform[(public maybe-renamed/type ...)]
@defform[(pubment maybe-renamed/type ...)]
@defform[(override maybe-renamed/type ...)]
@defform[(augment maybe-renamed/type ...)]
@defform[(private id/type ...)]
@defform[(inherit maybe-renamed/type ...)])]{
These forms are mostly equivalent to the forms of the same names from
the @racketmodname[racket/class] library and will expand to them. However,
they also allow the initialization argument, field, or method names to be
annotated with types as described above for the @racket[class] form.
}
@section{Types} @section{Types}
@defform[#:literals (init init-field init-rest field augment) @defform[#:literals (init init-field init-rest field augment)

View File

@ -0,0 +1,218 @@
#lang racket/base
;; This module provides helper syntax classes and macros that are used
;; to implement the typed class macro. It's separated in order to allow
;; other parts of TR to use the bindings of init, public, etc. without
;; requiring prims.rkt
(require (prefix-in untyped: racket/class)
"colon.rkt"
(for-syntax racket/base
syntax/parse
syntax/stx
"../private/syntax-properties.rkt"))
(provide (for-syntax class-clause
clause
clause?
clause-stx
clause-kind
clause-ids
init-clause
init-clause?
init-clause-optional?)
init
init-field
field
inherit-field
init-rest
public
pubment
override
augment
private
inherit)
;; for tests
(module+ internal (provide (for-syntax init-decl)))
(begin-for-syntax
;; A Clause is a (clause Syntax Id Listof<Syntax> Listof<Option<Type>>)
;;
;; interp. a class clause such as init or field.
;; stx - the syntax of the entire clause with types erased
;; kind - the kind of clause (e.g., init, field)
;; ids - list of the ids defined in this clause
;; types - types for each id, #f if a type is not provided
(struct clause (stx kind ids types) #:transparent)
;; An InitClause is a (init-clause Syntax Id Listof<Syntax> Boolean)
;;
;; interp. an init class clause
(struct init-clause clause (optional?) #:transparent)
(define-literal-set class-literals
(:))
;; interp:
;; optional? - optional init arg or not (has default value or not)
;; ids - internal and external id for this argument
;; type - type annotation, if any
;; form - type erased form
(define-syntax-class init-decl
#:attributes (optional? ids type form)
#:literal-sets (class-literals)
(pattern id:id
#:attr optional? #f
#:with ids #'(id id)
#:attr type #f
#:with form this-syntax)
(pattern (id:id : type:expr)
#:attr optional? #f
#:with ids #'(id id)
#:with form #'id)
(pattern (ren:renamed (~optional (~seq : type:expr)))
#:attr optional? #f
#:with ids #'ren.ids
#:with form #'(ren))
(pattern (mren:maybe-renamed
(~optional (~seq : type:expr))
default-value:expr)
#:attr optional? #t
#:with ids #'mren.ids
#:with form #'(mren default-value)))
(define-syntax-class field-decl
#:attributes (ids type form)
#:literal-sets (class-literals)
(pattern (mren:maybe-renamed
(~optional (~seq : type:expr))
default-value:expr)
#:with ids #'mren.ids
#:with form #'(mren default-value)))
(define-syntax-class method-decl
#:attributes (ids type form)
#:literal-sets (class-literals)
(pattern mren:maybe-renamed
#:with ids #'mren.ids
#:attr type #f
#:with form this-syntax)
(pattern (mren:maybe-renamed : type:expr)
#:with ids #'mren.ids
#:with form #'mren))
(define-syntax-class private-decl
#:attributes (ids type form)
#:literal-sets (class-literals)
(pattern id:id
#:attr ids #'id
#:attr type #f
#:with form this-syntax)
(pattern (id:id : type:expr)
#:attr ids #'id
#:with form #'id))
(define-syntax-class renamed
#:attributes (ids)
(pattern (internal-id:id external-id:id)
#:with ids #'(internal-id external-id)))
(define-syntax-class maybe-renamed
#:attributes (ids)
(pattern id:id
#:with ids #'(id id))
(pattern ren:renamed
#:with ids #'ren.ids))
(define-syntax-class init-like-clause-names
(pattern (~or (~literal untyped:init)
(~literal untyped:init-field))))
;; matches ids with clauses shaped like method clauses,
;; not necessarily clauses that declare methods
(define-syntax-class method-like-clause-names
(pattern (~or (~literal untyped:inherit-field)
(~literal untyped:public)
(~literal untyped:pubment)
(~literal untyped:public-final)
(~literal untyped:override)
(~literal untyped:overment)
(~literal untyped:override-final)
(~literal untyped:augment)
(~literal untyped:augride)
(~literal untyped:augment-final)
(~literal untyped:inherit)
(~literal untyped:inherit/super)
(~literal untyped:inherit/inner)
(~literal untyped:rename-super))))
(define-syntax-class private-like-clause-names
(pattern (~or (~literal untyped:private)
(~literal untyped:abstract))))
(define-syntax-class class-clause
(pattern (clause-name:init-like-clause-names names:init-decl ...)
#:attr data
(init-clause #'(clause-name names.form ...)
#'clause-name
(stx->list #'(names.ids ...))
(attribute names.type)
(attribute names.optional?)))
(pattern ((~literal untyped:init-rest) name:private-decl)
#:attr data (clause #'(untyped:init-rest name.form)
#'untyped:init-rest
(stx->list #'(name.ids))
(list (attribute name.type))))
(pattern ((~literal untyped:field) names:field-decl ...)
#:attr data (clause #'(untyped:field names.form ...)
#'untyped:field
(stx->list #'(names.ids ...))
(attribute names.type)))
(pattern (clause-name:method-like-clause-names names:method-decl ...)
#:attr data
(clause #'(clause-name names.form ...)
#'clause-name
(stx->list #'(names.ids ...))
(attribute names.type)))
(pattern (clause-name:private-like-clause-names names:private-decl ...)
#:attr data
(clause #'(clause-name names.form ...)
#'clause-name
(stx->list #'(names.ids ...))
(attribute names.type)))))
;; overriden declaration forms
(define-syntax (define-decl-forms stx)
(syntax-parse stx
[(_ ((?clause-name:id ?orig-name:id ?decl-class:id) ...))
#'(begin (define-syntax (?clause-name stx)
(syntax-parse stx
[(_ (~var ??decl ?decl-class) (... ...))
#`(begin #,@(for/list ([id (in-list (attribute ??decl.ids))]
[type (in-list (attribute ??decl.type))]
#:when type)
(tr:class:top-level-property
(tr:class:type-annotation-property
#`(: #,(if (stx-pair? id) (stx-car id) id) #,type)
#t)
#t))
;; set a property here to avoid taint-related issues because
;; we can't transplant the identifiers in the expansion (into the
;; class local table) in certain cases
#,(tr:class:clause-ids-property
#`(?orig-name #,@(attribute ??decl.form))
(attribute ??decl.ids)))]))
...)]))
(define-decl-forms ([init untyped:init init-decl]
[init-field untyped:init-field init-decl]
[field untyped:field field-decl]
[inherit-field untyped:inherit-field method-decl]
[init-rest untyped:init-rest private-decl]
[public untyped:public method-decl]
[pubment untyped:pubment method-decl]
[override untyped:override method-decl]
[augment untyped:augment method-decl]
[private untyped:private private-decl]
[inherit untyped:inherit method-decl]))

View File

@ -2,13 +2,8 @@
;; This module provides TR primitives for classes and objects ;; This module provides TR primitives for classes and objects
(require (rename-in (except-in racket/class (require (prefix-in untyped: racket/class)
define/public "class-clauses.rkt"
define/override
define/pubment
define/augment
define/private)
[class untyped-class])
"colon.rkt" "colon.rkt"
"../typecheck/internal-forms.rkt" "../typecheck/internal-forms.rkt"
"../private/class-literals.rkt" "../private/class-literals.rkt"
@ -16,21 +11,12 @@
(for-syntax (for-syntax
racket/base racket/base
racket/class racket/class
racket/dict
racket/list racket/list
racket/match racket/match
racket/syntax racket/syntax
;; Note: This imports `generate-class-expand-context` from
;; the internals of the class system. It's needed for
;; local expansion to establish the right context, but it
;; is hacky.
racket/private/classidmap
syntax/flatten-begin
syntax/id-table
syntax/kerncase syntax/kerncase
syntax/parse syntax/parse
syntax/stx syntax/stx
unstable/list
"annotate-classes.rkt" "annotate-classes.rkt"
"../private/syntax-properties.rkt" "../private/syntax-properties.rkt"
"../utils/tc-utils.rkt")) "../utils/tc-utils.rkt"))
@ -42,9 +28,21 @@
define/override define/override
define/pubment define/pubment
define/augment define/augment
define/private) define/private
;; override these for type annotations
init
init-field
field
inherit-field
init-rest
public
pubment
override
augment
private
inherit)
;; overriden forms ;; overriden define forms
(define-syntax-rule (define-define/class-kw ((?id ?class-kw) ...)) (define-syntax-rule (define-define/class-kw ((?id ?class-kw) ...))
(begin (define-syntax (?id stx) (begin (define-syntax (?id stx)
(syntax-parse stx (syntax-parse stx
@ -62,394 +60,213 @@
[define/private private])) [define/private private]))
(begin-for-syntax (begin-for-syntax
;; TRClassInfo stores information in the class macro that lets the
;; TR class helper macros coordinate amongst each other.
;;
;; It is a (tr-class-info List<Clause> List<Identifier>)
;;
;; clauses - stores in reverse order all class clauses that appeared
;; in the class expression
;; private-fields - a list of private field names
(struct tr-class-info (clauses private-fields) #:mutable)
;; forms that are not allowed by Typed Racket yet ;; forms that are not allowed by Typed Racket yet
(define unsupported-forms (define unsupported-forms
(list (quote-syntax augride) (list (quote-syntax untyped:augride)
;; FIXME: see if override contracts are enough ;; FIXME: see if override contracts are enough
;; to keep these at bay or whether they ;; to keep these at bay or whether they
;; need to be handled ;; need to be handled
(quote-syntax public-final) (quote-syntax untyped:public-final)
(quote-syntax override-final) (quote-syntax untyped:override-final)
(quote-syntax augment-final) (quote-syntax untyped:augment-final)
(quote-syntax overment) (quote-syntax untyped:overment)
(quote-syntax abstract) (quote-syntax untyped:abstract)
(quote-syntax rename-super) (quote-syntax untyped:rename-super)
(quote-syntax inherit/super) (quote-syntax untyped:inherit/super)
(quote-syntax inherit/inner) (quote-syntax untyped:inherit/inner)
(quote-syntax rename-inner))) (quote-syntax untyped:rename-inner)))
;; basically the same stop forms that class-internal uses ;; similar to the same stop forms that the class macro uses
(define stop-forms (define stop-forms
(append (kernel-form-identifier-list) (append (kernel-form-identifier-list)
unsupported-forms unsupported-forms
(list (list
(quote-syntax :) (quote-syntax :)
(quote-syntax #%app) (quote-syntax untyped:init)
(quote-syntax lambda) (quote-syntax untyped:field)
(quote-syntax init) (quote-syntax untyped:init-field)
(quote-syntax field) (quote-syntax untyped:init-rest)
(quote-syntax init-field) (quote-syntax untyped:inherit-field)
(quote-syntax init-rest) (quote-syntax untyped:private)
(quote-syntax inherit-field) (quote-syntax untyped:public)
(quote-syntax private) (quote-syntax untyped:override)
(quote-syntax public) (quote-syntax untyped:pubment)
(quote-syntax override) (quote-syntax untyped:augment)
(quote-syntax pubment) (quote-syntax untyped:inherit)
(quote-syntax augment) (quote-syntax untyped:super)
(quote-syntax inherit) (quote-syntax untyped:inner)
(quote-syntax super) (quote-syntax untyped:super-new)
(quote-syntax inner) (quote-syntax untyped:super-instantiate)
(quote-syntax this) (quote-syntax untyped:super-make-object)
(quote-syntax this%) (quote-syntax untyped:inspect))))
(quote-syntax super-new)
(quote-syntax super-instantiate)
(quote-syntax super-make-object)
(quote-syntax inspect)))))
;; export some syntax-time definitions for testing purposes
(module+ internal
(provide (for-syntax init-decl class-clause class-clause-or-other
extract-names clause init-clause get-optional-inits)))
(begin-for-syntax
;; A Clause is a (clause Syntax Id Listof<Syntax> Listof<Option<Type>>)
;;
;; interp. a class clause such as init or field.
;; stx - the syntax of the entire clause with types erased
;; kind - the kind of clause (e.g., init, field)
;; ids - list of the ids defined in this clause
;; types - types for each id, #f if a type is not provided
(struct clause (stx kind ids types))
;; 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
;; class clauses such as init or field.
(struct non-clause (stx))
(define-literal-set class-literals
(:))
(define-splicing-syntax-class maybe-type-parameter (define-splicing-syntax-class maybe-type-parameter
(pattern (~seq (~or #:∀ #:forall) (type-variable:id ...)) (pattern (~seq (~or #:∀ #:forall) (type-variable:id ...))
#:attr type-variables #'(type-variable ...)) #:attr type-variables #'(type-variable ...))
(pattern (~seq) (pattern (~seq)
#:attr type-variables #'())) #:attr type-variables #'())))
;; interp: ;; export some syntax-time definitions for testing purposes
;; optional? - optional init arg or not (has default value or not) (module+ internal
;; ids - internal and external id for this argument (provide (for-syntax get-optional-inits)))
;; type - type annotation, if any
;; form - type erased form
(define-syntax-class init-decl
#:attributes (optional? ids type form)
#:literal-sets (class-literals)
(pattern id:id
#:attr optional? #f
#:with ids #'(id id)
#:attr type #f
#:with form this-syntax)
(pattern (id:id : type:expr)
#:attr optional? #f
#:with ids #'(id id)
#:with form #'id)
(pattern (ren:renamed (~optional (~seq : type:expr)))
#:attr optional? #f
#:with ids #'ren.ids
#:with form #'(ren))
(pattern (mren:maybe-renamed
(~optional (~seq : type:expr))
default-value:expr)
#:attr optional? #t
#:with ids #'mren.ids
#:with form #'(mren default-value)))
(define-syntax-class field-decl
#:attributes (ids type form)
#:literal-sets (class-literals)
(pattern (mren:maybe-renamed
(~optional (~seq : type:expr))
default-value:expr)
#:with ids #'mren.ids
#:with form #'(mren default-value)))
(define-syntax-class method-decl
#:attributes (ids type form)
#:literal-sets (class-literals)
(pattern mren:maybe-renamed
#:with ids #'mren.ids
#:attr type #f
#:with form this-syntax)
(pattern (mren:maybe-renamed : type:expr)
#:with ids #'mren.ids
#:with form #'mren))
(define-syntax-class private-decl
#:attributes (id type form)
#:literal-sets (class-literals)
(pattern id:id
#:attr type #f
#:with form this-syntax)
(pattern (id:id : type:expr)
#:with form #'id))
(define-syntax-class renamed
#:attributes (ids)
(pattern (internal-id:id external-id:id)
#:with ids #'(internal-id external-id)))
(define-syntax-class maybe-renamed
#:attributes (ids)
(pattern id:id
#:with ids #'(id id))
(pattern ren:renamed
#:with ids #'ren.ids))
(define-syntax-class init-like-clause-names
(pattern (~or (~literal init)
(~literal init-field))))
;; matches ids with clauses shaped like method clauses,
;; not necessarily clauses that declare methods
(define-syntax-class method-like-clause-names
(pattern (~or (~literal inherit-field)
(~literal public)
(~literal pubment)
(~literal public-final)
(~literal override)
(~literal overment)
(~literal override-final)
(~literal augment)
(~literal augride)
(~literal augment-final)
(~literal inherit)
(~literal inherit/super)
(~literal inherit/inner)
(~literal rename-super))))
(define-syntax-class private-like-clause-names
(pattern (~or (~literal private)
(~literal abstract))))
(define-syntax-class class-clause
(pattern (clause-name:init-like-clause-names names:init-decl ...)
;; FIXME: in the future, use a data structure and
;; make this an attribute instead to represent
;; internal and external names
#:attr data
(init-clause #'(clause-name names.form ...)
#'clause-name
(stx->list #'(names.ids ...))
(attribute names.type)
(attribute names.optional?)))
(pattern ((~literal init-rest) name:private-decl)
#:attr data (clause #'(init-rest name.form)
#'init-rest
(stx->list #'(name.id))
(list (attribute name.type))))
(pattern ((~literal field) names:field-decl ...)
#:attr data (clause #'(field names.form ...)
#'field
(stx->list #'(names.ids ...))
(attribute names.type)))
(pattern (clause-name:method-like-clause-names names:method-decl ...)
#:attr data
(clause #'(clause-name names.form ...)
#'clause-name
(stx->list #'(names.ids ...))
(attribute names.type)))
(pattern (clause-name:private-like-clause-names names:private-decl ...)
#:attr data
(clause #'(clause-name names.form ...)
#'clause-name
(stx->list #'(names.id ...))
(attribute names.type))))
(define-syntax-class class-clause-or-other
#:attributes (data)
(pattern :class-clause)
(pattern e:expr #:attr data (non-clause #'e)))
;; Listof<Clause> -> Dict<Identifier, Names>
;; Extract names from init, public, etc. clauses
(define (extract-names clauses)
(for/fold ([clauses (make-immutable-free-id-table)])
([clause (in-list clauses)])
(dict-update clauses (clause-kind clause)
(λ (old-names)
(append old-names (clause-ids clause)))
'())))
;; FIXME: less magic
;; magic used to disarm syntax after expansion
(define class-insp (variable-reference->module-declaration-inspector
(#%variable-reference)))
(define (disarm stx)
(syntax-disarm stx class-insp))
;; Expand the syntax inside the class body
;; this is mostly cribbed from class-internal.rkt
(define (expand-expressions stxs ctx def-ctx)
(define (class-expand stx)
(local-expand stx ctx stop-forms def-ctx))
(let loop ([stxs stxs])
(cond [(null? stxs) null]
[else
(define stx (disarm (class-expand (car stxs))))
(syntax-parse stx
#:literals (begin define-syntaxes define-values)
[(begin . _)
(loop (append (flatten-begin stx) (cdr stxs)))]
;; Handle syntax definitions in the expanded syntax
;; i.e., macro definitions in the class body
;; see class-internal.rkt as well
[(define-syntaxes (name:id ...) rhs:expr)
(syntax-local-bind-syntaxes
(syntax->list #'(name ...)) #'rhs def-ctx)
(cons stx (loop (cdr stxs)))]
[(define-values (name:id ...) rhs:expr)
(syntax-local-bind-syntaxes
(syntax->list #'(name ...)) #f def-ctx)
(cons stx (loop (cdr stxs)))]
[_ (cons stx (loop (cdr stxs)))])])))
;; add-names-to-intdef-context : Intdef-Ctx Dict<Id, Names> -> Void
;; Establish accessor names in the internal definition context
;; to avoid unbound identifier errors at this level
(define (add-names-to-intdef-context def-ctx name-dict)
(define (add-kind kind)
(define names (map stx-car (dict-ref name-dict kind null)))
(syntax-local-bind-syntaxes names #f def-ctx))
(add-kind #'init-field)
(add-kind #'field)
(add-kind #'public)
(add-kind #'pubment)))
(define-syntax (class stx) (define-syntax (class stx)
(syntax-parse stx (syntax-parse stx
[(_ super forall:maybe-type-parameter e ...) [(_ super forall:maybe-type-parameter e ...)
(define class-ctx (generate-class-expand-context)) (define/with-syntax class-info (generate-temporary))
(define def-ctx (syntax-local-make-definition-context))
(define expanded-stx
(expand-expressions (syntax->list #'(e ...)) class-ctx def-ctx))
(syntax-parse expanded-stx
[(class-elems:class-clause-or-other ...)
(define-values (clauses others)
(filter-multiple (attribute class-elems.data)
clause?
non-clause?))
(define name-dict (extract-names clauses))
(check-unsupported-features name-dict)
(add-names-to-intdef-context def-ctx name-dict)
(internal-definition-context-seal def-ctx)
(define-values (annotated-methods other-top-level private-fields)
(process-class-contents others name-dict))
(define annotated-super (tr:class:super-property #'super #t))
(define ordered-inits (get-all-init-names clauses))
(define optional-inits (get-optional-inits clauses))
(ignore (ignore
(tr:class (tr:class
(quasisyntax/loc stx (quasisyntax/loc stx
(let-values () (untyped:class #,(tr:class:super-property #'super #t)
#,(internal (make-class-name-table (attribute forall.type-variables) (define-syntax class-info (tr-class-info null null))
private-fields (add-annotations class-info e) ...
ordered-inits (make-locals-table class-info)
optional-inits (make-class-name-table
name-dict)) class-info
(untyped-class #,annotated-super #,(attribute forall.type-variables))))))]))
#,@(map clause-stx clauses)
;; construct in-body type annotations for clauses
#,@(apply append
(for/list ([a-clause clauses])
(match-define (clause _1 _2 ids types) a-clause)
(for/list ([id ids] [type types]
#:when type)
;; FIXME: it might be cleaner to use the type-label-property
;; here and use the property to build annotation tables
;; in the class type-checker.
(tr:class:type-annotation-property
(tr:class:top-level-property
#`(: #,(if (stx-pair? id) (stx-car id) id)
#,type)
#t)
#t))))
#,@(map non-clause-stx annotated-methods)
#,(tr:class:top-level-property
#`(begin #,@(map non-clause-stx other-top-level))
#t)
#,(make-locals-table name-dict private-fields))))))])]))
(begin-for-syntax ;; Add syntax properties and other metadata to class form so that the typechecker
;; process-class-contents : Listof<Syntax> Dict<Id, Listof<Id>> ;; can understand the expansion later
;; -> Listof<Syntax> Listof<Syntax> Listof<Syntax> (define-syntax (add-annotations stx)
;; 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 '()]
[private-fields '()])
([content contents])
(define stx (non-clause-stx content))
(syntax-parse stx (syntax-parse stx
#:literals (: define-values super-new #:literal-sets (kernel-literals)
super-make-object super-instantiate) [(_ class-info:id class-exp)
(define expanded (local-expand #'class-exp (syntax-local-context) stop-forms))
(syntax-parse expanded
#:literal-sets (kernel-literals)
#:literals (: untyped:super-new untyped:super-make-object
untyped:super-instantiate)
[(begin e ...)
(quasisyntax/loc #'class-exp
(begin (add-annotations class-info e) ...))]
[cls:class-clause
(define info (syntax-local-value #'class-info))
(define clause-data (attribute cls.data))
(match-define (struct clause (stx kind ids types)) clause-data)
;; to avoid macro taint issues
(define prop-val (tr:class:clause-ids-property #'cls))
(define clause-data*
(cond [(and prop-val (init-clause? clause-data))
(init-clause stx kind prop-val types
(init-clause-optional? clause-data))]
[prop-val
(clause stx kind prop-val types)]
[else clause-data]))
(set-tr-class-info-clauses!
info
(cons clause-data* (tr-class-info-clauses info)))
(check-unsupported-feature kind #'class-exp)
#'class-exp]
;; if it's a method definition for a declared method, then ;; if it's a method definition for a declared method, then
;; mark it as something to type-check ;; mark it as something to type-check
[(define-values (id) . rst) [(define-values (id) body)
#:when (method-id? #'id name-dict) #:when (method-procedure? #'body)
(values (cons (non-clause (tr:class:method-property stx (syntax-e #'id))) (tr:class:method-property #'class-exp (syntax-e #'id))]
methods)
rest-top private-fields)]
;; private field definition ;; private field definition
[(define-values (id ...) . rst) [(define-values (id ...) . rst)
(values methods (define info (syntax-local-value #'class-info))
(append rest-top (list content)) (set-tr-class-info-private-fields!
info
(append (syntax->list #'(id ...)) (append (syntax->list #'(id ...))
private-fields))] (tr-class-info-private-fields info)))
;; set this property so that the initialization expression for
;; this field is counted as a top-level class expression
(tr:class:top-level-property #'class-exp #t)]
;; special : annotation for augment interface ;; special : annotation for augment interface
[(: name:id type:expr #:augment augment-type:expr) [(: name:id type:expr #:augment augment-type:expr)
(define new-clause (quasisyntax/loc #'class-exp
(non-clause (tr:class:type-annotation-property (begin
#'(quote-syntax (:-augment name augment-type)) #t))) #,(tr:class:top-level-property
(define plain-annotation (tr:class:type-annotation-property
(non-clause (tr:class:type-annotation-property #'(quote-syntax (:-augment name augment-type)) #t) #t)
(syntax/loc stx (: name type)) #t))) #,(tr:class:top-level-property
(values methods (tr:class:type-annotation-property
(append rest-top (list plain-annotation new-clause)) (syntax/loc #'class-exp (: name type)) #t) #t)))]
private-fields)]
;; Just process this to add the property ;; Just process this to add the property
[(: name:id . rst) [(: name:id . rst)
(define plain-annotation (tr:class:top-level-property
(non-clause (tr:class:type-annotation-property (tr:class:type-annotation-property
(syntax/loc stx (: name . rst)) #t))) #'class-exp
(values methods #t)
(append rest-top (list plain-annotation)) #t)]
private-fields)]
;; Identify super-new for the benefit of the type checker ;; Identify super-new for the benefit of the type checker
[(~or (super-new [init-id init-expr] ...) [(~or (untyped:super-new [init-id init-expr] ...)
(super-make-object init-expr ...) (untyped:super-make-object init-expr ...)
(super-instantiate (init-expr ...) [name expr] ...)) (untyped:super-instantiate (init-expr ...) [name expr] ...))
(define new-non-clause (tr:class:top-level-property
(non-clause (tr:class:super-new-property stx #t))) (tr:class:super-new-property #'class-exp #t)
(values methods (append rest-top (list new-non-clause)) #t)]
private-fields)] [_ (tr:class:top-level-property #'class-exp #t)])]))
[_ (values methods (append rest-top (list content))
private-fields)])))
;; method-id? : Id Dict<Id, Id> -> Boolean ;; Construct a table in the expansion that lets TR know about the generated
;; Check whether the given id is a known method name ;; identifiers that are used for methods, fields, and such
(define (method-id? id name-dict) (define-syntax (make-locals-table stx)
(memf (λ (n) (free-identifier=? id n)) (syntax-parse stx
(append (stx-map stx-car (dict-ref name-dict #'public '())) [(_ class-info:id)
(stx-map stx-car (dict-ref name-dict #'pubment '())) (match-define (tr-class-info clauses private-fields)
(stx-map stx-car (dict-ref name-dict #'override '())) (syntax-local-value #'class-info))
(stx-map stx-car (dict-ref name-dict #'augment '())) (do-make-locals-table (reverse clauses) private-fields)]))
(dict-ref name-dict #'private '()))))
;; Construct a table in the expansion that just records the names of clauses
;; in the class for convenience in later processing
(define-syntax (make-class-name-table stx)
(syntax-parse stx
[(_ class-info:id (type-variable:id ...))
(match-define (tr-class-info clauses private-fields)
(syntax-local-value #'class-info))
(do-make-class-name-table #'(type-variable ...)
(reverse clauses)
private-fields)]))
(begin-for-syntax
;; Determine if the given syntax object matches the "method-procedure"
;; non-terminal documented for the class macro
(define (method-procedure? stx)
(define stop-list (list #'lambda #'case-lambda
#'#%plain-lambda #'let-values
#'letrec-values))
(define expanded (local-expand stx (syntax-local-context) stop-list))
(define stx*
(syntax-parse expanded
#:literal-sets (kernel-literals)
;; an extra #%expression is inserted by the local expansion but
;; won't appear in the actual expansion, so ignore it
[(#%expression e) #'e]
[_ expanded]))
(syntax-parse stx*
#:literal-sets (kernel-literals)
#:literals (lambda λ)
[((~or lambda λ) formals e ...) #t]
[(case-lambda (formals e ...) ...) #t]
[(#%plain-lambda formals e ...) #t]
[((~or let-values letrec-values) ([(x) m] ...) y:id)
(andmap method-procedure? (syntax->list #'(m ...)))]
[((~or let-values letrec-values) ([(x) m] ...) m1)
(and (andmap method-procedure? (syntax->list #'(m ...)))
(method-procedure? #'m1))]
[_ #f]))
;; clauses->names : (-> Clause Boolean) Listof<Clause> -> Listof<Id>
;; filter clauses by some property and spit out the names in those clauses
(define (clauses->names prop clauses [keep-pair? #f])
(apply append
(for/list ([clause (in-list clauses)]
#:when (prop clause))
(define ids (clause-ids clause))
(for/list ([id (in-list ids)])
(if (and (not keep-pair?) (stx-pair? id))
(stx-car id)
id)))))
;; get-optional-inits : Listof<Clause> -> Listof<Id> ;; get-optional-inits : Listof<Clause> -> Listof<Id>
;; Get a list of the internal names of optional inits ;; Get a list of the internal names of optional inits
@ -465,49 +282,45 @@
;; get-all-init-names : Listof<Clause> -> Listof<Id> ;; get-all-init-names : Listof<Clause> -> Listof<Id>
;; Get a list of all the (internal) init names in order ;; Get a list of all the (internal) init names in order
(define (get-all-init-names clauses) (define (get-all-init-names clauses)
(flatten (clauses->names init-clause? clauses))
(for/list ([clause clauses]
#:when (init-clause? clause))
(stx-map stx-car (clause-ids clause)))))
;; check-unsupported-features : Dict<Identifier, Names> -> Void ;; check-unsupported-feature : Identifier Syntax -> Void
;; Check if features that are not supported were used and ;; Check if the given identifier corresponds to an unsupported class form
;; raise an error if they are present ;; and emit an error using the given syntax object if it does
(define (check-unsupported-features id-table) (define (check-unsupported-feature id stx)
(for ([form unsupported-forms]) (when (member id unsupported-forms free-identifier=?)
(define entry (dict-ref id-table form null)) (tc-error/stx id "unsupported class clause: ~a" stx)))
(unless (null? entry)
(tc-error/stx
(car entry)
"unsupported class clause: ~a"
(syntax-e form)))))
;; make-class-name-table : Listof<Id> Listof<Id> Listof<Id> ;; do-make-class-name-table : Listof<Id> Listof<Clause> Listof<Id> -> Stx
;; Listof<Id> Dict<Id, Id> -> Stx
;; construct syntax used by the class type-checker as a reliable source ;; construct syntax used by the class type-checker as a reliable source
;; for the member names that are in a given class, plus any type ;; for the member names that are in a given class, plus any type
;; variables that are bound ;; variables that are bound
(define (make-class-name-table foralls (define (do-make-class-name-table foralls
private-fields clauses
ordered-inits private-fields)
optional-inits (define (get-names kind)
name-dict) (clauses->names (λ (clause)
(free-identifier=? (clause-kind clause) kind))
clauses #t))
(tr:class:name-table-property
(internal
#`(class-internal #`(class-internal
(#:forall #,@foralls) (#:forall #,@foralls)
(#:all-inits #,@ordered-inits) (#:all-inits #,@(get-all-init-names clauses))
(#:init #,@(dict-ref name-dict #'init '())) (#:init #,@(get-names #'untyped:init))
(#:init-field #,@(dict-ref name-dict #'init-field '())) (#:init-field #,@(get-names #'untyped:init-field))
(#:init-rest #,@(dict-ref name-dict #'init-rest '())) (#:init-rest #,@(get-names #'untyped:init-rest))
(#:optional-init #,@optional-inits) (#:optional-init #,@(get-optional-inits clauses))
(#:field #,@(dict-ref name-dict #'field '())) (#:field #,@(get-names #'untyped:field))
(#:public #,@(dict-ref name-dict #'public '())) (#:public #,@(get-names #'untyped:public))
(#:override #,@(dict-ref name-dict #'override '())) (#:override #,@(get-names #'untyped:override))
(#:private #,@(dict-ref name-dict #'private '())) (#:private #,@(get-names #'untyped:private))
(#:private-field #,@private-fields) (#:private-field #,@private-fields)
(#:inherit #,@(dict-ref name-dict #'inherit '())) (#:inherit #,@(get-names #'untyped:inherit))
(#:inherit-field #,@(dict-ref name-dict #'inherit-field '())) (#:inherit-field #,@(get-names #'untyped:inherit-field))
(#:augment #,@(dict-ref name-dict #'augment '())) (#:augment #,@(get-names #'untyped:augment))
(#:pubment #,@(dict-ref name-dict #'pubment '())))) (#:pubment #,@(get-names #'untyped:pubment))))
#t))
;; This is a neat/horrible trick ;; This is a neat/horrible trick
;; ;;
@ -517,25 +330,22 @@
;; The identifiers inside the lambdas below will expand via ;; The identifiers inside the lambdas below will expand via
;; set!-transformers to the appropriate accessors, which lets ;; set!-transformers to the appropriate accessors, which lets
;; us figure out the accessor identifiers. ;; us figure out the accessor identifiers.
(define (make-locals-table name-dict private-field-names) (define (do-make-locals-table clauses private-field-names)
(define public-names (define (get-names kind)
(stx-map stx-car (dict-ref name-dict #'public '()))) (clauses->names (λ (clause)
(define override-names (free-identifier=? (clause-kind clause) kind))
(stx-map stx-car (dict-ref name-dict #'override '()))) clauses))
(define private-names (dict-ref name-dict #'private '())) (define public-names (get-names #'untyped:public))
(define field-names (define override-names (get-names #'untyped:override))
(append (stx-map stx-car (dict-ref name-dict #'field '())) (define private-names (get-names #'untyped:private))
(stx-map stx-car (dict-ref name-dict #'init-field '())))) (define field-names (append (get-names #'untyped:field)
(define init-names (get-names #'untyped:init-field)))
(stx-map stx-car (dict-ref name-dict #'init '()))) (define init-names (get-names #'untyped:init))
(define init-rest-name (dict-ref name-dict #'init-rest '())) (define init-rest-name (get-names #'untyped:init-rest))
(define inherit-names (define inherit-names (get-names #'untyped:inherit))
(stx-map stx-car (dict-ref name-dict #'inherit '()))) (define inherit-field-names (get-names #'untyped:inherit-field))
(define inherit-field-names (define augment-names (append (get-names #'untyped:pubment)
(stx-map stx-car (dict-ref name-dict #'inherit-field '()))) (get-names #'untyped:augment)))
(define augment-names
(append (stx-map stx-car (dict-ref name-dict #'pubment '()))
(stx-map stx-car (dict-ref name-dict #'augment '()))))
(tr:class:local-table-property (tr:class:local-table-property
#`(let-values ([(#,@public-names) #`(let-values ([(#,@public-names)
(values #,@(map (λ (stx) #`(λ () (#,stx))) (values #,@(map (λ (stx) #`(λ () (#,stx)))
@ -562,10 +372,10 @@
(values #,@(map (λ (stx) #`(λ () (#,stx))) (values #,@(map (λ (stx) #`(λ () (#,stx)))
inherit-names))] inherit-names))]
[(#,@override-names) [(#,@override-names)
(values #,@(map (λ (stx) #`(λ () (#,stx) (super #,stx))) (values #,@(map (λ (stx) #`(λ () (#,stx) (untyped:super #,stx)))
override-names))] override-names))]
[(#,@augment-names) [(#,@augment-names)
(values #,@(map (λ (stx) #`(λ () (#,stx) (inner #f #,stx))) (values #,@(map (λ (stx) #`(λ () (#,stx) (untyped:inner #f #,stx)))
augment-names))]) augment-names))])
(void)) (void))
#t))) #t)))

View File

@ -73,6 +73,8 @@
(tr:class:type-annotation tr:class:type-annotation) (tr:class:type-annotation tr:class:type-annotation)
(tr:class:super tr:class:super) (tr:class:super tr:class:super)
(tr:class:local-table tr:class:local-table) (tr:class:local-table tr:class:local-table)
(tr:class:name-table tr:class:name-table)
(tr:class:clause-ids tr:class:clause-ids)
(tr:class:method tr:class:method) (tr:class:method tr:class:method)
) )

View File

@ -63,7 +63,7 @@
(define-syntax-class internal-class-data (define-syntax-class internal-class-data
#:literal-sets (kernel-literals) #:literal-sets (kernel-literals)
#:literals (class-internal values) #:literals (class-internal values)
(pattern (begin (quote-syntax (pattern (let-values ([() (begin (quote-syntax
(class-internal (class-internal
(#:forall type-parameter:id ...) (#:forall type-parameter:id ...)
(#:all-inits all-init-names:id ...) (#:all-inits all-init-names:id ...)
@ -80,7 +80,8 @@
(#:inherit-field inherit-field-names:name-pair ...) (#:inherit-field inherit-field-names:name-pair ...)
(#:augment augment-names:name-pair ...) (#:augment augment-names:name-pair ...)
(#:pubment pubment-names:name-pair ...))) (#:pubment pubment-names:name-pair ...)))
(#%plain-app values)) (#%plain-app values))])
_)
#:with type-parameters #'(type-parameter ...) #:with type-parameters #'(type-parameter ...)
#:with all-init-internals #'(all-init-names ...) #:with all-init-internals #'(all-init-names ...)
#:with init-internals #'(init-names.internal ...) #:with init-internals #'(init-names.internal ...)
@ -153,30 +154,11 @@
(define-syntax-class class-expansion (define-syntax-class class-expansion
#:literals (let-values letrec-syntaxes+values #%plain-app quote) #:literals (let-values letrec-syntaxes+values #%plain-app quote)
#:attributes (superclass-expr #:attributes (superclass-expr
type-parameters
all-init-internals
init-internals init-externals
init-field-internals init-field-externals
init-rest-name
optional-inits
field-internals field-externals
public-internals public-externals
override-internals override-externals
inherit-internals inherit-externals
inherit-field-internals inherit-field-externals
augment-internals augment-externals
pubment-internals pubment-externals
private-names private-field-names
make-methods make-methods
initializer-body initializer-body
initializer-self-id initializer-self-id
initializer-args-id) initializer-args-id)
(pattern (let-values () (pattern (#%plain-app
(letrec-syntaxes+values
()
((() ;; residual class: data
:internal-class-data))
(#%plain-app
compose-class:id compose-class:id
name:expr name:expr
superclass-expr:expr superclass-expr:expr
@ -184,7 +166,7 @@
internal:expr ... internal:expr ...
(~and make-methods :make-methods-class) (~and make-methods :make-methods-class)
(quote :boolean) (quote :boolean)
(quote #f)))))) (quote #f))))
;; This is similar to `type-declaration` from "internal-forms.rkt", but ;; This is similar to `type-declaration` from "internal-forms.rkt", but
;; the expansion is slightly different in a class so we use this instead. ;; the expansion is slightly different in a class so we use this instead.
@ -234,8 +216,12 @@
;; FIXME: maybe should check the property on this expression ;; FIXME: maybe should check the property on this expression
;; as a sanity check too ;; as a sanity check too
(define super-type (tc-expr #'cls.superclass-expr)) (define super-type (tc-expr #'cls.superclass-expr))
(define class-name-table
(car (trawl-for-property #'cls.make-methods tr:class:name-table-property)))
(syntax-parse class-name-table
[tbl:internal-class-data
;; Save parse attributes to pass through to helper functions ;; Save parse attributes to pass through to helper functions
(define type-parameters (syntax->datum #'cls.type-parameters)) (define type-parameters (syntax->datum #'tbl.type-parameters))
(define fresh-parameters (map gensym type-parameters)) (define fresh-parameters (map gensym type-parameters))
(define parse-info (define parse-info
(hash 'type-parameters type-parameters (hash 'type-parameters type-parameters
@ -245,78 +231,78 @@
'initializer-self-id #'cls.initializer-self-id 'initializer-self-id #'cls.initializer-self-id
'initializer-args-id #'cls.initializer-args-id 'initializer-args-id #'cls.initializer-args-id
'initializer-body #'cls.initializer-body 'initializer-body #'cls.initializer-body
'optional-inits (syntax->datum #'cls.optional-inits) 'optional-inits (syntax->datum #'tbl.optional-inits)
'only-init-internals (syntax->datum #'cls.init-internals) 'only-init-internals (syntax->datum #'tbl.init-internals)
'only-init-names (syntax->datum #'cls.init-externals) 'only-init-names (syntax->datum #'tbl.init-externals)
;; the order of these names reflect the order in the class, ;; the order of these names reflect the order in the class,
;; so use this list when retaining the order is important ;; so use this list when retaining the order is important
'init-internals (syntax->datum #'cls.all-init-internals) 'init-internals (syntax->datum #'tbl.all-init-internals)
'init-rest-name (and (attribute cls.init-rest-name) 'init-rest-name (and (attribute tbl.init-rest-name)
(syntax-e (attribute cls.init-rest-name))) (syntax-e (attribute tbl.init-rest-name)))
'public-internals (syntax->datum #'cls.public-internals) 'public-internals (syntax->datum #'tbl.public-internals)
'override-internals (syntax->datum #'cls.override-internals) 'override-internals (syntax->datum #'tbl.override-internals)
'pubment-internals (syntax->datum #'cls.pubment-internals) 'pubment-internals (syntax->datum #'tbl.pubment-internals)
'augment-internals (syntax->datum #'cls.augment-internals) 'augment-internals (syntax->datum #'tbl.augment-internals)
'method-internals 'method-internals
(set-union (syntax->datum #'cls.public-internals) (set-union (syntax->datum #'tbl.public-internals)
(syntax->datum #'cls.override-internals)) (syntax->datum #'tbl.override-internals))
'field-internals 'field-internals
(set-union (syntax->datum #'cls.field-internals) (set-union (syntax->datum #'tbl.field-internals)
(syntax->datum #'cls.init-field-internals)) (syntax->datum #'tbl.init-field-internals))
'inherit-internals 'inherit-internals
(syntax->datum #'cls.inherit-internals) (syntax->datum #'tbl.inherit-internals)
'inherit-field-internals 'inherit-field-internals
(syntax->datum #'cls.inherit-field-internals) (syntax->datum #'tbl.inherit-field-internals)
'init-names 'init-names
(set-union (syntax->datum #'cls.init-externals) (set-union (syntax->datum #'tbl.init-externals)
(syntax->datum #'cls.init-field-externals)) (syntax->datum #'tbl.init-field-externals))
'field-names 'field-names
(set-union (syntax->datum #'cls.field-externals) (set-union (syntax->datum #'tbl.field-externals)
(syntax->datum #'cls.init-field-externals)) (syntax->datum #'tbl.init-field-externals))
'public-names (syntax->datum #'cls.public-externals) 'public-names (syntax->datum #'tbl.public-externals)
'override-names (syntax->datum #'cls.override-externals) 'override-names (syntax->datum #'tbl.override-externals)
'pubment-names (syntax->datum #'cls.pubment-externals) 'pubment-names (syntax->datum #'tbl.pubment-externals)
'augment-names (syntax->datum #'cls.augment-externals) 'augment-names (syntax->datum #'tbl.augment-externals)
'inherit-names (syntax->datum #'cls.inherit-externals) 'inherit-names (syntax->datum #'tbl.inherit-externals)
'inherit-field-names 'inherit-field-names
(syntax->datum #'cls.inherit-field-externals) (syntax->datum #'tbl.inherit-field-externals)
'private-names (syntax->datum #'cls.private-names) 'private-names (syntax->datum #'tbl.private-names)
'private-fields (syntax->datum #'cls.private-field-names) 'private-fields (syntax->datum #'tbl.private-field-names)
'overridable-names 'overridable-names
(set-union (syntax->datum #'cls.public-externals) (set-union (syntax->datum #'tbl.public-externals)
(syntax->datum #'cls.override-externals)) (syntax->datum #'tbl.override-externals))
'augmentable-names 'augmentable-names
(set-union (syntax->datum #'cls.pubment-externals) (set-union (syntax->datum #'tbl.pubment-externals)
(syntax->datum #'cls.augment-externals)) (syntax->datum #'tbl.augment-externals))
'method-names 'method-names
(set-union (syntax->datum #'cls.public-externals) (set-union (syntax->datum #'tbl.public-externals)
(syntax->datum #'cls.override-externals) (syntax->datum #'tbl.override-externals)
(syntax->datum #'cls.augment-externals) (syntax->datum #'tbl.augment-externals)
(syntax->datum #'cls.pubment-externals)) (syntax->datum #'tbl.pubment-externals))
'all-internal 'all-internal
(append (syntax->datum #'cls.init-internals) (append (syntax->datum #'tbl.init-internals)
(syntax->datum #'cls.init-field-internals) (syntax->datum #'tbl.init-field-internals)
(syntax->datum #'cls.field-internals) (syntax->datum #'tbl.field-internals)
(syntax->datum #'cls.public-internals) (syntax->datum #'tbl.public-internals)
(syntax->datum #'cls.override-internals) (syntax->datum #'tbl.override-internals)
(syntax->datum #'cls.inherit-internals) (syntax->datum #'tbl.inherit-internals)
(syntax->datum #'cls.inherit-field-internals) (syntax->datum #'tbl.inherit-field-internals)
(syntax->datum #'cls.pubment-internals) (syntax->datum #'tbl.pubment-internals)
(syntax->datum #'cls.augment-internals)) (syntax->datum #'tbl.augment-internals))
'all-external 'all-external
(append (syntax->datum #'cls.init-externals) (append (syntax->datum #'tbl.init-externals)
(syntax->datum #'cls.init-field-externals) (syntax->datum #'tbl.init-field-externals)
(syntax->datum #'cls.field-externals) (syntax->datum #'tbl.field-externals)
(syntax->datum #'cls.public-externals) (syntax->datum #'tbl.public-externals)
(syntax->datum #'cls.override-externals) (syntax->datum #'tbl.override-externals)
(syntax->datum #'cls.inherit-externals) (syntax->datum #'tbl.inherit-externals)
(syntax->datum #'cls.inherit-field-externals) (syntax->datum #'tbl.inherit-field-externals)
(syntax->datum #'cls.pubment-externals) (syntax->datum #'tbl.pubment-externals)
(syntax->datum #'cls.augment-externals)))) (syntax->datum #'tbl.augment-externals))))
(with-timing (with-timing
(do-timestamp (format "methods ~a" (dict-ref parse-info 'method-names))) (do-timestamp (format "methods ~a" (dict-ref parse-info 'method-names)))
(extend-tvars/new type-parameters fresh-parameters (extend-tvars/new type-parameters fresh-parameters
(do-check expected super-type parse-info)))])) (do-check expected super-type parse-info)))])]))
;; do-check : Type Type Dict -> Type ;; do-check : Type Type Dict -> Type
;; The actual type-checking ;; The actual type-checking

View File

@ -6,7 +6,9 @@
(require "../utils/utils.rkt" (require "../utils/utils.rkt"
(rep type-rep rep-utils) (rep type-rep rep-utils)
(types resolve) (types resolve)
(except-in racket/class private) (prefix-in untyped: racket/class)
(except-in (base-env class-clauses)
private)
racket/dict racket/dict
racket/list racket/list
racket/match racket/match
@ -14,7 +16,7 @@
syntax/stx syntax/stx
(only-in unstable/list check-duplicate) (only-in unstable/list check-duplicate)
(only-in unstable/sequence in-syntax) (only-in unstable/sequence in-syntax)
(for-template racket/class)) (for-template (base-env class-clauses)))
(provide Class: (provide Class:
row-constraints row-constraints
@ -25,6 +27,11 @@
object-type-clauses object-type-clauses
class-type-clauses) class-type-clauses)
(define-literal-set class-type-literals
(init init-field init-rest field augment
untyped:init untyped:init-field untyped:init-rest
untyped:field untyped:augment))
;; Data definitions ;; Data definitions
;; ;;
;; A RowConstraint is a ;; A RowConstraint is a
@ -37,11 +44,11 @@
;; Syntax classes for rows ;; Syntax classes for rows
(define-splicing-syntax-class row-constraints (define-splicing-syntax-class row-constraints
#:literals (init init-field field augment) #:literal-sets (class-type-literals)
(pattern (~seq (~or (init iname:id ...) (pattern (~seq (~or ((~or init untyped:init) iname:id ...)
(init-field ifname:id ...) ((~or init-field untyped:init-field) ifname:id ...)
(field fname:id ...) ((~or field untyped:field) fname:id ...)
(augment aname:id ...) ((~or augment untyped:augment) aname:id ...)
mname:id) mname:id)
...) ...)
#:attr init-names (flatten/datum #'((iname ...) ...)) #:attr init-names (flatten/datum #'((iname ...) ...))
@ -92,8 +99,9 @@
(define-splicing-syntax-class (row-clauses parse-type) (define-splicing-syntax-class (row-clauses parse-type)
#:description "Row type clause" #:description "Row type clause"
#:attributes (row) #:attributes (row)
#:literals (init-rest) #:literal-sets (class-type-literals)
(pattern (~seq (~or (~optional (init-rest init-rest-type:expr)) (pattern (~seq (~or (~optional ((~or init-rest untyped:init-rest)
init-rest-type:expr))
(~var clause (type-clause parse-type))) (~var clause (type-clause parse-type)))
...) ...)
#:attr inits (apply append (attribute clause.init-entries)) #:attr inits (apply append (attribute clause.init-entries))
@ -180,8 +188,9 @@
(define-splicing-syntax-class object-type-clauses (define-splicing-syntax-class object-type-clauses
#:description "Object type clause" #:description "Object type clause"
#:attributes (field-names field-types method-names method-types) #:attributes (field-names field-types method-names method-types)
#:literals (field) #:literal-sets (class-type-literals)
(pattern (~seq (~or (field field-clause:field-or-method-type ...) (pattern (~seq (~or ((~or field untyped:field)
field-clause:field-or-method-type ...)
method-clause:field-or-method-type) method-clause:field-or-method-type)
...) ...)
#:with field-names (flatten-class-clause #'((field-clause.label ...) ...)) #:with field-names (flatten-class-clause #'((field-clause.label ...) ...))
@ -204,10 +213,11 @@
#:description "Class type clause" #:description "Class type clause"
#:attributes (row-var extends-types #:attributes (row-var extends-types
inits fields methods augments init-rest) inits fields methods augments init-rest)
#:literals (init-rest) #:literal-sets (class-type-literals)
(pattern (~seq (~or (~optional (~seq #:row-var row-var:id)) (pattern (~seq (~or (~optional (~seq #:row-var row-var:id))
(~seq #:implements extends-type:id) (~seq #:implements extends-type:id)
(~optional (init-rest init-rest-type:expr)) (~optional ((~or init-rest untyped:init-rest)
init-rest-type:expr))
(~var clause (type-clause parse-type))) (~var clause (type-clause parse-type)))
...) ...)
#:attr inits (apply append (attribute clause.init-entries)) #:attr inits (apply append (attribute clause.init-entries))
@ -250,8 +260,8 @@
(define-syntax-class (type-clause parse-type) (define-syntax-class (type-clause parse-type)
#:attributes (init-entries field-entries #:attributes (init-entries field-entries
method-entries augment-entries) method-entries augment-entries)
#:literals (init init-field field augment) #:literal-sets (class-type-literals)
(pattern (init init-clause:init-type ...) (pattern ((~or init untyped:init) init-clause:init-type ...)
#:attr init-entries #:attr init-entries
(make-init-entries (make-init-entries
#'(init-clause.label ...) #'(init-clause.label ...)
@ -261,7 +271,8 @@
#:attr field-entries null #:attr field-entries null
#:attr method-entries null #:attr method-entries null
#:attr augment-entries null) #:attr augment-entries null)
(pattern (init-field init-field-clause:init-type ...) (pattern ((~or init-field untyped:init-field)
init-field-clause:init-type ...)
#:attr init-entries #:attr init-entries
(make-init-entries (make-init-entries
#'(init-field-clause.label ...) #'(init-field-clause.label ...)
@ -275,7 +286,7 @@
parse-type) parse-type)
#:attr method-entries null #:attr method-entries null
#:attr augment-entries null) #:attr augment-entries null)
(pattern (field field-clause:field-or-method-type ...) (pattern ((~or field untyped:field) field-clause:field-or-method-type ...)
#:attr init-entries null #:attr init-entries null
#:attr field-entries #:attr field-entries
(make-field/augment-entries (make-field/augment-entries
@ -284,7 +295,8 @@
parse-type) parse-type)
#:attr method-entries null #:attr method-entries null
#:attr augment-entries null) #:attr augment-entries null)
(pattern (augment augment-clause:field-or-method-type ...) (pattern ((~or augment untyped:augment)
augment-clause:field-or-method-type ...)
#:attr init-entries null #:attr init-entries null
#:attr field-entries null #:attr field-entries null
#:attr method-entries null #:attr method-entries null

View File

@ -1,18 +1,9 @@
#lang racket/base #lang racket/base
(require (except-in racket/class (require racket/require
class (subtract-in racket/class
define/public typed-racket/base-env/class-prims)
define/override
define/pubment
define/augment
define/private)
typed-racket/base-env/class-prims) typed-racket/base-env/class-prims)
(provide class (provide (all-from-out typed-racket/base-env/class-prims)
define/public
define/override
define/pubment
define/augment
define/private
(all-from-out racket/class)) (all-from-out racket/class))

View File

@ -1,6 +1,6 @@
#lang typed/racket/base #lang typed/racket/base
(require racket/class (require typed/racket/class
typed/private/utils) typed/private/utils)
(provide Frame% (provide Frame%

View File

@ -12,7 +12,7 @@
(provide fish%)) (provide fish%))
(module fish-client typed/racket/base (module fish-client typed/racket/base
(require racket/class) (require typed/racket/class)
(require/typed (submod ".." fish) [fish% Fish%]) (require/typed (submod ".." fish) [fish% Fish%])
(define-type-alias Fish% (Class (init [weight Real]) (define-type-alias Fish% (Class (init [weight Real])

View File

@ -17,9 +17,7 @@
;; see typecheck-tests.rkt for rationale on imports ;; see typecheck-tests.rkt for rationale on imports
(require rackunit (require rackunit
(except-in racket/class typed/racket/class
class define/public define/override
define/pubment define/augment define/private)
(except-in typed-racket/utils/utils private) (except-in typed-racket/utils/utils private)
(except-in (base-env extra-procs prims class-prims (except-in (base-env extra-procs prims class-prims
base-types base-types-extra) base-types base-types-extra)
@ -1325,8 +1323,8 @@
(super-new) (super-new)
(: x String) (: x String)
(field [x : Symbol 0])) (field [x : Symbol 0]))
#:ret (ret (-class #:field ([x -Symbol]))) #:ret (ret (-class #:field ([x -String])))
#:msg #rx"duplicate type annotation.*new type: String"] #:msg #rx"duplicate type annotation.*new type: Symbol"]
;; fails, expected type and annotation don't match ;; fails, expected type and annotation don't match
[tc-err (let () [tc-err (let ()
(: c% (Class (field [x String]))) (: c% (Class (field [x String])))

View File

@ -10,7 +10,10 @@
syntax/parse syntax/parse
syntax/stx syntax/stx
;; phase-shift down for use in tests below ;; phase-shift down for use in tests below
(for-template (submod typed-racket/base-env/class-prims internal))) (for-template (submod typed-racket/base-env/class-prims internal)
(submod typed-racket/base-env/class-clauses internal)
(only-in typed-racket/base-env/class-clauses
class-clause clause init-clause)))
(provide tests) (provide tests)
(gen-test-main) (gen-test-main)
@ -44,22 +47,6 @@
(check-true (syntax-parses? #'([x y]) init-decl)) (check-true (syntax-parses? #'([x y]) init-decl))
(check-true (syntax-parses? #'(x 0) init-decl)) (check-true (syntax-parses? #'(x 0) init-decl))
(check-true (syntax-parses? #'([x y] 0) init-decl)) (check-true (syntax-parses? #'([x y] 0) init-decl))
(check-true (syntax-parses? #'(init x y z) class-clause))
(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 x) #'(y y) #'(z z))
(list #f #f #f))
(clause #'(public f g h)
#'public
(list #'(f f) #'(g g) #'(h h))
(list #f #f #f))))
(make-immutable-free-id-table
(hash #'public (list #'(f f) #'(g g) #'(h h))
#'init (list #'(x x) #'(y y) #'(z z))))
(check-equal?/id (check-equal?/id
(get-optional-inits (get-optional-inits
@ -67,5 +54,5 @@
(list #f) (list #t)) (list #f) (list #t))
(init-clause #'(init [(a b)]) #'init #'([a b]) (init-clause #'(init [(a b)]) #'init #'([a b])
(list #f) (list #f)))) (list #f) (list #f))))
(list #'x))))) (list #'x))))

View File

@ -19,7 +19,8 @@
(base-env base-types base-types-extra colon) (base-env base-types base-types-extra colon)
;; needed for parsing case-lambda/case-> types ;; needed for parsing case-lambda/case-> types
(only-in (base-env case-lambda) case-lambda) (only-in (base-env case-lambda) case-lambda)
(only-in racket/class init init-field field augment) (prefix-in un: (only-in racket/class init init-field field augment))
(only-in typed/racket/class init init-field field augment)
rackunit) rackunit)
@ -261,6 +262,8 @@
[(Class) (-class)] [(Class) (-class)]
[(Class (init [x Number] [y Number])) [(Class (init [x Number] [y Number]))
(-class #:init ([x -Number #f] [y -Number #f]))] (-class #:init ([x -Number #f] [y -Number #f]))]
[(Class (un:init [x Number] [y Number]))
(-class #:init ([x -Number #f] [y -Number #f]))]
[(Class (init [x Number] [y Number #:optional])) [(Class (init [x Number] [y Number #:optional]))
(-class #:init ([x -Number #f] [y -Number #t]))] (-class #:init ([x -Number #f] [y -Number #t]))]
[(Class (init [x Number]) (init-field [y Number])) [(Class (init [x Number]) (init-field [y Number]))
@ -271,8 +274,12 @@
(-class #:init ([x -Number #f]) #:method ([m (t:-> N N)]))] (-class #:init ([x -Number #f]) #:method ([m (t:-> N N)]))]
[(Class [m (Number -> Number)] (field [x Number])) [(Class [m (Number -> Number)] (field [x Number]))
(-class #:field ([x -Number]) #:method ([m (t:-> N N)]))] (-class #:field ([x -Number]) #:method ([m (t:-> N N)]))]
[(Class [m (Number -> Number)] (un:field [x Number]))
(-class #:field ([x -Number]) #:method ([m (t:-> N N)]))]
[(Class (augment [m (Number -> Number)])) [(Class (augment [m (Number -> Number)]))
(-class #:augment ([m (t:-> N N)]))] (-class #:augment ([m (t:-> N N)]))]
[(Class (un:augment [m (Number -> Number)]))
(-class #:augment ([m (t:-> N N)]))]
[(Class (augment [m (Number -> Number)]) (field [x Number])) [(Class (augment [m (Number -> Number)]) (field [x Number]))
(-class #:augment ([m (t:-> N N)]) #:field ([x -Number]))] (-class #:augment ([m (t:-> N N)]) #:field ([x -Number]))]
[(Class (augment [m (-> Number)]) [m (-> Number)]) [(Class (augment [m (-> Number)]) [m (-> Number)])