diff --git a/pkgs/typed-racket-pkgs/typed-racket-test/tests/typed-racket/unit-tests/class-tests.rkt b/pkgs/typed-racket-pkgs/typed-racket-test/tests/typed-racket/unit-tests/class-tests.rkt deleted file mode 100644 index 6f0b25a8..00000000 --- a/pkgs/typed-racket-pkgs/typed-racket-test/tests/typed-racket/unit-tests/class-tests.rkt +++ /dev/null @@ -1,1170 +0,0 @@ -#lang racket - -;; Unit tests for typed classes -;; -;; FIXME: make this work with the unit testing framework for -;; typecheck eventually (it's finnicky). -;; -;; FIXME: these tests are slow - -(require "test-utils.rkt" - rackunit - (for-syntax syntax/parse)) - -(provide tests) -(gen-test-main) - -(define test-error-port (make-parameter (open-output-nowhere))) - -(define-syntax-rule (run/tr-module e ...) - (parameterize ([current-output-port (open-output-nowhere)] - [current-error-port (test-error-port)]) - (define ns (make-base-namespace)) - (eval (quote (module typed typed/racket - e ...)) - ns) - (eval (quote (require 'typed)) ns))) - -(define-syntax-rule (check-ok e ...) - (begin (check-not-exn (thunk (run/tr-module e ...))))) - -(define-syntax (check-err stx) - (syntax-parse stx - [(_ #:exn rx-or-pred e ...) - #'(parameterize ([test-error-port (open-output-string)]) - (check-exn - (λ (exn) - (cond [(regexp? rx-or-pred) - (and (exn:fail:syntax? exn) - (or (regexp-match? rx-or-pred (exn-message exn)) - (regexp-match? - rx-or-pred - (get-output-string (test-error-port)))))] - [(procedure? rx-or-pred) - (and (exn:fail:syntax? exn) - (rx-or-pred exn))] - [else (error "expected predicate or regexp")])) - (thunk (run/tr-module e ...))))] - [(_ e ...) - #'(check-exn - exn:fail:syntax? - (thunk (run/tr-module e ...)))])) - -(define tests - (test-suite - "Class type-checking tests" - - ;; Basic class with init and public method - (check-ok - (: 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)) - - ;; Fails, bad superclass expression - (check-err #:exn #rx"expected a superclass but" - (: d% (Class (init [x Integer]) - [m (Integer -> Integer)])) - (define d% (class 5 - (super-new) - (init x) - (define/public (m x) 0)))) - - ;; Method using argument type - (check-ok - (: e% (Class (init [x Integer]) - [m (Integer -> Integer)])) - (define e% (class object% - (super-new) - (init x) - (define/public (m x) x)))) - - ;; Send inside a method - (check-ok - (: f% (Class (init [x Integer]) - [m (Integer -> Integer)])) - (define f% (class object% - (super-new) - (init x) - (define/public (m x) (send this m 3))))) - - ;; Fails, send to missing method - (check-err #:exn #rx"method z not understood" - (: 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))))) - - ;; Send to other methods - (check-ok - (: h% (Class [n (-> Integer)] - [m (Integer -> Integer)])) - (define h% (class object% - (super-new) - (define/public (n) 0) - (define/public (m x) (send this n))))) - - ;; Local sends - (check-ok - (: i% (Class [n (-> Integer)] - [m (Integer -> Integer)])) - (define i% (class object% - (super-new) - (define/public (n) 0) - (define/public (m x) (n))))) - - ;; Field access via get-field - (check-ok - (: j% (Class (field [n Integer]) - [m (-> Integer)])) - (define j% (class object% - (super-new) - (field [n 0]) - (define/public (m) (get-field n this))))) - - ;; fails, field's default value has wrong type - (check-err #:exn #rx"Expected Integer, but got String" - (class object% (super-new) - (: x Integer) - (field [x "foo"]))) - - ;; Fail, field access to missing field - (check-err #:exn #rx"expected an object with field n" - (: k% (Class [m (-> Integer)])) - (define k% (class object% - (super-new) - (define/public (m) (get-field n this))))) - - ;; Fail, conflict with parent field - (check-err #:exn #rx"defines conflicting public field n" - (: 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)))) - - ;; Fail, conflict with parent method - (check-err #:exn #rx"defines conflicting public method m" - (: 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)))) - - ;; Inheritance - (check-ok - (: 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)))) - - ;; should fail, too many methods - (check-err - #:exn #rx"public method m that is not in the expected type" - (: o% (Class)) - (define o% (class object% - (super-new) - (define/public (m) 0)))) - - ;; same as previous - (check-err - #:exn #rx"public method n that is not in the expected type" - (: c% (Class [m (Integer -> Integer)])) - (define c% (class object% (super-new) - (define/public (m x) (add1 x)) - (define/public (n) 0)))) - - ;; fails, too many inits - (check-err - #:exn #rx"initialization argument x that is not in the expected type" - (: c% (Class)) - (define c% (class object% (super-new) - (init x)))) - - ;; fails, init should be optional but is mandatory - (check-err #:exn #rx"missing optional init argument str" - (: c% (Class (init [str String #:optional]))) - (define c% (class object% (super-new) - (init str)))) - - ;; fails, too many fields - (check-err - #:exn #rx"public field x that is not in the expected type" - (: c% (Class (field [str String]))) - (define c% (class object% (super-new) - (field [str "foo"] [x 0])))) - - ;; test that an init with no annotation still type-checks - ;; (though it will have the Any type) - (check-ok - (define c% (class object% (super-new) (init x)))) - - ;; test that a field with no annotation still type-checks - ;; (though it will have the Any type) - (check-ok - (define c% (class object% (super-new) (field [x 0])))) - - ;; Mixin on classes without row polymorphism - (check-ok - (: 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%)) - - ;; Fail, bad mixin - (check-err #:exn #rx"missing public method n" - (: 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%)) - - ;; Fail, bad mixin argument - (check-err #:exn #rx"Expected \\(Class \\(m \\(-> Integer\\)\\)\\)" - (: mixin ((Class [m (-> Integer)]) - -> - (Class [m (-> Integer)] - [n (-> String)]))) - (define (mixin cls) - (class cls - (super-new) - (define/public (n) "hi"))) - - (: arg-class% (Class [k (-> Integer)])) - (define arg-class% - (class object% - (super-new) - (define/public (k) 0))) - - (mixin arg-class%)) - - ;; classes that don't use define/public directly - (check-ok - (: 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)) - - ;; check that classes work in let clauses - (check-ok - (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))) - - ;; 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 #:exn #rx"typed classes must call super-new" - (: c% (Class (init [x Integer]))) - (define c% (class object% (init x)))) - - ;; fails, non-top-level super-new - ;; FIXME: this case also spits out additional untyped identifier - ;; errors which should be squelched maybe - (check-err #:exn #rx"typed classes must call super-new" - (: c% (Class (init [x Integer]))) - (define c% (class object% (let () (super-new)) (init x)))) - - ;; fails, bad super-new argument - (check-err #:exn #rx"Expected Integer, but got String" - (: c% (Class (init [x Integer]))) - (define c% (class object% (super-new) (init x))) - (: d% (Class)) - (define d% (class c% (super-new [x "bad"])))) - - ;; test override - (check-ok - (: 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))))) - - ;; test local call to overriden method - (check-ok - (: 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))))) - - ;; fails, superclass missing public for override - (check-err #:exn #rx"superclass missing overridable method m" - (: d% (Class [m (Integer -> Integer)])) - (define d% (class object% (super-new) - (define/override (m y) (* 2 y))))) - - ;; local field access and set! - (check-ok - (: 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))))))) - - ;; test top-level expressions in the class - (check-ok - (: c% (Class [m (Integer -> Integer)])) - (define c% (class object% (super-new) - (define/public (m y) 0) - (+ 3 5)))) - - ;; test top-level method call - (check-ok - (: c% (Class [m (Integer -> Integer)])) - (define c% (class object% (super-new) - (define/public (m y) 0) - (m 3)))) - - ;; test top-level field access - (check-ok - (: c% (Class (field [f String]))) - (define c% (class object% (super-new) - (field [f "foo"]) - (string-append f "z")))) - - ;; fails, bad top-level expression - (check-err #:exn #rx"Expected Number, but got String" - (: c% (Class [m (Integer -> Integer)])) - (define c% (class object% (super-new) - (define/public (m y) 0) - (+ "foo" 5)))) - - ;; fails, ill-typed method call - (check-err #:exn #rx"Expected Integer, but got String" - (: c% (Class [m (Integer -> Integer)])) - (define c% (class object% (super-new) - (define/public (m y) 0) - (m "foo")))) - - ;; fails, ill-typed field access - (check-err #:exn #rx"Expected String, but got Positive-Byte" - (: c% (Class (field [f String]))) - (define c% (class object% (super-new) - (field [f "foo"]) - (set! f 5)))) - - ;; test private field - (check-ok - (class object% - (super-new) - (: x Integer) - (define x 5) - (set! x 8) - (+ x 1)) - (: d% (Class (field [y String]))) - (define d% - (class object% - (super-new) - (: x Integer) - (define x 5) - (: y String) - (field [y "foo"])))) - - ;; fails, bad private field set! - (check-err #:exn #rx"Expected Integer, but got String" - (class object% - (super-new) - (: x Integer) - (define x 5) - (set! x "foo"))) - - ;; fails, bad private field default - (check-err #:exn #rx"Expected Integer, but got String" - (class object% - (super-new) - (: x Integer) - (define x "foo"))) - - ;; fails, private field needs type annotation - (check-err #:exn #rx"Expected Nothing" - (class object% - (super-new) - (define x "foo"))) - - ;; test private method - (check-ok - (class object% (super-new) - (: x (-> Integer)) - (define/private (x) 3) - (: m (-> Integer)) - (define/public (m) (x)))) - - ;; fails, public and private types conflict - (check-err #:exn #rx"Expected String, but got Integer" - (class object% (super-new) - (: x (-> Integer)) - (define/private (x) 3) - (: m (-> String)) - (define/public (m) (x)))) - - ;; fails, not enough annotation on private - (check-err #:exn #rx"Cannot apply expression of type Any" - (class object% (super-new) - (define/private (x) 3) - (: m (-> Integer)) - (define/public (m) (x)))) - - ;; fails, ill-typed private method implementation - (check-err #:exn #rx"Expected Integer, but got String" - (class object% (super-new) - (: x (-> Integer)) - (define/private (x) "bad result"))) - - ;; test optional init arg - (check-ok - (: c% (Class (init [x Integer #:optional]))) - (define c% (class object% (super-new) - (: x Integer) - (init [x 0])))) - - ;; test init coverage when all optionals are - ;; in the superclass - (check-ok - (: 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)))) - - ;; fails, expected mandatory but got optional - (check-err - #:exn #rx"optional init argument x that is not in the expected type" - (: c% (Class (init [x Integer]))) - (define c% (class object% (super-new) - (: x Integer) - (init [x 0])))) - - ;; fails, mandatory init not provided - (check-err #:exn #rx"value not provided for named init arg x" - (define d% (class object% (super-new) - (: x Integer) - (init x))) - (new d%)) - - ;; test that provided super-class inits don't count - ;; towards the type of current class - (check-ok - (: c% (Class)) - (define c% (class (class object% (super-new) - (: x Integer) - (init x)) - (super-new [x 3])))) - - ;; fails, super-class init already provided - (check-err - (define c% (class (class object% (super-new) - (: x Integer) - (init x)) - (super-new [x 3]))) - (new c% [x 5])) - - ;; fails, super-new can only be called once per class - (check-err - (class object% - (super-new) - (super-new))) - - ;; test passing an init arg to super-new - (check-ok - (define c% (class (class object% (super-new) - (: x Integer) - (init x)) - (: x Integer) - (init x) - (super-new [x x]))) - (new c% [x 5])) - - ;; fails, bad argument type to super-new - (check-err - (define c% (class (class object% (super-new) - (: x Integer) - (init x)) - (: x String) - (init x) - (super-new [x x])))) - - ;; test inherit method - (check-ok - (class (class object% (super-new) - (: m (Integer -> Integer)) - (define/public (m x) (add1 x))) - (super-new) - (inherit m) - (m 5))) - - ;; test internal name with inherit - (check-ok - (class (class object% (super-new) - (: m (Integer -> Integer)) - (define/public (m x) (add1 x))) - (super-new) - (inherit [n m]) - (n 5))) - - ;; test inherit field - (check-ok - (class (class object% (super-new) - (field [x : Integer 0])) - (super-new) - (inherit-field x))) - - ;; test internal name with inherit-field - (check-ok - (class (class object% (super-new) - (field [x : Integer 0])) - (super-new) - (inherit-field [y x]) - (set! y 1))) - - ;; fails, superclass missing inherited field - (check-err #:exn #rx"superclass missing field" - (class (class object% (super-new)) - (super-new) - (inherit-field [y x]))) - - ;; fails, missing super method for inherit - (check-err - (class (class object% (super-new)) (super-new) (inherit z))) - - ;; fails, bad argument type to inherited method - (check-err - (class (class object% (super-new) - (: m (Integer -> Integer)) - (define/public (m x) (add1 x))) - (super-new) - (inherit m) - (m "foo"))) - - ;; test that keyword methods type-check - ;; FIXME: send with keywords does not work yet - (check-ok - (: c% (Class [n (Integer #:foo Integer -> Integer)])) - (define c% - (class object% - (super-new) - (define/public (n x #:foo foo) - (+ foo x))))) - - ;; test instance subtyping - (check-ok - (define c% - (class object% - (super-new) - (: x (U False Number)) - (field [x 0]))) - (: x (Instance (Class))) - (define x (new c%))) - - ;; test use of `this` in field default - (check-ok - (class object% - (super-new) - (: x Integer) - (field [x 0]) - (: y Integer) - (field [y (get-field x this)]))) - - ;; test super calls - (check-ok - (define c% - (class object% - (super-new) - (: m (Integer -> Integer)) - (define/public (m x) 0))) - (define d% - (class c% - (super-new) - (define/override (m x) (add1 (super m 5))))) - (send (new d%) m 1)) - - ;; test super calls at top-level - (check-ok - (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)))) - - ;; fails, bad super call argument - (check-err - (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 - (check-ok - (define c% (class object% (super-new) - (public [m n]) - (define m (lambda () 0)))) - (send (new c%) n)) - - ;; test local calls with internal/external - (check-ok - (define c% (class object% (super-new) - (: m (-> Integer)) - (public [m n]) - (define m (lambda () 0)) - (: z (-> Integer)) - (define/public (z) (m)))) - (send (new c%) z)) - - ;; internal/external the same is ok - (check-ok - (define c% (class object% (super-new) - (public [m m]) - (define m (lambda () 0)))) - (send (new c%) m)) - - ;; fails, internal name not accessible - (check-err - (define c% (class object% (super-new) - (public [m n]) - (define m (lambda () 0)))) - (send (new c%) m)) - - ;; test internal/external with expected - (check-ok - (: c% (Class [n (-> Integer)])) - (define c% (class object% (super-new) - (public [m n]) - (define m (lambda () 0)))) - (send (new c%) n)) - - ;; test internal/external field - (check-ok - (define c% (class object% (super-new) - (: f Integer) - (field ([f g] 0)))) - (get-field g (new c%))) - - ;; fail, internal name not accessible - (check-err - (define c% (class object% (super-new) - (: f Integer) - (field ([f g] 0)))) - (get-field f (new c%))) - - ;; test internal/external init - (check-ok - (define c% (class object% (super-new) - (: i Integer) - (init ([i j])))) - (new c% [j 5])) - - ;; fails, internal name not accessible - (check-err - (define c% (class object% (super-new) - (: i Integer) - (init ([i j])))) - (new c% [i 5])) - - ;; test init default values - (check-ok - (class object% (super-new) - (: z Integer) - (init [z 0]))) - - ;; fails, bad default init value - (check-err - (class object% (super-new) - (: z Integer) - (init [z "foo"]))) - - ;; test init field default value - (check-ok - (define c% (class object% (super-new) - (: x Integer) - (init-field ([x y] 0))))) - - ;; fails, wrong init-field default - (check-err - (define c% (class object% (super-new) - (: x Integer) - (init-field ([x y] "foo"))))) - - ;; test type-checking method with internal/external - (check-err - (: 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 - (check-ok - (define c% (class object% (super-new) - (: m (Integer -> Integer)) - (define/public (m x) - 0))) - (send (new c%) m 5)) - - ;; fails, because the local call type is unknown - ;; and is assumed to be Any - (check-err #:exn #rx"since it is not a function type" - (class object% (super-new) - (define/public (m) (n)) - (define/public (n x) 0))) - - ;; test type-checking for classes without any - ;; internal type annotations on methods - (check-ok - (define c% (class object% (super-new) - (define/public (m) 0))) - (send (new c%) m)) - - ;; test inheritance without expected - (check-ok - (define c% (class (class object% (super-new) - (: m (-> Integer)) - (define/public (m) 0)) - (super-new) - (: n (-> Integer)) - (define/public (n) 1))) - (send (new c%) m) - (send (new c%) n)) - - ;; test fields without expected class type - (check-ok - (define c% (class object% (super-new) - (: x Integer) - (field [x 0]))) - (get-field x (new c%))) - - ;; row polymorphism, basic example with instantiation - (check-ok - (: 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]))) - - ;; fails, because the instantiation uses a field that - ;; is supposed to be absent via the row constraint - (check-err - (: 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]))) - - ;; fails, mixin argument is missing required field - (check-err - (: 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)))) - - ;; mixin application succeeds - (check-ok - (: 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])))) - - ;; Basic row constraint inference - (check-ok - (: 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]))) - - ;; fails, inferred constraint and instantiation don't match - (check-err - (: 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]))) - - ;; Check simple use of pubment - (check-ok - (define c% - (class object% - (super-new) - (: m (Integer -> Integer)) - (define/pubment (m x) 0))) - (send (new c%) m 3)) - - ;; Local calls to pubment method - (check-ok - (define c% - (class object% - (super-new) - (: m (Integer -> Integer)) - (define/pubment (m x) 0) - (: n (-> Number)) - (define/public (n) (m 5)))) - (send (new c%) n)) - - ;; Inheritance with augment - (check-ok - (define c% - (class object% - (super-new) - (: m (Integer -> Integer)) - (define/pubment (m x) 0))) - (define d% - (class c% - (super-new) - (define/augment (m x) - (+ 1 x)))) - (send (new c%) m 5)) - - ;; Pubment with inner - (check-ok - (define c% - (class object% - (super-new) - (: m (Integer -> Integer)) - (define/pubment (m x) - (inner 0 m x)))) - (define d% - (class c% - (super-new) - (define/augment (m x) - (+ 1 x)))) - (send (new c%) m 0)) - - ;; make sure augment type is reflected in class type - (check-ok - (: 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)))) - - ;; pubment with different augment type - (check-ok - (define c% - (class object% - (super-new) - (: m (Integer -> Integer) - #:augment (String -> String)) - (define/pubment (m x) - (inner "" m "foo") 0))) - (define d% - (class c% - (super-new) - (define/augment (m x) - (string-append x "bar")))) - (send (new c%) m 0)) - - ;; fail, bad inner argument - (check-err #:exn #rx"Expected String, but got Integer" - (define c% - (class object% - (super-new) - (: m (Integer -> Integer) - #:augment (String -> String)) - (define/pubment (m x) - (inner "" m x) 0))) - (define d% - (class c% - (super-new) - (define/augment (m x) - (string-append x "bar")))) - (send (new c%) m 0)) - - ;; Fail, bad inner default - (check-err #:exn #rx"Expected Integer, but got String" - (define c% - (class object% - (super-new) - (: m (Integer -> Integer)) - (define/pubment (m x) - (inner "foo" m x))))) - - ;; Fail, wrong number of arguments to inner - (check-err #:exn #rx"Wrong number of arguments, expected 2" - (define c% - (class object% - (super-new) - (: m (Integer -> Integer)) - (define/pubment (m x) - (inner 3 m))))) - - ;; Fail, bad augment type - (check-err #:exn #rx"Expected Integer, but got String" - (define c% - (class object% - (super-new) - (: m (Integer -> Integer)) - (define/pubment (m x) - (inner 0 m x)))) - (define d% - (class c% - (super-new) - (define/augment (m x) "bad type")))) - - ;; Fail, cannot augment non-augmentable method - (check-err #:exn #rx"superclass missing augmentable method m" - (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)))) - - ;; Pubment with separate internal/external names - (check-ok - (define c% - (class object% - (super-new) - (: m (Integer -> Integer)) - (pubment [n m]) - (define n (λ (x) 0)))) - (send (new c%) m 0)) - - ;; Pubment with expected class type - (check-ok - (: c% (Class [m (Natural -> Natural)] - (augment [m (Natural -> Natural)]))) - (define c% - (class object% - (super-new) - (define/pubment (m x) 0))) - (send (new c%) m 3)) - - ;; fails, expected type not a class - (check-err #:exn #rx"Expected Number" - (: c% Number) - (define c% - (class object% - (super-new) - (: x Integer) - (init-field x)))) - - ;; test polymorphic class - (check-ok - (: c% (All (A) (Class (init-field [x A])))) - (define c% - (class object% - (super-new) - (init-field x))) - (new (inst c% Integer) [x 0])) - - ;; fails due to ill-typed polymorphic class body - (check-err #:exn #rx"Expected A, but got Positive-Byte" - (: c% (All (A) (Class (init-field [x A])))) - (define c% - (class object% - (super-new) - (init-field x) - (set! x 5)))) - - ;; test polymorphism with keyword - (check-ok - (define point% - (class object% - #:forall 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"])) - - ;; test polymorphism with two type parameters - (check-ok - (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])) - - ;; test class polymorphism with method - (check-ok - (define id% - (class object% - #:forall (X) - (super-new) - (: m (X -> X)) - (define/public (m x) x))) - (send (new (inst id% Integer)) m 0)) - - ;; fails because m is not parametric - (check-err #:exn #rx"Expected X.*, but got String" - (class object% - #:forall (X) - (super-new) - (: m (X -> X)) - (define/public (m x) (string-append x)))) - - ;; fails because default init value cannot be polymorphic - (check-err #:exn #rx"Default init value has wrong type" - (class object% - #:forall Z - (super-new) - (init-field [x : Z] [y : Z 0]))) - - ;; fails because default field value cannot be polymorphic - (check-err #:exn #rx"Expected Z.*, but got Zero" - (class object% - #:forall Z - (super-new) - (field [x : Z 0]))) - - ;; test in-clause type annotations (next several tests) - (check-ok - (define c% - (class object% - (super-new) - (field [x : Integer 0]))) - (+ 1 (get-field x (new c%)))) - - (check-ok - (define c% - (class object% - (super-new) - (init-field [x : Integer]))) - (+ 1 (get-field x (new c% [x 5])))) - - (check-ok - (define c% - (class object% - (super-new) - (public [m : (Integer -> Integer)]) - (define (m x) (* x 2)))) - (send (new c%) m 52)) - - (check-ok - (define c% - (class object% - (super-new) - (private [m : (Integer -> Integer)]) - (define (m x) (* x 2))))) - - (check-ok - (define c% - (class object% - (super-new) - (field [(x y) : Integer 0]))) - (+ 1 (get-field y (new c%)))) - - ;; fails, duplicate type annotation - (check-err #:exn #rx"Duplicate type annotation of Real" - (class object% - (super-new) - (: x Real) - (field [x : Integer 0]))) - - ;; fails, expected type and annotation don't match - (check-err #:exn #rx"Expected \\(Class \\(field \\(x String" - (: c% (Class (field [x String]))) - (define c% - (class object% (super-new) - (field [x : Integer 5])))) - - ;; fails, but make sure it's not an internal error - (check-err #:exn #rx"Cannot apply expression of type Any" - (class object% (super-new) - (define/pubment (foo x) 0) - (define/public (g x) (foo 3)))))) - diff --git a/pkgs/typed-racket-pkgs/typed-racket-test/tests/typed-racket/unit-tests/typecheck-tests.rkt b/pkgs/typed-racket-pkgs/typed-racket-test/tests/typed-racket/unit-tests/typecheck-tests.rkt index 56fcc252..e6f82173 100644 --- a/pkgs/typed-racket-pkgs/typed-racket-test/tests/typed-racket/unit-tests/typecheck-tests.rkt +++ b/pkgs/typed-racket-pkgs/typed-racket-test/tests/typed-racket/unit-tests/typecheck-tests.rkt @@ -55,7 +55,7 @@ (require "evaluator.rkt" - "test-utils.rkt" + (except-in "test-utils.rkt" private) syntax/location (for-syntax racket/base @@ -145,6 +145,7 @@ ;; Needed for bindings of identifiers in expressions racket/bool + (except-in racket/class class) racket/file racket/fixnum racket/flonum @@ -163,7 +164,7 @@ racket/udp racket/vector - typed-racket/utils/utils + (except-in typed-racket/utils/utils private) ;; Needed for bindings of types and TR primitives in expressions (except-in (base-env extra-procs prims base-types base-types-extra) define lambda λ case-lambda) @@ -2294,6 +2295,1133 @@ : (make-Path null (list 0 0)))) (-FS -top -bot))] ) + (test-suite + "class typechecking tests" + ;; 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)) + #:msg #rx"expected a superclass but"] + ;; 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)) + #:msg #rx"method z not understood"] + ;; 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] + ;; 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] + ;; fails, field's default value has wrong type + [tc-err (class object% (super-new) + (: x Symbol) + (field [x "foo"])) + #: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)) + #:msg #rx"expected an object with 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)) + #:msg #rx"defines conflicting public 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)) + #:msg #rx"defines conflicting public 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] + ;; should fail, too many methods + [tc-err (let () + (: o% (Class)) + (define o% (class object% + (super-new) + (define/public (m) 0))) + (void)) + #:msg #rx"public method m that is not in the 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)) + #:msg #rx"public method n that is not in the expected type"] + ;; fails, too many inits + [tc-err (let () + (: c% (Class)) + (define c% (class object% (super-new) + (init x))) + (void)) + #:msg #rx"initialization argument x that is not in the 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)) + #:msg #rx"missing optional init argument 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)) + #:msg #rx"public field x that is not in the 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%)) + #:msg #rx"missing public 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)) + #:msg #rx"expected: \\(Class \\(m \\(-> Symbol\\)\\)\\)"] + ;; 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)) + #:msg #rx"typed classes 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)) + #:msg #rx"typed classes 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)) + #: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))] + ;; 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)) + #: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)) + #: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)) + #: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")) + #:msg #rx"expected: Symbol.*given: String"] + ;; fails, bad private field default + [tc-err (class object% + (super-new) + (: x Symbol) + (define x "foo")) + #:msg #rx"expected: Symbol.*given: String"] + ;; fails, private field needs type annotation + [tc-err (class object% (super-new) (define x "foo")) + #:msg #rx"expected: \\(U\\)"] + ;; 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))) + #: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))) + #: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")) + #: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)) + #:msg #rx"optional init argument x that is not in the expected type"] + ;; fails, mandatory init not provided + [tc-err (let () + (define d% (class object% (super-new) + (: x Integer) + (init x))) + (new d%)) + #: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]))] + ;; fails, super-new can only be called once per class + [tc-err (class object% (super-new) (super-new))] + ;; 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]))] + ;; 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 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])) + #:msg #rx"superclass missing field"] + ;; fails, missing super method for inherit + [tc-err (class (class object% (super-new)) (super-new) (inherit z))] + ;; 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"))] + ;; test that keyword methods type-check + ;; FIXME: send with keywords does not work yet + [tc-e (let () + (: c% (Class [n (Integer #:foo Integer -> Integer)])) + (define c% + (class object% + (super-new) + (define/public (n x #:foo foo) + (+ foo x)))) + (void)) + -Void] + ;; 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] + ;; 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 (-FS -top -bot))] + ;; 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]))] + ;; 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"]))] + ;; 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")))] + ;; 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)) + #: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 (-FS -top -bot))] + ;; 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])))] + ;; 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))))] + ;; 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])))] + ;; 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)) + #: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))) + #: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))) + #:msg #rx"Wrong number of arguments, 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)) + #: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)) + #:msg #rx"superclass missing augmentable method m"] + ;; Pubment with separate internal/external names + ;; FIXME: broken right now due to : macro changes + [tc-e (let () + (define c% + (class object% + (super-new) + (: m (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)) + #: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)) + #:msg #rx"expected: A.*given: String"] + ;; test polymorphism with keyword + [tc-e (let () + (define point% + (class object% + #:forall 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")) + #: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])) + #:msg #rx"Default init value has wrong type"] + ;; fails because default field value cannot be polymorphic + [tc-err (class object% + #:forall Z + (super-new) + (field [x : Z "a"])) + #: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])) + #:msg #rx"Duplicate type annotation of String"] + ;; 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)) + #:msg #rx"expected: \\(Class \\(field \\(x String"] + ;; 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))) + #:msg #rx"Cannot apply expression of type Any"]) (test-suite "tc-literal tests" (tc-l 5 -PosByte)