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

Control flow based type analysis #8010

Merged
merged 70 commits into from
Apr 22, 2016
Merged
Changes from 1 commit
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
e67d15a
Initial implementation of control flow based type analysis
ahejlsberg Mar 22, 2016
afa1714
Add type annotations to suppress circularity errors
ahejlsberg Mar 22, 2016
7c45c7b
Fixing tests
ahejlsberg Mar 22, 2016
80c2e5e
Accepting new baselines
ahejlsberg Mar 22, 2016
33985b2
Adding a few optimizations
ahejlsberg Mar 24, 2016
ed5002c
Handle assignment of union types in getAssignmentReducedType
ahejlsberg Mar 25, 2016
6d25a42
Remove incorrect type predicate (could be true even when result is fa…
ahejlsberg Mar 25, 2016
bf78470
Fix overly aggressive optimization
ahejlsberg Mar 25, 2016
4f936c4
Add control flow tests
ivogabe Mar 25, 2016
7f02357
Merge pull request #7690 from ivogabe/controlFlowTypesTest
ahejlsberg Mar 25, 2016
9e965d4
Fix issues in analysis of do..while and for..in/for..of
ahejlsberg Mar 26, 2016
9de0a5d
Fix comment in test
ahejlsberg Mar 26, 2016
560bc3f
Accepting new baselines
ahejlsberg Mar 26, 2016
0820249
Fixing some tests
ahejlsberg Mar 26, 2016
5a5d89a
Accepting new baselines
ahejlsberg Mar 26, 2016
424074b
Use type {} for vacuous type guards / New getTypeWithFacts function
ahejlsberg Mar 30, 2016
c6f4de3
Remove unnecessary cast
ahejlsberg Mar 30, 2016
e53f390
Fix some tests
ahejlsberg Mar 30, 2016
a38d863
Accepting new baselines
ahejlsberg Mar 30, 2016
3d0fa31
Delete removeNullableKind, use getTypeWithFacts instead
ahejlsberg Mar 30, 2016
ce81ba5
Support unknown types (host object names) in typeof type guards
ahejlsberg Mar 31, 2016
354fd10
Separate error messages for 'null', 'undefined', or both.
ahejlsberg Apr 1, 2016
5179dd6
Flow analysis of &&, ||, and destructuring assignments
ahejlsberg Apr 8, 2016
019f5bd
Accepting new baselines
ahejlsberg Apr 8, 2016
f13c92f
Handle shorthand property assignments
ahejlsberg Apr 8, 2016
b03d087
Accepting new baselines
ahejlsberg Apr 8, 2016
7a32129
Support destructuring declarations in control flow analysis
ahejlsberg Apr 9, 2016
6fb9424
Accepting new baselines
ahejlsberg Apr 9, 2016
e45bac8
Adding test
ahejlsberg Apr 10, 2016
7dfcad6
Fixing fourslash tests
ahejlsberg Apr 10, 2016
92df029
Fixing tests
ahejlsberg Apr 10, 2016
32e6464
Accepting new baselines
ahejlsberg Apr 10, 2016
560e768
Fix linting errors
ahejlsberg Apr 10, 2016
b1e9f43
Merge branch 'master' into controlFlowTypes
ahejlsberg Apr 10, 2016
4c250d0
Accepting new baselines
ahejlsberg Apr 10, 2016
7c7a1c0
A few cosmetic changes
ahejlsberg Apr 12, 2016
df62fa0
Merge branch 'master' into controlFlowTypes
ahejlsberg Apr 12, 2016
586ac55
Fix finishFlow function and rename to finishFlowLabel
ahejlsberg Apr 12, 2016
cd88f1e
Adding regression test
ahejlsberg Apr 12, 2016
472ab7c
Accepting new baselines
ahejlsberg Apr 12, 2016
b689c07
Improving error reporting as suggested in code review
ahejlsberg Apr 13, 2016
1ed9871
Fix typo
ahejlsberg Apr 13, 2016
921efec
Improved handing of evolving types in iteration statements
ahejlsberg Apr 16, 2016
9aea708
Adding tests
ahejlsberg Apr 16, 2016
10889a0
Accepting new baselines
ahejlsberg Apr 16, 2016
2595f04
Removing unused properties
ahejlsberg Apr 17, 2016
b83dc88
Improve expression type caching to ensure consistent results
ahejlsberg Apr 18, 2016
538e22a
Adding tests
ahejlsberg Apr 18, 2016
b5104cb
Accepting new baselines
ahejlsberg Apr 18, 2016
87f55fa
Only evaluate assigned type when declared type is a union type
ahejlsberg Apr 18, 2016
9defdde
Accepting new baselines
ahejlsberg Apr 18, 2016
d28a4fe
typeof x === "function" type guards include Function interface
ahejlsberg Apr 18, 2016
d735b7a
Variables from different source files default to their declared type
ahejlsberg Apr 19, 2016
c8bf6d8
Variables from different module declarations default to their declare…
ahejlsberg Apr 19, 2016
ea96dfd
Support comma operator in type guards
ahejlsberg Apr 20, 2016
33e359f
Adding test
ahejlsberg Apr 20, 2016
bab8ef4
Accepting new baselines
ahejlsberg Apr 20, 2016
e9a7d3d
Removing unused logic
ahejlsberg Apr 20, 2016
a0101c0
Improve consistency of instanceof and user defined type guards
ahejlsberg Apr 21, 2016
729dfce
Fix incorrect user defined type guard function in compiler
ahejlsberg Apr 21, 2016
3045cf5
Add regression test
ahejlsberg Apr 21, 2016
06928b6
Accepting new baselines
ahejlsberg Apr 21, 2016
8a0bc3b
Support assignments in truthiness type guards
ahejlsberg Apr 21, 2016
d2b89be
Adding test
ahejlsberg Apr 21, 2016
ab4b039
Removing unused logic
ahejlsberg Apr 21, 2016
e12b2a7
Correct issue with exported variables in code flow analysis
ahejlsberg Apr 21, 2016
4fb31ac
Update fourslash test
ahejlsberg Apr 21, 2016
f06d3f6
Only narrow to {} in getNarrowedType when types are completely unrelated
ahejlsberg Apr 22, 2016
42e3fc4
Revert previous change
ahejlsberg Apr 22, 2016
0dee5ad
Accepting new baselines
ahejlsberg Apr 22, 2016
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
Prev Previous commit
Next Next commit
Improve expression type caching to ensure consistent results
  • Loading branch information
ahejlsberg committed Apr 18, 2016
commit b83dc88f9b9f0d874adea078a8c5efceb6841ab0
71 changes: 48 additions & 23 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,9 @@ namespace ts {
let jsxElementClassType: Type;

let deferredNodes: Node[];
let inFlowCheck = false;

let flowStackStart = 0;
let flowStackCount = 0;

const tupleTypes: Map<TupleType> = {};
const unionTypes: Map<UnionType> = {};
Expand All @@ -195,6 +197,8 @@ namespace ts {
const symbolLinks: SymbolLinks[] = [];
const nodeLinks: NodeLinks[] = [];
const flowTypeCaches: Map<Type>[] = [];
const flowStackNodes: FlowNode[] = [];
const flowStackCacheKeys: string[] = [];
const potentialThisCollisions: Node[] = [];
const awaitedTypeStack: number[] = [];

Expand Down Expand Up @@ -7409,7 +7413,7 @@ namespace ts {

function getTypeWithDefault(type: Type, defaultExpression: Expression) {
if (defaultExpression) {
const defaultType = checkExpressionCached(defaultExpression);
const defaultType = checkExpression(defaultExpression);
return getUnionType([getTypeWithFacts(type, TypeFacts.NEUndefined), defaultType]);
}
return type;
Expand All @@ -7436,7 +7440,7 @@ namespace ts {
function getAssignedTypeOfBinaryExpression(node: BinaryExpression): Type {
return node.parent.kind === SyntaxKind.ArrayLiteralExpression || node.parent.kind === SyntaxKind.PropertyAssignment ?
getTypeWithDefault(getAssignedType(node), node.right) :
checkExpressionCached(node.right);
checkExpression(node.right);
}

function getAssignedTypeOfArrayLiteralElement(node: ArrayLiteralExpression, element: Expression): Type {
Expand Down Expand Up @@ -7487,9 +7491,17 @@ namespace ts {
return getTypeWithDefault(type, node.initializer);
}

function getTypeOfInitializer(node: Expression) {
// Return the cached type if one is available. If the type of the variable was inferred
// from its initializer, we'll already have cached the type. Otherwise we compute it now
// without caching such that transient types are reflected.
const links = getNodeLinks(node);
return links.resolvedType || checkExpression(node);
}

function getInitialTypeOfVariableDeclaration(node: VariableDeclaration) {
if (node.initializer) {
return checkExpressionCached(node.initializer);
return getTypeOfInitializer(node.initializer);
}
if (node.parent.parent.kind === SyntaxKind.ForInStatement) {
return stringType;
Expand Down Expand Up @@ -7529,11 +7541,7 @@ namespace ts {

function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType: Type) {
let key: string;
const saveInFlowCheck = inFlowCheck;
inFlowCheck = true;
const result = reference.flowNode ? getTypeAtFlowNode(reference.flowNode) : declaredType;
inFlowCheck = saveInFlowCheck;
return result;
return reference.flowNode ? getTypeAtFlowNode(reference.flowNode) : declaredType;

function getTypeAtFlowNode(flow: FlowNode): Type {
while (true) {
Expand Down Expand Up @@ -7601,30 +7609,41 @@ namespace ts {
if (!key) {
key = getFlowCacheKey(reference);
}
let type = cache[key];
if (type) {
return type;
const cached = cache[key];
if (cached) {
return cached;
}
cache[key] = resolvingFlowType;
type = getTypeAtFlowNode(flow);
cache[key] = type !== resolvingFlowType ? type : undefined;
return type;
// Return undefined if we're already processing the given node.
for (let i = flowStackStart; i < flowStackCount; i++) {
if (flowStackNodes[i] === flow && flowStackCacheKeys[i] === key) {
return undefined;
}
}
// Record node and key on stack of nodes being processed.
flowStackNodes[flowStackCount] = flow;
flowStackCacheKeys[flowStackCount] = key;
flowStackCount++;
const type = getTypeAtFlowNode(flow);
flowStackCount--;
// Record the result only if the cache is still empty. If checkExpressionCached was called
// during processing it is possible we've already recorded a result.
return cache[key] || (cache[key] = type);
}

function getTypeAtFlowLabel(flow: FlowLabel) {
const antecedentTypes: Type[] = [];
for (const antecedent of flow.antecedents) {
const t = getTypeAtFlowNodeCached(antecedent);
if (t !== resolvingFlowType) {
const type = getTypeAtFlowNodeCached(antecedent);
if (type) {
// If the type at a particular antecedent path is the declared type and the
// reference is known to always be assigned (i.e. when declared and initial types
// are the same), there is no reason to process more antecedents since the only
// possible outcome is subtypes that will be removed in the final union type anyway.
if (t === declaredType && declaredType === initialType) {
return t;
if (type === declaredType && declaredType === initialType) {
return type;
}
if (!contains(antecedentTypes, t)) {
antecedentTypes.push(t);
if (!contains(antecedentTypes, type)) {
antecedentTypes.push(type);
}
}
}
Expand Down Expand Up @@ -11107,7 +11126,7 @@ namespace ts {
// If signature resolution originated in control flow type analysis (for example to compute the
// assigned type in a flow assignment) we don't cache the result as it may be based on temporary
// types from the control flow analysis.
links.resolvedSignature = inFlowCheck ? cached : result;
links.resolvedSignature = flowStackStart === flowStackCount ? result : cached;
return result;
}

Expand Down Expand Up @@ -12246,7 +12265,13 @@ namespace ts {
function checkExpressionCached(node: Expression, contextualMapper?: TypeMapper): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
// When computing a type that we're going to cache, we need to ignore any ongoing control flow
// analysis because variables may have transient types in indeterminable states. Moving flowStackStart
// to the top of the stack ensures all transient types are computed from a known point.
const saveFlowStackStart = flowStackStart;
flowStackStart = flowStackCount;
links.resolvedType = checkExpression(node, contextualMapper);
flowStackStart = saveFlowStackStart;
}
return links.resolvedType;
}
Expand Down