Skip to content
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

Propagate saved variance flags from cached comparisons #31688

Merged
merged 5 commits into from May 31, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
28 changes: 22 additions & 6 deletions src/compiler/checker.ts
Expand Up @@ -12981,6 +12981,18 @@ namespace ts {
return result;
}

function propagateSidebandVarianceFlags(typeArguments: readonly Type[], variances: VarianceFlags[]) {
weswigham marked this conversation as resolved.
Show resolved Hide resolved
for (let i = 0; i < variances.length; i++) {
weswigham marked this conversation as resolved.
Show resolved Hide resolved
const v = variances[i];
if (v & VarianceFlags.Unmeasurable) {
instantiateType(typeArguments[i], reportUnmeasurableMarkers);
}
if (v & VarianceFlags.Unreliable) {
instantiateType(typeArguments[i], reportUnreliableMarkers);
}
}
}

// Determine if possibly recursive types are related. First, check if the result is already available in the global cache.
// Second, check if we have already started a comparison of the given two types in which case we assume the result to be true.
// Third, check if both types are part of deeply nested chains of generic type instantiations and if so assume the types are
Expand All @@ -12998,6 +13010,16 @@ namespace ts {
// as a failure, and should be updated as a reported failure by the bottom of this function.
}
else {
if (outofbandVarianceMarkerHandler) {
// We're in the middle of variance checking - integrate any unmeasurable/unreliable flags from this cached component
if (source.flags & (TypeFlags.Object | TypeFlags.Conditional) && source.aliasSymbol &&
source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol) {
propagateSidebandVarianceFlags(source.aliasTypeArguments, getAliasVariances(source.aliasSymbol));
}
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target && length((<TypeReference>source).typeArguments)) {
propagateSidebandVarianceFlags((<TypeReference>source).typeArguments!, getVariances((<TypeReference>source).target));
}
}
return related === RelationComparisonResult.Succeeded ? Ternary.True : Ternary.False;
}
}
Expand Down Expand Up @@ -14070,12 +14092,6 @@ namespace ts {
if (unreliable) {
variance |= VarianceFlags.Unreliable;
}
const covariantID = getRelationKey(typeWithSub, typeWithSuper, assignableRelation);
const contravariantID = getRelationKey(typeWithSuper, typeWithSub, assignableRelation);
// We delete the results of these checks, as we want them to actually be run, see the `Unmeasurable` variance we cache,
// And then fall back to a structural result.
assignableRelation.delete(covariantID);
assignableRelation.delete(contravariantID);
}
variances.push(variance);
}
Expand Down
55 changes: 55 additions & 0 deletions tests/baselines/reference/identicalTypesNoDifferByCheckOrder.js
@@ -0,0 +1,55 @@
//// [identicalTypesNoDifferByCheckOrder.ts]
interface SomeProps {
x?: string;
y?: number;
renderAs?: FunctionComponent1<SomeProps>
}

type SomePropsX = Required<Pick<SomeProps, "x">> & Omit<SomeProps, "x">;

interface SomePropsClone {
x?: string;
y?: number;
renderAs?: FunctionComponent2<SomeProps>
}

type SomePropsCloneX = Required<Pick<SomePropsClone, "x">> & Omit<SomePropsClone, "x">;

type Validator<T> = {(): boolean, opt?: T};
type WeakValidationMap<T> = {[K in keyof T]?: null extends T[K] ? Validator<T[K] | null | undefined> : Validator<T[K]>};

interface FunctionComponent1<P> {
(props: P & { children?: unknown }): void;
propTypes?: WeakValidationMap<P>;
}

interface FunctionComponent2<P> {
(props: P & { children?: unknown }): void;
propTypes?: WeakValidationMap<P>;
}

function needsComponentOfSomeProps3(...x: SomePropsClone[]): void {}
const comp3: FunctionComponent2<SomePropsCloneX> = null as any;
needsComponentOfSomeProps3({ renderAs: comp3 });

function needsComponentOfSomeProps2(...x: SomeProps[]): void {}
const comp2: FunctionComponent1<SomePropsX> = null as any;
needsComponentOfSomeProps2({ renderAs: comp2 });

