Skip to content

Commit 42303c0

Browse files
author
Andrew Kent
committed
initial rest cycle support
1 parent 2ef852a commit 42303c0

26 files changed

+538
-217
lines changed

typed-racket-lib/typed-racket/base-env/base-env.rkt

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -923,21 +923,9 @@
923923
[hash-eqv? (-> -HashTableTop B)]
924924
[hash-equal? (-> -HashTableTop B)]
925925
[hash-weak? (asym-pred -HashTableTop B (-PS (-is-type 0 -Weak-HashTableTop) (-not-type 0 -Weak-HashTableTop)))]
926-
[hash (-poly (a b) (cl->* (-> (-Immutable-HT a b))
927-
(a b . -> . (-Immutable-HT a b))
928-
(a b a b . -> . (-Immutable-HT a b))
929-
(a b a b a b . -> . (-Immutable-HT a b))
930-
(a b a b a b a b . -> . (-Immutable-HT a b))))]
931-
[hasheqv (-poly (a b) (cl->* (-> (-Immutable-HT a b))
932-
(a b . -> . (-Immutable-HT a b))
933-
(a b a b . -> . (-Immutable-HT a b))
934-
(a b a b a b . -> . (-Immutable-HT a b))
935-
(a b a b a b a b . -> . (-Immutable-HT a b))))]
936-
[hasheq (-poly (a b) (cl->* (-> (-Immutable-HT a b))
937-
(a b . -> . (-Immutable-HT a b))
938-
(a b a b . -> . (-Immutable-HT a b))
939-
(a b a b a b . -> . (-Immutable-HT a b))
940-
(a b a b a b a b . -> . (-Immutable-HT a b))))]
926+
[hash (-poly (a b) (->* (list) (make-Rest (list a b)) (-Immutable-HT a b)))]
927+
[hasheqv (-poly (a b) (->* (list) (make-Rest (list a b)) (-Immutable-HT a b)))]
928+
[hasheq (-poly (a b) (->* (list) (make-Rest (list a b)) (-Immutable-HT a b)))]
941929
[make-hash (-poly (a b) (->opt [(-lst (-pair a b))] (-Mutable-HT a b)))]
942930
[make-hasheq (-poly (a b) (->opt [(-lst (-pair a b))] (-Mutable-HT a b)))]
943931
[make-hasheqv (-poly (a b) (->opt [(-lst (-pair a b))] (-Mutable-HT a b)))]

typed-racket-lib/typed-racket/env/init-envs.rkt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,8 @@
324324
,(and rest (type->sexp rest))
325325
(list ,@(map type->sexp kws))
326326
,(type->sexp rng))]
327+
[(Rest: tys )
328+
`(make-Rest (list ,@(map type->sexp tys)))]
327329
[(RestDots: ty db)
328330
`(make-RestDots ,(type->sexp ty)
329331
(quote ,db))]

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

