Skip to content

Commit 619a900

Browse files
committed
WIP bounded polymorphism
1 parent 7479bef commit 619a900

File tree

7 files changed

+129
-40
lines changed

7 files changed

+129
-40
lines changed

typed-racket-lib/typed-racket/infer/infer-unit.rkt

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -48,36 +48,37 @@
4848
(define-struct/cond-contract context
4949
([bounds (listof symbol?)]
5050
[vars (listof symbol?)]
51-
[indices (listof symbol?)]) #:transparent)
51+
[indices (listof symbol?)]
52+
[type-bounds (hash/c symbol? Type?)]) #:transparent)
5253

5354
(define (context-add-vars ctx vars)
5455
(match ctx
55-
[(context V X Y)
56-
(context V (append vars X) Y)]))
56+
[(context V X Y TB)
57+
(context V (append vars X) Y TB)]))
5758

5859
(define (context-add-var ctx var)
5960
(match ctx
60-
[(context V X Y)
61-
(context V (cons var X) Y)]))
61+
[(context V X Y TB)
62+
(context V (cons var X) Y TB)]))
6263

6364
(define (context-add ctx #:bounds [bounds empty] #:vars [vars empty] #:indices [indices empty])
6465
(match ctx
65-
[(context V X Y)
66-
(context (append bounds V) (append vars X) (append indices Y))]))
66+
[(context V X Y TB)
67+
(context (append bounds V) (append vars X) (append indices Y) TB)]))
6768