//// [identicalTypesNoDifferByCheckOrder.js]
function needsComponentOfSomeProps3() {
var x = [];
for (var _i = 0; _i < arguments.length; _i++) {
x[_i] = arguments[_i];
}
}
var comp3 = null;
needsComponentOfSomeProps3({ renderAs: comp3 });
function needsComponentOfSomeProps2() {
var x = [];
for (var _i = 0; _i < arguments.length; _i++) {
x[_i] = arguments[_i];
}
}
var comp2 = null;
needsComponentOfSomeProps2({ renderAs: comp2 });
127 changes: 127 additions & 0 deletions tests/baselines/reference/identicalTypesNoDifferByCheckOrder.symbols
@@ -0,0 +1,127 @@
=== tests/cases/compiler/identicalTypesNoDifferByCheckOrder.ts ===
interface SomeProps {
>SomeProps : Symbol(SomeProps, Decl(identicalTypesNoDifferByCheckOrder.ts, 0, 0))

x?: string;
>x : Symbol(SomeProps.x, Decl(identicalTypesNoDifferByCheckOrder.ts, 0, 21))

y?: number;
>y : Symbol(SomeProps.y, Decl(identicalTypesNoDifferByCheckOrder.ts, 1, 15))

renderAs?: FunctionComponent1<SomeProps>
>renderAs : Symbol(SomeProps.renderAs, Decl(identicalTypesNoDifferByCheckOrder.ts, 2, 15))
>FunctionComponent1 : Symbol(FunctionComponent1, Decl(identicalTypesNoDifferByCheckOrder.ts, 17, 120))
>SomeProps : Symbol(SomeProps, Decl(identicalTypesNoDifferByCheckOrder.ts, 0, 0))
}

type SomePropsX = Required<Pick<SomeProps, "x">> & Omit<SomeProps, "x">;
>SomePropsX : Symbol(SomePropsX, Decl(identicalTypesNoDifferByCheckOrder.ts, 4, 1))
>Required : Symbol(Required, Decl(lib.es5.d.ts, --, --))
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
>SomeProps : Symbol(SomeProps, Decl(identicalTypesNoDifferByCheckOrder.ts, 0, 0))
>Omit : Symbol(Omit, Decl(lib.es5.d.ts, --, --))
>SomeProps : Symbol(SomeProps, Decl(identicalTypesNoDifferByCheckOrder.ts, 0, 0))

interface SomePropsClone {
>SomePropsClone : Symbol(SomePropsClone, Decl(identicalTypesNoDifferByCheckOrder.ts, 6, 72))

x?: string;
>x : Symbol(SomePropsClone.x, Decl(identicalTypesNoDifferByCheckOrder.ts, 8, 26))

y?: number;
>y : Symbol(SomePropsClone.y, Decl(identicalTypesNoDifferByCheckOrder.ts, 9, 15))

renderAs?: FunctionComponent2<SomeProps>
>renderAs : Symbol(SomePropsClone.renderAs, Decl(identicalTypesNoDifferByCheckOrder.ts, 10, 15))
>FunctionComponent2 : Symbol(FunctionComponent2, Decl(identicalTypesNoDifferByCheckOrder.ts, 22, 1))
>SomeProps : Symbol(SomeProps, Decl(identicalTypesNoDifferByCheckOrder.ts, 0, 0))
}

type SomePropsCloneX = Required<Pick<SomePropsClone, "x">> & Omit<SomePropsClone, "x">;
>SomePropsCloneX : Symbol(SomePropsCloneX, Decl(identicalTypesNoDifferByCheckOrder.ts, 12, 1))
>Required : Symbol(Required, Decl(lib.es5.d.ts, --, --))
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
>SomePropsClone : Symbol(SomePropsClone, Decl(identicalTypesNoDifferByCheckOrder.ts, 6, 72))
>Omit : Symbol(Omit, Decl(lib.es5.d.ts, --, --))
>SomePropsClone : Symbol(SomePropsClone, Decl(identicalTypesNoDifferByCheckOrder.ts, 6, 72))