Lines changed: 64 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"signatures.rkt" "fail.rkt"
2323
"promote-demote.rkt"
2424
racket/match
25+
;racket/trace
2526
(contract-req)
2627
(for-syntax
2728
racket/base
@@ -175,20 +176,24 @@
175176
;; of them.
176177
(struct seq (types end) #:transparent)
177178
(struct null-end () #:transparent)
178-
(struct uniform-end (type) #:transparent)
179+
(define -null-end (null-end))
180+
;; cycle is the pattern of the rest of the seq,
181+
;; e.g. a rest argument of Num would just be (list Num)
182+
;; a rest arg of (Num Str) would be (list Num Str)
183+
(struct cycle-end (cycle) #:transparent)
179184
(struct dotted-end (type bound) #:transparent)
180185

181186
(define (Values->seq v)
182187
(match v
183-
[(Values: ts) (seq ts (null-end))]
188+
[(Values: ts) (seq ts -null-end)]
184189
[(ValuesDots: ts dty dbound) (seq ts (dotted-end (-result dty) dbound))]
185190
[_ #f]))
186191

187192

188193
(define (List->end v)
189194
(match v
190-
[(== -Null) (null-end)]
191-
[(Listof: t) (uniform-end t)]
195+
[(== -Null) -null-end]
196+
[(Listof: t) (cycle-end (list t))]
192197
[(ListDots: t dbound) (dotted-end t dbound)]
193198
[_ #f]))
194199

@@ -256,19 +261,27 @@
256261
[((seq ss (null-end))
257262
(seq ts (null-end)))
258263
(cgen/list context ss ts)]
259-
;; One is null-end the other is uniform-end
264+
;; One is null-end the other is cycle-end
260265
[((seq ss (null-end))
261-
(seq ts (uniform-end t-rest)))
262-
(cgen/list context ss (list-extend ss ts t-rest))]
263-
[((seq ss (uniform-end s-rest))
266+
(seq ts (cycle-end t-rest)))
267+
(define ss-len (length ss))
268+
(define ts-len (length ts))
269+
#:return-unless (<= ts-len ss-len) #f
270+
(define fewer-args (- ss-len ts-len))
271+
(define cycle-len (length t-rest))
272+
#:return-unless (eqv? 0 (remainder fewer-args cycle-len)) #f
273+
(define repetitions (quotient fewer-args cycle-len))
274+
(define new-ts (append ts (repeat-list t-rest repetitions)))
275+
(cgen/list context ss new-ts)]
276+
[((seq ss (cycle-end _))
264277
(seq ts (null-end)))
265278
#f]
266-
;; Both are uniform-end
267-
[((seq ss (uniform-end s-rest))
268-
(seq ts (uniform-end t-rest)))
269-
(cgen/list context
270-
(cons s-rest ss)
271-
(cons t-rest (list-extend ss ts t-rest)))]
279+
;; Both are cycle-end
280+
[((seq ss (cycle-end s-rest))
281+
(seq ts (and t-end (cycle-end t-rest))))
282+
(cgen/seq context
283+
(seq (append s-rest ss) -null-end)
284+
(seq (append t-rest ts) t-end))]
272285
;; dotted below, nothing above
273286
[((seq ss (dotted-end dty dbound))
274287
(seq ts (null-end)))
@@ -326,27 +339,37 @@
326339
(% move-dotted-rest-to-dmap (cgen (context-add-var context dbound*) s-dty t-dty) dbound* dbound)))]
327340

328341
;; * <: ...
329-
[((seq ss (uniform-end s-rest))
342+
[((seq ss (cycle-end (list s-rest-ty)))
330343
(seq ts (dotted-end t-dty dbound)))
331344
#:return-unless (inferable-index? context dbound)
332345
#f
333346
#:return-unless (<= (length ts) (length ss))
334347
#f
335348
(define new-bound (gensym dbound))
336-
(define-values (vars new-tys) (generate-dbound-prefix dbound t-dty (- (length ss) (length ts))
337-
new-bound))
349+
(define-values (vars new-tys)
350+
(generate-dbound-prefix dbound t-dty (- (length ss) (length ts))
351+
new-bound))
338352
(define-values (ss-front ss-back) (split-at ss (length ts)))
339353
(% cset-meet
340354
(cgen/list context ss-front ts)
341355
(% move-vars+rest-to-dmap
342356
(% cset-meet
343-
(cgen/list (context-add context #:bounds (list new-bound) #:vars vars #:indices (list new-bound))
344-
ss-back new-tys)
345-
(cgen (context-add-var context dbound) s-rest t-dty))
357+
(cgen/list (context-add context
358+
#:bounds (list new-bound)
359+
#:vars vars
360+
#:indices (list new-bound))
361+
ss-back
362+
new-tys)
363+
(cgen (context-add-var context dbound) s-rest-ty t-dty))
346364
vars dbound #:exact #t))]
365+
;; TODO figure out how above code could be modified to support
366+
;; cycle-end w/ a cycle of len > 1
367+
[((seq ss (cycle-end _))
368+
(seq ts (dotted-end _ _)))
369+
#f]
347370

348371
[((seq ss (dotted-end s-dty dbound))
349-
(seq ts (uniform-end t-rest)))
372+
(seq ts (cycle-end (list t-rest-ty))))
350373
(cond
351374
[(inferable-index? context dbound)
352375
(define new-bound (gensym dbound))
@@ -356,16 +379,21 @@
356379
(% cset-meet
357380
(cgen/list context ss (if (positive? length-delta)
358381
(drop-right ts length-delta)
359-
(list-extend ss ts t-rest)))
382+
(list-extend ss ts t-rest-ty)))
360383
(% move-vars+rest-to-dmap
361384
(% cset-meet
362385
(cgen/list (context-add context #:bounds (list new-bound) #:vars vars #:indices (list new-bound))
363386
new-tys (take-right ts (max 0 length-delta)))
364-
(cgen (context-add-var context dbound) s-dty t-rest))
387+
(cgen (context-add-var context dbound) s-dty t-rest-ty))
365388
vars dbound))]
366389
[else
367390
(extend-tvars (list dbound)
368-
(cgen/seq (context-add context #:bounds (list dbound)) (seq ss (uniform-end s-dty)) t-seq))])]))
391+
(cgen/seq (context-add context #:bounds (list dbound))
392+
(seq ss (cycle-end (list s-dty)))
393+
t-seq))])]
394+
[((seq ts (dotted-end _ _))
395+
(seq ss (cycle-end _)))
396+
#f]))
369397

370398
(define/cond-contract (cgen/arrow context s-arr t-arr)
371399
(context? Arrow? Arrow? . -> . (or/c #f cset?))
@@ -374,10 +402,10 @@
374402
(Arrow: ts t-rest t-kws t))
375403
(define (rest->end rest)
376404
(match rest
377-
[(? Type?) (uniform-end rest)]
405+
[(Rest: rst-ts) (cycle-end rst-ts)]
378406
[(RestDots: ty dbound)
379407
(dotted-end ty dbound)]
380-
[_ (null-end)]))
408+
[_ -null-end]))
381409

382410
(define s-seq (seq ss (rest->end s-rest)))
383411
(define t-seq (seq ts (rest->end t-rest)))
@@ -947,9 +975,16 @@
947975
;; like infer, but T-var is the vararg type:
948976
(define (infer/vararg X Y S T T-var R [expected #f]
949977
#:objs [objs '()])
950-
(define new-T (if T-var (list-extend S T T-var) T))
951978
(and ((length S) . >= . (length T))
952-
(infer X Y S new-T R expected #:objs objs)))
979+
(let* ([fewer-ts (- (length S) (length T))]
980+
[new-T (match T-var
981+
[(? Type? var-t) (list-extend S T var-t)]
982+
[(Rest: rst-ts)
983+
#:when (eqv? 0 (remainder fewer-ts (length rst-ts)))
984+
(append T (repeat-list rst-ts
985+
(quotient fewer-ts (length rst-ts))))]
986+
[_ T])])
987+
(infer X Y S new-T R expected #:objs objs))))
953988

954989
;; like infer, but dotted-var is the bound on the ...
955990
;; and T-dotted is the repeated type
@@ -988,6 +1023,6 @@
9881023
;(trace substs-gen)
9891024
;(trace cgen)
9901025
;(trace cgen/list)
991-
;(trace cgen/arr)
1026+
;(trace cgen/arrow)
9921027
;(trace cgen/seq)
9931028

typed-racket-lib/typed-racket/infer/promote-demote.rkt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,21 +36,21 @@
3636
;; Returns the changed arr or #f if there is no arr above it
3737
(define (arr-change arr)
3838
(match arr
39-
[(Arrow: dom rest kws rng)
39+
[(Arrow: dom rst kws rng)
4040
(cond
4141
[(apply V-in? V (get-propsets rng))
4242
#f]
43-
[(and (RestDots? rest)
44-
(memq (RestDots-nm rest) V))
43+
[(and (RestDots? rst)
44+
(memq (RestDots-nm rst) V))
4545
(make-Arrow
4646
(map contra dom)
47-
(contra (RestDots-ty rest))
47+
(contra (RestDots-ty rst))
4848
(map contra kws)
4949
(co rng))]
5050
[else
5151
(make-Arrow
5252
(map contra dom)
53-
(and rest (contra rest))
53+
(and rst (contra rst))
5454
(map contra kws)
5555
(co rng))])]))
5656
(match cur

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
;; domain
5151
(listof Type?)
5252
;; rest
53-
(or/c #f Type?)
53+
(or/c #f Type? Rest?)
5454
;; range
5555
(or/c #f Values/c ValuesDots?))
5656
;; [optional] expected type

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

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@
287287
;; does not need to be delayed since there's no parsing done
288288
#:attr result #'t))
289289

290-
(define-splicing-syntax-class ->*-rest
290+
(define-splicing-syntax-class optional->*-rest
291291
#:description "rest argument type for ->*"
292292
#:attributes (type)
293293
(pattern (~optional (~seq #:rest type:non-keyword-ty))))
@@ -958,10 +958,32 @@
958958
(parse-type #'rng)
959959
: (-PS (attribute latent.positive) (attribute latent.negative))
960960
: (attribute latent.object)))]
961+
;; like ->* below but w/ a #:rest-pat present
961962
[(:->*^ (~var mand (->*-args #t))
962963
(~optional (~var opt (->*-args #f))
963964
#:defaults ([opt.doms null] [opt.kws null]))
964-
rest:->*-rest
965+
#:rest-pat (hrest-types-stx:non-keyword-ty ...)
966+
rng)
967+
(with-arity (length (attribute mand.doms))
968+
(define doms (for/list ([d (attribute mand.doms)])
969+
(parse-type d)))
970+
(define opt-doms (for/list ([d (attribute opt.doms)])
971+
(parse-type d)))
972+
(define hrest-tys (stx-map parse-type #'(hrest-types-stx ...)))
973+
(cond
974+
[(< (length hrest-tys) 2)
975+
(parse-error
976+
"heterogeneous rest specifications must include at least 2 types"
977+
"given" (syntax->datum #'(hrest-types-stx ...)))]
978+
[else
979+
(opt-fn doms opt-doms (parse-values-type #'rng)
980+
#:rest (make-Rest hrest-tys)
981+
#:kws (map force (append (attribute mand.kws)
982+
(attribute opt.kws))))]))]
983+
[(:->*^ (~var mand (->*-args #t))
984+
(~optional (~var opt (->*-args #f))
985+
#:defaults ([opt.doms null] [opt.kws null]))
986+
rest:optional->*-rest
965987
rng)
966988
(with-arity (length (attribute mand.doms))
967989
(define doms (for/list ([d (attribute mand.doms)])
@@ -970,7 +992,7 @@
970992
(parse-type d)))
971993
(opt-fn doms opt-doms (parse-values-type #'rng)
972994
#:rest (and (attribute rest.type)
973-
(parse-type (attribute rest.type)))
995+
(make-Rest (list (parse-type (attribute rest.type)))))
974996
#:kws (map force (append (attribute mand.kws)
975997
(attribute opt.kws)))))]
976998
[:->^

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,8 @@
732732
(channel/sc (t->sc t))]
733733
[(Evt: t)
734734
(evt/sc (t->sc t))]
735+
[(Rest: (list rst-t)) (listof/sc (t->sc rst-t))]
736+
[(? Rest? rst) (t->sc (Rest->Mu rst))]
735737
[(? Prop? rep) (prop->sc rep)]
736738
[_
737739
(fail #:reason "contract generation not supported for this type")]))))
@@ -808,7 +810,7 @@
808810
(values (map conv mand-kws)
809811
(map conv opt-kws))))
810812
(define range (map t->sc rngs))
811-
(define rest (and rst (listof/sc (t->sc/neg rst))))
813+
(define rest (and rst (t->sc/neg rst)))
812814
(function/sc (from-typed? typed-side) (process-dom mand-args) opt-args mand-kws opt-kws rest range))
813815
(handle-arrow-range first-arrow convert-arrow)]
814816
[else
@@ -823,7 +825,7 @@
823825
" with optional keyword arguments")))
824826
(if case->
825827
(arr/sc (process-dom (map t->sc/neg dom))
826-
(and rst (listof/sc (t->sc/neg rst)))
828+
(and rst (t->sc/neg rst))
827829
(map t->sc rngs))
828830
(function/sc
829831
(from-typed? typed-side)
@@ -832,7 +834,7 @@
832834
(map conv mand-kws)
833835
(map conv opt-kws)
834836
(match rst
835-
[(? Type?) (listof/sc (t->sc/neg rst))]
837+
[(? Rest?) (t->sc/neg rst)]
836838
[(RestDots: dty dbound)
837839
(listof/sc
838840
(t->sc/neg dty

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,19 @@
508508
(= (length kws) 1)
509509
(equal? kws (sort kws Keyword<?))))
510510

511+
;; a Rest argument description
512+
;; tys: the cycle describing the rest args
513+
;; e.g.
514+
;; tys = (list Number) means all provided rest args
515+
;; must all be a Number (see `+')
516+
;; tys = (list A B) means the rest arguments must be
517+
;; of even cardinality, and must be an A followed
518+
;; by a B repeated (e.g. A B A B A B)
519+
;; etc
520+
(def-rep Rest ([tys (cons/c Type? (listof Type?))])
521+
[#:frees (f) (combine-frees (map f tys))]
522+
[#:fmap (f) (make-Rest (map f tys))]
523+
[#:for-each (f) (for-each f tys)])
511524

512525
(def-rep RestDots ([ty Type?]
513526
[nm (or/c natural-number/c symbol?)])
@@ -525,7 +538,7 @@
525538

526539

527540
(def-rep Arrow ([dom (listof Type?)]
528-
[rst (or/c #f Type? RestDots?)]
541+
[rst (or/c #f Rest? RestDots?)]
529542
[kws (and/c (listof Keyword?) keyword-sorted/c)]
530543
[rng SomeValues?])
531544
[#:frees (f)

typed-racket-lib/typed-racket/typecheck/check-subforms-unit.rkt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@
5656
(tc/funapp #'here #'(here) t (list (ret arg1)) #f)]
5757
[(Fun: (list _ ...
5858
(Arrow: (list)
59-
(? Type? rst)
59+
(Rest: (list rst-t))
6060
(list (Keyword: _ _ #f) ...)
6161
_ )
6262
_ ...))
63-
#:when (subtype prop-type rst)
64-
(tc/funapp #'here #'(here) t (list (ret rst)) #f)]
63+
#:when (subtype prop-type rst-t)
64+
(tc/funapp #'here #'(here) t (list (ret rst-t)) #f)]
6565
[(? resolvable? t)
6666
(loop (resolve t))]
6767
[(or (Poly: ns _) (PolyDots: (list ns ... _) _))

0 commit comments

Comments
 (0)