From ed338013e09632b6e8b44722d3d52fca1a715e9e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 19 Sep 2016 16:08:39 -0700 Subject: [PATCH] Use silent never type in control flow loop analysis --- src/compiler/checker.ts | 42 +++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a8a445220fc..bef1b0bcb9b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -132,6 +132,7 @@ namespace ts { const esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, "symbol"); const voidType = createIntrinsicType(TypeFlags.Void, "void"); const neverType = createIntrinsicType(TypeFlags.Never, "never"); + const silentNeverType = createIntrinsicType(TypeFlags.Never, "never"); const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); const emptyGenericType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); @@ -147,6 +148,7 @@ namespace ts { const anySignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); const unknownSignature = createSignature(undefined, undefined, undefined, emptyArray, unknownType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); const resolvingSignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); + const silentNeverSignature = createSignature(undefined, undefined, undefined, emptyArray, silentNeverType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true); @@ -8070,6 +8072,9 @@ namespace ts { // we remove type string. function getAssignmentReducedType(declaredType: UnionType, assignedType: Type) { if (declaredType !== assignedType) { + if (assignedType.flags & TypeFlags.Never) { + return assignedType; + } const reducedType = filterType(declaredType, t => typeMaybeAssignableTo(assignedType, t)); if (!(reducedType.flags & TypeFlags.Never)) { return reducedType; @@ -8445,12 +8450,13 @@ namespace ts { // attempt to narrow the antecedent type. If that produces the never type, and if // the antecedent type is incomplete (i.e. a transient type in a loop), then we // take the type guard as an indication that control *could* reach here once we - // have the complete type. We proceed by reverting to the declared type and then - // narrow that. + // have the complete type. We proceed by switching to the silent never type which + // doesn't report errors when operators are applied to it. Note that this is the + // *only* place a silent never type is ever generated. const assumeTrue = (flow.flags & FlowFlags.TrueCondition) !== 0; type = narrowType(type, flow.expression, assumeTrue); if (type.flags & TypeFlags.Never && isIncomplete(flowType)) { - type = narrowType(declaredType, flow.expression, assumeTrue); + type = silentNeverType; } } return createFlowType(type, isIncomplete(flowType)); @@ -10889,7 +10895,7 @@ namespace ts { function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) { const type = checkNonNullExpression(left); - if (isTypeAny(type)) { + if (isTypeAny(type) || type === silentNeverType) { return type; } @@ -11036,8 +11042,8 @@ namespace ts { const objectType = getApparentType(checkNonNullExpression(node.expression)); const indexType = node.argumentExpression ? checkExpression(node.argumentExpression) : unknownType; - if (objectType === unknownType) { - return unknownType; + if (objectType === unknownType || objectType === silentNeverType) { + return objectType; } const isConstEnum = isConstEnumObjectType(objectType); @@ -12087,6 +12093,9 @@ namespace ts { } const funcType = checkNonNullExpression(node.expression); + if (funcType === silentNeverType) { + return silentNeverSignature; + } const apparentType = getApparentType(funcType); if (apparentType === unknownType) { @@ -12159,6 +12168,9 @@ namespace ts { } let expressionType = checkNonNullExpression(node.expression); + if (expressionType === silentNeverType) { + return silentNeverSignature; + } // If expressionType's apparent type(section 3.8.1) is an object type with one or // more construct signatures, the expression is processed in the same manner as a @@ -13024,6 +13036,9 @@ namespace ts { function checkPrefixUnaryExpression(node: PrefixUnaryExpression): Type { const operandType = checkExpression(node.operand); + if (operandType === silentNeverType) { + return silentNeverType; + } if (node.operator === SyntaxKind.MinusToken && node.operand.kind === SyntaxKind.NumericLiteral) { return getLiteralTypeForText(TypeFlags.NumberLiteral, "" + -(node.operand).text); } @@ -13057,6 +13072,9 @@ namespace ts { function checkPostfixUnaryExpression(node: PostfixUnaryExpression): Type { const operandType = checkExpression(node.operand); + if (operandType === silentNeverType) { + return silentNeverType; + } const ok = checkArithmeticOperandType(node.operand, getNonNullableType(operandType), Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_or_an_enum_type); if (ok) { @@ -13121,6 +13139,9 @@ namespace ts { } function checkInstanceOfExpression(left: Expression, right: Expression, leftType: Type, rightType: Type): Type { + if (leftType === silentNeverType || rightType === silentNeverType) { + return silentNeverType; + } // TypeScript 1.0 spec (April 2014): 4.15.4 // The instanceof operator requires the left operand to be of type Any, an object type, or a type parameter type, // and the right operand to be of type Any or a subtype of the 'Function' interface type. @@ -13137,6 +13158,9 @@ namespace ts { } function checkInExpression(left: Expression, right: Expression, leftType: Type, rightType: Type): Type { + if (leftType === silentNeverType || rightType === silentNeverType) { + return silentNeverType; + } // TypeScript 1.0 spec (April 2014): 4.15.5 // The in operator requires the left operand to be of type Any, the String primitive type, or the Number primitive type, // and the right operand to be of type Any, an object type, or a type parameter type. @@ -13401,6 +13425,9 @@ namespace ts { case SyntaxKind.CaretEqualsToken: case SyntaxKind.AmpersandToken: case SyntaxKind.AmpersandEqualsToken: + if (leftType === silentNeverType || rightType === silentNeverType) { + return silentNeverType; + } // TypeScript 1.0 spec (April 2014): 4.19.1 // These operators require their operands to be of type Any, the Number primitive type, // or an enum type. Operands of an enum type are treated @@ -13433,6 +13460,9 @@ namespace ts { return numberType; case SyntaxKind.PlusToken: case SyntaxKind.PlusEqualsToken: + if (leftType === silentNeverType || rightType === silentNeverType) { + return silentNeverType; + } // TypeScript 1.0 spec (April 2014): 4.19.2 // The binary + operator requires both operands to be of the Number primitive type or an enum type, // or at least one of the operands to be of type Any or the String primitive type.