
For private `define-values` in classes with multiple variables, don't eagerly throw type errors in the synthesis step. Instead, wait until the later checking step when the environment will be correctly set up. When the initial synthesis typecheck fails, yield type Any for the environment. If the typecheck should really fail, this is ok. If not, then the user can add a type annotation. A better long-term strategy is to change the handling of environments so that the type environment gets refined as definitions are checked. This way all annotations that the user writes are factored into the initial environment and unannotated variables will have their types synthesized.
2113 lines
75 KiB
Racket
2113 lines
75 KiB
Racket
#lang racket/base
|
|
|
|
;; Unit tests for typed classes
|
|
|
|
(require (submod "typecheck-tests.rkt" test-helpers)
|
|
(except-in "test-utils.rkt" private)
|
|
(for-syntax racket/base
|
|
typed-racket/tc-setup
|
|
typed-racket/utils/tc-utils))
|
|
|
|
(provide tests)
|
|
(gen-test-main)
|
|
|
|
(begin-for-syntax
|
|
;; for checking the printing of type aliases
|
|
(current-type-names (init-current-type-names)))
|
|
|
|
;; see typecheck-tests.rkt for rationale on imports
|
|
(require rackunit
|
|
typed/racket/class
|
|
(except-in typed-racket/utils/utils private)
|
|
(except-in (base-env extra-procs prims class-prims
|
|
base-types base-types-extra)
|
|
define lambda λ case-lambda)
|
|
(prefix-in tr: (only-in (base-env prims) define lambda λ case-lambda))
|
|
(for-syntax (rep type-rep filter-rep object-rep)
|
|
(rename-in (types abbrev union numeric-tower filter-ops utils)
|
|
[Un t:Un]
|
|
[-> t:->])))
|
|
|
|
(define tests
|
|
(test-suite
|
|
"class typechecking tests"
|
|
#reader typed-racket/typed-reader
|
|
;; Basic class with init and public method
|
|
[tc-e (let ()
|
|
(: c% (Class (init [x Integer])
|
|
[m (Integer -> Integer)]))
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(init x)
|
|
(define/public (m x) 0)))
|
|
(send (new c% [x 1]) m 5))
|
|
-Integer]
|
|
;; Fails, bad superclass expression
|
|
[tc-err (let ()
|
|
(: d% (Class (init [x Integer])
|
|
[m (Integer -> Integer)]))
|
|
(define d% (class 5
|
|
(super-new)
|
|
(init x)
|
|
(define/public (m x) 0)))
|
|
(void))
|
|
#:ret (ret -Void)
|
|
#:msg #rx"superclass expression should produce a class"]
|
|
;; Method using argument type
|
|
[tc-e (let ()
|
|
(: e% (Class (init [x Integer])
|
|
[m (Integer -> Integer)]))
|
|
(define e% (class object%
|
|
(super-new)
|
|
(init x)
|
|
(define/public (m x) x)))
|
|
(void))
|
|
-Void]
|
|
;; Send inside a method
|
|
[tc-e (let ()
|
|
(: f% (Class (init [x Integer])
|
|
[m (Integer -> Integer)]))
|
|
(define f% (class object%
|
|
(super-new)
|
|
(init x)
|
|
(define/public (m x) (send this m 3))))
|
|
(void))
|
|
-Void]
|
|
;; Fails, send to missing method
|
|
[tc-err (let ()
|
|
(: g% (Class (init [x Integer #:optional])
|
|
[m (Integer -> Integer)]))
|
|
(define g% (class object%
|
|
(super-new)
|
|
(init [x 0])
|
|
(define/public (m x) (send this z))))
|
|
(void))
|
|
#:ret (ret -Void)
|
|
#:msg #rx"method not understood.*method name: z"]
|
|
;; Send to other methods
|
|
[tc-e (let ()
|
|
(: h% (Class [n (-> Integer)]
|
|
[m (Integer -> Integer)]))
|
|
(define h% (class object%
|
|
(super-new)
|
|
(define/public (n) 0)
|
|
(define/public (m x) (send this n))))
|
|
(void))
|
|
-Void]
|
|
;; Local sends
|
|
[tc-e (let ()
|
|
(: i% (Class [n (-> Integer)]
|
|
[m (Integer -> Integer)]))
|
|
(define i% (class object%
|
|
(super-new)
|
|
(define/public (n) 0)
|
|
(define/public (m x) (n))))
|
|
(void))
|
|
-Void]
|
|
;; Send to non object
|
|
[tc-err (send 4 m 3)
|
|
#:ret (ret (-val 5) -bot-filter)
|
|
#:expected (ret (-val 5) -no-filter -no-obj)]
|
|
;; Fails, sending to multiple/unknown values
|
|
[tc-err (send (values 'a 'b) m 'c)
|
|
#:msg #rx"expected single value"]
|
|
[tc-err (send (eval "3") m 'c)
|
|
#:msg #rx"expected single value"]
|
|
;; Send to a union of objects in various ways
|
|
[tc-e (let ()
|
|
(: f (-> (U (Object [m (-> String)])
|
|
(Object [m (-> Symbol)]
|
|
[n (-> Void)]))
|
|
(U Symbol String)))
|
|
(define (f o) (send o m))
|
|
(f (new (class object% (super-new) (define/public (m) "foo")))))
|
|
(t:Un -String -Symbol)]
|
|
[tc-e (let ()
|
|
(: f (-> (U (Object [m (-> (values String Symbol))])
|
|
(Object [m (-> (values Symbol String))]
|
|
[n (-> Void)]))
|
|
(values (U Symbol String) (U Symbol String))))
|
|
(define (f o) (send o m))
|
|
(f (new (class object% (super-new)
|
|
(define/public (m) (values "foo" 'bar))))))
|
|
#:ret (ret (list (t:Un -String -Symbol) (t:Un -String -Symbol)))]
|
|
[tc-err
|
|
(let ()
|
|
(define obj
|
|
(if (> (random) 0.5)
|
|
(new (class object% (super-new)
|
|
(define/public (m) "foo")))
|
|
(new (class object% (super-new)
|
|
(define/public (m) (values "foo" "bar"))))))
|
|
(send obj m))
|
|
#:msg #rx"Expected the same number of values.*got 1 and 2"]
|
|
;; Field access via get-field
|
|
[tc-e (let ()
|
|
(: j% (Class (field [n Integer])
|
|
[m (-> Integer)]))
|
|
(define j% (class object%
|
|
(super-new)
|
|
(field [n 0])
|
|
(define/public (m) (get-field n this))))
|
|
(void))
|
|
-Void]
|
|
;; Test set-field!
|
|
[tc-e (set-field! x
|
|
(new (class object%
|
|
(super-new)
|
|
(field [x : String "foo"])))
|
|
"bar")
|
|
-Void]
|
|
;; fails, check set-field! type error
|
|
[tc-err (set-field! x
|
|
(new (class object%
|
|
(super-new)
|
|
(field [x : String "foo"])))
|
|
'not-string)
|
|
#:ret (ret -Void)
|
|
#:msg #rx"set-field! only allowed with"]
|
|
;; fails, field's default value has wrong type
|
|
[tc-err (class object% (super-new)
|
|
(: x Symbol)
|
|
(field [x "foo"]))
|
|
#:ret (ret (-class #:field ([x -Symbol])))
|
|
#:msg #rx"expected: Symbol.*given: String"]
|
|
;; Fail, field access to missing field
|
|
[tc-err (let ()
|
|
(: k% (Class [m (-> Integer)]))
|
|
(define k% (class object%
|
|
(super-new)
|
|
(define/public (m) (get-field n this))))
|
|
(void))
|
|
#:ret (ret -Void)
|
|
#:msg #rx"missing an expected field.*field: n"]
|
|
;; Fail, conflict with parent field
|
|
[tc-err (let ()
|
|
(: j% (Class (field [n Integer])
|
|
[m (-> Integer)]))
|
|
(define j% (class object%
|
|
(super-new)
|
|
(field [n 0])
|
|
(define/public (m) (get-field n this))))
|
|
(: l% (Class (field [n Integer])
|
|
[m (-> Integer)]))
|
|
(define l% (class j%
|
|
(field [n 17])
|
|
(super-new)))
|
|
(void))
|
|
#:ret (ret -Void)
|
|
#:msg #rx"has a conflicting public field.*field: n"]
|
|
;; Fail, conflict with parent method
|
|
[tc-err (let ()
|
|
(: j% (Class [m (-> Integer)]))
|
|
(define j% (class object%
|
|
(super-new)
|
|
(define/public (m) 15)))
|
|
(: m% (Class [m (-> Integer)]))
|
|
(define m% (class j%
|
|
(super-new)
|
|
(define/public (m) 17)))
|
|
(void))
|
|
#:ret (ret -Void)
|
|
#:msg #rx"has a conflicting public method.*method: m"]
|
|
;; Inheritance
|
|
[tc-e (let ()
|
|
(: j% (Class (field [n Integer])
|
|
[m (-> Integer)]))
|
|
(define j% (class object%
|
|
(super-new)
|
|
(field [n 0])
|
|
(define/public (m) (get-field n this))))
|
|
(: n% (Class (field [n Integer])
|
|
[m (-> Integer)]))
|
|
(define n% (class j% (super-new)))
|
|
(void))
|
|
-Void]
|
|
;; fail, superclass expression is not a class with no expected type
|
|
[tc-err (class "foo" (super-new))
|
|
#:ret (ret (-class))
|
|
#:msg "expected: a class"]
|
|
;; should fail, too many methods
|
|
[tc-err (let ()
|
|
(: o% (Class))
|
|
(define o% (class object%
|
|
(super-new)
|
|
(define/public (m) 0)))
|
|
(void))
|
|
#:ret (ret -Void)
|
|
#:msg #rx"method `m' that is not in expected type"]
|
|
;; same as previous
|
|
[tc-err (let ()
|
|
(: c% (Class [m (Integer -> Integer)]))
|
|
(define c% (class object% (super-new)
|
|
(define/public (m x) (add1 x))
|
|
(define/public (n) 0)))
|
|
(void))
|
|
#:ret (ret -Void)
|
|
#:msg #rx"method `n' that is not in expected type"]
|
|
;; fails, too many inits
|
|
[tc-err (let ()
|
|
(: c% (Class))
|
|
(define c% (class object% (super-new)
|
|
(init x)))
|
|
(void))
|
|
#:ret (ret -Void)
|
|
#:msg #rx"init `x' that is not in expected type"]
|
|
;; fails, init should be optional but is mandatory
|
|
[tc-err (let ()
|
|
(: c% (Class (init [str String #:optional])))
|
|
(define c% (class object% (super-new)
|
|
(init str)))
|
|
(void))
|
|
#:ret (ret -Void)
|
|
#:msg #rx"expected: optional init `str'.*given: mandatory init `str'"]
|
|
;; fails, too many fields
|
|
[tc-err (let ()
|
|
(: c% (Class (field [str String])))
|
|
(define c% (class object% (super-new)
|
|
(field [str "foo"] [x 0])))
|
|
(void))
|
|
#:ret (ret -Void)
|
|
#:msg #rx"field `x' that is not in expected type"]
|
|
;; test that an init with no annotation still type-checks
|
|
;; (though it will have the Any type)
|
|
[tc-e (let () (class object% (super-new) (init x)) (void)) -Void]
|
|
;; test that a field with no annotation still type-checks
|
|
;; (though it will have the Any type)
|
|
[tc-e (let () (class object% (super-new) (field [x 0])) (void)) -Void]
|
|
;; Mixin on classes without row polymorphism
|
|
[tc-e (let ()
|
|
(: mixin ((Class [m (-> Integer)])
|
|
->
|
|
(Class [m (-> Integer)]
|
|
[n (-> String)])))
|
|
(define (mixin cls)
|
|
(class cls
|
|
(super-new)
|
|
(define/public (n) "hi")))
|
|
|
|
(: arg-class% (Class [m (-> Integer)]))
|
|
(define arg-class%
|
|
(class object%
|
|
(super-new)
|
|
(define/public (m) 0)))
|
|
|
|
(mixin arg-class%)
|
|
(void))
|
|
-Void]
|
|
;; Fail, bad mixin
|
|
[tc-err (let ()
|
|
(: mixin ((Class [m (-> Integer)])
|
|
->
|
|
(Class [m (-> Integer)]
|
|
[n (-> String)])))
|
|
(define (mixin cls)
|
|
(class cls
|
|
(super-new)))
|
|
|
|
(: arg-class% (Class [m (-> Integer)]))
|
|
(define arg-class%
|
|
(class object%
|
|
(super-new)
|
|
(define/public (m) 0)))
|
|
|
|
(mixin arg-class%))
|
|
#:ret (ret (-class #:method ([m (t:-> -Integer)] [n (t:-> -String)])))
|
|
#:msg #rx"lacks expected method `n'"]
|
|
;; Fail, bad mixin argument
|
|
[tc-err (let ()
|
|
(: mixin ((Class [m (-> Symbol)])
|
|
->
|
|
(Class [m (-> Symbol)]
|
|
[n (-> String)])))
|
|
(define (mixin cls)
|
|
(class cls
|
|
(super-new)
|
|
(define/public (n) "hi")))
|
|
|
|
(: arg-class% (Class [k (-> Symbol)]))
|
|
(define arg-class%
|
|
(class object%
|
|
(super-new)
|
|
(define/public (k) 'k)))
|
|
|
|
(mixin arg-class%)
|
|
(void))
|
|
#:ret (ret -Void)
|
|
#:msg #rx"lacks expected method `m'"]
|
|
;; classes that don't use define/public directly
|
|
[tc-e (let ()
|
|
(: c% (Class [m (Number -> String)]))
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(public m)
|
|
(define-values (m)
|
|
(lambda (x) (number->string x)))))
|
|
(send (new c%) m 4))
|
|
-String]
|
|
;; check that classes work in let clauses
|
|
[tc-e (let: ([c% : (Class [m (Number -> String)])
|
|
(class object%
|
|
(super-new)
|
|
(public m)
|
|
(define-values (m)
|
|
(lambda (x) (number->string x))))])
|
|
(send (new c%) m 4))
|
|
-String]
|
|
;; check a good super-new call
|
|
[tc-e (let ()
|
|
(: c% (Class (init [x Integer])))
|
|
(define c% (class object% (super-new) (init x)))
|
|
(: d% (Class))
|
|
(define d% (class c% (super-new [x (+ 3 5)])))
|
|
(void))
|
|
-Void]
|
|
;; fails, missing super-new
|
|
[tc-err (let ()
|
|
(: c% (Class (init [x Integer])))
|
|
(define c% (class object% (init x)))
|
|
(void))
|
|
#:ret (ret -Void)
|
|
#:msg #rx"must call `super-new'"]
|
|
;; fails, non-top-level super-new
|
|
;; FIXME: this case also spits out additional untyped identifier
|
|
;; errors which should be squelched maybe
|
|
[tc-err (let ()
|
|
(: c% (Class (init [x Integer])))
|
|
(define c% (class object% (let () (super-new)) (init x)))
|
|
(void))
|
|
#:ret (ret -Void)
|
|
#:msg #rx"must call `super-new'"]
|
|
;; fails, bad super-new argument
|
|
[tc-err (let ()
|
|
(: c% (Class (init [x Symbol])))
|
|
(define c% (class object% (super-new) (init x)))
|
|
(: d% (Class))
|
|
(define d% (class c% (super-new [x "bad"])))
|
|
(void))
|
|
#:ret (ret -Void)
|
|
#:msg #rx"expected: Symbol.*given: String"]
|
|
;; test override
|
|
[tc-e (let ()
|
|
(: c% (Class [m (Integer -> Integer)]))
|
|
(define c% (class object% (super-new)
|
|
(define/public (m y) (add1 y))))
|
|
(: d% (Class [m (Integer -> Integer)]))
|
|
(define d% (class c% (super-new)
|
|
(define/override (m y) (* 2 y))))
|
|
(void))
|
|
-Void]
|
|
;; test local call to overriden method
|
|
[tc-e (let ()
|
|
(: c% (Class [m (Integer -> Integer)]))
|
|
(define c% (class object% (super-new)
|
|
(define/public (m y) (add1 y))))
|
|
(: d% (Class [n (Integer -> Integer)]
|
|
[m (Integer -> Integer)]))
|
|
(define d% (class c% (super-new)
|
|
(define/public (n x) (m x))
|
|
(define/override (m y) (* 2 y))))
|
|
(void))
|
|
-Void]
|
|
;; fails, superclass missing public for override
|
|
[tc-err (let ()
|
|
(: d% (Class [m (String -> String)]))
|
|
(define d% (class object% (super-new)
|
|
(define/override (m y)
|
|
(string-append (assert y string?) "foo"))))
|
|
(void))
|
|
#:ret (ret -Void)]
|
|
;; local field access and set!
|
|
[tc-e (let ()
|
|
(: c% (Class (field [x Integer])
|
|
[m (Integer -> Integer)]))
|
|
(define c% (class object% (super-new)
|
|
(field [x 0])
|
|
(define/public (m y)
|
|
(begin0 x (set! x (+ x 1))))))
|
|
(void))
|
|
-Void]
|
|
;; test top-level expressions in the class
|
|
[tc-e (let ()
|
|
(: c% (Class [m (Integer -> Integer)]))
|
|
(define c% (class object% (super-new)
|
|
(define/public (m y) 0)
|
|
(+ 3 5)))
|
|
(void))
|
|
-Void]
|
|
;; test top-level method call
|
|
[tc-e (let ()
|
|
(: c% (Class [m (Integer -> Integer)]))
|
|
(define c% (class object% (super-new)
|
|
(define/public (m y) 0)
|
|
(m 3)))
|
|
(void))
|
|
-Void]
|
|
;; test top-level field access
|
|
[tc-e (let ()
|
|
(: c% (Class (field [f String])))
|
|
(define c% (class object% (super-new)
|
|
(field [f "foo"])
|
|
(string-append f "z")))
|
|
(void))
|
|
-Void]
|
|
;; fails, bad top-level expression
|
|
[tc-err (let ()
|
|
(: c% (Class [m (Symbol -> Symbol)]))
|
|
(define c% (class object% (super-new)
|
|
(define/public (m y) 'a)
|
|
(string-append (string->symbol "a") "a")))
|
|
(void))
|
|
#:ret (ret -Void)
|
|
#:msg #rx"expected: String.*given: Symbol"]
|
|
;; fails, ill-typed method call
|
|
[tc-err (let ()
|
|
(: c% (Class [m (Symbol -> Symbol)]))
|
|
(define c% (class object% (super-new)
|
|
(define/public (m y) 'a)
|
|
(m "foo")))
|
|
(void))
|
|
#:ret (ret -Void)
|
|
#:msg #rx"expected: Symbol.*given: String"]
|
|
;; fails, ill-typed field access
|
|
[tc-err (let ()
|
|
(: c% (Class (field [f String])))
|
|
(define c% (class object% (super-new)
|
|
(field [f "foo"])
|
|
(set! f 'a)))
|
|
(void))
|
|
#:ret (ret -Void)
|
|
#:msg #rx"expected: String.*given: 'a"]
|
|
;; test private field
|
|
[tc-e (let ()
|
|
(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"])))
|
|
(void))
|
|
-Void]
|
|
;; fails, bad private field set!
|
|
[tc-err (class object%
|
|
(super-new)
|
|
(: x Symbol)
|
|
(define x 'a)
|
|
(set! x "foo"))
|
|
#:ret (ret (-class))
|
|
#:msg #rx"expected: Symbol.*given: String"]
|
|
;; fails, bad private field default
|
|
[tc-err (class object%
|
|
(super-new)
|
|
(: x Symbol)
|
|
(define x "foo"))
|
|
#:ret (ret (-class))
|
|
#:msg #rx"expected: Symbol.*given: String"]
|
|
;; ok, synthesis works on private fields
|
|
[tc-e (class object% (super-new)
|
|
(define x "foo") (string-append x "bar"))
|
|
(-class)]
|
|
;; private field with function
|
|
[tc-e (class object%
|
|
(super-new)
|
|
(: f (-> String))
|
|
(define (f) "foo"))
|
|
(-class)]
|
|
[tc-err (let ()
|
|
(class object%
|
|
(super-new)
|
|
(: f (-> String))
|
|
(define (f) 'bad))
|
|
(error "foo"))
|
|
#:msg #rx"type mismatch.*expected: String"]
|
|
;; multiple names in define-values private fields
|
|
[tc-e (class object%
|
|
(super-new)
|
|
(define-values (x y z) (values 'x 'y 'z)))
|
|
(-class)]
|
|
;; test private method
|
|
[tc-e (let ()
|
|
(class object% (super-new)
|
|
(: x (-> Integer))
|
|
(define/private (x) 3)
|
|
(: m (-> Integer))
|
|
(define/public (m) (x)))
|
|
(void))
|
|
-Void]
|
|
;; fails, public and private types conflict
|
|
[tc-err (class object% (super-new)
|
|
(: x (-> Symbol))
|
|
(define/private (x) 'a)
|
|
(: m (-> String))
|
|
(define/public (m) (x)))
|
|
#:ret (ret (-class #:method ([m (t:-> -String)])))
|
|
#:msg #rx"expected: String.*given: Symbol"]
|
|
;; fails, not enough annotation on private
|
|
[tc-err (class object% (super-new)
|
|
(define/private (x) 3)
|
|
(: m (-> Integer))
|
|
(define/public (m) (x)))
|
|
#:ret (ret (-class #:method ([m (t:-> -Integer)])))
|
|
#:msg #rx"Cannot apply expression of type Any"]
|
|
;; fails, ill-typed private method implementation
|
|
[tc-err (class object% (super-new)
|
|
(: x (-> Symbol))
|
|
(define/private (x) "bad result"))
|
|
#:ret (ret (-class))
|
|
#:msg #rx"expected: Symbol.*given: String"]
|
|
;; test optional init arg
|
|
[tc-e (let ()
|
|
(: c% (Class (init [x Integer #:optional])))
|
|
(define c% (class object% (super-new)
|
|
(: x Integer)
|
|
(init [x 0])))
|
|
(void))
|
|
-Void]
|
|
;; test init coverage when all optionals are
|
|
;; in the superclass
|
|
[tc-e (let ()
|
|
(: c% (Class (init [x Integer #:optional])))
|
|
(: d% (Class (init [x Integer #:optional])))
|
|
(define c% (class object% (super-new)
|
|
(: x Integer)
|
|
(init [x 0])))
|
|
(define d% (class c% (super-new)))
|
|
(void))
|
|
-Void]
|
|
;; fails, expected mandatory but got optional
|
|
[tc-err (let ()
|
|
(: c% (Class (init [x Integer])))
|
|
(define c% (class object% (super-new)
|
|
(: x Integer)
|
|
(init [x 0])))
|
|
(void))
|
|
#:ret (ret -Void)
|
|
#:msg #rx"expected: mandatory init `x'.*given: optional init `x'"]
|
|
;; fails, mandatory init not provided
|
|
[tc-err (let ()
|
|
(define d% (class object% (super-new)
|
|
(: x Integer)
|
|
(init x)))
|
|
(new d%))
|
|
#:ret (ret (-object #:init ([x -Integer #f])))
|
|
#:msg #rx"value not provided for named init arg x"]
|
|
;; test that provided super-class inits don't count
|
|
;; towards the type of current class
|
|
[tc-e (let ()
|
|
(: c% (Class))
|
|
(define c% (class (class object% (super-new)
|
|
(: x Integer)
|
|
(init x))
|
|
(super-new [x 3])))
|
|
(void))
|
|
-Void]
|
|
;; fails, super-class init already provided
|
|
[tc-err (let ()
|
|
(define c% (class (class object% (super-new)
|
|
(: x Integer)
|
|
(init x))
|
|
(super-new [x 3])))
|
|
(new c% [x 5]))
|
|
#:ret (ret (-object))]
|
|
;; fails, super-new can only be called once per class
|
|
[tc-err (class object% (super-new) (super-new))
|
|
#:ret (ret (-class))
|
|
#:msg #rx"`super-new' a single time"]
|
|
;; test passing an init arg to super-new
|
|
[tc-e (let ()
|
|
(define c% (class (class object% (super-new)
|
|
(: x Integer)
|
|
(init x))
|
|
(: x Integer)
|
|
(init x)
|
|
(super-new [x x])))
|
|
(new c% [x 5])
|
|
(void))
|
|
-Void]
|
|
;; fails, bad argument type to super-new
|
|
[tc-err (class (class object% (super-new)
|
|
(: x Integer)
|
|
(init x))
|
|
(: x String)
|
|
(init x)
|
|
(super-new [x x]))
|
|
#:ret (ret (-class #:init ([x -String #f])))]
|
|
;; fails, superclass does not accept this init arg
|
|
[tc-err (class object% (super-new [x 3]))
|
|
#:ret (ret (-class))
|
|
#:msg "not accepted by superclass"]
|
|
;; test inherit method
|
|
[tc-e (let ()
|
|
(class (class object% (super-new)
|
|
(: m (Integer -> Integer))
|
|
(define/public (m x) (add1 x)))
|
|
(super-new)
|
|
(inherit m)
|
|
(m 5))
|
|
(void))
|
|
-Void]
|
|
;; test inherit method in another method (next 3)
|
|
[tc-e (let ()
|
|
(class (class object% (super-new)
|
|
(: m (-> String String))
|
|
(define/public (m x) (string-append x "m")))
|
|
(super-new)
|
|
(inherit m)
|
|
(: n (-> String))
|
|
(define/public (n) (m "foo")))
|
|
(void))
|
|
-Void]
|
|
[tc-e (let ()
|
|
(class (class object% (super-new)
|
|
(: m (-> String String))
|
|
(define/public (m x) (string-append x "m")))
|
|
(super-new)
|
|
(inherit m)
|
|
(define/public (n) (m "foo")))
|
|
(void))
|
|
-Void]
|
|
[tc-e (let ()
|
|
(class (class object% (super-new)
|
|
(: m (-> String String))
|
|
(define/public (m x) (string-append x "m")))
|
|
(super-new)
|
|
(inherit m)
|
|
(define/private (n) (m "foo")))
|
|
(void))
|
|
-Void]
|
|
;; test internal name with inherit
|
|
[tc-e (let ()
|
|
(class (class object% (super-new)
|
|
(: m (Integer -> Integer))
|
|
(define/public (m x) (add1 x)))
|
|
(super-new)
|
|
(inherit [n m])
|
|
(n 5))
|
|
(void))
|
|
-Void]
|
|
;; test inherit field
|
|
[tc-e (let ()
|
|
(class (class object% (super-new)
|
|
(field [x : Integer 0]))
|
|
(super-new)
|
|
(inherit-field x))
|
|
(void))
|
|
-Void]
|
|
;; test internal name with inherit-field
|
|
[tc-e (let ()
|
|
(class (class object% (super-new)
|
|
(field [x : String "b"]))
|
|
(super-new)
|
|
(inherit-field [y x])
|
|
(set! y "a"))
|
|
(void))
|
|
-Void]
|
|
;; fails, superclass missing inherited field
|
|
[tc-err (class (class object% (super-new))
|
|
(super-new)
|
|
(inherit-field [y x]))
|
|
#:ret (ret (-class))
|
|
#:msg #rx"superclass is missing a required field"]
|
|
;; fails, missing super method for inherit
|
|
[tc-err (class (class object% (super-new)) (super-new) (inherit z))
|
|
#:ret (ret (-class))]
|
|
;; fails, bad argument type to inherited method
|
|
[tc-err (class (class object% (super-new)
|
|
(: m (Integer -> Integer))
|
|
(define/public (m x) (add1 x)))
|
|
(super-new)
|
|
(inherit m)
|
|
(m "foo"))
|
|
#:ret (ret (-class #:method ([m (t:-> -Integer -Integer)])))]
|
|
;; test that keyword methods type-check
|
|
[tc-e (let ()
|
|
(: c% (Class [n (Integer #:foo Integer -> Integer)]))
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(define/public (n x #:foo foo)
|
|
(+ foo x))))
|
|
(send (new c%) n 0 #:foo 1))
|
|
-Integer]
|
|
;; fails, bad kw argument
|
|
[tc-err (let ()
|
|
(: c% (Class [n (Integer #:foo Integer -> Integer)]))
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(define/public (n x #:foo foo)
|
|
(+ foo x))))
|
|
(send (new c%) n 0 #:foo "foo")
|
|
(error "foo"))
|
|
#:msg #rx"expected Integer.*got String"]
|
|
;; test instance subtyping
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(: x (U False Number))
|
|
(field [x 0])))
|
|
(: x (Instance (Class)))
|
|
(define x (new c%))
|
|
(void))
|
|
-Void]
|
|
;; failing instance subtyping
|
|
[tc-err (let ()
|
|
(define x (new (class object% (super-new) (define/public (m) "m"))))
|
|
(ann x (Object [n (-> String)]))
|
|
(error "foo"))
|
|
#:msg #rx"lacks expected method `n'"]
|
|
[tc-err (let ()
|
|
(define x (new (class object% (super-new))))
|
|
(ann x (Object (field [x String])))
|
|
(error "foo"))
|
|
#:msg #rx"lacks expected field `x'"]
|
|
[tc-err (let ()
|
|
(define x (new (class object% (super-new) (define/public (m) "m"))))
|
|
(ann x (Object [m (-> Symbol)]))
|
|
(error "foo"))
|
|
#:msg #rx"expected: \\(-> Symbol\\).*given: \\(-> String"]
|
|
[tc-err (let ()
|
|
(define x (new (class object% (super-new) (field [x : Symbol 'x]))))
|
|
(ann x (Object (field [x String])))
|
|
(error "foo"))
|
|
#:msg #rx"expected: String.*given: Symbol"]
|
|
;; test use of `this` in field default
|
|
[tc-e (let ()
|
|
(class object%
|
|
(super-new)
|
|
(: x Integer)
|
|
(field [x 0])
|
|
(: y Integer)
|
|
(field [y (get-field x this)]))
|
|
(void))
|
|
-Void]
|
|
;; test super calls
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(: m (String -> String))
|
|
(define/public (m x) "a")))
|
|
(define d%
|
|
(class c%
|
|
(super-new)
|
|
(define/override (m x)
|
|
(string-append "x" (super m "b")))))
|
|
(send (new d%) m "c"))
|
|
-String]
|
|
;; test super calls at top-level
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(: m (Integer -> Integer))
|
|
(define/public (m x) 0)))
|
|
(define d%
|
|
(class c%
|
|
(super-new)
|
|
(super m 5)
|
|
(define/override (m x) 5)))
|
|
(void))
|
|
-Void]
|
|
;; fails, bad super call argument
|
|
[tc-err (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(: m (Integer -> Integer))
|
|
(define/public (m x) 0)))
|
|
(define d%
|
|
(class c%
|
|
(super-new)
|
|
(super m "foo")
|
|
(define/override (m x) 5))))]
|
|
|
|
;; test different internal/external names
|
|
[tc-e (let ()
|
|
(define c% (class object% (super-new)
|
|
(public [m n])
|
|
(define m (lambda () 0))))
|
|
(send (new c%) n)
|
|
(void))
|
|
-Void]
|
|
;; test local calls with internal/external
|
|
[tc-e (let ()
|
|
(define c% (class object% (super-new)
|
|
(: m (-> String))
|
|
(public [m n])
|
|
(define m (lambda () "a"))
|
|
(: z (-> String))
|
|
(define/public (z) (m))))
|
|
(send (new c%) z))
|
|
-String]
|
|
;; internal/external the same is ok
|
|
[tc-e (let ()
|
|
(define c% (class object% (super-new)
|
|
(public [m m])
|
|
(define m (lambda () "a"))))
|
|
(send (new c%) m))
|
|
#:ret (ret -String -true-filter)]
|
|
;; fails, internal name not accessible
|
|
[tc-err (let ()
|
|
(define c% (class object% (super-new)
|
|
(public [m n])
|
|
(define m (lambda () 0))))
|
|
(send (new c%) m))]
|
|
;; test internal/external with expected
|
|
[tc-e (let ()
|
|
(: c% (Class [n (-> String)]))
|
|
(define c% (class object% (super-new)
|
|
(public [m n])
|
|
(define m (lambda () "a"))))
|
|
(send (new c%) n))
|
|
-String]
|
|
;; test internal/external field
|
|
[tc-e (let ()
|
|
(define c% (class object% (super-new)
|
|
(: f String)
|
|
(field ([f g] "a"))))
|
|
(get-field g (new c%)))
|
|
-String]
|
|
;; fail, internal name not accessible
|
|
[tc-err (let ()
|
|
(define c% (class object% (super-new)
|
|
(: f String)
|
|
(field ([f g] "a"))))
|
|
(get-field f (new c%)))]
|
|
;; test internal/external init
|
|
[tc-e (let ()
|
|
(define c% (class object% (super-new)
|
|
(: i String)
|
|
(init ([i j]))))
|
|
(new c% [j "a"])
|
|
(void))
|
|
-Void]
|
|
;; fails, internal name not accessible
|
|
[tc-err (let ()
|
|
(define c% (class object% (super-new)
|
|
(: i Integer)
|
|
(init ([i j]))))
|
|
(new c% [i 5]))
|
|
#:ret (ret (-object #:init ([j -Integer #f])))]
|
|
;; test that different internal names can map to the same external name
|
|
;; and that the internal-external name mapping is set correctly.
|
|
[tc-e (class object%
|
|
(super-new)
|
|
(: x* String)
|
|
(init [(x* x)])
|
|
(field [x "x"]))
|
|
(-class #:init ([x -String #f]) #:field ([x Univ]))]
|
|
;; test init default values
|
|
[tc-e (let ()
|
|
(class object% (super-new)
|
|
(: z Integer)
|
|
(init [z 0]))
|
|
(void))
|
|
-Void]
|
|
;; fails, bad default init value
|
|
[tc-err (class object% (super-new)
|
|
(: z Integer)
|
|
(init [z "foo"]))
|
|
#: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)
|
|
(: x Integer)
|
|
(init-field ([x y] 0))))
|
|
(void))
|
|
-Void]
|
|
;; fails, wrong init-field default
|
|
[tc-err (class object% (super-new)
|
|
(: x Integer)
|
|
(init-field ([x y] "foo")))
|
|
#:ret (ret (-class #:init ([y -Integer #t]) #:field ([y -Integer])))]
|
|
;; test type-checking method with internal/external
|
|
[tc-err (let ()
|
|
(: 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
|
|
[tc-e (let ()
|
|
(define c% (class object% (super-new)
|
|
(: m (String -> String))
|
|
(define/public (m x) "a")))
|
|
(send (new c%) m "b"))
|
|
-String]
|
|
;; fails, because the local call type is unknown
|
|
;; and is assumed to be Any
|
|
[tc-err (class object% (super-new)
|
|
(define/public (m) (n))
|
|
(define/public (n x) 0))
|
|
#:ret (ret (-class #:method ([m (t:-> -Bottom)] [n (t:-> Univ -Zero : -true-filter)])))
|
|
#:msg #rx"since it is not a function type"]
|
|
;; test type-checking for classes without any
|
|
;; internal type annotations on methods
|
|
[tc-e (let ()
|
|
(define c% (class object% (super-new)
|
|
(define/public (m) "a")))
|
|
(send (new c%) m))
|
|
#:ret (ret -String -true-filter)]
|
|
;; test inheritance without expected
|
|
[tc-e (let ()
|
|
(define c% (class (class object% (super-new)
|
|
(: m (-> String))
|
|
(define/public (m) "a"))
|
|
(super-new)
|
|
(: n (-> String))
|
|
(define/public (n) "b")))
|
|
(send (new c%) m)
|
|
(send (new c%) n))
|
|
-String]
|
|
;; test fields without expected class type
|
|
[tc-e (let ()
|
|
(define c% (class object% (super-new)
|
|
(: x String)
|
|
(field [x "a"])))
|
|
(get-field x (new c%)))
|
|
-String]
|
|
;; row polymorphism, basic example with instantiation
|
|
[tc-e (let ()
|
|
(: f (All (A #:row (field x))
|
|
((Class #:row-var A)
|
|
->
|
|
(Class #:row-var A (field [x Integer])))))
|
|
(define (f cls)
|
|
(class cls (super-new)
|
|
(field [x 5])))
|
|
(inst f #:row (field [y Integer]))
|
|
(void))
|
|
-Void]
|
|
;; fails, because the instantiation uses a field that
|
|
;; is supposed to be absent via the row constraint
|
|
[tc-err (let ()
|
|
(: f (All (A #:row (field x))
|
|
((Class #:row-var A)
|
|
->
|
|
(Class #:row-var A (field [x Integer])))))
|
|
(define (f cls)
|
|
(class cls (super-new)
|
|
(field [x 5])))
|
|
(inst f #:row (field [x Integer])))
|
|
#:ret (ret (t:-> (-class
|
|
#:row (make-Row null `([x ,-Integer]) null null #f))
|
|
(-class
|
|
#:row (make-Row null `([x ,-Integer]) null null #f)
|
|
#:field ([x -Integer])))
|
|
-true-filter)]
|
|
;; fails, mixin argument is missing required field
|
|
[tc-err (let ()
|
|
(: f (All (A #:row (field x))
|
|
((Class #:row-var A)
|
|
->
|
|
(Class #:row-var A (field [x Integer])))))
|
|
(define (f cls)
|
|
(class cls (super-new)
|
|
(field [x 5])))
|
|
(define instantiated
|
|
(inst f #:row (field [y Integer])))
|
|
(instantiated
|
|
(class object% (super-new))))
|
|
#:ret (ret (-class
|
|
#:row (make-Row null (list (list 'y -Integer)) null null #f)
|
|
#:field ([x -Integer])))]
|
|
;; fails, the argument object lacks required fields (with inference)
|
|
[tc-err (let ()
|
|
(: mixin (All (r #:row)
|
|
(-> (Class (field [x Any]) #:row-var r)
|
|
(Class (field [x Any]) #:row-var r))))
|
|
(define (mixin cls) cls)
|
|
(mixin object%))
|
|
#:ret (ret (-class #:row (make-Row null null null null #f)
|
|
#:field ([x Univ])))
|
|
#:msg #rx"lacks expected field `x'"]
|
|
;; mixin application succeeds
|
|
[tc-e (let ()
|
|
(: f (All (A #:row (field x))
|
|
((Class #:row-var A)
|
|
->
|
|
(Class #:row-var A (field [x Integer])))))
|
|
(define (f cls)
|
|
(class cls (super-new)
|
|
(field [x 5])))
|
|
(define instantiated
|
|
(inst f #:row (field [y Integer])))
|
|
(instantiated
|
|
(class object% (super-new)
|
|
(: y Integer)
|
|
(field [y 0])))
|
|
(void))
|
|
-Void]
|
|
;; Basic row constraint inference
|
|
[tc-e (let ()
|
|
(: f (All (A #:row) ; inferred
|
|
((Class #:row-var A)
|
|
->
|
|
(Class #:row-var A (field [x Integer])))))
|
|
(define (f cls)
|
|
(class cls (super-new)
|
|
(field [x 5])))
|
|
(inst f #:row (field [y Integer]))
|
|
(void))
|
|
-Void]
|
|
;; fails, inferred constraint and instantiation don't match
|
|
[tc-err (let ()
|
|
(: f (All (A #:row)
|
|
((Class #:row-var A)
|
|
->
|
|
(Class #:row-var A (field [x Integer])))))
|
|
(define (f cls)
|
|
(class cls (super-new)
|
|
(field [x 5])))
|
|
(inst f #:row (field [x Integer])))
|
|
#:ret (ret (t:-> (-class
|
|
#:row (make-Row null `([x ,-Integer]) null null #f))
|
|
(-class
|
|
#:row (make-Row null `([x ,-Integer]) null null #f)
|
|
#:field ([x -Integer])))
|
|
-true-filter)]
|
|
;; test bad manipulation of rows for inheritance
|
|
[tc-e (let ()
|
|
(: c% (Class (init [x String] [y String])))
|
|
(define c% (class object% (super-new) (init x y)))
|
|
(: f (All (r #:row) (-> (Class #:row-var r) (Class #:row-var r))))
|
|
(define (f x) x)
|
|
;; should have the same type as c%
|
|
(define c2% (f c%))
|
|
(: d% (Class (init [y String])))
|
|
;; should be the same as inheriting from c%
|
|
(define d% (class c2% (super-new [x "foo"])))
|
|
(void))
|
|
-Void]
|
|
;; Check simple use of pubment
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(: m : String -> String)
|
|
(define/pubment (m x) "a")))
|
|
(send (new c%) m "b"))
|
|
-String]
|
|
;; Local calls to pubment method
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(: m : String -> String)
|
|
(define/pubment (m x) "a")
|
|
(: n (-> String))
|
|
(define/public (n) (m "b"))))
|
|
(send (new c%) n))
|
|
-String]
|
|
;; Inheritance with augment
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(: m : String -> String)
|
|
(define/pubment (m x) "a")))
|
|
(define d%
|
|
(class c%
|
|
(super-new)
|
|
(define/augment (m x) (string-append x "b"))))
|
|
(send (new c%) m "c"))
|
|
-String]
|
|
;; Pubment with inner
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(: m : String -> String)
|
|
(define/pubment (m x)
|
|
(inner "a" m x))))
|
|
(define d%
|
|
(class c%
|
|
(super-new)
|
|
(define/augment (m x)
|
|
(string-append "foo" x))))
|
|
(send (new c%) m "b"))
|
|
-String]
|
|
;; make sure augment type is reflected in class type
|
|
[tc-e (let ()
|
|
(: c% (Class (augment [m (String -> Integer)])
|
|
[m (Integer -> Integer)]))
|
|
(define c%
|
|
(class object% (super-new)
|
|
(: m (Integer -> Integer)
|
|
#:augment (String -> Integer))
|
|
(define/pubment (m x) x)))
|
|
(void))
|
|
-Void]
|
|
;; pubment with different augment type
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(: m (Symbol -> Symbol)
|
|
#:augment (String -> String))
|
|
(define/pubment (m x)
|
|
(inner "" m "foo") 'a)))
|
|
(define d%
|
|
(class c%
|
|
(super-new)
|
|
(define/augment (m x)
|
|
(string-append x "bar"))))
|
|
(send (new c%) m 'b))
|
|
-Symbol]
|
|
;; fail, bad inner argument
|
|
[tc-err (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(: m (Symbol -> Symbol)
|
|
#:augment (String -> String))
|
|
(define/pubment (m x)
|
|
(inner "" m x) 'a)))
|
|
(define d%
|
|
(class c%
|
|
(super-new)
|
|
(define/augment (m x)
|
|
(string-append x "bar"))))
|
|
(send (new c%) m 'b))
|
|
#:ret (ret -Symbol)
|
|
#:msg #rx"expected: String.*given: Symbol"]
|
|
;; Fail, bad inner default
|
|
[tc-err (class object%
|
|
(super-new)
|
|
(: m (Symbol -> Symbol))
|
|
(define/pubment (m x)
|
|
(inner "foo" m x)))
|
|
#:ret (ret (-class #:method ([m (t:-> -Symbol -Symbol)])
|
|
#:augment ([m (t:-> -Symbol -Symbol)])))
|
|
#:msg #rx"expected: Symbol.*given: String"]
|
|
;; Fail, wrong number of arguments to inner
|
|
[tc-err (class object%
|
|
(super-new)
|
|
(: m (Integer -> Integer))
|
|
(define/pubment (m x)
|
|
(inner 3 m)))
|
|
#:ret (ret (-class #:method ([m (t:-> -Integer -Integer)])
|
|
#:augment ([m (t:-> -Integer -Integer)])))
|
|
#:msg #rx"wrong number of arguments provided.*expected: 2"]
|
|
;; Fail, bad augment type
|
|
[tc-err (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(: m (Symbol -> Symbol))
|
|
(define/pubment (m x)
|
|
(inner 'a m x))))
|
|
(define d%
|
|
(class c%
|
|
(super-new)
|
|
(define/augment (m x) "bad type")))
|
|
(void))
|
|
#:ret (ret -Void)
|
|
#:msg #rx"expected: Symbol.*given: String"]
|
|
;; Fail, cannot augment non-augmentable method
|
|
[tc-err (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(: m (Integer -> Integer))
|
|
(define/public (m x) 0)))
|
|
(define d%
|
|
(class c%
|
|
(super-new)
|
|
(define/augment (m x) 1)))
|
|
(void))
|
|
#:ret (ret -Void)
|
|
#:msg #rx"superclass is missing a required augmentable method"]
|
|
;; Pubment with separate internal/external names
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(: n (Symbol -> Symbol))
|
|
(pubment [n m])
|
|
(define n (λ (x) 'a))))
|
|
(send (new c%) m 'b))
|
|
-Symbol]
|
|
;; Pubment with expected class type
|
|
[tc-e (let ()
|
|
(: c% (Class [m (String -> String)]
|
|
(augment [m (String -> String)])))
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(define/pubment (m x) "a")))
|
|
(send (new c%) m "b"))
|
|
-String]
|
|
;; fails, expected type not a class
|
|
[tc-err (let ()
|
|
(: c% String)
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(: x Symbol)
|
|
(init-field x)))
|
|
(void))
|
|
#:ret (ret -Void)
|
|
#:msg #rx"expected: String"]
|
|
;; test polymorphic class
|
|
[tc-e (let ()
|
|
(: c% (All (A) (Class (init-field [x A]))))
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(init-field x)))
|
|
(new (inst c% Integer) [x 0])
|
|
(void))
|
|
-Void]
|
|
;; fails due to ill-typed polymorphic class body
|
|
[tc-err (let ()
|
|
(: c% (All (A) (Class (init-field [x A]))))
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(init-field x)
|
|
(set! x "a")))
|
|
(void))
|
|
#:ret (ret -Void)
|
|
#:msg #rx"expected: A.*given: String"]
|
|
;; test polymorphism with keyword
|
|
[tc-e (let ()
|
|
(define point%
|
|
(class object%
|
|
#:∀ (X)
|
|
(super-new)
|
|
(init-field [x : X] [y : X])))
|
|
(new (inst point% Integer) [x 0] [y 5])
|
|
(new (inst point% String) [x "foo"] [y "bar"])
|
|
(void))
|
|
-Void]
|
|
;; test polymorphism with two type parameters
|
|
[tc-e (let ()
|
|
(define point%
|
|
(class object%
|
|
#:forall (X Y)
|
|
(super-new)
|
|
(init-field [x : X] [y : Y])))
|
|
(new (inst point% Integer String) [x 0] [y "foo"])
|
|
(new (inst point% String Integer) [x "foo"] [y 3])
|
|
(void))
|
|
-Void]
|
|
;; test class polymorphism with method
|
|
[tc-e (let ()
|
|
(define id%
|
|
(class object%
|
|
#:forall (X)
|
|
(super-new)
|
|
(: m (X -> X))
|
|
(define/public (m x) x)))
|
|
(send (new (inst id% String)) m "a"))
|
|
-String]
|
|
;; fails because m is not parametric
|
|
[tc-err (class object%
|
|
#:forall (X)
|
|
(super-new)
|
|
(: m (X -> X))
|
|
(define/public (m x) "a"))
|
|
#:ret (ret (-poly (X) (-class #:method ([m (t:-> X X)]))))
|
|
#:msg #rx"expected: X.*given: String"]
|
|
;; fails because default init value cannot be polymorphic
|
|
[tc-err (class object%
|
|
#:forall (Z)
|
|
(super-new)
|
|
(init-field [x : Z] [y : Z 0]))
|
|
#:ret (ret (-poly (Z) (-class #:init-field ([x Z #f] [y Z #t]))))
|
|
#:msg #rx"expected: Z.*given: Zero"]
|
|
;; fails because default field value cannot be polymorphic
|
|
[tc-err (class object%
|
|
#:forall (Z)
|
|
(super-new)
|
|
(field [x : Z "a"]))
|
|
#:ret (ret (-poly (Z) (-class #:field ([x Z]))))
|
|
#:msg #rx"expected: Z.*given: String"]
|
|
;; test in-clause type annotations (next several tests)
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(field [x : String "a"])))
|
|
(string-append "b" (get-field x (new c%))))
|
|
-String]
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(init-field [x : String "a"])))
|
|
(string-append "c" (get-field x (new c% [x "b"]))))
|
|
-String]
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(public [m : (String -> String)])
|
|
(define (m x) (string-append x "foo"))))
|
|
(send (new c%) m "bar"))
|
|
-String]
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(private [m : (String -> String)])
|
|
(define (m x) (string-append x "foo"))))
|
|
(void))
|
|
-Void]
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(field [(x y) : String "a"])))
|
|
(string-append "foo" (get-field y (new c%))))
|
|
-String]
|
|
;; fails, duplicate type annotation
|
|
[tc-err (class object%
|
|
(super-new)
|
|
(: x String)
|
|
(field [x : Symbol 0]))
|
|
#:ret (ret (-class #:field ([x -String])))
|
|
#:msg #rx"duplicate type annotation.*new type: Symbol"]
|
|
;; fails, expected type and annotation don't match
|
|
[tc-err (let ()
|
|
(: c% (Class (field [x String])))
|
|
(define c% (class object% (super-new)
|
|
(field [x : Symbol 'a])))
|
|
(void))
|
|
#:ret (ret -Void)
|
|
#:msg #rx"expected: String.*given: Symbol"]
|
|
;; fails, but make sure it's not an internal error
|
|
[tc-err (class object% (super-new)
|
|
(define/pubment (foo x) 0)
|
|
(define/public (g x) (foo 3)))
|
|
#:ret (ret (-class #:method ([g (t:-> Univ -Bottom)]
|
|
[foo (t:-> Univ -Zero : -true-filter)])
|
|
#:augment ([foo top-func])))
|
|
#:msg #rx"Cannot apply expression of type Any"]
|
|
;; the next several tests are for positional init arguments
|
|
[tc-e (let ()
|
|
(define c% (class object% (super-new) (init a b)))
|
|
(new c% [a "a"] [b "b"])
|
|
(make-object c% "a" "b")
|
|
(instantiate c% ("a") [b "b"])
|
|
(void))
|
|
-Void]
|
|
[tc-e (let ()
|
|
(define c% (class object% (super-new) (init a [b 'b])))
|
|
(new c% [a "a"] [b "b"])
|
|
(new c% [a "a"])
|
|
(make-object c% "a")
|
|
(make-object c% "a" "b")
|
|
(instantiate c% () [a "a"] [b "b"])
|
|
(instantiate c% ("a") [b "b"])
|
|
(instantiate c% ("a" "b"))
|
|
(void))
|
|
-Void]
|
|
[tc-e (let ()
|
|
(define c% (class (class object%
|
|
(super-new)
|
|
(init [b 'b]))
|
|
(super-new) (init [a 'a])))
|
|
(new c% [a "a"] [b "b"])
|
|
(new c% [b "b"])
|
|
(new c% [a "a"])
|
|
(make-object c% "a")
|
|
(make-object c% "a" "b")
|
|
(instantiate c% () [a "a"] [b "b"])
|
|
(instantiate c% ("a") [b "b"])
|
|
(instantiate c% ("a" "b"))
|
|
(void))
|
|
-Void]
|
|
[tc-e (let ()
|
|
(define c% (class object%
|
|
(super-new)
|
|
(init-rest [rst : (List String String)])))
|
|
(make-object c% "a" "b")
|
|
(void))
|
|
-Void]
|
|
[tc-e (let ()
|
|
(define c% (class object%
|
|
(super-new)
|
|
(init [a : Symbol])
|
|
(init-rest [rst : (List String String)])))
|
|
(make-object c% 'a "b" "c")
|
|
(void))
|
|
-Void]
|
|
[tc-e (let ()
|
|
(define c% (class object%
|
|
(super-new)
|
|
(init-rest [rst : (U (List Symbol)
|
|
(List String String))])))
|
|
(make-object c% "b" "c")
|
|
(make-object c% 'a)
|
|
(void))
|
|
-Void]
|
|
[tc-err (let ()
|
|
(define c% (class object%
|
|
(super-new)
|
|
(init-rest [rst : (List Symbol)])))
|
|
(make-object c% "wrong"))
|
|
#:ret (ret (make-Instance (make-Class #f null null null null (-Tuple (list -Symbol)))))
|
|
#:msg #rx"expected: \\(List Symbol.*given: \\(List String"]
|
|
;; PR 14408, test init-field order
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(init-field [x : String] [y : Symbol])))
|
|
(make-object c% "str" 'sym)
|
|
(void))
|
|
-Void]
|
|
;; a variant of the last, but testing that init and init-field
|
|
;; interleave correctly in the class type
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(init [a : 'a]) (init-field [x : 'x] [y : 'y]) (init [b 'b])))
|
|
(make-object c% 'a 'x 'y 'b)
|
|
(void))
|
|
-Void]
|
|
;; fail, too many positional arguments to superclass
|
|
[tc-err (class object% (super-make-object "foo"))
|
|
#:ret (ret (-class))
|
|
#:msg #rx"too many positional init arguments"]
|
|
;; check that case-lambda methods work
|
|
[tc-e (let ()
|
|
(class object%
|
|
(super-new)
|
|
(field [y : Integer 0])
|
|
(: m (case-> (Any -> Integer)))
|
|
(public m)
|
|
(define m (case-lambda [(x) y])))
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(: m (case-> (Any -> Void)))
|
|
(public m)
|
|
(define m (case-lambda [(x) (void)]))))
|
|
(send (new c%) m 'anything))
|
|
-Void]
|
|
;; fails, test that case-lambda bodies are checked
|
|
[tc-err (class object%
|
|
(super-new)
|
|
(: m (case-> (Any -> Integer)))
|
|
(public m)
|
|
(define m (case-lambda [(x) "bad"])))
|
|
#:ret (ret (-class #:method [(m (t:-> Univ -Integer))]))
|
|
#:msg #rx"expected: Integer.*given: String"]
|
|
;; test that rest args work
|
|
[tc-e (let ()
|
|
(class object% (super-new)
|
|
(: m (-> Integer * Integer))
|
|
(define/public (m . xs) (apply + xs)))
|
|
(void))
|
|
-Void]
|
|
;; test that Name types are ok with get-field and as an
|
|
;; expected type for class type-checking
|
|
[tc-e (let ()
|
|
(define-type-alias Foo%
|
|
(Class (init-field [x String])
|
|
[m (-> (Instance Foo%) String)]))
|
|
(: foo% Foo%)
|
|
(define foo%
|
|
(class object% (super-new)
|
|
(init-field x)
|
|
(define/public (m a-foo) (get-field x a-foo))))
|
|
(void))
|
|
-Void]
|
|
;; test that filters are correctly handled for polymorphic classes
|
|
[tc-e (let ()
|
|
(class object%
|
|
(super-new)
|
|
(init x)))
|
|
#:ret (ret (-poly (A) (-class #:init ([x A #f]))))
|
|
#:expected (ret (-poly (A) (-class #:init ([x A #f]))) -no-filter -no-obj)]
|
|
;; test uses of a macro in the body of the class
|
|
[tc-e
|
|
(let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(define-syntax-rule (my-meth (m arg ...) . body)
|
|
(define/public (m arg ...) . body))
|
|
(my-meth (hello) (displayln "hello world"))))
|
|
(send (new c%) hello))
|
|
-Void]
|
|
;; the next few tests check failing class instantiation
|
|
[tc-err (make-object object% 1)
|
|
#:msg #rx"expected: 0 arguments.*given: 1 arguments"]
|
|
[tc-err (make-object (ann object% ClassTop))
|
|
#:msg #rx"cannot instantiate.*ClassTop"]
|
|
[tc-err (make-object 3)
|
|
#:msg #rx"value of a non-class type"]
|
|
;; PR 14726
|
|
;; test opt-arg but non-keyword method
|
|
[tc-e (let ()
|
|
(define-type-alias A%
|
|
(Class [foo (->* [Integer] Void)]))
|
|
(: a% A%)
|
|
(define a%
|
|
(class object%
|
|
(super-new)
|
|
(define/public (foo [i #f]) (void))))
|
|
(new a%))
|
|
(-object #:method ([foo (t:-> -Integer -Void)]))]
|
|
[tc-e (let ()
|
|
(define-type-alias A%
|
|
(Class [foo (->* [] [Integer] Void)]))
|
|
(: a% A%)
|
|
(define a%
|
|
(class object%
|
|
(super-new)
|
|
(define/public (foo [i #f]) (void))))
|
|
(new a%))
|
|
(-object #:method ([foo (cl->* (t:-> -Void) (t:-> -Integer -Void))]))]
|
|
;; PR 14810 - make sure inner type mapping has the right order
|
|
[tc-e (let ()
|
|
(define-type-alias Foo%
|
|
(Class [m (-> Any Symbol)]
|
|
[o (-> Any Any Symbol)]
|
|
(augment [m (-> Any Symbol)]
|
|
[o (-> Any Any Symbol)])))
|
|
(define-type-alias Bar% (Class #:implements Foo%))
|
|
(: foo% Foo%)
|
|
(define foo%
|
|
(class object%
|
|
(super-new)
|
|
(define/pubment (m x) 'foo-m)
|
|
(define/pubment (o x y) 'foo-o)))
|
|
(: bar% Bar%)
|
|
(define bar%
|
|
(class foo%
|
|
(super-new)
|
|
(define/augment (m x) (inner 'bar-m m x))
|
|
(define/augment (o x y) (inner 'bar-o o x y))))
|
|
(void))
|
|
-Void]
|
|
;; Test implements clauses
|
|
[tc-e (let ()
|
|
(define-type-alias A% (Class [foo (-> Void)]))
|
|
(define-type-alias B% (Class #:implements A% [bar (-> Void)]))
|
|
(: b% B%)
|
|
(define b% (class object%
|
|
(super-new)
|
|
(define/public (foo) (void))
|
|
(define/public (bar) (void))))
|
|
(new b%))
|
|
(-object #:method ([foo (t:-> -Void)] [bar (t:-> -Void)]))]
|
|
[tc-err (let ()
|
|
(define-type-alias A% (Class [foo (-> Void)]))
|
|
(define-type-alias B% (Class #:implements A% [bar (-> Void)]))
|
|
(: b% B%)
|
|
(define b% (class object%
|
|
(super-new)
|
|
(define/public (bar) (void))))
|
|
(error "foo"))
|
|
#:msg "lacks expected method `foo'"]
|
|
[tc-e (let ()
|
|
(define-type-alias A% (Class (init [y Symbol])))
|
|
(define-type-alias B% (Class #:implements/inits A% (init [x String])))
|
|
(: b% B%)
|
|
(define b% (class object%
|
|
(super-new)
|
|
(init x y)))
|
|
(make-object b% "foo" 'bar)
|
|
(void))
|
|
-Void]
|
|
[tc-e (let ()
|
|
(define-type-alias A% (Class (init [y Symbol])))
|
|
(define-type-alias B% (Class [m (-> Void)]))
|
|
(define-type-alias C% (Class #:implements/inits A%
|
|
#:implements B%
|
|
(init [x String])))
|
|
(: c% C%)
|
|
(define c% (class object%
|
|
(super-new)
|
|
(define/public (m) (void))
|
|
(init x y)))
|
|
(make-object c% "foo" 'bar)
|
|
(void))
|
|
-Void]
|
|
[tc-err (let ()
|
|
(define-type-alias A% (Class (init [y String])))
|
|
(define-type-alias B% (Class #:implements/inits A%
|
|
#:implements/inits A%))
|
|
(error "foo"))
|
|
;; FIXME: this error message is pretty bad
|
|
#:msg "expected Class type clause"]
|
|
[tc-err (let ()
|
|
(define-type-alias A% (Class (init [y String])))
|
|
(define-type-alias B% (Class #:implements/inits A% (init [x String])))
|
|
(: b% B%)
|
|
(define b% (class object%
|
|
(super-new)
|
|
(init y x)))
|
|
(error "foo"))
|
|
#:msg #rx"initialization argument order.*expected: \\(x y\\).*given: \\(y x\\)"]
|
|
;; PR 14669 (next two)
|
|
[tc-e (let ()
|
|
(define-type-alias A (Class [m (-> Any)]))
|
|
(define-type-alias B (Class #:implements A [m (-> Void)]))
|
|
(define-type-alias C (Class #:implements A))
|
|
(define-type-alias D (Class #:implements C #:implements B))
|
|
(: d% D)
|
|
(define d% (class object% (super-new) (define/public (m) (void))))
|
|
(send (new d%) m))
|
|
-Void]
|
|
[tc-e (let ()
|
|
(define-type-alias A (Class [m (-> Any)]))
|
|
(define-type-alias B (Class #:implements A [m (-> Void)]))
|
|
(define-type-alias C (Class #:implements A))
|
|
(define-type-alias D (Class #:implements B #:implements C))
|
|
(: d% D)
|
|
(define d% (class object% (super-new) (define/public (m) (void))))
|
|
(send (new d%) m))
|
|
Univ]
|
|
;; Test for an infinite loop bug during subtyping for classes
|
|
[tc-e (let ()
|
|
(define-type-alias A% (Class [get-this (-> (Instance A%))]))
|
|
(define-type-alias B% (Class [get-this (-> (Instance B%))]))
|
|
(: a% A%)
|
|
(define a%
|
|
(class object%
|
|
(super-new)
|
|
(define/public (get-this) this)))
|
|
(: b% B%)
|
|
(define b%
|
|
(class a%
|
|
(super-new)
|
|
(define/override (get-this) this)))
|
|
(void))
|
|
-Void]
|
|
;; Test that depth subtyping is accounted for with overriden methods
|
|
[tc-e (let ()
|
|
(define-type-alias B% (Class [n (-> Real)]))
|
|
(: b% B%)
|
|
(define b%
|
|
(class object%
|
|
(super-new)
|
|
(define/public (n) 123.456)))
|
|
(define-type-alias C% (Class #:implements B% [n (-> Integer)]))
|
|
(: c% C%)
|
|
(define c%
|
|
(class b%
|
|
(super-new)
|
|
(override* [n (lambda () 5)])))
|
|
(send (new c%) n))
|
|
-Integer]
|
|
;; PR 14904
|
|
[tc-e (class object%
|
|
(super-new)
|
|
(: foo (All (X) (-> X X)))
|
|
(define/public foo (tr:lambda #:forall (A) ([x : A]) x)))
|
|
(-class #:method [(foo (-poly (a) (t:-> a a)))])]
|
|
[tc-e (class object%
|
|
(super-new)
|
|
(define/public foo (case-lambda [(str) (void)] [(sym size) (void)])))
|
|
(-class #:method [(foo (cl->* (t:-> Univ Univ -Void) (t:-> Univ -Void)))])]
|
|
;; PR 14911
|
|
[tc-e (class object%
|
|
(super-new)
|
|
(: bar (-> String String))
|
|
(define bar (lambda (x) x))
|
|
(bar "foo"))
|
|
(-class)]
|
|
;; The next several tests are for check-below error messages for checking
|
|
;; expected types that are Class types
|
|
[tc-err (let ()
|
|
(: f (All (X #:row) (-> (Class #:row-var X) (Class #:row-var X))))
|
|
(define (f cls) (class object% (super-new)))
|
|
(error "foo"))
|
|
#:msg #rx"expected: Class with row variable `X.*given: Class with no row variable"]
|
|
[tc-err (let ()
|
|
(: f (All (X #:row) (-> (Class #:row-var X) (Class))))
|
|
(define (f cls) cls)
|
|
(error "foo"))
|
|
#:msg #rx"expected: Class with no row variable.*given: Class with row variable `X"]
|
|
[tc-err (let ()
|
|
(ann (class object% (super-new))
|
|
(Class (init [x String])))
|
|
(error "foo"))
|
|
#:msg #rx"lacks expected init `x'"]
|
|
[tc-err (let ()
|
|
(ann (class object% (super-new))
|
|
(Class (field [x String])))
|
|
(error "foo"))
|
|
#:msg #rx"lacks expected field `x'"]
|
|
[tc-err (let ()
|
|
(ann (class object% (super-new))
|
|
(Class [m (-> String)]))
|
|
(error "foo"))
|
|
#:msg #rx"lacks expected method `m'"]
|
|
[tc-err (let ()
|
|
(ann (class object% (super-new) (init [x : Symbol]))
|
|
(Class (init [x String])))
|
|
(error "foo"))
|
|
#:msg #rx"expected: String.*given: Symbol"]
|
|
[tc-err (let ()
|
|
(ann (class object% (super-new) (init [x : String "x"]))
|
|
(Class (init [x String])))
|
|
(error "foo"))
|
|
#:msg #rx"expected: mandatory init `x'.*given: optional init `x'"]
|
|
[tc-err (let ()
|
|
(ann (class object% (super-new) (init [x : String]))
|
|
(Class (init [x String #:optional])))
|
|
(error "foo"))
|
|
#:msg #rx"expected: optional init `x'.*given: mandatory init `x'"]
|
|
[tc-err (let ()
|
|
(ann (class object% (super-new) (field [x : Symbol 'x]))
|
|
(Class (field [x String])))
|
|
(error "foo"))
|
|
#:msg #rx"expected: String.*given: Symbol"]
|
|
[tc-err (let ()
|
|
(define c% (class object% (super-new) (define/public (m) (void))))
|
|
(ann c% (Class [m (-> String)]))
|
|
(error "foo"))
|
|
#:msg #rx"expected: \\(-> String\\).*given: \\(-> Void\\)"]
|
|
[tc-err (let ()
|
|
(ann (class object% (super-new) (init-rest [rst : (Listof String)]))
|
|
(Class))
|
|
(error "foo"))
|
|
#:msg #rx"expected: Class with no init-rest type.*given: Class with init-rest type"]
|
|
[tc-err (let ()
|
|
(ann (class object% (super-new))
|
|
(Class [init-rest (Listof Void)]))
|
|
(error "foo"))
|
|
#:msg #rx"expected: Class with init-rest type.*given: Class with no init-rest type"]
|
|
[tc-err (let ()
|
|
(ann (class object% (super-new) (init-rest [rst : (Listof String)]))
|
|
(Class (init-rest (Listof Void))))
|
|
(error "foo"))
|
|
#:msg #rx"expected: \\(Listof Void\\).*given: \\(Listof String\\)"]
|
|
;; PR 14942
|
|
[tc-err (let ()
|
|
(: x (Instance BadAlias))
|
|
(define x (error "foo"))
|
|
(: y (Object (field [x Any])))
|
|
(define y x)
|
|
(error "foo"))
|
|
#:msg "parse error in type"]
|
|
;; Test type aliases inside class bodies
|
|
[tc-e (class object%
|
|
(super-new)
|
|
(define-type-alias X String)
|
|
(: x X)
|
|
(define x "foo"))
|
|
(-class)]
|
|
[tc-e (class object%
|
|
(super-new)
|
|
(define-type-alias (F X) (Listof X))
|
|
(: x (F String))
|
|
(define x (list "foo")))
|
|
(-class)]
|
|
[tc-err (let ()
|
|
(class object%
|
|
(super-new)
|
|
(define-type-alias X String)
|
|
(: x X)
|
|
(define x "foo"))
|
|
(: x2 X)
|
|
(define x2 "bar")
|
|
(error "foo"))
|
|
#:msg "type name `X' is unbound"]
|
|
[tc-e (let ()
|
|
(class object%
|
|
(super-new)
|
|
(define-type-alias X String)
|
|
(: x X)
|
|
(define x "foo"))
|
|
(define-type-alias X Symbol)
|
|
(: x2 X)
|
|
(define x2 'bar)
|
|
(void))
|
|
-Void]
|
|
;; Check strange method definition forms. Some of these are unlikely to actually come
|
|
;; up but are allowed by the grammar of classes.
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(public m)
|
|
(define-values (m)
|
|
(let-values ([(x) (lambda () (void))]
|
|
[(y) (lambda () (void))])
|
|
(let-values ([(z) (lambda () (void))])
|
|
z)))))
|
|
(send (new c%) m))
|
|
-Void]
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(public m)
|
|
(define-values (m)
|
|
(let-values ([(x) (lambda () (void))])
|
|
(let-values ([(y) (lambda () (void))])
|
|
(lambda () (y) (x)))))))
|
|
(send (new c%) m))
|
|
-Void]
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(public m)
|
|
(define-values (m)
|
|
(letrec-values ([(x) (lambda () (void))])
|
|
(letrec-values ([(y) (lambda () (void))])
|
|
(lambda () (y) (x)))))))
|
|
(send (new c%) m))
|
|
-Void]
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(public m)
|
|
(define-values (m)
|
|
(let-values ([(x) (lambda () (void))])
|
|
(let-values ([(y) (lambda () (void))])
|
|
(case-lambda [() (void)]))))))
|
|
(send (new c%) m))
|
|
-Void]
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(public m)
|
|
(define-values (m)
|
|
(let-values ([(x) (lambda () (void))])
|
|
(let-values ([(y) (lambda () (void))])
|
|
(case-lambda [() (x)]))))))
|
|
(send (new c%) m))
|
|
-Void]
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(public m)
|
|
(define-values (m)
|
|
(let-values ([(x) (lambda () (void))])
|
|
(let-values ([(y) (case-lambda [() (x)])])
|
|
y)))))
|
|
(send (new c%) m))
|
|
-Void]
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(public m)
|
|
(define-values (m)
|
|
(let-values ([(x) (lambda () (void))])
|
|
(let-values ([(y) (case-lambda [() (x)])])
|
|
(tr:lambda (#:x [x "x"]) (void)))))))
|
|
(send (new c%) m))
|
|
-Void]
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object% (super-new)
|
|
(: m (-> Integer #:x Integer Integer))
|
|
(public m)
|
|
(define-values (m)
|
|
(let-values ([(m) (tr:lambda (x #:x y) (add1 y))]) m))))
|
|
(send (new c%) m 0 #:x 1))
|
|
-Integer]
|
|
;; This tests a bug that came up while adding support for the test
|
|
;; cases directly above
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object% (super-new)
|
|
(define/public (m [x : Symbol 'y])
|
|
(symbol->string x) (void))))
|
|
(send (new c%) m))
|
|
-Void]
|
|
;; Next several tests are for occurrence typing on private fields
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(: x (U String #f))
|
|
(define x "foo")
|
|
(: m (-> String))
|
|
(define/public (m)
|
|
(if (string? x) (string-append x "bar") "baz"))))
|
|
(send (new c%) m))
|
|
-String]
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(: x (U String #f))
|
|
(define x "foo")
|
|
(: m (-> String))
|
|
(define/public (m)
|
|
;; ensure just x works
|
|
(if x (string-append x "bar") "baz"))))
|
|
(send (new c%) m))
|
|
-String]
|
|
[tc-e (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(: x (List (U String #f)))
|
|
(define x (list "foo"))
|
|
(: m (-> String))
|
|
(define/public (m)
|
|
(if (string? (car x)) ; car path
|
|
(string-append (car x) "bar")
|
|
"baz"))))
|
|
(send (new c%) m))
|
|
-String]
|
|
[tc-e (class object%
|
|
(super-new)
|
|
(: x (Option String))
|
|
(define x "foo")
|
|
;; let-aliasing + occ. typing on fields
|
|
(let ([y x]) (if (string? y) (string-append x) "")))
|
|
(-class)]
|
|
[tc-e (class object%
|
|
(super-new)
|
|
(: x (Option String))
|
|
(define x "foo")
|
|
(let ([y x]) (if y (string-append x) "")))
|
|
(-class)]
|
|
;; Failure tests for occurrence typing on private fields. The types
|
|
;; are obfuscated a bit to prevent interference from type aliases in
|
|
;; another test.
|
|
[tc-err (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(: x (U String 'obfuscate))
|
|
(define x "foo")
|
|
(set! x 'obfuscate) ; prevents occ. typing
|
|
(: m (-> String))
|
|
(define/public (m)
|
|
(if (string? x) (string-append x "bar") "baz"))))
|
|
(error "foo"))
|
|
#:msg #rx"expected: String.*given: \\(U String 'obfuscate\\)"]
|
|
[tc-err (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(: x (U String 'obfuscate))
|
|
(define x "foo")
|
|
(field [f (begin (set! x 'obfuscate) "hello")])
|
|
(: m (-> String))
|
|
(define/public (m)
|
|
(if (string? x) (string-append x "bar") "baz"))))
|
|
(error "foo"))
|
|
#:msg #rx"expected: String.*given: \\(U String 'obfuscate\\)"]
|
|
[tc-err (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(: x (U String 'obfuscate))
|
|
(define x "foo")
|
|
(define/public (n) (set! x 'obfuscate))
|
|
(: m (-> String))
|
|
(define/public (m)
|
|
(if (string? x) (string-append x "bar") "baz"))))
|
|
(error "foo"))
|
|
#:msg #rx"expected: String.*given: \\(U String 'obfuscate\\)"]
|
|
;; tests that we are not creating objects for mutable private fields
|
|
[tc-e (let ()
|
|
(class object%
|
|
(super-new)
|
|
(: bsp-trees (U #f Integer))
|
|
(define bsp-trees #f)
|
|
(: m (-> Any))
|
|
(define (m) (set! bsp-trees 5))
|
|
|
|
(: sync-bsp-trees (-> Integer))
|
|
(define/private (sync-bsp-trees)
|
|
(let ([bsp-trees-val bsp-trees])
|
|
(cond
|
|
[bsp-trees-val bsp-trees-val]
|
|
[else 5]))))
|
|
(void))
|
|
-Void]
|
|
;; tests private fields declared with define-values
|
|
[tc-e (let ()
|
|
(send
|
|
(new
|
|
(class object%
|
|
(super-new)
|
|
(define-values (a b) (values 1 "foo"))
|
|
(: get-ab (-> (Values Integer String)))
|
|
(define/public (get-ab) (values a b))))
|
|
get-ab)
|
|
(void))
|
|
-Void]
|
|
[tc-e (let ()
|
|
(send
|
|
(new
|
|
(class object%
|
|
(super-new)
|
|
(define-values (a b)
|
|
(let ([x 1] [y "foo"]) (values x y)))
|
|
(: get-ab (-> (Values Integer String)))
|
|
(define/public (get-ab) (values a b))))
|
|
get-ab)
|
|
(void))
|
|
-Void]
|
|
;; Failure tests for soundness of private field initialization
|
|
[tc-err (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(: a String)
|
|
(define-values (a b) (values 1 2))
|
|
(: get-a (-> String))
|
|
(define/public (get-a) a)))
|
|
(error "foo"))
|
|
#:msg #rx"expected: String.*given: One"]
|
|
[tc-err (let ()
|
|
(define c%
|
|
(class object%
|
|
(super-new)
|
|
(: a String)
|
|
(define-values (a b) (let ([z 1]) (values z z)))
|
|
(: get-a (-> String))
|
|
(define/public (get-a) a)))
|
|
(error "foo"))
|
|
#:msg #rx"expected: String.*given: One"]
|
|
;; Make sure `send` works on a recursively typed object
|
|
[tc-e (let ()
|
|
(: o (Rec X (Object [m (-> Void)] [n (-> X Void)])))
|
|
(define o
|
|
(make-object (class object%
|
|
(super-new)
|
|
(define/public (m) (void))
|
|
(define/public (n x) (void)))))
|
|
(send o m))
|
|
-Void]
|
|
;; A test for GH issue #218. Make sure that multiple private fields
|
|
;; are typechecked in the right context.
|
|
[tc-e (let ()
|
|
(define-type-alias C%
|
|
(Class (init-field (path Path-String))))
|
|
(: c% C%)
|
|
(define c%
|
|
(class object%
|
|
(init-field path)
|
|
(: in Input-Port)
|
|
(: out Output-Port)
|
|
(define-values (in out)
|
|
(values (open-input-file path) (open-output-file path)))
|
|
(super-new)))
|
|
(void))
|
|
-Void]))
|