type Validator<T> = {(): boolean, opt?: T};
>Validator : Symbol(Validator, Decl(identicalTypesNoDifferByCheckOrder.ts, 14, 87))
>T : Symbol(T, Decl(identicalTypesNoDifferByCheckOrder.ts, 16, 15))
>opt : Symbol(opt, Decl(identicalTypesNoDifferByCheckOrder.ts, 16, 33))
>T : Symbol(T, Decl(identicalTypesNoDifferByCheckOrder.ts, 16, 15))

type WeakValidationMap<T> = {[K in keyof T]?: null extends T[K] ? Validator<T[K] | null | undefined> : Validator<T[K]>};
>WeakValidationMap : Symbol(WeakValidationMap, Decl(identicalTypesNoDifferByCheckOrder.ts, 16, 43))
>T : Symbol(T, Decl(identicalTypesNoDifferByCheckOrder.ts, 17, 23))
>K : Symbol(K, Decl(identicalTypesNoDifferByCheckOrder.ts, 17, 30))
>T : Symbol(T, Decl(identicalTypesNoDifferByCheckOrder.ts, 17, 23))
>T : Symbol(T, Decl(identicalTypesNoDifferByCheckOrder.ts, 17, 23))
>K : Symbol(K, Decl(identicalTypesNoDifferByCheckOrder.ts, 17, 30))
>Validator : Symbol(Validator, Decl(identicalTypesNoDifferByCheckOrder.ts, 14, 87))
>T : Symbol(T, Decl(identicalTypesNoDifferByCheckOrder.ts, 17, 23))
>K : Symbol(K, Decl(identicalTypesNoDifferByCheckOrder.ts, 17, 30))
>Validator : Symbol(Validator, Decl(identicalTypesNoDifferByCheckOrder.ts, 14, 87))
>T : Symbol(T, Decl(identicalTypesNoDifferByCheckOrder.ts, 17, 23))
>K : Symbol(K, Decl(identicalTypesNoDifferByCheckOrder.ts, 17, 30))

interface FunctionComponent1<P> {
>FunctionComponent1 : Symbol(FunctionComponent1, Decl(identicalTypesNoDifferByCheckOrder.ts, 17, 120))
>P : Symbol(P, Decl(identicalTypesNoDifferByCheckOrder.ts, 19, 29))

(props: P & { children?: unknown }): void;
>props : Symbol(props, Decl(identicalTypesNoDifferByCheckOrder.ts, 20, 5))
>P : Symbol(P, Decl(identicalTypesNoDifferByCheckOrder.ts, 19, 29))
>children : Symbol(children, Decl(identicalTypesNoDifferByCheckOrder.ts, 20, 17))

propTypes?: WeakValidationMap<P>;
>propTypes : Symbol(FunctionComponent1.propTypes, Decl(identicalTypesNoDifferByCheckOrder.ts, 20, 46))
>WeakValidationMap : Symbol(WeakValidationMap, Decl(identicalTypesNoDifferByCheckOrder.ts, 16, 43))
>P : Symbol(P, Decl(identicalTypesNoDifferByCheckOrder.ts, 19, 29))
}

interface FunctionComponent2<P> {
>FunctionComponent2 : Symbol(FunctionComponent2, Decl(identicalTypesNoDifferByCheckOrder.ts, 22, 1))
>P : Symbol(P, Decl(identicalTypesNoDifferByCheckOrder.ts, 24, 29))

(props: P & { children?: unknown }): void;
>props : Symbol(props, Decl(identicalTypesNoDifferByCheckOrder.ts, 25, 5))
>P : Symbol(P, Decl(identicalTypesNoDifferByCheckOrder.ts, 24, 29))
>children : Symbol(children, Decl(identicalTypesNoDifferByCheckOrder.ts, 25, 17))

propTypes?: WeakValidationMap<P>;
>propTypes : Symbol(FunctionComponent2.propTypes, Decl(identicalTypesNoDifferByCheckOrder.ts, 25, 46))
>WeakValidationMap : Symbol(WeakValidationMap, Decl(identicalTypesNoDifferByCheckOrder.ts, 16, 43))
>P : Symbol(P, Decl(identicalTypesNoDifferByCheckOrder.ts, 24, 29))
}

