Skip to content

Commit e4ce713

Browse files
ahejlsbergAndarist
authored andcommitted
Improve recursion identity for direct type instantiations (#3445)
Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
1 parent d94ad71 commit e4ce713

14 files changed

Lines changed: 574 additions & 30 deletions

_packages/native-preview/src/enums/objectFlags.enum.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export enum ObjectFlags {
3737
IdenticalBaseTypeCalculated = 1 << 27,
3838
IdenticalBaseTypeExists = 1 << 28,
3939
UnresolvedMembers = 1 << 29,
40+
FromTypeNode = 1 << 30,
4041
IsGenericTypeComputed = 1 << 22,
4142
IsGenericObjectType = 1 << 23,
4243
IsGenericIndexType = 1 << 24,

_packages/native-preview/src/enums/objectFlags.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export var ObjectFlags: any;
3737
ObjectFlags[ObjectFlags["IdenticalBaseTypeCalculated"] = 134217728] = "IdenticalBaseTypeCalculated";
3838
ObjectFlags[ObjectFlags["IdenticalBaseTypeExists"] = 268435456] = "IdenticalBaseTypeExists";
3939
ObjectFlags[ObjectFlags["UnresolvedMembers"] = 536870912] = "UnresolvedMembers";
40+
ObjectFlags[ObjectFlags["FromTypeNode"] = 1073741824] = "FromTypeNode";
4041
ObjectFlags[ObjectFlags["IsGenericTypeComputed"] = 4194304] = "IsGenericTypeComputed";
4142
ObjectFlags[ObjectFlags["IsGenericObjectType"] = 8388608] = "IsGenericObjectType";
4243
ObjectFlags[ObjectFlags["IsGenericIndexType"] = 16777216] = "IsGenericIndexType";

internal/checker/checker.go

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22780,7 +22780,7 @@ func (c *Checker) getTypeFromClassOrInterfaceReference(node *ast.Node, symbol *a
2278022780
// of the class or interface.
2278122781
localTypeArguments := c.fillMissingTypeArguments(c.getTypeArgumentsFromNode(node), typeParameters, minTypeArgumentCount, isJs)
2278222782
typeArguments := append(d.OuterTypeParameters(), localTypeArguments...)
22783-
return c.createTypeReference(t, typeArguments)
22783+
return c.createTypeReferenceEx(t, typeArguments, ObjectFlagsFromTypeNode)
2278422784
}
2278522785
if c.checkNoTypeArguments(node, symbol) {
2278622786
return t
@@ -22875,11 +22875,11 @@ func (c *Checker) createNormalizedTypeReference(target *Type, typeArguments []*T
2287522875
return c.createTypeReference(target, typeArguments)
2287622876
}
2287722877

22878-
func (c *Checker) createNormalizedTupleType(target *Type, elementTypes []*Type) *Type {
22878+
func (c *Checker) createNormalizedTupleTypeEx(target *Type, elementTypes []*Type, objectFlags ObjectFlags) *Type {
2287922879
d := target.AsTupleType()
2288022880
if d.combinedFlags&ElementFlagsNonRequired == 0 {
2288122881
// No need to normalize when we only have regular required elements
22882-
return c.createTypeReference(target, elementTypes)
22882+
return c.createTypeReferenceEx(target, elementTypes, objectFlags)
2288322883
}
2288422884
if d.combinedFlags&ElementFlagsVariadic != 0 {
2288522885
for i, e := range elementTypes {
@@ -22893,7 +22893,7 @@ func (c *Checker) createNormalizedTupleType(target *Type, elementTypes []*Type)
2289322893
})
2289422894
if c.checkCrossProductUnion(checkTypes) {
2289522895
return c.mapType(e, func(t *Type) *Type {
22896-
return c.createNormalizedTupleType(target, core.ReplaceElement(elementTypes, i, t))
22896+
return c.createNormalizedTupleTypeEx(target, core.ReplaceElement(elementTypes, i, t), objectFlags)
2289722897
})
2289822898
}
2289922899
}
@@ -22918,11 +22918,15 @@ func (c *Checker) createNormalizedTupleType(target *Type, elementTypes []*Type)
2291822918
case tupleTarget == c.emptyGenericType:
2291922919
return c.emptyObjectType
2292022920
case len(n.types) != 0:
22921-
return c.createTypeReference(tupleTarget, n.types)
22921+
return c.createTypeReferenceEx(tupleTarget, n.types, objectFlags)
2292222922
}
2292322923
return tupleTarget
2292422924
}
2292522925

22926+
func (c *Checker) createNormalizedTupleType(target *Type, elementTypes []*Type) *Type {
22927+
return c.createNormalizedTupleTypeEx(target, elementTypes, ObjectFlagsNone)
22928+
}
22929+
2292622930
type TupleNormalizer struct {
2292722931
c *Checker
2292822932
types []*Type
@@ -23701,7 +23705,11 @@ func (c *Checker) getTypeFromArrayOrTupleTypeNode(node *ast.Node) *Type {
2370123705
} else {
2370223706
elementTypes = core.Map(node.Elements(), c.getTypeFromTypeNode)
2370323707
}
23704-
links.resolvedType = c.createNormalizedTypeReference(target, elementTypes)
23708+
if target.objectFlags&ObjectFlagsTuple != 0 {
23709+
links.resolvedType = c.createNormalizedTupleTypeEx(target, elementTypes, ObjectFlagsFromTypeNode)
23710+
} else {
23711+
links.resolvedType = c.createTypeReferenceEx(target, elementTypes, ObjectFlagsFromTypeNode)
23712+
}
2370523713
}
2370623714
}
2370723715
return links.resolvedType
@@ -24667,13 +24675,16 @@ func (c *Checker) tryCreateTypeReference(target *Type, typeArguments []*Type) *T
2466724675
}
2466824676

