Skip to content

Fix invariant generic error elaboration logic #19774

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9432,6 +9432,7 @@ namespace ts {

function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary {
let result: Ternary;
let originalErrorInfo: DiagnosticMessageChain;
const saveErrorInfo = errorInfo;
if (target.flags & TypeFlags.TypeParameter) {
// A source type { [P in keyof T]: X } is related to a target type T if X is related to T[P].
Expand Down Expand Up @@ -9511,6 +9512,7 @@ namespace ts {
// if we have indexed access types with identical index types, see if relationship holds for
// the two object types.
if (result = isRelatedTo((<IndexedAccessType>source).objectType, (<IndexedAccessType>target).objectType, reportErrors)) {
errorInfo = saveErrorInfo;
return result;
}
}
Expand Down Expand Up @@ -9542,6 +9544,10 @@ namespace ts {
if (!(reportErrors && some(variances, v => v === Variance.Invariant))) {
return Ternary.False;
}
// We remember the original error information so we can restore it in case the structural
// comparison unexpectedly succeeds. This can happen when the structural comparison result
// is a Ternary.Maybe for example caused by the recursion depth limiter.
originalErrorInfo = errorInfo;
errorInfo = saveErrorInfo;
}
}
Expand Down Expand Up @@ -9580,8 +9586,11 @@ namespace ts {
}
}
if (result) {
errorInfo = saveErrorInfo;
return result;
if (!originalErrorInfo) {
errorInfo = saveErrorInfo;
return result;
}
errorInfo = originalErrorInfo;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
tests/cases/compiler/invariantGenericErrorElaboration.ts(3,7): error TS2322: Type 'Num' is not assignable to type 'Runtype<any>'.
Types of property 'constraint' are incompatible.
Type 'Constraint<Num>' is not assignable to type 'Constraint<Runtype<any>>'.
Types of property 'constraint' are incompatible.
Type 'Constraint<Constraint<Num>>' is not assignable to type 'Constraint<Constraint<Runtype<any>>>'.
Types of property 'constraint' are incompatible.
Type 'Constraint<Constraint<Constraint<Num>>>' is not assignable to type 'Constraint<Constraint<Constraint<Runtype<any>>>>'.
Type 'Constraint<Constraint<Runtype<any>>>' is not assignable to type 'Constraint<Constraint<Num>>'.
Types of property 'underlying' are incompatible.
Type 'Constraint<Runtype<any>>' is not assignable to type 'Constraint<Num>'.
tests/cases/compiler/invariantGenericErrorElaboration.ts(4,17): error TS2345: Argument of type '{ foo: Num; }' is not assignable to parameter of type '{ [_: string]: Runtype<any>; }'.
Property 'foo' is incompatible with index signature.
Type 'Num' is not assignable to type 'Runtype<any>'.


==== tests/cases/compiler/invariantGenericErrorElaboration.ts (2 errors) ====
// Repro from #19746

const wat: Runtype<any> = Num;
~~~
!!! error TS2322: Type 'Num' is not assignable to type 'Runtype<any>'.
!!! error TS2322: Types of property 'constraint' are incompatible.
!!! error TS2322: Type 'Constraint<Num>' is not assignable to type 'Constraint<Runtype<any>>'.
!!! error TS2322: Types of property 'constraint' are incompatible.
!!! error TS2322: Type 'Constraint<Constraint<Num>>' is not assignable to type 'Constraint<Constraint<Runtype<any>>>'.
!!! error TS2322: Types of property 'constraint' are incompatible.
!!! error TS2322: Type 'Constraint<Constraint<Constraint<Num>>>' is not assignable to type 'Constraint<Constraint<Constraint<Runtype<any>>>>'.
!!! error TS2322: Type 'Constraint<Constraint<Runtype<any>>>' is not assignable to type 'Constraint<Constraint<Num>>'.
!!! error TS2322: Types of property 'underlying' are incompatible.
!!! error TS2322: Type 'Constraint<Runtype<any>>' is not assignable to type 'Constraint<Num>'.
const Foo = Obj({ foo: Num })
~~~~~~~~~~~~
!!! error TS2345: Argument of type '{ foo: Num; }' is not assignable to parameter of type '{ [_: string]: Runtype<any>; }'.
!!! error TS2345: Property 'foo' is incompatible with index signature.
!!! error TS2345: Type 'Num' is not assignable to type 'Runtype<any>'.

interface Runtype<A> {
constraint: Constraint<this>
witness: A
}

interface Num extends Runtype<number> {
tag: 'number'
}
declare const Num: Num

interface Obj<O extends { [_ in string]: Runtype<any> }> extends Runtype<{[K in keyof O]: O[K]['witness'] }> {}
declare function Obj<O extends { [_: string]: Runtype<any> }>(fields: O): Obj<O>;

interface Constraint<A extends Runtype<any>> extends Runtype<A['witness']> {
underlying: A,
check: (x: A['witness']) => void,
}

30 changes: 30 additions & 0 deletions tests/baselines/reference/invariantGenericErrorElaboration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//// [invariantGenericErrorElaboration.ts]
// Repro from #19746

const wat: Runtype<any> = Num;
const Foo = Obj({ foo: Num })

interface Runtype<A> {
constraint: Constraint<this>
witness: A
}

interface Num extends Runtype<number> {
tag: 'number'
}
declare const Num: Num

interface Obj<O extends { [_ in string]: Runtype<any> }> extends Runtype<{[K in keyof O]: O[K]['witness'] }> {}
declare function Obj<O extends { [_: string]: Runtype<any> }>(fields: O): Obj<O>;

interface Constraint<A extends Runtype<any>> extends Runtype<A['witness']> {
underlying: A,
check: (x: A['witness']) => void,
}


//// [invariantGenericErrorElaboration.js]
"use strict";
// Repro from #19746
var wat = Num;
var Foo = Obj({ foo: Num });
76 changes: 76 additions & 0 deletions tests/baselines/reference/invariantGenericErrorElaboration.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
=== tests/cases/compiler/invariantGenericErrorElaboration.ts ===
// Repro from #19746

const wat: Runtype<any> = Num;
>wat : Symbol(wat, Decl(invariantGenericErrorElaboration.ts, 2, 5))
>Runtype : Symbol(Runtype, Decl(invariantGenericErrorElaboration.ts, 3, 29))
>Num : Symbol(Num, Decl(invariantGenericErrorElaboration.ts, 8, 1), Decl(invariantGenericErrorElaboration.ts, 13, 13))

const Foo = Obj({ foo: Num })
>Foo : Symbol(Foo, Decl(invariantGenericErrorElaboration.ts, 3, 5))
>Obj : Symbol(Obj, Decl(invariantGenericErrorElaboration.ts, 13, 22), Decl(invariantGenericErrorElaboration.ts, 15, 111))
>foo : Symbol(foo, Decl(invariantGenericErrorElaboration.ts, 3, 17))
>Num : Symbol(Num, Decl(invariantGenericErrorElaboration.ts, 8, 1), Decl(invariantGenericErrorElaboration.ts, 13, 13))

interface Runtype<A> {
>Runtype : Symbol(Runtype, Decl(invariantGenericErrorElaboration.ts, 3, 29))
>A : Symbol(A, Decl(invariantGenericErrorElaboration.ts, 5, 18))

constraint: Constraint<this>
>constraint : Symbol(Runtype.constraint, Decl(invariantGenericErrorElaboration.ts, 5, 22))
>Constraint : Symbol(Constraint, Decl(invariantGenericErrorElaboration.ts, 16, 81))

witness: A
>witness : Symbol(Runtype.witness, Decl(invariantGenericErrorElaboration.ts, 6, 30))
>A : Symbol(A, Decl(invariantGenericErrorElaboration.ts, 5, 18))
}

interface Num extends Runtype<number> {
>Num : Symbol(Num, Decl(invariantGenericErrorElaboration.ts, 8, 1), Decl(invariantGenericErrorElaboration.ts, 13, 13))
>Runtype : Symbol(Runtype, Decl(invariantGenericErrorElaboration.ts, 3, 29))

tag: 'number'
>tag : Symbol(Num.tag, Decl(invariantGenericErrorElaboration.ts, 10, 39))
}
declare const Num: Num
>Num : Symbol(Num, Decl(invariantGenericErrorElaboration.ts, 8, 1), Decl(invariantGenericErrorElaboration.ts, 13, 13))
>Num : Symbol(Num, Decl(invariantGenericErrorElaboration.ts, 8, 1), Decl(invariantGenericErrorElaboration.ts, 13, 13))

interface Obj<O extends { [_ in string]: Runtype<any> }> extends Runtype<{[K in keyof O]: O[K]['witness'] }> {}
>Obj : Symbol(Obj, Decl(invariantGenericErrorElaboration.ts, 13, 22), Decl(invariantGenericErrorElaboration.ts, 15, 111))
>O : Symbol(O, Decl(invariantGenericErrorElaboration.ts, 15, 14))
>_ : Symbol(_, Decl(invariantGenericErrorElaboration.ts, 15, 27))
>Runtype : Symbol(Runtype, Decl(invariantGenericErrorElaboration.ts, 3, 29))
>Runtype : Symbol(Runtype, Decl(invariantGenericErrorElaboration.ts, 3, 29))
>K : Symbol(K, Decl(invariantGenericErrorElaboration.ts, 15, 75))
>O : Symbol(O, Decl(invariantGenericErrorElaboration.ts, 15, 14))
>O : Symbol(O, Decl(invariantGenericErrorElaboration.ts, 15, 14))
>K : Symbol(K, Decl(invariantGenericErrorElaboration.ts, 15, 75))

declare function Obj<O extends { [_: string]: Runtype<any> }>(fields: O): Obj<O>;
>Obj : Symbol(Obj, Decl(invariantGenericErrorElaboration.ts, 13, 22), Decl(invariantGenericErrorElaboration.ts, 15, 111))
>O : Symbol(O, Decl(invariantGenericErrorElaboration.ts, 16, 21))
>_ : Symbol(_, Decl(invariantGenericErrorElaboration.ts, 16, 34))
>Runtype : Symbol(Runtype, Decl(invariantGenericErrorElaboration.ts, 3, 29))
>fields : Symbol(fields, Decl(invariantGenericErrorElaboration.ts, 16, 62))
>O : Symbol(O, Decl(invariantGenericErrorElaboration.ts, 16, 21))
>Obj : Symbol(Obj, Decl(invariantGenericErrorElaboration.ts, 13, 22), Decl(invariantGenericErrorElaboration.ts, 15, 111))
>O : Symbol(O, Decl(invariantGenericErrorElaboration.ts, 16, 21))

interface Constraint<A extends Runtype<any>> extends Runtype<A['witness']> {
>Constraint : Symbol(Constraint, Decl(invariantGenericErrorElaboration.ts, 16, 81))
>A : Symbol(A, Decl(invariantGenericErrorElaboration.ts, 18, 21))
>Runtype : Symbol(Runtype, Decl(invariantGenericErrorElaboration.ts, 3, 29))
>Runtype : Symbol(Runtype, Decl(invariantGenericErrorElaboration.ts, 3, 29))
>A : Symbol(A, Decl(invariantGenericErrorElaboration.ts, 18, 21))

underlying: A,
>underlying : Symbol(Constraint.underlying, Decl(invariantGenericErrorElaboration.ts, 18, 76))
>A : Symbol(A, Decl(invariantGenericErrorElaboration.ts, 18, 21))

check: (x: A['witness']) => void,
>check : Symbol(Constraint.check, Decl(invariantGenericErrorElaboration.ts, 19, 16))
>x : Symbol(x, Decl(invariantGenericErrorElaboration.ts, 20, 10))
>A : Symbol(A, Decl(invariantGenericErrorElaboration.ts, 18, 21))
}

78 changes: 78 additions & 0 deletions tests/baselines/reference/invariantGenericErrorElaboration.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
=== tests/cases/compiler/invariantGenericErrorElaboration.ts ===
// Repro from #19746

const wat: Runtype<any> = Num;
>wat : Runtype<any>
>Runtype : Runtype<A>
>Num : Num

const Foo = Obj({ foo: Num })
>Foo : any
>Obj({ foo: Num }) : any
>Obj : <O extends { [_: string]: Runtype<any>; }>(fields: O) => Obj<O>
>{ foo: Num } : { foo: Num; }
>foo : Num
>Num : Num

interface Runtype<A> {
>Runtype : Runtype<A>
>A : A

constraint: Constraint<this>
>constraint : Constraint<this>
>Constraint : Constraint<A>

witness: A
>witness : A
>A : A
}

interface Num extends Runtype<number> {
>Num : Num
>Runtype : Runtype<A>

tag: 'number'
>tag : "number"
}
declare const Num: Num
>Num : Num
>Num : Num

interface Obj<O extends { [_ in string]: Runtype<any> }> extends Runtype<{[K in keyof O]: O[K]['witness'] }> {}
>Obj : Obj<O>
>O : O
>_ : _
>Runtype : Runtype<A>
>Runtype : Runtype<A>
>K : K
>O : O
>O : O
>K : K

declare function Obj<O extends { [_: string]: Runtype<any> }>(fields: O): Obj<O>;
>Obj : <O extends { [_: string]: Runtype<any>; }>(fields: O) => Obj<O>
>O : O
>_ : string
>Runtype : Runtype<A>
>fields : O
>O : O
>Obj : Obj<O>
>O : O

interface Constraint<A extends Runtype<any>> extends Runtype<A['witness']> {
>Constraint : Constraint<A>
>A : A
>Runtype : Runtype<A>
>Runtype : Runtype<A>
>A : A

underlying: A,
>underlying : A
>A : A

check: (x: A['witness']) => void,
>check : (x: A["witness"]) => void
>x : A["witness"]
>A : A
}

24 changes: 24 additions & 0 deletions tests/cases/compiler/invariantGenericErrorElaboration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// @strict: true

// Repro from #19746

const wat: Runtype<any> = Num;
const Foo = Obj({ foo: Num })

interface Runtype<A> {
constraint: Constraint<this>
witness: A
}

interface Num extends Runtype<number> {
tag: 'number'
}
declare const Num: Num

interface Obj<O extends { [_ in string]: Runtype<any> }> extends Runtype<{[K in keyof O]: O[K]['witness'] }> {}
declare function Obj<O extends { [_: string]: Runtype<any> }>(fields: O): Obj<O>;

interface Constraint<A extends Runtype<any>> extends Runtype<A['witness']> {
underlying: A,
check: (x: A['witness']) => void,
}