6869
(define (inferable-index? ctx bound)
6970
(match ctx
70-
[(context _ _ Y)
71+
[(context _ _ Y TB)
7172
(memq bound Y)]))
7273

7374
(define ((inferable-var? ctx) var)
7475
(match ctx
75-
[(context _ X _)
76+
[(context _ X _ TB)
7677
(memq var X)]))
7778

7879
(define (empty-cset/context ctx)
7980
(match ctx
80-
[(context _ X Y)
81+
[(context _ X Y TB)
8182
(empty-cset X Y)]))
8283

8384

@@ -474,7 +475,7 @@
474475
;; produces a cset which determines a substitution that makes S a subtype of T
475476
;; implements the V |-_X S <: T => C judgment from Pierce+Turner, extended with
476477
;; the index variables from the TOPLAS paper
477-
(define/cond-contract (cgen context S T [obj #f])
478+
(define/cond-contract (cgen context S T [obj #f] [bound #f])
478479
(->* (context? (or/c Values/c ValuesDots? AnyValues?)
479480
(or/c Values/c ValuesDots? AnyValues?))
480481
((or/c #f OptObject?))
@@ -483,7 +484,7 @@
483484
(define/cond-contract (cg S T [obj #f])
484485
(->* (Type? Type?) ((or/c #f OptObject?))
485486
(or/c #f cset?))
486-
(cgen context S T obj))
487+
(cgen context S T obj bound))
487488
(define/cond-contract (cg/inv S T)
488489
(Type? Type? . -> . (or/c #f cset?))
489490
(cgen/inv context S T))
@@ -576,16 +577,22 @@
576577
[_ #f])
577578
#f
578579
;; constrain v to be below T (but don't mention bounds)
579-
(singleton -Bottom v (var-demote T (context-bounds context)))]
580+
(eprintf "hi...........~a ~n" v)
581+
(if (subtype (hash-ref (context-type-bounds context) v) T obj)
582+
(singleton -Bottom v (var-demote T (context-bounds context)))
583+
#f)]
580584

581585
[(S (F: (? (inferable-var? context) v)))
582586
#:return-when
583587
(match S
584588
[(F: v*) (and (bound-index? v*) (not (bound-tvar? v*)))]
585589
[_ #f])
586590
#f
591+
(eprintf "bye...........~a ~a <: ~a ~n" v S (hash-ref (context-type-bounds context) v))
587592
;; constrain v to be above S (but don't mention bounds)
588-
(singleton (var-promote S (context-bounds context)) v Univ)]
593+
(if (subtype S (hash-ref (context-type-bounds context) v) obj)
594+
(singleton (var-promote S (context-bounds context)) v (hash-ref (context-type-bounds context) v))
595+
#f)]
589596

590597
;; recursive names should get resolved as they're seen
591598
[(s (? Name? t))
@@ -1003,17 +1010,19 @@
10031010
(let ()
10041011
(define/cond-contract (infer X Y S T R [expected #f]
10051012
#:multiple? [multiple-substitutions? #f]
1013+
#:bounds [bounds '#()]
10061014
#:objs [objs '()])
10071015
(((listof symbol?) (listof symbol?) (listof Type?) (listof Type?)
10081016
(or/c #f Values/c AnyValues? ValuesDots?))
10091017
((or/c #f Values/c AnyValues? ValuesDots?)
10101018
#:multiple? boolean?
1019+
#:bounds (hash/c symbol? Type?)
10111020
#:objs (listof OptObject?))
10121021
. ->* . (or/c boolean?
10131022
substitution/c
10141023
(cons/c substitution/c
10151024
(listof substitution/c))))
1016-
(define ctx (context null X Y))
1025+
(define ctx (context null X Y bounds))
10171026
(define expected-cset
10181027
(if expected
10191028
(cgen ctx R expected)
@@ -1030,7 +1039,8 @@
10301039

10311040
;; like infer, but T-var is the vararg type:
10321041
(define (infer/vararg X Y S T T-var R [expected #f]
1033-
#:objs [objs '()])
1042+
#:objs [objs '()]
1043+
#:bounds [bounds '#hash()])
10341044
(and ((length S) . >= . (length T))
10351045
(let* ([fewer-ts (- (length S) (length T))]
10361046
[new-T (match T-var
@@ -1040,7 +1050,8 @@
10401050
(append T (repeat-list rst-ts
10411051
(quotient fewer-ts (length rst-ts))))]
10421052
[_ T])])
1043-
(infer X Y S new-T R expected #:objs objs))))
1053+
(infer X Y S new-T R expected #:objs objs
1054+
#:bounds bounds))))
10441055

10451056
;; like infer, but dotted-var is the bound on the ...
10461057
;; and T-dotted is the repeated type
@@ -1055,7 +1066,7 @@
10551066
(generate-dbound-prefix dotted-var T-dotted (length rest-S) #f))
10561067
(define (subst t)
10571068
(substitute-dots (map make-F new-vars) #f dotted-var t))
1058-
(define ctx (context null (append new-vars X) (list dotted-var)))
1069+
(define ctx (context null (append new-vars X) (list dotted-var) '#hash()))
10591070

10601071
(define expected-cset (if expected
10611072
(cgen ctx (subst R) expected)

typed-racket-lib/typed-racket/infer/signatures.rkt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@
3939
((or/c #f Values/c AnyValues? ValuesDots?)
4040
;; optional multiple substitutions?
4141
#:multiple? boolean?
42-
#:objs (listof OptObject?))
42+
#:objs (listof OptObject?)
43+
#:bounds (hash/c symbol? Type?))
4344
. ->* . any)]
4445
[cond-contracted infer/vararg ((;; variables from the forall
4546
(listof symbol?)
@@ -55,7 +56,8 @@
5556
(or/c #f Values/c AnyValues? ValuesDots?))
5657
;; [optional] expected type
5758
((or/c #f Values/c AnyValues? ValuesDots?)
58-
#:objs (listof OptObject?))
59+
#:objs (listof OptObject?)
60+
#:bounds (hash/c symbol? Type?))
5961
. ->* . any)]
6062
[cond-contracted infer/dots (((listof symbol?)
6163
symbol?

typed-racket-lib/typed-racket/private/parse-type.rkt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@
193193
(parse-literal-alls #'t.type))]
194194
[_ null]))
195195

196+
(define-syntax-class maybe-bounded
197+
#:datum-literals (<:)
198+
(pattern (name:id <: bound:id)))
196199

197200
;; Syntax -> Type
198201
;; Parse a Forall type
@@ -215,7 +218,18 @@
215218
"variable" (syntax-e maybe-dup)))
216219
(let* ([vars (stx-map syntax-e #'(vars ...))])
217220
(extend-tvars vars
218-
(make-Poly vars (parse-type #'t.type))))]
221+
(make-Poly vars (parse-type #'t.type))))]
222+
[(:All^ (vars:maybe-bounded ...) . t:omit-parens)
223+
(define maybe-dup (check-duplicate-identifier (attribute vars.name)))
224+
(when maybe-dup
225+
(parse-error "duplicate type variable"
226+
"variable" (syntax-e maybe-dup)))
227+
(define bounds (for/hash ([i (stx-map syntax-e (attribute vars.name))]
228+
[j (attribute vars.bound)])
229+
(values i (parse-type j))))
230+
(let* ([vars (stx-map syntax-e (attribute vars.name))])
231+
(extend-tvars vars
232+
(make-Poly vars (parse-type #'t.type) #:bounds bounds)))]
219233
;; Next two are row polymorphic cases
220234
[(:All^ (var:id #:row) . t:omit-parens)
221235
(add-disappeared-use #'kw)

typed-racket-lib/typed-racket/rep/type-rep.rkt

Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@
4646
PolyDots-unsafe:
4747
Mu? Poly? PolyDots? PolyRow?
4848
Poly-n
49+
F-n
50+
F-bound
51+
F?
4952
PolyDots-n
5053
Class? Row? Row:
5154
free-vars*
@@ -84,6 +87,8 @@
8487
[PolyDots:* PolyDots:]
8588
[PolyRow:* PolyRow:]
8689
[Mu* make-Mu]
90+
[F* make-F]
91+
[F:* F:]
8792
[make-Mu unsafe-make-Mu]
8893
[Poly* make-Poly]
8994
[PolyDots* make-PolyDots]
@@ -139,13 +144,32 @@
139144

140145
;; free type variables
141146
;; n is a Name
142-
(def-type F ([n symbol?])
147+
(def-type F ([n symbol?]
148+
[bound (or/c #f Type?)])
149+
#:no-provide
143150
[#:frees
144151
[#:vars (_) (single-free-var n)]
145152
[#:idxs (_) empty-free-vars]]
146153
[#:fmap (_ #:self self) self]
147154
[#:for-each (_) (void)])
148155

156+
(define (F* n [bound #f])
157+
(make-F n bound))
158+
159+
160+
(define-match-expander F:*
161+
(lambda (stx)
162+
(syntax-case stx ()
163+
[(_ n)
164+
#'(? F? (app (lambda (t)
165+
(F-n t))
166+
n))]
167+
[(_ n b)
168+
#'(? F? (app (lambda (t)
169+
(list (F-n t) (F-bound t)))
170+
(list n b)))])))
171+
172+
149173
(define Name-table (make-free-id-table))
150174

151175
;; Name, an indirection of a type through the environment
@@ -519,10 +543,14 @@
519543
;; n is how many variables are bound here
520544
;; body is a type
521545
(def-type Poly ([n exact-nonnegative-integer?]
546+
[bounds (hash/c exact-nonnegative-integer?
547+
Type?
548+
#:immutable #t
549+
#:flat #t)]
522550
[body Type?])
523551
#:no-provide
524552
[#:frees (f) (f body)]
525-
[#:fmap (f) (make-Poly n (f body))]
553+
[#:fmap (f) (make-Poly n bounds (f body))]
526554
[#:for-each (f) (f body)]
527555
[#:mask (λ (t) (mask (Poly-body t)))])
528556

@@ -1456,7 +1484,7 @@
14561484
;; De Bruijn indices
14571485
[(B: idx) (transform idx lvl cur #f)]
14581486
;; Type variables
1459-
[(F: var) (transform var lvl cur #f)]
1487+
[(F: var _) (transform var lvl cur #f)]
14601488
;; forms w/ dotted type vars/indices
14611489
[(RestDots: ty d)
14621490
(make-RestDots (rec ty) (transform d lvl d #t))]
@@ -1477,7 +1505,7 @@
14771505
(make-PolyRow constraints (rec/lvl body (add1 lvl)))]
14781506
[(PolyDots: n body)
14791507
(make-PolyDots n (rec/lvl body (+ n lvl)))]
1480-
[(Poly: n body)
1508+
[(Poly: n bound body)
14811509
(make-Poly n (rec/lvl body (+ n lvl)))]
14821510
[_ (Rep-fmap cur rec)])))
14831511

@@ -1618,7 +1646,7 @@
16181646
(define (Mu-body* name t)
16191647
(match t
16201648
[(Mu: body)
1621-
(instantiate-type body (make-F name))]))
1649+
(instantiate-type body (F* name))]))
16221650

16231651
;; unfold : Mu -> Type
16241652
(define/cond-contract (unfold t)
@@ -1638,19 +1666,33 @@
16381666
;;
16391667
;; list<symbol> type #:original-names list<symbol> -> type
16401668
;;
1641-
(define (Poly* names body #:original-names [orig names])
1669+
(define (Poly* names body #:bounds [bounds '#hash()] #:original-names [orig names])
16421670
(if (null? names) body
1643-
(let ([v (make-Poly (length names) (abstract-type body names))])
1671+
(let* ([len (length names)]
1672+
[new-bounds (let ([max-idx (sub1 len)])
1673+
(for/hash ([(n v) bounds])
1674+
(values (- max-idx (index-of names n)) v)))]
1675+
[v (make-Poly len new-bounds (abstract-type body names))])
16441676
(hash-set! type-var-name-table v orig)
16451677
v)))
16461678

16471679
;; Poly 'smart' destructor
16481680
(define (Poly-body* names t)
16491681
(match t
1650-
[(Poly: n body)
1682+
[(Poly: n bounds body)
1683+
(define new-bounds (for/hash ([(idx v) bounds])
1684+
(values (list-ref names idx) v)))
16511685
(unless (= (length names) n)
16521686
(int-err "Wrong number of names: expected ~a got ~a" n (length names)))
1653-
(instantiate-type body (map make-F names))]))
1687+
(eprintf "new bounds is ~a ~n" new-bounds)
1688+
(instantiate-type body
1689+
1690+
(map (lambda (n)
1691+
(make-F n
1692+
(hash-ref new-bounds n #f)
1693+
)) names)
1694+
#;
1695+
(map F* names))]))
16541696

16551697
;; PolyDots 'smart' constructor
16561698
(define (PolyDots* names body)
@@ -1665,7 +1707,7 @@
16651707
[(PolyDots: n body)
16661708
(unless (= (length names) n)
16671709
(int-err "Wrong number of names: expected ~a got ~a" n (length names)))
1668-
(instantiate-type body (map make-F names))]))
1710+
(instantiate-type body (map F* names))]))
16691711

16701712

16711713
;; PolyRow 'smart' constructor
@@ -1683,7 +1725,7 @@
16831725
(define (PolyRow-body* names t)
16841726
(match t
16851727
[(PolyRow: constraints body)
1686-
(instantiate-type body (map make-F names))]))
1728+
(instantiate-type body (map names))]))
16871729

16881730

16891731
;;***************************************************************
@@ -1746,7 +1788,16 @@
17461788
(let* ([n (Poly-n t)]
17471789
[syms (build-list n (lambda _ (gensym)))])
17481790
(list syms (Poly-body* syms t))))
1749-
(list nps bp)))])))
1791+
(list nps bp)))]
1792+
[(_ nps bounds bp)
1793+
#'(? Poly?
1794+
(app (lambda (t)
1795+
(let* ([n (Poly-n t)]
1796+
[syms (build-list n (lambda _ (gensym)))]
1797+
[bounds (for/hash ([(idx v) (Poly-bounds t)])
1798+
(values (list-ref syms idx) v))])
1799+
(list syms bounds (Poly-body* syms t))))
1800+
(list nps bounds bp)))])))
17501801

17511802
;; This match expander uses the names from the hashtable
17521803
(define-match-expander Poly-names:
@@ -1939,7 +1990,7 @@
19391990
[(Some: n body)
19401991
(unless (= (length names) n)
19411992
(int-err "Wrong number of names: expected ~a got ~a" n (length names)))
1942-
(instantiate-type body (map make-F names))]))
1993+
(instantiate-type body (map names))]))
19431994

19441995

19451996
(define-match-expander Some-names:

typed-racket-lib/typed-racket/typecheck/tc-funapp.rkt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@
192192
#:expected expected)]
193193
;; regular polymorphic functions without dotted rest,
194194
;; we do not choose any instantiations with mandatory keyword arguments
195-
[(Poly: vars (Fun: arrows))
195+
[(Poly: vars bounds (Fun: arrows))
196196
;; check there are no RestDots
197197
#:when (not (for/or ([a (in-list arrows)])
198198
(RestDots? (Arrow-rst a))))
@@ -214,7 +214,8 @@
214214
(infer/vararg vars null argtys dom rst rng
215215
(and expected
216216
(tc-results->values expected))
217-
#:objs argobjs)))
217+
#:objs argobjs
218+
#:bounds bounds)))
218219
#:function-type f-type
219220
#:args-results args-res
220221
#:expected expected)]

typed-racket-lib/typed-racket/typecheck/tc-lambda-unit.rkt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,7 @@
715715
[(tc-result1: (app resolve (and t (Poly-fresh: ns fresh-ns expected*))))
716716
;; make sure the declared and annotated type variable arities match up
717717
;; with the expected type variable arity
718+
(eprintf "app resolve tc-result ~n")
718719
(for ([tvars (in-list tvarss)])
719720
(when (and (pair? tvars) (list? (car tvars)))
720721
(tc-error

0 commit comments

Comments
 (0)