Merge V, X, and Y arguments into a single context in infer.

original commit: 615776f8d1feb3d761f0dbf97ab0e14f61eff51b
This commit is contained in:
Eric Dobson 2014-06-28 08:20:29 -07:00
parent d3705800c3
commit 10a4412490

View File

@ -43,6 +43,46 @@
(define (seen-before s t)
(cons (Type-seq s) (Type-seq t)))
;; Context, contains which type variables and indices to infer and which cannot be mentioned in
;; constraints.
(define-struct/cond-contract context
([bounds (listof symbol?)]
[vars (listof symbol?)]
[indices (listof symbol?)]) #:transparent)
(define (context-add-vars ctx vars)
(match ctx
[(context V X Y)
(context V (append vars X) Y)]))
(define (context-add-var ctx var)
(match ctx
[(context V X Y)
(context V (cons var X) Y)]))
(define (context-add ctx #:bounds [bounds empty] #:vars [vars empty] #:indices [indices empty])
(match ctx
[(context V X Y)
(context (append bounds V) (append vars X) (append indices Y))]))
(define (inferable-index? ctx bound)
(match ctx
[(context _ _ Y)
(memq bound Y)]))
(define ((inferable-var? ctx) var)
(match ctx
[(context _ X _)
(memq var X)]))
(define (empty-cset/context ctx)
(match ctx
[(context _ X Y)
(empty-cset X Y)]))
;; Type Type Seen -> Seen
;; Add the type pair to the set of seen type pairs
(define/cond-contract (remember s t A)
@ -184,77 +224,77 @@
(substitute (make-F var) v ty*))))
(define/cond-contract (cgen/filter V X Y s t)
((listof symbol?) (listof symbol?) (listof symbol?) Filter? Filter? . -> . (or/c #f cset?))
(define/cond-contract (cgen/filter context s t)
(context? Filter? Filter? . -> . (or/c #f cset?))
(match* (s t)
[(e e) (empty-cset X Y)]
[(e (Top:)) (empty-cset X Y)]
[(e e) (empty-cset/context context)]
[(e (Top:)) (empty-cset/context context)]
;; FIXME - is there something to be said about the logical ones?
[((TypeFilter: s p) (TypeFilter: t p)) (cgen/inv V X Y s t)]
[((NotTypeFilter: s p) (NotTypeFilter: t p)) (cgen/inv V X Y s t)]
[((TypeFilter: s p) (TypeFilter: t p)) (cgen/inv context s t)]
[((NotTypeFilter: s p) (NotTypeFilter: t p)) (cgen/inv context s t)]
[(_ _) #f]))
;; s and t must be *latent* filter sets
(define/cond-contract (cgen/filter-set V X Y s t)
((listof symbol?) (listof symbol?) (listof symbol?) FilterSet? FilterSet? . -> . (or/c #f cset?))
(define/cond-contract (cgen/filter-set context s t)
(context? FilterSet? FilterSet? . -> . (or/c #f cset?))
(match* (s t)
[(e e) (empty-cset X Y)]
[(e e) (empty-cset/context context)]
[((FilterSet: s+ s-) (FilterSet: t+ t-))
(% cset-meet (cgen/filter V X Y s+ t+) (cgen/filter V X Y s- t-))]
(% cset-meet (cgen/filter context s+ t+) (cgen/filter context s- t-))]
[(_ _) #f]))
(define/cond-contract (cgen/object V X Y s t)
((listof symbol?) (listof symbol?) (listof symbol?) Object? Object? . -> . (or/c #f cset?))
(define/cond-contract (cgen/object context s t)
(context? Object? Object? . -> . (or/c #f cset?))
(match* (s t)
[(e e) (empty-cset X Y)]
[(e (Empty:)) (empty-cset X Y)]
[(e e) (empty-cset/context context)]
[(e (Empty:)) (empty-cset/context context)]
;; FIXME - do something here
[(_ _) #f]))
(define/cond-contract (cgen/seq V X Y s-seq t-seq)
((listof symbol?) (listof symbol?) (listof symbol?) seq? seq? . -> . (or/c #f cset?))
(define/cond-contract (cgen/seq context s-seq t-seq)
(context? seq? seq? . -> . (or/c #f cset?))
(match*/early (s-seq t-seq)
;; The simplest case - both are null-end
[((seq ss (null-end))
(seq ts (null-end)))
(cgen/list V X Y ss ts)]
(cgen/list context ss ts)]
;; One is null-end the other is uniform-end
[((seq ss (null-end))
(seq ts (uniform-end t-rest)))
(cgen/list V X Y ss (extend ss ts t-rest))]
(cgen/list context ss (extend ss ts t-rest))]
[((seq ss (uniform-end s-rest))
(seq ts (null-end)))
#f]
;; Both are uniform-end
[((seq ss (uniform-end s-rest))
(seq ts (uniform-end t-rest)))
(cgen/list V X Y
(cgen/list context
(cons s-rest ss)
(cons t-rest (extend ss ts t-rest)))]
;; dotted below, nothing above
[((seq ss (dotted-end dty dbound))
(seq ts (null-end)))
#:return-unless (memq dbound Y)
#:return-unless (inferable-index? context dbound)
#f
#:return-unless (<= (length ss) (length ts))
#f
(define-values (vars new-tys) (generate-dbound-prefix dbound dty (- (length ts) (length ss)) #f))
(define-values (ts-front ts-back) (split-at ts (length ss)))
(% cset-meet
(cgen/list V X Y ss ts-front)
(% move-vars-to-dmap (cgen/list V (append vars X) Y new-tys ts-back) dbound vars))]
(cgen/list context ss ts-front)
(% move-vars-to-dmap (cgen/list (context-add context #:vars vars) new-tys ts-back) dbound vars))]
;; dotted above, nothing below
[((seq ss (null-end))
(seq ts (dotted-end dty dbound)))
#:return-unless (memq dbound Y)
#:return-unless (inferable-index? context dbound)
#f
#:return-unless (<= (length ts) (length ss))
#f
(define-values (vars new-tys) (generate-dbound-prefix dbound dty (- (length ss) (length ts)) #f))
(define-values (ss-front ss-back) (split-at ss (length ts)))
(% cset-meet
(cgen/list V X Y ss-front ts)
(% move-vars-to-dmap (cgen/list V (append vars X) Y ss-back new-tys) dbound vars))]
(cgen/list context ss-front ts)
(% move-vars-to-dmap (cgen/list (context-add-vars context vars) ss-back new-tys) dbound vars))]
;; same dotted bound
[((seq ss (dotted-end s-dty dbound))
@ -262,33 +302,35 @@
#:return-unless (= (length ss) (length ts))
#f
(% cset-meet
(cgen/list V X Y ss ts)
(if (memq dbound Y)
(cgen/list context ss ts)
(if (inferable-index? context dbound)
(extend-tvars (list dbound)
(% move-vars+rest-to-dmap (cgen V (cons dbound X) Y s-dty t-dty) null dbound))
(cgen V X Y s-dty t-dty)))]
(% move-vars+rest-to-dmap (cgen (context-add-var context dbound) s-dty t-dty) null dbound))
(cgen context s-dty t-dty)))]
;; bounds are different
[((seq ss (dotted-end s-dty (? (λ (db) (memq db Y)) dbound)))
(seq ts (dotted-end t-dty dbound*)))
#:return-unless (= (length ss) (length ts)) #f
#:return-when (memq dbound* Y) #f
(% cset-meet
(cgen/list V X Y ss ts)
(extend-tvars (list dbound*)
(% move-dotted-rest-to-dmap (cgen V (cons dbound X) Y s-dty t-dty) dbound dbound*)))]
[((seq ss (dotted-end s-dty dbound))
(seq ts (dotted-end t-dty (? (λ (db) (memq db Y)) dbound*))))
(seq ts (dotted-end t-dty dbound*)))
#:when (inferable-index? context dbound)
#:return-unless (= (length ss) (length ts)) #f
#:return-when (inferable-index? context dbound*) #f
(% cset-meet
(cgen/list context ss ts)
(extend-tvars (list dbound*)
(% move-dotted-rest-to-dmap (cgen (context-add-var context dbound) s-dty t-dty) dbound dbound*)))]
[((seq ss (dotted-end s-dty dbound))
(seq ts (dotted-end t-dty dbound*)))
#:when (inferable-index? context dbound*)
#:return-unless (= (length ss) (length ts)) #f
(% cset-meet
(cgen/list V X Y ss ts)
(cgen/list context ss ts)
(extend-tvars (list dbound)
(% move-dotted-rest-to-dmap (cgen V (cons dbound* X) Y s-dty t-dty) dbound* dbound)))]
(% move-dotted-rest-to-dmap (cgen (context-add-var context dbound*) s-dty t-dty) dbound* dbound)))]
;; * <: ...
[((seq ss (uniform-end s-rest))
(seq ts (dotted-end t-dty dbound)))
#:return-unless (memq dbound Y)
#:return-unless (inferable-index? context dbound)
#f
#:return-unless (<= (length ts) (length ss))
#f
@ -297,37 +339,38 @@
new-bound))
(define-values (ss-front ss-back) (split-at ss (length ts)))
(% cset-meet
(cgen/list V X Y ss-front ts)
(cgen/list context ss-front ts)
(% move-vars+rest-to-dmap
(% cset-meet
(cgen/list (cons new-bound V) (append vars X) (cons new-bound Y) ss-back new-tys)
(cgen V (cons dbound X) Y s-rest t-dty))
(cgen/list (context-add context #:bounds (list new-bound) #:vars vars #:indices (list new-bound))
ss-back new-tys)
(cgen (context-add-var context dbound) s-rest t-dty))
vars dbound #:exact #t))]
[((seq ss (dotted-end s-dty dbound))
(seq ts (uniform-end t-rest)))
(cond
[(memq dbound Y)
[(inferable-index? context dbound)
(define new-bound (gensym dbound))
(define length-delta (- (length ts) (length ss)))
(define-values (vars new-tys)
(generate-dbound-prefix dbound s-dty (max 0 length-delta) new-bound))
(% cset-meet
(cgen/list V X Y ss (if (positive? length-delta)
(drop-right ts length-delta)
(extend ss ts t-rest)))
(cgen/list context ss (if (positive? length-delta)
(drop-right ts length-delta)
(extend ss ts t-rest)))
(% move-vars+rest-to-dmap
(% cset-meet
(cgen/list (cons new-bound V) (append vars X) (cons new-bound Y)
(cgen/list (context-add context #:bounds (list new-bound) #:vars vars #:indices (list new-bound))
new-tys (take-right ts (max 0 length-delta)))
(cgen V (cons dbound X) Y s-dty t-rest))
(cgen (context-add-var context dbound) s-dty t-rest))
vars dbound))]
[else
(extend-tvars (list dbound)
(cgen/seq (cons dbound V) X Y (seq ss (uniform-end s-dty)) t-seq))])]))
(cgen/seq (context-add context #:bounds (list dbound)) (seq ss (uniform-end s-dty)) t-seq))])]))
(define/cond-contract (cgen/arr V X Y s-arr t-arr)
((listof symbol?) (listof symbol?) (listof symbol?) arr? arr? . -> . (or/c #f cset?))
(define/cond-contract (cgen/arr context s-arr t-arr)
(context? arr? arr? . -> . (or/c #f cset?))
(match* (s-arr t-arr)
[((arr: ss s s-rest s-drest s-kws) (arr: ts t t-rest t-drest t-kws))
@ -342,45 +385,41 @@
(and (null? s-kws)
(null? t-kws)
(% cset-meet
(cgen V X Y s t)
(cgen/seq V X Y t-seq s-seq)))]))
(cgen context s t)
(cgen/seq context t-seq s-seq)))]))
(define/cond-contract (cgen/flds V X Y flds-s flds-t)
((listof symbol?) (listof symbol?) (listof symbol?) (listof fld?) (listof fld?)
. -> . (or/c #f cset?))
(define/cond-contract (cgen/flds context flds-s flds-t)
(context? (listof fld?) (listof fld?) . -> . (or/c #f cset?))
(% cset-meet*
(for/list/fail ([s (in-list flds-s)] [t (in-list flds-t)])
(match* (s t)
;; mutable - invariant
[((fld: s _ #t) (fld: t _ #t)) (cgen/inv V X Y s t)]
[((fld: s _ #t) (fld: t _ #t)) (cgen/inv context s t)]
;; immutable - covariant
[((fld: s _ #f) (fld: t _ #f)) (cgen V X Y s t)]))))
[((fld: s _ #f) (fld: t _ #f)) (cgen context s t)]))))
(define (cgen/inv V X Y s t)
(% cset-meet (cgen V X Y s t) (cgen V X Y t s)))
(define (cgen/inv context s t)
(% cset-meet (cgen context s t) (cgen context t s)))
;; V : a set of variables not to mention in the constraints
;; X : the set of type variables to be constrained
;; Y : the set of index variables to be constrained
;; context : the context of what to infer/not infer
;; S : a type to be the subtype of T
;; T : a type
;; produces a cset which determines a substitution that makes S a subtype of T
;; implements the V |-_X S <: T => C judgment from Pierce+Turner, extended with
;; the index variables from the TOPLAS paper
(define/cond-contract (cgen V X Y S T)
((listof symbol?) (listof symbol?) (listof symbol?)
(or/c Values/c ValuesDots? AnyValues?) (or/c Values/c ValuesDots? AnyValues?)
(define/cond-contract (cgen context S T)
(context? (or/c Values/c ValuesDots? AnyValues?) (or/c Values/c ValuesDots? AnyValues?)
. -> . (or/c #F cset?))
;; useful quick loop
(define/cond-contract (cg S T)
(Type/c Type/c . -> . (or/c #f cset?))
(cgen V X Y S T))
(cgen context S T))
(define/cond-contract (cg/inv S T)
(Type/c Type/c . -> . (or/c #f cset?))
(cgen/inv V X Y S T))
;; this places no constraints on any variables in X
(define empty (empty-cset X Y))
(cgen/inv context S T))
;; this places no constraints on any variables
(define empty (empty-cset/context context))
;; this constrains just x (which is a single var)
(define (singleton S x T)
(insert empty x S T))
@ -401,7 +440,7 @@
[(_ (Univ:)) empty]
;; AnyValues
[((AnyValues: s-f) (AnyValues: t-f))
(cgen/filter V X Y s-f t-f)]
(cgen/filter context s-f t-f)]
[((or (Values: (list (Result: _ fs _) ...))
(ValuesDots: (list (Result: _ fs _) ...) _ _))
@ -411,7 +450,7 @@
(for/list ([f (in-list fs)])
(match f
[(FilterSet: f+ f-)
(% cset-meet (cgen/filter V X Y f+ t-f) (cgen/filter V X Y f- t-f))]))))]
(% cset-meet (cgen/filter context f+ t-f) (cgen/filter context f- t-f))]))))]
;; check all non Type/c first so that calling subtype is safe
@ -419,8 +458,8 @@
[((Result: s f-s o-s)
(Result: t f-t o-t))
(% cset-meet (cg s t)
(cgen/filter-set V X Y f-s f-t)
(cgen/object V X Y o-s o-t))]
(cgen/filter-set context f-s f-t)
(cgen/object context o-s o-t))]
;; Values just delegate to cgen/seq, except special handling for -Bottom.
;; A single -Bottom in a Values means that there is no value returned and so any other
@ -430,10 +469,10 @@
(define bottom-case
(match S
[(Values: (list (Result: s f-s o-s)))
(cgen V X Y s -Bottom)]
(cgen context s -Bottom)]
[else #f]))
(define regular-case
(cgen/seq V X Y s-seq t-seq))
(cgen/seq context s-seq t-seq))
;; If we want the OR of the csets that the two cases return.
(cset-join
(filter values
@ -446,7 +485,7 @@
;; Lists delegate to sequences
[((ListSeq: s-seq) (ListSeq: t-seq))
(cgen/seq V X Y s-seq t-seq)]
(cgen/seq context s-seq t-seq)]
;; refinements are erased to their bound
[((Refinement: S _) T)
@ -454,24 +493,24 @@
;; variables that are in X and should be constrained
;; all other variables are compatible only with themselves
[((F: (? (λ (e) (memq e X)) v)) T)
[((F: (? (inferable-var? context) v)) T)
#:return-when
(match T
;; fail when v* is an index variable
[(F: v*) (and (bound-index? v*) (not (bound-tvar? v*)))]
[_ #f])
#f
;; constrain v to be below T (but don't mention V)
(singleton (Un) v (var-demote T V))]
;; constrain v to be below T (but don't mention bounds)
(singleton (Un) v (var-demote T (context-bounds context)))]
[(S (F: (? (lambda (e) (memq e X)) v)))
[(S (F: (? (inferable-var? context) v)))
#:return-when
(match S
[(F: v*) (and (bound-index? v*) (not (bound-tvar? v*)))]
[_ #f])
#f
;; constrain v to be above S (but don't mention V)
(singleton (var-promote S V) v Univ)]
;; constrain v to be above S (but don't mention bounds)
(singleton (var-promote S (context-bounds context)) v Univ)]
;; recursive names should get resolved as they're seen
[(s (? Name? t))
@ -480,7 +519,7 @@
(cg (resolve-once s) t)]
;; constrain b1 to be below T, but don't mention the new vars
[((Poly: v1 b1) T) (cgen (append v1 V) X Y b1 T)]
[((Poly: v1 b1) T) (cgen (context-add context #:bounds v1) b1 T)]
;; constrain *each* element of es to be below T, and then combine the constraints
[((Union: es) T)
@ -506,7 +545,7 @@
(cg proc proc*)]
[proc* #f]
[else empty])])
(% cset-meet proc-c (cgen/flds V X Y flds flds*)))]
(% cset-meet proc-c (cgen/flds context flds flds*)))]
;; two struct names, need to resolve b/c one could be a parent
[((Name: n _ _ #t) (Name: n* _ _ #t))
@ -518,7 +557,7 @@
(% cset-meet (cg a a*) (cg b b*))]
;; sequences are covariant
[((Sequence: ts) (Sequence: ts*))
(cgen/list V X Y ts ts*)]
(cgen/list context ts ts*)]
[((Listof: t) (Sequence: (list t*)))
(cg t t*)]
[((Pair: t1 t2) (Sequence: (list t*)))
@ -533,10 +572,10 @@
(% cset-meet* (for/list/fail ([t (in-list ts)])
(cg t t*)))]
[((HeterogeneousVector: ts) (HeterogeneousVector: ts*))
(% cset-meet (cgen/list V X Y ts ts*) (cgen/list V X Y ts* ts))]
(% cset-meet (cgen/list context ts ts*) (cgen/list context ts* ts))]
[((HeterogeneousVector: ts) (Vector: s))
(define ts* (map (λ _ s) ts)) ;; invariant, everything has to match
(% cset-meet (cgen/list V X Y ts ts*) (cgen/list V X Y ts* ts))]
(% cset-meet (cgen/list context ts ts*) (cgen/list context ts* ts))]
[((HeterogeneousVector: ts) (Sequence: (list t*)))
(% cset-meet* (for/list/fail ([t (in-list ts)])
(cg t t*)))]
@ -567,7 +606,7 @@
(and (subtype S t) t)))
(% cg type t*)]
[((Hashtable: k v) (Sequence: (list k* v*)))
(cgen/list V X Y (list k v) (list k* v*))]
(cgen/list context (list k v) (list k* v*))]
[((Set: t) (Sequence: (list t*)))
(cg t t*)]
@ -662,7 +701,7 @@
(for/list/fail ([t-arr (in-list t-arr)])
;; for each element of t-arr, we need to get at least one element of s-arr that works
(let ([results (for*/list ([s-arr (in-list s-arr)]
[v (in-value (cgen/arr V X Y s-arr t-arr))]
[v (in-value (cgen/arr context s-arr t-arr))]
#:when v)
v)])
;; ensure that something produces a constraint set
@ -742,25 +781,22 @@
(and entry (t-subst? entry))))
(extend-idxs subst)))]))
;; V : a set of variables not to mention in the constraints
;; X : the set of type variables to be constrained
;; Y : the set of index variables to be constrained
;; context : the context of what to infer/not infer
;; S : a list of types to be the subtypes of T
;; T : a list of types
;; expected-cset : a cset representing the expected type, to meet early and
;; keep the number of constraints in check. (empty by default)
;; produces a cset which determines a substitution that makes the Ss subtypes of the Ts
(define/cond-contract (cgen/list V X Y S T
(define/cond-contract (cgen/list context S T
#:expected-cset [expected-cset (empty-cset '() '())])
(((listof symbol?) (listof symbol?) (listof symbol?) (listof Values/c) (listof Values/c))
(#:expected-cset cset?) . ->* . (or/c cset? #f))
((context? (listof Values/c) (listof Values/c)) (#:expected-cset cset?) . ->* . (or/c cset? #f))
(and (= (length S) (length T))
(% cset-meet*
(for/list/fail ([s (in-list S)] [t (in-list T)])
;; We meet early to prune the csets to a reasonable size.
;; This weakens the inference a bit, but sometimes avoids
;; constraint explosion.
(% cset-meet (cgen V X Y s t) expected-cset)))))
(% cset-meet (cgen context s t) expected-cset)))))
@ -780,13 +816,16 @@
(or/c #f Values/c ValuesDots?))
((or/c #f Values/c AnyValues? ValuesDots?))
. ->* . (or/c boolean? substitution/c))
(let* ([expected-cset (if expected
(cgen null X Y R expected)
(empty-cset '() '()))]
[cs (and expected-cset
(cgen/list null X Y S T #:expected-cset expected-cset))]
[cs* (% cset-meet cs expected-cset)])
(and cs* (if R (subst-gen cs* X Y R) #t))))
(define ctx (context null X Y ))
(define expected-cset
(if expected
(cgen ctx R expected)
(empty-cset '() '())))
(and expected-cset
(let* ([cs (cgen/list ctx S T #:expected-cset expected-cset)]
[cs* (% cset-meet cs expected-cset)])
(and cs* (if R (subst-gen cs* X Y R) #t)))))
;(trace infer)
infer)) ;to export a variable binding and not syntax
;; like infer, but T-var is the vararg type:
@ -801,16 +840,16 @@
(early-return
(define short-S (take S (length T)))
(define rest-S (drop S (length T)))
(define ctx (context null X (list dotted-var)))
(define expected-cset (if expected
(cgen null X (list dotted-var) R expected)
(cgen ctx R expected)
(empty-cset '() '())))
#:return-unless expected-cset #f
(define cs-short (cgen/list null X (list dotted-var) short-S T
#:expected-cset expected-cset))
(define cs-short (cgen/list ctx short-S T #:expected-cset expected-cset))
#:return-unless cs-short #f
(define-values (new-vars new-Ts)
(generate-dbound-prefix dotted-var T-dotted (length rest-S) #f))
(define cs-dotted (cgen/list null (append new-vars X) (list dotted-var) rest-S new-Ts
(define cs-dotted (cgen/list (context-add-vars ctx new-vars) rest-S new-Ts
#:expected-cset expected-cset))
#:return-unless cs-dotted #f
(define cs-dotted* (move-vars-to-dmap cs-dotted dotted-var new-vars))
@ -822,3 +861,8 @@
(subst-gen m X (list dotted-var) R)))
;(trace subst-gen)
;(trace cgen)
;(trace cgen/list)
;(trace cgen/arr)
;(trace cgen/seq)