Check super-new
calls
Still need to use this information to adjust the class type init clauses
This commit is contained in:
parent
77847de944
commit
060aaa8b26
|
@ -225,24 +225,7 @@
|
||||||
non-clause?))
|
non-clause?))
|
||||||
(define name-dict (extract-names clauses))
|
(define name-dict (extract-names clauses))
|
||||||
(define-values (annotated-methods other-top-level)
|
(define-values (annotated-methods other-top-level)
|
||||||
(for/fold ([methods '()]
|
(process-class-contents others name-dict))
|
||||||
[rest-top '()])
|
|
||||||
([other others])
|
|
||||||
(define stx (non-clause-stx other))
|
|
||||||
(syntax-parse stx
|
|
||||||
;; if it's a method definition for a declared method, then
|
|
||||||
;; mark it as something to type-check
|
|
||||||
[(define-values (id) . rst)
|
|
||||||
#:when (memf (λ (n) (free-identifier=? #'id n))
|
|
||||||
(dict-ref name-dict #'public))
|
|
||||||
(values (cons (non-clause (syntax-property stx
|
|
||||||
'tr:class:method
|
|
||||||
(syntax-e #'id)))
|
|
||||||
methods)
|
|
||||||
rest-top)]
|
|
||||||
;; FIXME: this needs to handle external/internal names too
|
|
||||||
;; FIXME: this needs to track overrides and other things
|
|
||||||
[_ (values methods (append rest-top (list other)))])))
|
|
||||||
(define annotated-super
|
(define annotated-super
|
||||||
(syntax-property #'super 'tr:class:super #t))
|
(syntax-property #'super 'tr:class:super #t))
|
||||||
(syntax-property
|
(syntax-property
|
||||||
|
@ -267,6 +250,35 @@
|
||||||
'typechecker:ignore #t)])]))
|
'typechecker:ignore #t)])]))
|
||||||
|
|
||||||
(begin-for-syntax
|
(begin-for-syntax
|
||||||
|
;; process-class-contents : Listof<Syntax> Dict<Id, Listof<Id>>
|
||||||
|
;; -> 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 '()])
|
||||||
|
([content contents])
|
||||||
|
(define stx (non-clause-stx content))
|
||||||
|
(syntax-parse stx
|
||||||
|
#:literals (define-values super-new)
|
||||||
|
;; if it's a method definition for a declared method, then
|
||||||
|
;; mark it as something to type-check
|
||||||
|
;; FIXME: this needs to handle external/internal names too
|
||||||
|
;; FIXME: this needs to track overrides and other things
|
||||||
|
[(define-values (id) . rst)
|
||||||
|
#:when (memf (λ (n) (free-identifier=? #'id n))
|
||||||
|
(dict-ref name-dict #'public))
|
||||||
|
(values (cons (non-clause (syntax-property stx
|
||||||
|
'tr:class:method
|
||||||
|
(syntax-e #'id)))
|
||||||
|
methods)
|
||||||
|
rest-top)]
|
||||||
|
;; 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)))])))
|
||||||
;; This is a neat/horrible trick
|
;; This is a neat/horrible trick
|
||||||
;;
|
;;
|
||||||
;; In order to detect the mappings that class-internal.rkt has
|
;; In order to detect the mappings that class-internal.rkt has
|
||||||
|
|
|
@ -145,6 +145,9 @@
|
||||||
(map cons
|
(map cons
|
||||||
(syntax->datum #'(method ...))
|
(syntax->datum #'(method ...))
|
||||||
(syntax->list #'(local-method ...)))]))
|
(syntax->list #'(local-method ...)))]))
|
||||||
|
;; find the `super-new` call (or error if missing)
|
||||||
|
(define super-new-stx (trawl-for-property #'body 'tr:class:super-new))
|
||||||
|
(check-super-new super-new-stx super-inits)
|
||||||
;; trawl the body and find methods and type-check them
|
;; trawl the body and find methods and type-check them
|
||||||
(define meths (trawl-for-property #'body 'tr:class:method))
|
(define meths (trawl-for-property #'body 'tr:class:method))
|
||||||
(with-lexical-env/extend (map (λ (m) (dict-ref local-table m))
|
(with-lexical-env/extend (map (λ (m) (dict-ref local-table m))
|
||||||
|
@ -172,6 +175,28 @@
|
||||||
(define top-level-exprs (trawl-for-property #'body 'tr:class:top-level))
|
(define top-level-exprs (trawl-for-property #'body 'tr:class:top-level))
|
||||||
(void)]))
|
(void)]))
|
||||||
|
|
||||||
|
;; check-super-new : Listof<Syntax> Inits -> Void
|
||||||
|
;; Check if the super-new call is well-typed
|
||||||
|
(define (check-super-new super-new-stx super-inits)
|
||||||
|
(cond [(null? super-new-stx)
|
||||||
|
(tc-error/expr
|
||||||
|
"typed classes must call super-new at the class top-level")]
|
||||||
|
[else
|
||||||
|
(syntax-parse (car super-new-stx)
|
||||||
|
#:literals (#%plain-app list cons quote)
|
||||||
|
[(#%plain-app super-go _ _ _ _ _
|
||||||
|
(#%plain-app
|
||||||
|
list
|
||||||
|
(#%plain-app cons (quote init-id) arg:expr)
|
||||||
|
...))
|
||||||
|
(for ([init-id (syntax->datum #'(init-id ...))]
|
||||||
|
[init-arg (syntax->list #'(arg ...))])
|
||||||
|
(define maybe-expected (dict-ref super-inits init-id #f))
|
||||||
|
(if maybe-expected
|
||||||
|
(tc-expr/check init-arg (ret (car maybe-expected)))
|
||||||
|
(tc-error/expr "init argument ~a not accepted by superclass"
|
||||||
|
init-id)))])]))
|
||||||
|
|
||||||
;; Syntax -> Listof<Syntax>
|
;; Syntax -> Listof<Syntax>
|
||||||
;; Look through the expansion of the class macro in search for
|
;; Look through the expansion of the class macro in search for
|
||||||
;; syntax with some property (e.g., methods)
|
;; syntax with some property (e.g., methods)
|
||||||
|
|
|
@ -235,5 +235,29 @@
|
||||||
(public m)
|
(public m)
|
||||||
(define-values (m)
|
(define-values (m)
|
||||||
(lambda (x) (number->string x))))])
|
(lambda (x) (number->string x))))])
|
||||||
(send (new c%) m 4)))))
|
(send (new c%) m 4)))
|
||||||
|
|
||||||
|
;; check a good super-new call
|
||||||
|
(check-ok
|
||||||
|
(: c% (Class (init [x Integer])))
|
||||||
|
(define c% (class: object% (super-new) (init x)))
|
||||||
|
(: d% (Class))
|
||||||
|
(define d% (class: c% (super-new [x (+ 3 5)]))))
|
||||||
|
|
||||||
|
;; fails, missing super-new
|
||||||
|
(check-err
|
||||||
|
(: c% (Class (init [x Integer])))
|
||||||
|
(define c% (class: object% (init x))))
|
||||||
|
|
||||||
|
;; fails, non-top-level super-new
|
||||||
|
(check-err
|
||||||
|
(: c% (Class (init [x Integer])))
|
||||||
|
(define c% (class: object% (let () (super-new)) (init x))))
|
||||||
|
|
||||||
|
;; fails, bad super-new argument
|
||||||
|
(check-err
|
||||||
|
(: c% (Class (init [x Integer])))
|
||||||
|
(define c% (class: object% (super-new) (init x)))
|
||||||
|
(: d% (Class))
|
||||||
|
(define d% (class: c% (super-new [x "bad"]))))))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user