Improve code for checking init args for TR classes

original commit: b2f9bf6e9eed9c1e71923225adb5340046c725b4
This commit is contained in:
Asumu Takikawa 2014-04-18 23:35:23 -04:00
parent 3e9cb6c332
commit 2b3e3d11d5
2 changed files with 30 additions and 35 deletions

View File

@ -11,6 +11,7 @@
racket/syntax
syntax/id-table
syntax/parse
syntax/stx
"signatures.rkt"
(private parse-type syntax-properties type-annotation)
(base-env class-prims)
@ -928,27 +929,9 @@
init-args:id
init-val:expr)))
(define init-name (syntax-e #'init-external))
(define init-type (car (dict-ref inits init-name '(#f))))
(cond [init-type
;; Catch the exception because the error that is produced
;; in the case of a type error is incomprehensible for a
;; programmer looking at surface syntax. Raise a custom
;; type error instead.
(with-handlers
([exn:fail:syntax?
(λ (e) (tc-error/delayed "Default init value has wrong type"))])
(parameterize ([delay-errors? #f])
(unless (equal? (syntax->datum #'init-val) '(quote #f))
(tc-expr/check #'init-val (ret (Un init-type (->* null init-type)))))))]
;; If the type can't be found, it means that there was no
;; expected type or no annotation was provided via (: ...).
;;
;; FIXME: is this the right place to raise this error, or
;; should it be caught earlier so that this function
;; can be simpler?
[else
(tc-error/delayed "Init argument ~a has no type annotation"
init-name)])]
(check-init-arg init-name
(car (dict-ref inits init-name '(#f)))
#'init-val)]
;; init-field with default
[(begin
(quote ((~or (~datum declare-field-assignment)
@ -967,18 +950,9 @@
#:when (free-identifier=? #'x #'y)
#:when (free-identifier=? #'obj1 #'obj2)
(define init-name (syntax-e #'name))
(define init-type (car (dict-ref inits init-name '(#f))))
(cond [init-type
(with-handlers
([exn:fail:syntax?
;; FIXME: produce a better error message
(λ (e) (tc-error/delayed "Default init value has wrong type"))])
(parameterize ([delay-errors? #f])
(unless (equal? (syntax->datum #'init-val) '(quote #f))
(tc-expr/check #'init-val (ret (Un init-type (->* null init-type)))))))]
[else
(tc-error/delayed "Init argument ~a has no type annotation"
init-name)])]
(check-init-arg init-name
(car (dict-ref inits init-name '(#f)))
#'init-val)]
;; any field or an init-field without default
[(begin
(quote ((~or (~datum declare-field-assignment)
@ -1000,6 +974,26 @@
(tc-expr simplified)]
[_ (void)])))
;; check-init-arg : Id Type Syntax -> Void
;; Check the initialization of an init arg variable against the
;; expected type provided by an annotation (or the default)
(define (check-init-arg init-name init-type init-val)
(define thunk?
(and (stx-pair? init-val)
(free-identifier=? #'#%plain-lambda (stx-car init-val))))
(unless (equal? (syntax->datum init-val) '(quote #f))
(cond [thunk?
(define type
(tc-expr/check/t init-val (ret (->* null init-type))))
(match type
[(Function: (list (arr: _ (Values: (list (Result: result _ _)))
_ _ _)))
(check-below result init-type)]
[_ (int-err "unexpected init value ~a"
(syntax->datum init-val))])]
[else
(tc-expr/check init-val (ret init-type))])))
;; synthesize-private-field-types : IdTable Dict Hash -> Void
;; Given top-level expressions in the class, synthesize types from
;; the initialization expressions for private fields.

View File

@ -808,7 +808,8 @@
[tc-err (class object% (super-new)
(: z Integer)
(init [z "foo"]))
#:ret (ret (-class #:init ([z -Integer #t])))]
#:ret (ret (-class #:init ([z -Integer #t])))
#:msg #rx"expected: Integer.*given: String"]
;; test init field default value
[tc-e (let ()
(define c% (class object% (super-new)
@ -1204,7 +1205,7 @@
(super-new)
(init-field [x : Z] [y : Z 0]))
#:ret (ret (-poly (Z) (-class #:init-field ([x Z #f] [y Z #t]))))
#:msg #rx"Default init value has wrong type"]
#:msg #rx"expected: Z.*given: Zero"]
;; fails because default field value cannot be polymorphic
[tc-err (class object%
#:forall (Z)