function needsComponentOfSomeProps3(...x: SomePropsClone[]): void {}
>needsComponentOfSomeProps3 : Symbol(needsComponentOfSomeProps3, Decl(identicalTypesNoDifferByCheckOrder.ts, 27, 1))
>x : Symbol(x, Decl(identicalTypesNoDifferByCheckOrder.ts, 29, 36))
>SomePropsClone : Symbol(SomePropsClone, Decl(identicalTypesNoDifferByCheckOrder.ts, 6, 72))

const comp3: FunctionComponent2<SomePropsCloneX> = null as any;
>comp3 : Symbol(comp3, Decl(identicalTypesNoDifferByCheckOrder.ts, 30, 5))
>FunctionComponent2 : Symbol(FunctionComponent2, Decl(identicalTypesNoDifferByCheckOrder.ts, 22, 1))
>SomePropsCloneX : Symbol(SomePropsCloneX, Decl(identicalTypesNoDifferByCheckOrder.ts, 12, 1))

needsComponentOfSomeProps3({ renderAs: comp3 });
>needsComponentOfSomeProps3 : Symbol(needsComponentOfSomeProps3, Decl(identicalTypesNoDifferByCheckOrder.ts, 27, 1))
>renderAs : Symbol(renderAs, Decl(identicalTypesNoDifferByCheckOrder.ts, 31, 28))
>comp3 : Symbol(comp3, Decl(identicalTypesNoDifferByCheckOrder.ts, 30, 5))

function needsComponentOfSomeProps2(...x: SomeProps[]): void {}
>needsComponentOfSomeProps2 : Symbol(needsComponentOfSomeProps2, Decl(identicalTypesNoDifferByCheckOrder.ts, 31, 48))
>x : Symbol(x, Decl(identicalTypesNoDifferByCheckOrder.ts, 33, 36))
>SomeProps : Symbol(SomeProps, Decl(identicalTypesNoDifferByCheckOrder.ts, 0, 0))

const comp2: FunctionComponent1<SomePropsX> = null as any;
>comp2 : Symbol(comp2, Decl(identicalTypesNoDifferByCheckOrder.ts, 34, 5))
>FunctionComponent1 : Symbol(FunctionComponent1, Decl(identicalTypesNoDifferByCheckOrder.ts, 17, 120))
>SomePropsX : Symbol(SomePropsX, Decl(identicalTypesNoDifferByCheckOrder.ts, 4, 1))

needsComponentOfSomeProps2({ renderAs: comp2 });
>needsComponentOfSomeProps2 : Symbol(needsComponentOfSomeProps2, Decl(identicalTypesNoDifferByCheckOrder.ts, 31, 48))
>renderAs : Symbol(renderAs, Decl(identicalTypesNoDifferByCheckOrder.ts, 35, 28))
>comp2 : Symbol(comp2, Decl(identicalTypesNoDifferByCheckOrder.ts, 34, 5))

88 changes: 88 additions & 0 deletions tests/baselines/reference/identicalTypesNoDifferByCheckOrder.types
@@ -0,0 +1,88 @@
=== tests/cases/compiler/identicalTypesNoDifferByCheckOrder.ts ===
interface SomeProps {
x?: string;
>x : string | undefined

y?: number;
>y : number | undefined

renderAs?: FunctionComponent1<SomeProps>
>renderAs : FunctionComponent1<SomeProps> | undefined
}

type SomePropsX = Required<Pick<SomeProps, "x">> & Omit<SomeProps, "x">;
>SomePropsX : SomePropsX

interface SomePropsClone {
x?: string;
>x : string | undefined

y?: number;
>y : number | undefined

renderAs?: FunctionComponent2<SomeProps>
>renderAs : FunctionComponent2<SomeProps> | undefined
}

type SomePropsCloneX = Required<Pick<SomePropsClone, "x">> & Omit<SomePropsClone, "x">;
>SomePropsCloneX : SomePropsCloneX

