Skip to content

Commit 9cf6154

Browse files
committed
Fix racket#1146: Validate row type variable positions
Row type variables (declared with #:row) may only appear in (Class #:row-var ...) positions, not directly as types. Previously, code like (All (r #:row) (-> r r)) was accepted but caused internal errors during type checking. This fix adds validation in parse-type.rkt to reject row type variables in invalid positions at parse time, providing a clear error message. Also keeps defensive checks in check-below.rkt for Row types and void values from error propagation. Closes racket#1146
1 parent b435903 commit 9cf6154

File tree

3 files changed

+59
-7
lines changed

3 files changed

+59
-7
lines changed

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

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,26 @@
197197
[_ null]))
198198

199199

200+
;; Symbol Type -> Boolean
201+
;; Check if a row type variable appears in invalid positions (not as Class row-ext)
202+
;; Returns #t if there are invalid usages
203+
(define (row-var-in-invalid-position? var-name type)
204+
(define found-invalid #f)
205+
(let check! ([ty type] [in-row-ext? #f])
206+
(match ty
207+
[(F: (== var-name))
208+
;; Found the row variable - it's invalid unless we're in a row-ext position
209+
(unless in-row-ext?
210+
(set! found-invalid #t))]
211+
[(Class: row-ext row _ _ _ _)
212+
;; For Class, check row-ext with the flag set
213+
(when row-ext
214+
(check! row-ext #t))
215+
;; Check the row itself and other parts normally
216+
(check! row #f)]
217+
[_ (Rep-for-each ty (lambda (t) (check! t #f)))]))
218+
found-invalid)
219+
200220
;; Syntax -> Type
201221
;; Parse a Forall type
202222
(define (parse-all-type stx do-parse)
@@ -227,6 +247,10 @@
227247
;; should be no need to extend the constraint environment
228248
(define body-type
229249
(extend-tvars (list var*) (do-parse #'t.type)))
250+
;; Check that row variable only appears in valid positions (Class #:row-var)
251+
(when (row-var-in-invalid-position? var* body-type)
252+
(parse-error "row type variable used in invalid position; row type variables may only appear in (Class #:row-var ...)"
253+
"variable" var*))
230254
(make-PolyRow
231255
(list var*)
232256
;; No constraints listed, so we need to infer the constraints
@@ -238,10 +262,15 @@
238262
(define constraints (attribute constr.constraints))
239263
(extend-row-constraints (list var*) (list constraints)
240264
(extend-tvars (list var*)
241-
(make-PolyRow
242-
(list var*)
243-
(do-parse #'t.type)
244-
constraints)))]
265+
(let ([body-type (do-parse #'t.type)])
266+
;; Check that row variable only appears in valid positions (Class #:row-var)
267+
(when (row-var-in-invalid-position? var* body-type)
268+
(parse-error "row type variable used in invalid position; row type variables may only appear in (Class #:row-var ...)"
269+
"variable" var*))
270+
(make-PolyRow
271+
(list var*)
272+
body-type
273+
constraints))))]
245274
[(:All^ (_:id ...) _ _ _ ...) (parse-error "too many forms in body of All type")]
246275
[(:All^ . rest) (parse-error "bad syntax")]))
247276

typed-racket-lib/typed-racket/typecheck/check-below.rkt

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@
1818
"tc-envops.rkt")
1919

2020
(provide/cond-contract
21-
[check-below (-->i ([s (t) (if (Type? t)
21+
[check-below (-->i ([s (t) (if (or (Type? t) (Row? t))
2222
(-or/c full-tc-results/c Type?)
2323
full-tc-results/c)]
24-
[t (-or/c Type? tc-results/c)])
25-
[_ (t) (if (Type? t) Type? full-tc-results/c)])]
24+
[t (-or/c Type? Row? tc-results/c)])
25+
[_ (t) (if (or (Type? t) (Row? t)) Type? full-tc-results/c)])]
2626
[cond-check-below (-->i ([s (-or/c Type? full-tc-results/c)]
2727
[t (s) (-or/c #f (if (Type? s) Type? tc-results/c))])
2828
[_ (s) (-or/c #f (if (Type? s) Type? full-tc-results/c))])])
@@ -193,6 +193,17 @@
193193
(expected-but-got t2 t1))
194194
(upgrade-trusted-rng t1 expected)]
195195

196+
;; Handle void or other invalid inputs from error propagation
197+
[((? void?) expected) (fix-results expected)]
198+
[(actual (? void?)) actual]
199+
200+
;; Handle Row types from row-polymorphic instantiation
201+
;; Row is not a Type?, so it needs special handling
202+
[((tc-result1: t1 _ _) (? Row?))
203+
;; For row polymorphism, just return the actual type
204+
;; The row constraint checking happens elsewhere
205+
t1]
206+
196207
[(a b) (int-err "unexpected input for check-below: ~a ~a" a b)]))
197208

198209
;; shallow: if the top-level arrow on t1 is reliable, then upgrade the top-level arrow in t2
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#;
2+
(exn-pred #rx"row type variable used in invalid position")
3+
#lang typed/racket
4+
5+
;; Issue #1146: Row type variables should only appear in (Class #:row-var ...),
6+
;; not directly as types like (-> r r). Previously this caused internal errors;
7+
;; now it produces a proper type error.
8+
9+
(: hi (All (r #:row)
10+
(-> r r)))
11+
(define (hi a)
12+
a)

0 commit comments

Comments
 (0)