Skip to content

Commit d846405

Browse files
Fix crash related to tuple rest arity (#2928)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> Co-authored-by: Ryan Cavanaugh <RyanCavanaugh@users.noreply.github.com>
1 parent bb0fabc commit d846405

5 files changed

Lines changed: 153 additions & 2 deletions

File tree

internal/checker/inference.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,9 @@ func (c *Checker) inferFromObjectTypes(n *InferenceState, source *Type, target *
713713
if constraint != nil && isTupleType(constraint) && constraint.TargetTupleType().combinedFlags&ElementFlagsVariable == 0 {
714714
impliedArity := constraint.TargetTupleType().fixedLength
715715
c.inferFromTypes(n, c.sliceTupleType(source, startLength, sourceArity-(startLength+impliedArity)), elementTypes[startLength])
716-
c.inferFromTypes(n, c.getElementTypeOfSliceOfTupleType(source, startLength+impliedArity, endLength, false, false), elementTypes[startLength+1])
716+
if restType := c.getElementTypeOfSliceOfTupleType(source, startLength+impliedArity, endLength, false, false); restType != nil {
717+
c.inferFromTypes(n, restType, elementTypes[startLength+1])
718+
}
717719
}
718720
}
719721
} else if elementInfos[startLength].flags&ElementFlagsRest != 0 && elementInfos[startLength+1].flags&ElementFlagsVariadic != 0 {
@@ -726,7 +728,9 @@ func (c *Checker) inferFromObjectTypes(n *InferenceState, source *Type, target *
726728
endIndex := sourceArity - getEndElementCount(target.TargetTupleType(), ElementFlagsFixed)
727729
startIndex := endIndex - impliedArity
728730
trailingSlice := c.createTupleTypeEx(c.getTypeArguments(source)[startIndex:endIndex], source.TargetTupleType().elementInfos[startIndex:endIndex], false /*readonly*/)
729-
c.inferFromTypes(n, c.getElementTypeOfSliceOfTupleType(source, startLength, endLength+impliedArity, false, false), elementTypes[startLength])
731+
if restType := c.getElementTypeOfSliceOfTupleType(source, startLength, endLength+impliedArity, false, false); restType != nil {
732+
c.inferFromTypes(n, restType, elementTypes[startLength])
733+
}
730734
c.inferFromTypes(n, trailingSlice, elementTypes[startLength+1])
731735
}
732736
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//// [tests/cases/compiler/inferFromTupleRestAndVariadic.ts] ////
2+
3+
//// [inferFromTupleRestAndVariadic.ts]
4+
// Crash when inferring from tuple types with rest and variadic elements
5+
// where the variadic element's constraint consumes all source tuple elements.
6+
// The pattern [...rest, ...T] where T is constrained by a fixed-size tuple
7+
// calls getElementTypeOfSliceOfTupleType which can return nil when the
8+
// source tuple is entirely consumed by T's implied arity.
9+
10+
type SubTup<T> = T extends [
11+
...(infer C)[],
12+
...infer B extends [any, any]
13+
] ? B : never;
14+
type Trigger = SubTup<[1, 2]>;
15+
16+
// Also test the [...T, ...rest] pattern
17+
type SubTup2<T> = T extends [
18+
...infer A extends [any, any],
19+
...(infer D)[],
20+
] ? A : never;
21+
type Trigger2 = SubTup2<[1, 2]>;
22+
23+
// Test with more elements than implied arity (should work fine)
24+
type Trigger3 = SubTup<[1, 2, 3, 4]>;
25+
type Trigger4 = SubTup2<[1, 2, 3, 4]>;
26+
27+
28+
//// [inferFromTupleRestAndVariadic.js]
29+
"use strict";
30+
// Crash when inferring from tuple types with rest and variadic elements
31+
// where the variadic element's constraint consumes all source tuple elements.
32+
// The pattern [...rest, ...T] where T is constrained by a fixed-size tuple
33+
// calls getElementTypeOfSliceOfTupleType which can return nil when the
34+
// source tuple is entirely consumed by T's implied arity.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//// [tests/cases/compiler/inferFromTupleRestAndVariadic.ts] ////
2+
3+
=== inferFromTupleRestAndVariadic.ts ===
4+
// Crash when inferring from tuple types with rest and variadic elements
5+
// where the variadic element's constraint consumes all source tuple elements.
6+
// The pattern [...rest, ...T] where T is constrained by a fixed-size tuple
7+
// calls getElementTypeOfSliceOfTupleType which can return nil when the
8+
// source tuple is entirely consumed by T's implied arity.
9+
10+
type SubTup<T> = T extends [
11+
>SubTup : Symbol(SubTup, Decl(inferFromTupleRestAndVariadic.ts, 0, 0))
12+
>T : Symbol(T, Decl(inferFromTupleRestAndVariadic.ts, 6, 12))
13+
>T : Symbol(T, Decl(inferFromTupleRestAndVariadic.ts, 6, 12))
14+
15+
...(infer C)[],
16+
>C : Symbol(C, Decl(inferFromTupleRestAndVariadic.ts, 7, 13))
17+
18+
...infer B extends [any, any]
19+
>B : Symbol(B, Decl(inferFromTupleRestAndVariadic.ts, 8, 12))
20+
21+
] ? B : never;
22+
>B : Symbol(B, Decl(inferFromTupleRestAndVariadic.ts, 8, 12))
23+
24+
type Trigger = SubTup<[1, 2]>;
25+
>Trigger : Symbol(Trigger, Decl(inferFromTupleRestAndVariadic.ts, 9, 14))
26+
>SubTup : Symbol(SubTup, Decl(inferFromTupleRestAndVariadic.ts, 0, 0))
27+
28+
// Also test the [...T, ...rest] pattern
29+
type SubTup2<T> = T extends [
30+
>SubTup2 : Symbol(SubTup2, Decl(inferFromTupleRestAndVariadic.ts, 10, 30))
31+
>T : Symbol(T, Decl(inferFromTupleRestAndVariadic.ts, 13, 13))
32+
>T : Symbol(T, Decl(inferFromTupleRestAndVariadic.ts, 13, 13))
33+
34+
...infer A extends [any, any],
35+
>A : Symbol(A, Decl(inferFromTupleRestAndVariadic.ts, 14, 12))
36+
37+
...(infer D)[],
38+
>D : Symbol(D, Decl(inferFromTupleRestAndVariadic.ts, 15, 13))
39+
40+
] ? A : never;
41+
>A : Symbol(A, Decl(inferFromTupleRestAndVariadic.ts, 14, 12))
42+
43+
type Trigger2 = SubTup2<[1, 2]>;
44+
>Trigger2 : Symbol(Trigger2, Decl(inferFromTupleRestAndVariadic.ts, 16, 14))
45+
>SubTup2 : Symbol(SubTup2, Decl(inferFromTupleRestAndVariadic.ts, 10, 30))
46+
47+
// Test with more elements than implied arity (should work fine)
48+
type Trigger3 = SubTup<[1, 2, 3, 4]>;
49+
>Trigger3 : Symbol(Trigger3, Decl(inferFromTupleRestAndVariadic.ts, 17, 32))
50+
>SubTup : Symbol(SubTup, Decl(inferFromTupleRestAndVariadic.ts, 0, 0))
51+
52+
type Trigger4 = SubTup2<[1, 2, 3, 4]>;
53+
>Trigger4 : Symbol(Trigger4, Decl(inferFromTupleRestAndVariadic.ts, 20, 37))
54+
>SubTup2 : Symbol(SubTup2, Decl(inferFromTupleRestAndVariadic.ts, 10, 30))
55+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//// [tests/cases/compiler/inferFromTupleRestAndVariadic.ts] ////
2+
3+
=== inferFromTupleRestAndVariadic.ts ===
4+
// Crash when inferring from tuple types with rest and variadic elements
5+
// where the variadic element's constraint consumes all source tuple elements.
6+
// The pattern [...rest, ...T] where T is constrained by a fixed-size tuple
7+
// calls getElementTypeOfSliceOfTupleType which can return nil when the
8+
// source tuple is entirely consumed by T's implied arity.
9+
10+
type SubTup<T> = T extends [
11+
>SubTup : SubTup<T>
12+
13+
...(infer C)[],
14+
...infer B extends [any, any]
15+
] ? B : never;
16+
type Trigger = SubTup<[1, 2]>;
17+
>Trigger : [1, 2]
18+
19+
// Also test the [...T, ...rest] pattern
20+
type SubTup2<T> = T extends [
21+
>SubTup2 : SubTup2<T>
22+
23+
...infer A extends [any, any],
24+
...(infer D)[],
25+
] ? A : never;
26+
type Trigger2 = SubTup2<[1, 2]>;
27+
>Trigger2 : [1, 2]
28+
29+
// Test with more elements than implied arity (should work fine)
30+
type Trigger3 = SubTup<[1, 2, 3, 4]>;
31+
>Trigger3 : [3, 4]
32+
33+
type Trigger4 = SubTup2<[1, 2, 3, 4]>;
34+
>Trigger4 : [1, 2]
35+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// @strict: true
2+
// Crash when inferring from tuple types with rest and variadic elements
3+
// where the variadic element's constraint consumes all source tuple elements.
4+
// The pattern [...rest, ...T] where T is constrained by a fixed-size tuple
5+
// calls getElementTypeOfSliceOfTupleType which can return nil when the
6+
// source tuple is entirely consumed by T's implied arity.
7+
8+
type SubTup<T> = T extends [
9+
...(infer C)[],
10+
...infer B extends [any, any]
11+
] ? B : never;
12+
type Trigger = SubTup<[1, 2]>;
13+
14+
// Also test the [...T, ...rest] pattern
15+
type SubTup2<T> = T extends [
16+
...infer A extends [any, any],
17+
...(infer D)[],
18+
] ? A : never;
19+
type Trigger2 = SubTup2<[1, 2]>;
20+
21+
// Test with more elements than implied arity (should work fine)
22+
type Trigger3 = SubTup<[1, 2, 3, 4]>;
23+
type Trigger4 = SubTup2<[1, 2, 3, 4]>;

0 commit comments

Comments
 (0)