type Validator<T> = {(): boolean, opt?: T};
>Validator : Validator<T>
>opt : T | undefined

type WeakValidationMap<T> = {[K in keyof T]?: null extends T[K] ? Validator<T[K] | null | undefined> : Validator<T[K]>};
>WeakValidationMap : WeakValidationMap<T>
>null : null
>null : null

interface FunctionComponent1<P> {
(props: P & { children?: unknown }): void;
>props : P & { children?: unknown; }
>children : unknown

propTypes?: WeakValidationMap<P>;
>propTypes : WeakValidationMap<P> | undefined
}

interface FunctionComponent2<P> {
(props: P & { children?: unknown }): void;
>props : P & { children?: unknown; }
>children : unknown

propTypes?: WeakValidationMap<P>;
>propTypes : WeakValidationMap<P> | undefined
}

function needsComponentOfSomeProps3(...x: SomePropsClone[]): void {}
>needsComponentOfSomeProps3 : (...x: SomePropsClone[]) => void
>x : SomePropsClone[]

const comp3: FunctionComponent2<SomePropsCloneX> = null as any;
>comp3 : FunctionComponent2<SomePropsCloneX>
>null as any : any
>null : null

needsComponentOfSomeProps3({ renderAs: comp3 });
>needsComponentOfSomeProps3({ renderAs: comp3 }) : void
>needsComponentOfSomeProps3 : (...x: SomePropsClone[]) => void
>{ renderAs: comp3 } : { renderAs: FunctionComponent2<SomePropsCloneX>; }
>renderAs : FunctionComponent2<SomePropsCloneX>
>comp3 : FunctionComponent2<SomePropsCloneX>

function needsComponentOfSomeProps2(...x: SomeProps[]): void {}
>needsComponentOfSomeProps2 : (...x: SomeProps[]) => void
>x : SomeProps[]

const comp2: FunctionComponent1<SomePropsX> = null as any;
>comp2 : FunctionComponent1<SomePropsX>
>null as any : any
>null : null

needsComponentOfSomeProps2({ renderAs: comp2 });
>needsComponentOfSomeProps2({ renderAs: comp2 }) : void
>needsComponentOfSomeProps2 : (...x: SomeProps[]) => void
>{ renderAs: comp2 } : { renderAs: FunctionComponent1<SomePropsX>; }
>renderAs : FunctionComponent1<SomePropsX>
>comp2 : FunctionComponent1<SomePropsX>

38 changes: 38 additions & 0 deletions tests/cases/compiler/identicalTypesNoDifferByCheckOrder.ts
@@ -0,0 +1,38 @@
// @strictNullChecks: true

interface SomeProps {
x?: string;
y?: number;
renderAs?: FunctionComponent1<SomeProps>
}

type SomePropsX = Required<Pick<SomeProps, "x">> & Omit<SomeProps, "x">;

interface SomePropsClone {
x?: string;
y?: number;
renderAs?: FunctionComponent2<SomeProps>
}

type SomePropsCloneX = Required<Pick<SomePropsClone, "x">> & Omit<SomePropsClone, "x">;

type Validator<T> = {(): boolean, opt?: T};
type WeakValidationMap<T> = {[K in keyof T]?: null extends T[K] ? Validator<T[K] | null | undefined> : Validator<T[K]>};

interface FunctionComponent1<P> {
(props: P & { children?: unknown }): void;
propTypes?: WeakValidationMap<P>;
}

interface FunctionComponent2<P> {
(props: P & { children?: unknown }): void;
propTypes?: WeakValidationMap<P>;
}

function needsComponentOfSomeProps3(...x: SomePropsClone[]): void {}
const comp3: FunctionComponent2<SomePropsCloneX> = null as any;
needsComponentOfSomeProps3({ renderAs: comp3 });

function needsComponentOfSomeProps2(...x: SomeProps[]): void {}
const comp2: FunctionComponent1<SomePropsX> = null as any;
needsComponentOfSomeProps2({ renderAs: comp2 });