2466924677
func (c *Checker) createTypeReference(target *Type, typeArguments []*Type) *Type {
24678+
return c.createTypeReferenceEx(target, typeArguments, ObjectFlagsNone)
24679+
}
24680+
24681+
func (c *Checker) createTypeReferenceEx(target *Type, typeArguments []*Type, objectFlags ObjectFlags) *Type {
2467024682
id := getTypeListKey(typeArguments)
2467124683
intf := target.AsInterfaceType()
2467224684
if t, ok := intf.instantiations[id]; ok {
2467324685
return t
2467424686
}
24675-
t := c.newObjectType(ObjectFlagsReference, target.symbol)
24676-
t.objectFlags |= c.getPropagatingFlagsOfTypes(typeArguments, TypeFlagsNone)
24687+
t := c.newObjectType(ObjectFlagsReference|objectFlags|c.getPropagatingFlagsOfTypes(typeArguments, TypeFlagsNone), target.symbol)
2467724688
d := t.AsTypeReference()
2467824689
d.target = target
2467924690
d.resolvedTypeArguments = typeArguments

internal/checker/relater.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -843,12 +843,14 @@ func getRecursionIdentity(t *Type) RecursionId {
843843
// unique AST node.
844844
return asRecursionId(t.AsTypeReference().node)
845845
}
846-
if t.symbol != nil && !(t.objectFlags&ObjectFlagsAnonymous != 0 && t.symbol.Flags&ast.SymbolFlagsClass != 0) {
846+
if t.symbol != nil && !(t.objectFlags&ObjectFlagsAnonymous != 0 && t.symbol.Flags&ast.SymbolFlagsClass != 0) && t.objectFlags&ObjectFlagsFromTypeNode == 0 {
847847
// We track object types that have a symbol by that symbol (representing the origin of the type), but
848-
// exclude the static side of a class since it shares its symbol with the instance side.
848+
// exclude the static sides of classes (since they share their symbols with the instance sides) and type
849+
// references that originate in resolution of AST type nodes (since such type nodes cannot be the source
850+
// of generative recursion without first being instantiated).
849851
return asRecursionId(t.symbol)
850852
}
851-
if isTupleType(t) {
853+
if isTupleType(t) && t.objectFlags&ObjectFlagsFromTypeNode == 0 {
852854
return asRecursionId(t.Target())
853855
}
854856
}

internal/checker/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,7 @@ const (
579579
ObjectFlagsIdenticalBaseTypeCalculated = 1 << 27 // has had `getSingleBaseForNonAugmentingSubtype` invoked on it already
580580
ObjectFlagsIdenticalBaseTypeExists = 1 << 28 // has a defined cachedEquivalentBaseType member
581581
ObjectFlagsUnresolvedMembers = 1 << 29 // Member resolution in process
582+
ObjectFlagsFromTypeNode = 1 << 30 // Originates in resolution of AST type node
582583
// Flags that require TypeFlags.UnionOrIntersection or TypeFlags.Substitution
583584
ObjectFlagsIsGenericTypeComputed = 1 << 22 // IsGenericObjectType flag has been computed
584585
ObjectFlagsIsGenericObjectType = 1 << 23 // Union or intersection contains generic object type
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
deeplyNestedArrayTypes.ts(34,5): error TS2322: Type 'B.Outer' is not assignable to type 'A.Outer'.
2+
Types of property 'inners' are incompatible.
3+
Type 'B.Inner[]' is not assignable to type 'A.Inner[]'.
4+
Type 'B.Inner' is not assignable to type 'A.Inner'.
5+
Types of property 'mids' are incompatible.
6+
Type 'B.Mid[]' is not assignable to type 'A.Mid[]'.
7+
Type 'B.Mid' is not assignable to type 'A.Mid'.
8+
Types of property 'leaves' are incompatible.
9+
Type 'B.Leaf[]' is not assignable to type 'A.Leaf[]'.
10+
Type 'B.Leaf' is not assignable to type 'A.Leaf'.
11+
Types of property 'id' are incompatible.
12+
Type 'number' is not assignable to type 'string'.
13+
14+
15+
==== deeplyNestedArrayTypes.ts (1 errors) ====
16+
// https://github.com/microsoft/typescript-go/issues/3426
17+
18+
namespace A {
19+
export type Outer = {
20+
inners: Inner[];
21+
}
22+
export type Inner = {
23+
mids: Mid[];
24+
}
25+
export type Mid = {
26+
leaves: Leaf[];
27+
}
28+
export type Leaf = {
29+
id: string;
30+
}
31+
}
32+
33+
namespace B {
34+
export type Outer = {
35+
inners: Inner[];
36+
}
37+
export type Inner = {
38+
mids: Mid[];
39+
}
40+
export type Mid = {
41+
leaves: Leaf[];
42+
}
43+
export type Leaf = {
44+
id: number;
45+
}
46+
}
47+
48+
function test(a: A.Outer, b: B.Outer) {
49+
a = b
50+
~
51+
!!! error TS2322: Type 'B.Outer' is not assignable to type 'A.Outer'.
52+
!!! error TS2322: Types of property 'inners' are incompatible.
53+
!!! error TS2322: Type 'B.Inner[]' is not assignable to type 'A.Inner[]'.
54+
!!! error TS2322: Type 'B.Inner' is not assignable to type 'A.Inner'.
55+
!!! error TS2322: Types of property 'mids' are incompatible.
56+
!!! error TS2322: Type 'B.Mid[]' is not assignable to type 'A.Mid[]'.
57+
!!! error TS2322: Type 'B.Mid' is not assignable to type 'A.Mid'.
58+
!!! error TS2322: Types of property 'leaves' are incompatible.
59+
!!! error TS2322: Type 'B.Leaf[]' is not assignable to type 'A.Leaf[]'.
60+
!!! error TS2322: Type 'B.Leaf' is not assignable to type 'A.Leaf'.
61+
!!! error TS2322: Types of property 'id' are incompatible.
62+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
63+
}
64+
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//// [tests/cases/compiler/deeplyNestedArrayTypes.ts] ////
2+
3+
=== deeplyNestedArrayTypes.ts ===
4+
// https://github.com/microsoft/typescript-go/issues/3426
5+
6+
namespace A {
7+
>A : Symbol(A, Decl(deeplyNestedArrayTypes.ts, 0, 0))
8+
9+
export type Outer = {
10+
>Outer : Symbol(Outer, Decl(deeplyNestedArrayTypes.ts, 2, 13))
11+
12+
inners: Inner[];
13+
>inners : Symbol(inners, Decl(deeplyNestedArrayTypes.ts, 3, 25))
14+
>Inner : Symbol(Inner, Decl(deeplyNestedArrayTypes.ts, 5, 5))
15+
}
16+
export type Inner = {
17+
>Inner : Symbol(Inner, Decl(deeplyNestedArrayTypes.ts, 5, 5))
18+
19+
mids: Mid[];
20+
>mids : Symbol(mids, Decl(deeplyNestedArrayTypes.ts, 6, 25))
21+
>Mid : Symbol(Mid, Decl(deeplyNestedArrayTypes.ts, 8, 5))
22+
}
23+
export type Mid = {
24+
>Mid : Symbol(Mid, Decl(deeplyNestedArrayTypes.ts, 8, 5))
25+
26+
leaves: Leaf[];
27+
>leaves : Symbol(leaves, Decl(deeplyNestedArrayTypes.ts, 9, 23))
28+
>Leaf : Symbol(Leaf, Decl(deeplyNestedArrayTypes.ts, 11, 5))
29+
}
30+
export type Leaf = {
31+
>Leaf : Symbol(Leaf, Decl(deeplyNestedArrayTypes.ts, 11, 5))
32+
33+
id: string;
34+
>id : Symbol(id, Decl(deeplyNestedArrayTypes.ts, 12, 24))
35+
}
36+
}
37+
38+
namespace B {
39+
>B : Symbol(B, Decl(deeplyNestedArrayTypes.ts, 15, 1))
40+
41+
export type Outer = {
42+
>Outer : Symbol(Outer, Decl(deeplyNestedArrayTypes.ts, 17, 13))
43+
44+
inners: Inner[];
45+
>inners : Symbol(inners, Decl(deeplyNestedArrayTypes.ts, 18, 25))
46+
>Inner : Symbol(Inner, Decl(deeplyNestedArrayTypes.ts, 20, 5))
47+
}
48+
export type Inner = {
49+
>Inner : Symbol(Inner, Decl(deeplyNestedArrayTypes.ts, 20, 5))
50+
51+
mids: Mid[];
52+
>mids : Symbol(mids, Decl(deeplyNestedArrayTypes.ts, 21, 25))
53+
>Mid : Symbol(Mid, Decl(deeplyNestedArrayTypes.ts, 23, 5))
54+
}
55+
export type Mid = {
56+
>Mid : Symbol(Mid, Decl(deeplyNestedArrayTypes.ts, 23, 5))
57+
58+
leaves: Leaf[];
59+
>leaves : Symbol(leaves, Decl(deeplyNestedArrayTypes.ts, 24, 23))
60+
>Leaf : Symbol(Leaf, Decl(deeplyNestedArrayTypes.ts, 26, 5))
61+
}
62+
export type Leaf = {
63+
>Leaf : Symbol(Leaf, Decl(deeplyNestedArrayTypes.ts, 26, 5))
64+
65+
id: number;
66+
>id : Symbol(id, Decl(deeplyNestedArrayTypes.ts, 27, 24))
67+
}
68+
}
69+
70+
function test(a: A.Outer, b: B.Outer) {
71+
>test : Symbol(test, Decl(deeplyNestedArrayTypes.ts, 30, 1))
72+
>a : Symbol(a, Decl(deeplyNestedArrayTypes.ts, 32, 14))
73+
>A : Symbol(A, Decl(deeplyNestedArrayTypes.ts, 0, 0))
74+
>Outer : Symbol(A.Outer, Decl(deeplyNestedArrayTypes.ts, 2, 13))
75+
>b : Symbol(b, Decl(deeplyNestedArrayTypes.ts, 32, 25))
76+
>B : Symbol(B, Decl(deeplyNestedArrayTypes.ts, 15, 1))
77+
>Outer : Symbol(B.Outer, Decl(deeplyNestedArrayTypes.ts, 17, 13))
78+
79+
a = b
80+
>a : Symbol(a, Decl(deeplyNestedArrayTypes.ts, 32, 14))
81+
>b : Symbol(b, Decl(deeplyNestedArrayTypes.ts, 32, 25))
82+
}
83+
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//// [tests/cases/compiler/deeplyNestedArrayTypes.ts] ////
2+
3+
=== deeplyNestedArrayTypes.ts ===
4+
// https://github.com/microsoft/typescript-go/issues/3426
5+
6+
namespace A {
7+
export type Outer = {
8+
>Outer : Outer
9+
10+
inners: Inner[];
11+
>inners : Inner[]
12+
}
13+
export type Inner = {
14+
>Inner : Inner
15+
16+
mids: Mid[];
17+
>mids : Mid[]
18+
}
19+
export type Mid = {
20+
>Mid : Mid
21+
22+
leaves: Leaf[];
23+
>leaves : Leaf[]
24+
}
25+
export type Leaf = {
26+
>Leaf : Leaf
27+
28+
id: string;
29+
>id : string
30+
}
31+
}
32+
33+
namespace B {
34+
export type Outer = {
35+
>Outer : Outer
36+
37+
inners: Inner[];
38+
>inners : Inner[]
39+
}
40+
export type Inner = {
41+
>Inner : Inner
42+
43+
mids: Mid[];
44+
>mids : Mid[]
45+
}
46+
export type Mid = {
47+
>Mid : Mid
48+
49+
leaves: Leaf[];
50+
>leaves : Leaf[]
51+
}
52+
export type Leaf = {
53+
>Leaf : Leaf
54+
55+
id: number;
56+
>id : number
57+
}
58+
}
59+
60+
function test(a: A.Outer, b: B.Outer) {
61+
>test : (a: A.Outer, b: B.Outer) => void
62+
>a : A.Outer
63+
>A : any
64+
>b : B.Outer
65+
>B : any
66+
67+
a = b
68+
>a = b : B.Outer
69+
>a : A.Outer
70+
>b : B.Outer
71+
}
72+

0 commit comments

Comments
 (0)