Fix check for generic types in control flow analysis (#45148)

* Fix check in hasNonBindingPatternContextualTypeWithNoGenericTypes

* Add regression tests

* Accept new baselines

* Compute both ObjectFlags.IsGenericXXXType flags in one go
This commit is contained in:
Anders Hejlsberg
2021-08-06 08:53:30 +02:00
committed by GitHub
parent 54898de39e
commit 3bd5079f1c
7 changed files with 177 additions and 38 deletions
+23 -32
View File
@@ -14902,40 +14902,35 @@ namespace ts {
return !!(type.flags & TypeFlags.TemplateLiteral) && every((type as TemplateLiteralType).types, isPatternLiteralPlaceholderType);
}
function isGenericType(type: Type): boolean {
return !!getGenericObjectFlags(type);
}
function isGenericObjectType(type: Type): boolean {
if (type.flags & TypeFlags.UnionOrIntersection) {
if (!((type as UnionOrIntersectionType).objectFlags & ObjectFlags.IsGenericObjectTypeComputed)) {
(type as UnionOrIntersectionType).objectFlags |= ObjectFlags.IsGenericObjectTypeComputed |
(some((type as UnionOrIntersectionType).types, isGenericObjectType) ? ObjectFlags.IsGenericObjectType : 0);
}
return !!((type as UnionOrIntersectionType).objectFlags & ObjectFlags.IsGenericObjectType);
}
if (type.flags & TypeFlags.Substitution) {
if (!((type as SubstitutionType).objectFlags & ObjectFlags.IsGenericObjectTypeComputed)) {
(type as SubstitutionType).objectFlags |= ObjectFlags.IsGenericObjectTypeComputed |
(isGenericObjectType((type as SubstitutionType).substitute) || isGenericObjectType((type as SubstitutionType).baseType) ? ObjectFlags.IsGenericObjectType : 0);
}
return !!((type as SubstitutionType).objectFlags & ObjectFlags.IsGenericObjectType);
}
return !!(type.flags & TypeFlags.InstantiableNonPrimitive) || isGenericMappedType(type) || isGenericTupleType(type);
return !!(getGenericObjectFlags(type) & ObjectFlags.IsGenericObjectType);
}
function isGenericIndexType(type: Type): boolean {
return !!(getGenericObjectFlags(type) & ObjectFlags.IsGenericIndexType);
}
function getGenericObjectFlags(type: Type): ObjectFlags {
if (type.flags & TypeFlags.UnionOrIntersection) {
if (!((type as UnionOrIntersectionType).objectFlags & ObjectFlags.IsGenericIndexTypeComputed)) {
(type as UnionOrIntersectionType).objectFlags |= ObjectFlags.IsGenericIndexTypeComputed |
(some((type as UnionOrIntersectionType).types, isGenericIndexType) ? ObjectFlags.IsGenericIndexType : 0);
if (!((type as UnionOrIntersectionType).objectFlags & ObjectFlags.IsGenericTypeComputed)) {
(type as UnionOrIntersectionType).objectFlags |= ObjectFlags.IsGenericTypeComputed |
reduceLeft((type as UnionOrIntersectionType).types, (flags, t) => flags | getGenericObjectFlags(t), 0);
}
return !!((type as UnionOrIntersectionType).objectFlags & ObjectFlags.IsGenericIndexType);
return (type as UnionOrIntersectionType).objectFlags & ObjectFlags.IsGenericType;
}
if (type.flags & TypeFlags.Substitution) {
if (!((type as SubstitutionType).objectFlags & ObjectFlags.IsGenericIndexTypeComputed)) {
(type as SubstitutionType).objectFlags |= ObjectFlags.IsGenericIndexTypeComputed |
(isGenericIndexType((type as SubstitutionType).substitute) || isGenericIndexType((type as SubstitutionType).baseType) ? ObjectFlags.IsGenericIndexType : 0);
if (!((type as SubstitutionType).objectFlags & ObjectFlags.IsGenericTypeComputed)) {
(type as SubstitutionType).objectFlags |= ObjectFlags.IsGenericTypeComputed |
getGenericObjectFlags((type as SubstitutionType).substitute) | getGenericObjectFlags((type as SubstitutionType).baseType);
}
return !!((type as SubstitutionType).objectFlags & ObjectFlags.IsGenericIndexType);
return (type as SubstitutionType).objectFlags & ObjectFlags.IsGenericType;
}
return !!(type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping)) && !isPatternLiteralType(type);
return (type.flags & TypeFlags.InstantiableNonPrimitive || isGenericMappedType(type) || isGenericTupleType(type) ? ObjectFlags.IsGenericObjectType : 0) |
(type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) && !isPatternLiteralType(type) ? ObjectFlags.IsGenericIndexType : 0);
}
function isThisTypeParameter(type: Type): boolean {
@@ -15212,7 +15207,7 @@ namespace ts {
while (true) {
const isUnwrapped = isTypicalNondistributiveConditional(root);
const checkType = instantiateType(unwrapNondistributiveConditionalTuple(root, getActualTypeVariable(root.checkType)), mapper);
const checkTypeInstantiable = isGenericObjectType(checkType) || isGenericIndexType(checkType);
const checkTypeInstantiable = isGenericType(checkType);
const extendsType = instantiateType(unwrapNondistributiveConditionalTuple(root, root.extendsType), mapper);
if (checkType === wildcardType || extendsType === wildcardType) {
return wildcardType;
@@ -15234,7 +15229,7 @@ namespace ts {
// Instantiate the extends type including inferences for 'infer T' type parameters
const inferredExtendsType = combinedMapper ? instantiateType(unwrapNondistributiveConditionalTuple(root, root.extendsType), combinedMapper) : extendsType;
// We attempt to resolve the conditional type only when the check and extends types are non-generic
if (!checkTypeInstantiable && !isGenericObjectType(inferredExtendsType) && !isGenericIndexType(inferredExtendsType)) {
if (!checkTypeInstantiable && !isGenericType(inferredExtendsType)) {
// Return falseType for a definitely false extends check. We check an instantiations of the two
// types with type parameters mapped to the wildcard type, the most permissive instantiations
// possible (the wildcard type is assignable to and from all types). If those are not related,
@@ -24269,17 +24264,13 @@ namespace ts {
return !!(type.flags & TypeFlags.Instantiable && !maybeTypeOfKind(getBaseConstraintOrType(type), TypeFlags.Nullable));
}
function containsGenericType(type: Type): boolean {
return !!(type.flags & TypeFlags.Instantiable || type.flags & TypeFlags.UnionOrIntersection && some((type as UnionOrIntersectionType).types, containsGenericType));
}
function hasNonBindingPatternContextualTypeWithNoGenericTypes(node: Node) {
// Computing the contextual type for a child of a JSX element involves resolving the type of the
// element's tag name, so we exclude that here to avoid circularities.
const contextualType = (isIdentifier(node) || isPropertyAccessExpression(node) || isElementAccessExpression(node)) &&
!((isJsxOpeningElement(node.parent) || isJsxSelfClosingElement(node.parent)) && node.parent.tagName === node) &&
getContextualType(node, ContextFlags.SkipBindingPatterns);
return contextualType && !someType(contextualType, containsGenericType);
return contextualType && !isGenericType(contextualType);
}
function getNarrowableTypeForReference(type: Type, reference: Node, checkMode?: CheckMode) {
@@ -41659,7 +41650,7 @@ namespace ts {
return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_must_have_a_type_annotation);
}
const type = getTypeFromTypeNode(parameter.type);
if (someType(type, t => !!(t.flags & TypeFlags.StringOrNumberLiteralOrUnique)) || isGenericIndexType(type) || isGenericObjectType(type)) {
if (someType(type, t => !!(t.flags & TypeFlags.StringOrNumberLiteralOrUnique)) || isGenericType(type)) {
return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_type_cannot_be_a_literal_type_or_generic_type_Consider_using_a_mapped_object_type_instead);
}
if (!everyType(type, isValidIndexKeyType)) {
+6 -6
View File
@@ -5266,23 +5266,23 @@ namespace ts {
// Flags that require TypeFlags.UnionOrIntersection or TypeFlags.Substitution
/* @internal */
IsGenericObjectTypeComputed = 1 << 22, // IsGenericObjectType flag has been computed
IsGenericTypeComputed = 1 << 22, // IsGenericObjectType flag has been computed
/* @internal */
IsGenericObjectType = 1 << 23, // Union or intersection contains generic object type
/* @internal */
IsGenericIndexTypeComputed = 1 << 24, // IsGenericIndexType flag has been computed
IsGenericIndexType = 1 << 24, // Union or intersection contains generic index type
/* @internal */
IsGenericIndexType = 1 << 25, // Union or intersection contains generic index type
IsGenericType = IsGenericObjectType | IsGenericIndexType,
// Flags that require TypeFlags.Union
/* @internal */
ContainsIntersections = 1 << 26, // Union contains intersections
ContainsIntersections = 1 << 25, // Union contains intersections
// Flags that require TypeFlags.Intersection
/* @internal */
IsNeverIntersectionComputed = 1 << 26, // IsNeverLike flag has been computed
IsNeverIntersectionComputed = 1 << 25, // IsNeverLike flag has been computed
/* @internal */
IsNeverIntersection = 1 << 27, // Intersection reduces to never
IsNeverIntersection = 1 << 26, // Intersection reduces to never
}
/* @internal */
@@ -204,4 +204,20 @@ tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts(168,9): error TS2
iSpec[null! as keyof PublicSpec];
}
}
// Repros from #45145
function f10<T extends { a: string } | undefined>(x: T, y: Partial<T>) {
y = x;
}
type SqlInsertSet<T> = T extends undefined ? object : { [P in keyof T]: unknown };
class SqlTable<T> {
protected validateRow(_row: Partial<SqlInsertSet<T>>): void {
}
public insertRow(row: SqlInsertSet<T>) {
this.validateRow(row);
}
}
@@ -174,6 +174,22 @@ class TableBaseEnum<
iSpec[null! as keyof PublicSpec];
}
}
// Repros from #45145
function f10<T extends { a: string } | undefined>(x: T, y: Partial<T>) {
y = x;
}
type SqlInsertSet<T> = T extends undefined ? object : { [P in keyof T]: unknown };
class SqlTable<T> {
protected validateRow(_row: Partial<SqlInsertSet<T>>): void {
}
public insertRow(row: SqlInsertSet<T>) {
this.validateRow(row);
}
}
//// [controlFlowGenericTypes.js]
@@ -313,3 +329,17 @@ var TableBaseEnum = /** @class */ (function () {
};
return TableBaseEnum;
}());
// Repros from #45145
function f10(x, y) {
y = x;
}
var SqlTable = /** @class */ (function () {
function SqlTable() {
}
SqlTable.prototype.validateRow = function (_row) {
};
SqlTable.prototype.insertRow = function (row) {
this.validateRow(row);
};
return SqlTable;
}());
@@ -525,3 +525,52 @@ class TableBaseEnum<
}
}
// Repros from #45145
function f10<T extends { a: string } | undefined>(x: T, y: Partial<T>) {
>f10 : Symbol(f10, Decl(controlFlowGenericTypes.ts, 174, 1))
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 178, 13))
>a : Symbol(a, Decl(controlFlowGenericTypes.ts, 178, 24))
>x : Symbol(x, Decl(controlFlowGenericTypes.ts, 178, 50))
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 178, 13))
>y : Symbol(y, Decl(controlFlowGenericTypes.ts, 178, 55))
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 178, 13))
y = x;
>y : Symbol(y, Decl(controlFlowGenericTypes.ts, 178, 55))
>x : Symbol(x, Decl(controlFlowGenericTypes.ts, 178, 50))
}
type SqlInsertSet<T> = T extends undefined ? object : { [P in keyof T]: unknown };
>SqlInsertSet : Symbol(SqlInsertSet, Decl(controlFlowGenericTypes.ts, 180, 1))
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 182, 18))
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 182, 18))
>P : Symbol(P, Decl(controlFlowGenericTypes.ts, 182, 57))
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 182, 18))
class SqlTable<T> {
>SqlTable : Symbol(SqlTable, Decl(controlFlowGenericTypes.ts, 182, 82))
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 184, 15))
protected validateRow(_row: Partial<SqlInsertSet<T>>): void {
>validateRow : Symbol(SqlTable.validateRow, Decl(controlFlowGenericTypes.ts, 184, 19))
>_row : Symbol(_row, Decl(controlFlowGenericTypes.ts, 185, 26))
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
>SqlInsertSet : Symbol(SqlInsertSet, Decl(controlFlowGenericTypes.ts, 180, 1))
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 184, 15))
}
public insertRow(row: SqlInsertSet<T>) {
>insertRow : Symbol(SqlTable.insertRow, Decl(controlFlowGenericTypes.ts, 186, 5))
>row : Symbol(row, Decl(controlFlowGenericTypes.ts, 187, 21))
>SqlInsertSet : Symbol(SqlInsertSet, Decl(controlFlowGenericTypes.ts, 180, 1))
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 184, 15))
this.validateRow(row);
>this.validateRow : Symbol(SqlTable.validateRow, Decl(controlFlowGenericTypes.ts, 184, 19))
>this : Symbol(SqlTable, Decl(controlFlowGenericTypes.ts, 182, 82))
>validateRow : Symbol(SqlTable.validateRow, Decl(controlFlowGenericTypes.ts, 184, 19))
>row : Symbol(row, Decl(controlFlowGenericTypes.ts, 187, 21))
}
}
@@ -505,3 +505,40 @@ class TableBaseEnum<
}
}
// Repros from #45145
function f10<T extends { a: string } | undefined>(x: T, y: Partial<T>) {
>f10 : <T extends { a: string; } | undefined>(x: T, y: Partial<T>) => void
>a : string
>x : T
>y : Partial<T>
y = x;
>y = x : T
>y : Partial<T>
>x : T
}
type SqlInsertSet<T> = T extends undefined ? object : { [P in keyof T]: unknown };
>SqlInsertSet : SqlInsertSet<T>
class SqlTable<T> {
>SqlTable : SqlTable<T>
protected validateRow(_row: Partial<SqlInsertSet<T>>): void {
>validateRow : (_row: Partial<SqlInsertSet<T>>) => void
>_row : Partial<SqlInsertSet<T>>
}
public insertRow(row: SqlInsertSet<T>) {
>insertRow : (row: SqlInsertSet<T>) => void
>row : SqlInsertSet<T>
this.validateRow(row);
>this.validateRow(row) : void
>this.validateRow : (_row: Partial<SqlInsertSet<T>>) => void
>this : this
>validateRow : (_row: Partial<SqlInsertSet<T>>) => void
>row : SqlInsertSet<T>
}
}
@@ -175,3 +175,19 @@ class TableBaseEnum<
iSpec[null! as keyof PublicSpec];
}
}
// Repros from #45145
function f10<T extends { a: string } | undefined>(x: T, y: Partial<T>) {
y = x;
}
type SqlInsertSet<T> = T extends undefined ? object : { [P in keyof T]: unknown };
class SqlTable<T> {
protected validateRow(_row: Partial<SqlInsertSet<T>>): void {
}
public insertRow(row: SqlInsertSet<T>) {
this.validateRow(row);
}
}