Merge pull request #27204 from Microsoft/fixPerIteration

Fix per-iteration bindings in for-loop head
This commit is contained in:
Ron Buckton 2018-09-27 13:23:03 -07:00 committed by GitHub
commit 9cf201c512
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 841 additions and 301 deletions

View file

@ -15752,6 +15752,10 @@ namespace ts {
return !!findAncestor(node, n => n === threshold ? "quit" : isFunctionLike(n));
}
function getPartOfForStatementContainingNode(node: Node, container: ForStatement) {
return findAncestor(node, n => n === container ? "quit" : n === container.initializer || n === container.condition || n === container.incrementor || n === container.statement);
}
function checkNestedBlockScopedBinding(node: Identifier, symbol: Symbol): void {
if (languageVersion >= ScriptTarget.ES2015 ||
(symbol.flags & (SymbolFlags.BlockScopedVariable | SymbolFlags.Class)) === 0 ||
@ -15780,7 +15784,25 @@ namespace ts {
if (containedInIterationStatement) {
if (usedInFunction) {
// mark iteration statement as containing block-scoped binding captured in some function
getNodeLinks(current).flags |= NodeCheckFlags.LoopWithCapturedBlockScopedBinding;
let capturesBlockScopeBindingInLoopBody = true;
if (isForStatement(container) &&
getAncestor(symbol.valueDeclaration, SyntaxKind.VariableDeclarationList)!.parent === container) {
const part = getPartOfForStatementContainingNode(node.parent, container);
if (part) {
const links = getNodeLinks(part);
links.flags |= NodeCheckFlags.ContainsCapturedBlockScopeBinding;
const capturedBindings = links.capturedBlockScopeBindings || (links.capturedBlockScopeBindings = []);
pushIfUnique(capturedBindings, symbol);
if (part === container.initializer) {
capturesBlockScopeBindingInLoopBody = false; // Initializer is outside of loop body
}
}
}
if (capturesBlockScopeBindingInLoopBody) {
getNodeLinks(current).flags |= NodeCheckFlags.LoopWithCapturedBlockScopedBinding;
}
}
// mark variables that are declared in loop initializer and reassigned inside the body of ForStatement.
@ -15800,6 +15822,11 @@ namespace ts {
}
}
function isBindingCapturedByNode(node: Node, decl: VariableDeclaration | BindingElement) {
const links = getNodeLinks(node);
return !!links && contains(links.capturedBlockScopeBindings, getSymbolOfNode(decl));
}
function isAssignedInBodyOfForStatement(node: Identifier, container: ForStatement): boolean {
// skip parenthesized nodes
let current: Node = node;
@ -28686,7 +28713,12 @@ namespace ts {
getAccessor
};
},
getSymbolOfExternalModuleSpecifier: moduleName => resolveExternalModuleNameWorker(moduleName, moduleName, /*moduleNotFoundError*/ undefined)
getSymbolOfExternalModuleSpecifier: moduleName => resolveExternalModuleNameWorker(moduleName, moduleName, /*moduleNotFoundError*/ undefined),
isBindingCapturedByNode: (node, decl) => {
const parseNode = getParseTreeNode(node);
const parseDecl = getParseTreeNode(decl);
return !!parseNode && !!parseDecl && (isVariableDeclaration(parseDecl) || isBindingElement(parseDecl)) && isBindingCapturedByNode(parseNode, parseDecl);
}
};
function isInHeritageClause(node: PropertyAccessEntityNameExpression) {

View file

@ -46,10 +46,16 @@ namespace ts {
* so nobody can observe this new value.
*/
interface LoopOutParameter {
flags: LoopOutParameterFlags;
originalName: Identifier;
outParamName: Identifier;
}
const enum LoopOutParameterFlags {
Body = 1 << 0, // Modified in the body of the iteration statement
Initializer = 1 << 1, // Set in the initializer of a ForStatement
}
const enum CopyDirection {
ToOriginal,
ToOutParameter
@ -129,10 +135,14 @@ namespace ts {
*/
hoistedLocalVariables?: Identifier[];
conditionVariable?: Identifier;
loopParameters: ParameterDeclaration[];
/**
* List of loop out parameters - detailed descripion can be found in the comment to LoopOutParameter
*/
loopOutParameters?: LoopOutParameter[];
loopOutParameters: LoopOutParameter[];
}
const enum SuperCaptureResult {
@ -347,7 +357,7 @@ namespace ts {
return (node.transformFlags & TransformFlags.ContainsES2015) !== 0
|| convertedLoopState !== undefined
|| (hierarchyFacts & HierarchyFacts.ConstructorWithCapturedSuper && (isStatement(node) || (node.kind === SyntaxKind.Block)))
|| (isIterationStatement(node, /*lookInLabeledStatements*/ false) && shouldConvertIterationStatementBody(node))
|| (isIterationStatement(node, /*lookInLabeledStatements*/ false) && shouldConvertIterationStatement(node))
|| (getEmitFlags(node) & EmitFlags.TypeScriptClassWrapper) !== 0;
}
@ -646,8 +656,8 @@ namespace ts {
}
}
let returnExpression: Expression = createLiteral(labelMarker);
if (convertedLoopState.loopOutParameters!.length) {
const outParams = convertedLoopState.loopOutParameters!;
if (convertedLoopState.loopOutParameters.length) {
const outParams = convertedLoopState.loopOutParameters;
let expr: Expression | undefined;
for (let i = 0; i < outParams.length; i++) {
const copyExpr = copyOutParameter(outParams[i], CopyDirection.ToOutParameter);
@ -2610,7 +2620,40 @@ namespace ts {
return visitEachChild(node, visitor, context);
}
function shouldConvertIterationStatementBody(node: IterationStatement): boolean {
interface ForStatementWithConvertibleInitializer extends ForStatement {
initializer: VariableDeclarationList;
}
interface ForStatementWithConvertibleCondition extends ForStatement {
condition: Expression;
}
interface ForStatementWithConvertibleIncrementor extends ForStatement {
incrementor: Expression;
}
function shouldConvertPartOfIterationStatement(node: Node) {
return (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ContainsCapturedBlockScopeBinding) !== 0;
}
function shouldConvertInitializerOfForStatement(node: IterationStatement): node is ForStatementWithConvertibleInitializer {
return isForStatement(node) && !!node.initializer && shouldConvertPartOfIterationStatement(node.initializer);
}
function shouldConvertConditionOfForStatement(node: IterationStatement): node is ForStatementWithConvertibleCondition {
return isForStatement(node) && !!node.condition && shouldConvertPartOfIterationStatement(node.condition);
}
function shouldConvertIncrementorOfForStatement(node: IterationStatement): node is ForStatementWithConvertibleIncrementor {
return isForStatement(node) && !!node.incrementor && shouldConvertPartOfIterationStatement(node.incrementor);
}
function shouldConvertIterationStatement(node: IterationStatement) {
return shouldConvertBodyOfIterationStatement(node)
|| shouldConvertInitializerOfForStatement(node);
}
function shouldConvertBodyOfIterationStatement(node: IterationStatement): boolean {
return (resolver.getNodeCheckFlags(node) & NodeCheckFlags.LoopWithCapturedBlockScopedBinding) !== 0;
}
@ -2639,7 +2682,7 @@ namespace ts {
}
function convertIterationStatementBodyIfNecessary(node: IterationStatement, outermostLabeledStatement: LabeledStatement | undefined, convert?: LoopConverter): VisitResult<Statement> {
if (!shouldConvertIterationStatementBody(node)) {
if (!shouldConvertIterationStatement(node)) {
let saveAllowedNonLabeledJumps: Jump | undefined;
if (convertedLoopState) {
// we get here if we are trying to emit normal loop loop inside converted loop
@ -2658,7 +2701,102 @@ namespace ts {
return result;
}
const functionName = createUniqueName("_loop");
const currentState = createConvertedLoopState(node);
const statements: Statement[] = [];
const outerConvertedLoopState = convertedLoopState;
convertedLoopState = currentState;
const initializerFunction = shouldConvertInitializerOfForStatement(node) ? createFunctionForInitializerOfForStatement(node, currentState) : undefined;
const bodyFunction = shouldConvertBodyOfIterationStatement(node) ? createFunctionForBodyOfIterationStatement(node, currentState, outerConvertedLoopState) : undefined;
convertedLoopState = outerConvertedLoopState;
if (initializerFunction) statements.push(initializerFunction.functionDeclaration);
if (bodyFunction) statements.push(bodyFunction.functionDeclaration);
addExtraDeclarationsForConvertedLoop(statements, currentState, outerConvertedLoopState);
if (initializerFunction) {
statements.push(generateCallToConvertedLoopInitializer(initializerFunction.functionName, initializerFunction.containsYield));
}
let loop: Statement;
if (bodyFunction) {
if (convert) {
loop = convert(node, outermostLabeledStatement, bodyFunction.part);
}
else {
const clone = convertIterationStatementCore(node, initializerFunction, createBlock(bodyFunction.part, /*multiLine*/ true));
aggregateTransformFlags(clone);
loop = restoreEnclosingLabel(clone, outermostLabeledStatement, convertedLoopState && resetLabel);
}
}
else {
const clone = convertIterationStatementCore(node, initializerFunction, visitNode(node.statement, visitor, isStatement, liftToBlock));
aggregateTransformFlags(clone);
loop = restoreEnclosingLabel(clone, outermostLabeledStatement, convertedLoopState && resetLabel);
}
statements.push(loop);
return statements;
}
function convertIterationStatementCore(node: IterationStatement, initializerFunction: IterationStatementPartFunction<VariableDeclarationList> | undefined, convertedLoopBody: Statement) {
switch (node.kind) {
case SyntaxKind.ForStatement: return convertForStatement(node as ForStatement, initializerFunction, convertedLoopBody);
case SyntaxKind.ForInStatement: return convertForInStatement(node as ForInStatement, convertedLoopBody);
case SyntaxKind.ForOfStatement: return convertForOfStatement(node as ForOfStatement, convertedLoopBody);
case SyntaxKind.DoStatement: return convertDoStatement(node as DoStatement, convertedLoopBody);
case SyntaxKind.WhileStatement: return convertWhileStatement(node as WhileStatement, convertedLoopBody);
default: return Debug.failBadSyntaxKind(node, "IterationStatement expected");
}
}
function convertForStatement(node: ForStatement, initializerFunction: IterationStatementPartFunction<VariableDeclarationList> | undefined, convertedLoopBody: Statement) {
const shouldConvertCondition = node.condition && shouldConvertPartOfIterationStatement(node.condition);
const shouldConvertIncrementor = shouldConvertCondition || node.incrementor && shouldConvertPartOfIterationStatement(node.incrementor);
return updateFor(
node,
visitNode(initializerFunction ? initializerFunction.part : node.initializer, visitor, isForInitializer),
visitNode(shouldConvertCondition ? undefined : node.condition, visitor, isExpression),
visitNode(shouldConvertIncrementor ? undefined : node.incrementor, visitor, isExpression),
convertedLoopBody
);
}
function convertForOfStatement(node: ForOfStatement, convertedLoopBody: Statement) {
return updateForOf(
node,
/*awaitModifier*/ undefined,
visitNode(node.initializer, visitor, isForInitializer),
visitNode(node.expression, visitor, isExpression),
convertedLoopBody);
}
function convertForInStatement(node: ForInStatement, convertedLoopBody: Statement) {
return updateForIn(
node,
visitNode(node.initializer, visitor, isForInitializer),
visitNode(node.expression, visitor, isExpression),
convertedLoopBody);
}
function convertDoStatement(node: DoStatement, convertedLoopBody: Statement) {
return updateDo(
node,
convertedLoopBody,
visitNode(node.expression, visitor, isExpression));
}
function convertWhileStatement(node: WhileStatement, convertedLoopBody: Statement) {
return updateWhile(
node,
visitNode(node.expression, visitor, isExpression),
convertedLoopBody);
}
function createConvertedLoopState(node: IterationStatement) {
let loopInitializer: VariableDeclarationList | undefined;
switch (node.kind) {
case SyntaxKind.ForStatement:
@ -2670,74 +2808,311 @@ namespace ts {
}
break;
}
// variables that will be passed to the loop as parameters
const loopParameters: ParameterDeclaration[] = [];
// variables declared in the loop initializer that will be changed inside the loop
const loopOutParameters: LoopOutParameter[] = [];
if (loopInitializer && (getCombinedNodeFlags(loopInitializer) & NodeFlags.BlockScoped)) {
const hasCapturedBindingsInForInitializer = shouldConvertInitializerOfForStatement(node);
for (const decl of loopInitializer.declarations) {
processLoopVariableDeclaration(decl, loopParameters, loopOutParameters);
processLoopVariableDeclaration(node, decl, loopParameters, loopOutParameters, hasCapturedBindingsInForInitializer);
}
}
const outerConvertedLoopState = convertedLoopState;
convertedLoopState = { loopOutParameters };
if (outerConvertedLoopState) {
const currentState: ConvertedLoopState = { loopParameters, loopOutParameters };
if (convertedLoopState) {
// convertedOuterLoopState !== undefined means that this converted loop is nested in another converted loop.
// if outer converted loop has already accumulated some state - pass it through
if (outerConvertedLoopState.argumentsName) {
if (convertedLoopState.argumentsName) {
// outer loop has already used 'arguments' so we've already have some name to alias it
// use the same name in all nested loops
convertedLoopState.argumentsName = outerConvertedLoopState.argumentsName;
currentState.argumentsName = convertedLoopState.argumentsName;
}
if (outerConvertedLoopState.thisName) {
if (convertedLoopState.thisName) {
// outer loop has already used 'this' so we've already have some name to alias it
// use the same name in all nested loops
convertedLoopState.thisName = outerConvertedLoopState.thisName;
currentState.thisName = convertedLoopState.thisName;
}
if (outerConvertedLoopState.hoistedLocalVariables) {
if (convertedLoopState.hoistedLocalVariables) {
// we've already collected some non-block scoped variable declarations in enclosing loop
// use the same storage in nested loop
convertedLoopState.hoistedLocalVariables = outerConvertedLoopState.hoistedLocalVariables;
currentState.hoistedLocalVariables = convertedLoopState.hoistedLocalVariables;
}
}
return currentState;
}
function addExtraDeclarationsForConvertedLoop(statements: Statement[], state: ConvertedLoopState, outerState: ConvertedLoopState | undefined) {
let extraVariableDeclarations: VariableDeclaration[] | undefined;
// propagate state from the inner loop to the outer loop if necessary
if (state.argumentsName) {
// if alias for arguments is set
if (outerState) {
// pass it to outer converted loop
outerState.argumentsName = state.argumentsName;
}
else {
// this is top level converted loop and we need to create an alias for 'arguments' object
(extraVariableDeclarations || (extraVariableDeclarations = [])).push(
createVariableDeclaration(
state.argumentsName,
/*type*/ undefined,
createIdentifier("arguments")
)
);
}
}
if (state.thisName) {
// if alias for this is set
if (outerState) {
// pass it to outer converted loop
outerState.thisName = state.thisName;
}
else {
// this is top level converted loop so we need to create an alias for 'this' here
// NOTE:
// if converted loops were all nested in arrow function then we'll always emit '_this' so convertedLoopState.thisName will not be set.
// If it is set this means that all nested loops are not nested in arrow function and it is safe to capture 'this'.
(extraVariableDeclarations || (extraVariableDeclarations = [])).push(
createVariableDeclaration(
state.thisName,
/*type*/ undefined,
createIdentifier("this")
)
);
}
}
if (state.hoistedLocalVariables) {
// if hoistedLocalVariables !== undefined this means that we've possibly collected some variable declarations to be hoisted later
if (outerState) {
// pass them to outer converted loop
outerState.hoistedLocalVariables = state.hoistedLocalVariables;
}
else {
if (!extraVariableDeclarations) {
extraVariableDeclarations = [];
}
// hoist collected variable declarations
for (const identifier of state.hoistedLocalVariables) {
extraVariableDeclarations.push(createVariableDeclaration(identifier));
}
}
}
// add extra variables to hold out parameters if necessary
if (state.loopOutParameters.length) {
if (!extraVariableDeclarations) {
extraVariableDeclarations = [];
}
for (const outParam of state.loopOutParameters) {
extraVariableDeclarations.push(createVariableDeclaration(outParam.outParamName));
}
}
if (state.conditionVariable) {
if (!extraVariableDeclarations) {
extraVariableDeclarations = [];
}
extraVariableDeclarations.push(createVariableDeclaration(state.conditionVariable, /*type*/ undefined, createFalse()));
}
// create variable statement to hold all introduced variable declarations
if (extraVariableDeclarations) {
statements.push(createVariableStatement(
/*modifiers*/ undefined,
createVariableDeclarationList(extraVariableDeclarations)
));
}
}
interface IterationStatementPartFunction<T> {
functionName: Identifier;
functionDeclaration: Statement;
containsYield: boolean;
part: T;
}
function createOutVariable(p: LoopOutParameter) {
return createVariableDeclaration(p.originalName, /*type*/ undefined, p.outParamName);
}
/**
* Creates a `_loop_init` function for a `ForStatement` with a block-scoped initializer
* that is captured in a closure inside of the initializer. The `_loop_init` function is
* used to preserve the per-iteration environment semantics of
* [13.7.4.8 RS: ForBodyEvaluation](https://tc39.github.io/ecma262/#sec-forbodyevaluation).
*/
function createFunctionForInitializerOfForStatement(node: ForStatementWithConvertibleInitializer, currentState: ConvertedLoopState): IterationStatementPartFunction<VariableDeclarationList> {
const functionName = createUniqueName("_loop_init");
const containsYield = (node.initializer.transformFlags & TransformFlags.ContainsYield) !== 0;
let emitFlags = EmitFlags.None;
if (currentState.containsLexicalThis) emitFlags |= EmitFlags.CapturesThis;
if (containsYield && hierarchyFacts & HierarchyFacts.AsyncFunctionBody) emitFlags |= EmitFlags.AsyncFunctionBody;
const statements: Statement[] = [];
statements.push(createVariableStatement(/*modifiers*/ undefined, node.initializer));
copyOutParameters(currentState.loopOutParameters, LoopOutParameterFlags.Initializer, CopyDirection.ToOutParameter, statements);
// This transforms the following ES2015 syntax:
//
// for (let i = (setImmediate(() => console.log(i)), 0); i < 2; i++) {
// // loop body
// }
//
// Into the following ES5 syntax:
//
// var _loop_init_1 = function () {
// var i = (setImmediate(() => console.log(i)), 0);
// out_i_1 = i;
// };
// var out_i_1;
// _loop_init_1();
// for (var i = out_i_1; i < 2; i++) {
// // loop body
// }
//
// Which prevents mutations to `i` in the per-iteration environment of the body
// from affecting the initial value for `i` outside of the per-iteration environment.
const functionDeclaration = createVariableStatement(
/*modifiers*/ undefined,
setEmitFlags(
createVariableDeclarationList([
createVariableDeclaration(
functionName,
/*type*/ undefined,
setEmitFlags(
createFunctionExpression(
/*modifiers*/ undefined,
containsYield ? createToken(SyntaxKind.AsteriskToken) : undefined,
/*name*/ undefined,
/*typeParameters*/ undefined,
/*parameters*/ undefined,
/*type*/ undefined,
visitNode(
createBlock(statements, /*multiLine*/ true),
visitor,
isBlock
)
),
emitFlags
)
)
]),
EmitFlags.NoHoisting
)
);
const part = createVariableDeclarationList(map(currentState.loopOutParameters, createOutVariable));
return { functionName, containsYield, functionDeclaration, part };
}
/**
* Creates a `_loop` function for an `IterationStatement` with a block-scoped initializer
* that is captured in a closure inside of the loop body. The `_loop` function is used to
* preserve the per-iteration environment semantics of
* [13.7.4.8 RS: ForBodyEvaluation](https://tc39.github.io/ecma262/#sec-forbodyevaluation).
*/
function createFunctionForBodyOfIterationStatement(node: IterationStatement, currentState: ConvertedLoopState, outerState: ConvertedLoopState | undefined): IterationStatementPartFunction<Statement[]> {
const functionName = createUniqueName("_loop");
startLexicalEnvironment();
let loopBody = visitNode(node.statement, visitor, isStatement, liftToBlock);
const statement = visitNode(node.statement, visitor, isStatement, liftToBlock);
const lexicalEnvironment = endLexicalEnvironment();
const currentState = convertedLoopState;
convertedLoopState = outerConvertedLoopState;
const statements: Statement[] = [];
if (shouldConvertConditionOfForStatement(node) || shouldConvertIncrementorOfForStatement(node)) {
// If a block-scoped variable declared in the initializer of `node` is captured in
// the condition or incrementor, we must move the condition and incrementor into
// the body of the for loop.
//
// This transforms the following ES2015 syntax:
//
// for (let i = 0; setImmediate(() => console.log(i)), i < 2; setImmediate(() => console.log(i)), i++) {
// // loop body
// }
//
// Into the following ES5 syntax:
//
// var _loop_1 = function (i) {
// if (inc_1)
// setImmediate(() => console.log(i)), i++;
// else
// inc_1 = true;
// if (!(setImmediate(() => console.log(i)), i < 2))
// return out_i_1 = i, "break";
// // loop body
// out_i_1 = i;
// }
// var out_i_1, inc_1 = false;
// for (var i = 0;;) {
// var state_1 = _loop_1(i);
// i = out_i_1;
// if (state_1 === "break")
// break;
// }
//
// Which prevents mutations to `i` in the per-iteration environment of the body
// from affecting the value of `i` in the previous per-iteration environment.
//
// Note that the incrementor of a `for` loop is evaluated in a *new* per-iteration
// environment that is carried over to the next iteration of the loop. As a result,
// we must indicate whether this is the first evaluation of the loop body so that
// we only evaluate the incrementor on subsequent evaluations.
if (loopOutParameters.length || lexicalEnvironment) {
const statements = isBlock(loopBody) ? loopBody.statements.slice() : [loopBody];
if (loopOutParameters.length) {
copyOutParameters(loopOutParameters, CopyDirection.ToOutParameter, statements);
currentState.conditionVariable = createUniqueName("inc");
statements.push(createIf(
currentState.conditionVariable,
createStatement(visitNode(node.incrementor, visitor, isExpression)),
createStatement(createAssignment(currentState.conditionVariable, createTrue()))
));
if (shouldConvertConditionOfForStatement(node)) {
statements.push(createIf(
createPrefix(SyntaxKind.ExclamationToken, visitNode(node.condition, visitor, isExpression)),
visitNode(createBreak(), visitor, isStatement)
));
}
addStatementsAfterPrologue(statements, lexicalEnvironment);
loopBody = createBlock(statements, /*multiline*/ true);
}
if (isBlock(loopBody)) {
loopBody.multiLine = true;
if (isBlock(statement)) {
addRange(statements, statement.statements);
}
else {
loopBody = createBlock([loopBody], /*multiline*/ true);
statements.push(statement);
}
copyOutParameters(currentState.loopOutParameters, LoopOutParameterFlags.Body, CopyDirection.ToOutParameter, statements);
addStatementsAfterPrologue(statements, lexicalEnvironment);
const loopBody = createBlock(statements, /*multiLine*/ true);
if (isBlock(statement)) setOriginalNode(loopBody, statement);
const containsYield = (node.statement.transformFlags & TransformFlags.ContainsYield) !== 0;
const isAsyncBlockContainingAwait = containsYield && (hierarchyFacts & HierarchyFacts.AsyncFunctionBody) !== 0;
let loopBodyFlags: EmitFlags = 0;
if (currentState.containsLexicalThis) {
loopBodyFlags |= EmitFlags.CapturesThis;
}
let emitFlags: EmitFlags = 0;
if (currentState.containsLexicalThis) emitFlags |= EmitFlags.CapturesThis;
if (containsYield && (hierarchyFacts & HierarchyFacts.AsyncFunctionBody) !== 0) emitFlags |= EmitFlags.AsyncFunctionBody;
if (isAsyncBlockContainingAwait) {
loopBodyFlags |= EmitFlags.AsyncFunctionBody;
}
// This transforms the following ES2015 syntax (in addition to other variations):
//
// for (let i = 0; i < 2; i++) {
// setImmediate(() => console.log(i));
// }
//
// Into the following ES5 syntax:
//
// var _loop_1 = function (i) {
// setImmediate(() => console.log(i));
// };
// for (var i = 0; i < 2; i++) {
// _loop_1(i);
// }
const convertedLoopVariable =
const functionDeclaration =
createVariableStatement(
/*modifiers*/ undefined,
setEmitFlags(
@ -2752,11 +3127,11 @@ namespace ts {
containsYield ? createToken(SyntaxKind.AsteriskToken) : undefined,
/*name*/ undefined,
/*typeParameters*/ undefined,
loopParameters,
currentState.loopParameters,
/*type*/ undefined,
<Block>loopBody
loopBody
),
loopBodyFlags
emitFlags
)
)
]
@ -2765,106 +3140,8 @@ namespace ts {
)
);
const statements: Statement[] = [convertedLoopVariable];
let extraVariableDeclarations: VariableDeclaration[] | undefined;
// propagate state from the inner loop to the outer loop if necessary
if (currentState.argumentsName) {
// if alias for arguments is set
if (outerConvertedLoopState) {
// pass it to outer converted loop
outerConvertedLoopState.argumentsName = currentState.argumentsName;
}
else {
// this is top level converted loop and we need to create an alias for 'arguments' object
(extraVariableDeclarations || (extraVariableDeclarations = [])).push(
createVariableDeclaration(
currentState.argumentsName,
/*type*/ undefined,
createIdentifier("arguments")
)
);
}
}
if (currentState.thisName) {
// if alias for this is set
if (outerConvertedLoopState) {
// pass it to outer converted loop
outerConvertedLoopState.thisName = currentState.thisName;
}
else {
// this is top level converted loop so we need to create an alias for 'this' here
// NOTE:
// if converted loops were all nested in arrow function then we'll always emit '_this' so convertedLoopState.thisName will not be set.
// If it is set this means that all nested loops are not nested in arrow function and it is safe to capture 'this'.
(extraVariableDeclarations || (extraVariableDeclarations = [])).push(
createVariableDeclaration(
currentState.thisName,
/*type*/ undefined,
createIdentifier("this")
)
);
}
}
if (currentState.hoistedLocalVariables) {
// if hoistedLocalVariables !== undefined this means that we've possibly collected some variable declarations to be hoisted later
if (outerConvertedLoopState) {
// pass them to outer converted loop
outerConvertedLoopState.hoistedLocalVariables = currentState.hoistedLocalVariables;
}
else {
if (!extraVariableDeclarations) {
extraVariableDeclarations = [];
}
// hoist collected variable declarations
for (const identifier of currentState.hoistedLocalVariables) {
extraVariableDeclarations.push(createVariableDeclaration(identifier));
}
}
}
// add extra variables to hold out parameters if necessary
if (loopOutParameters.length) {
if (!extraVariableDeclarations) {
extraVariableDeclarations = [];
}
for (const outParam of loopOutParameters) {
extraVariableDeclarations.push(createVariableDeclaration(outParam.outParamName));
}
}
// create variable statement to hold all introduced variable declarations
if (extraVariableDeclarations) {
statements.push(createVariableStatement(
/*modifiers*/ undefined,
createVariableDeclarationList(extraVariableDeclarations)
));
}
const convertedLoopBodyStatements = generateCallToConvertedLoop(functionName, loopParameters, currentState, containsYield);
let loop: Statement;
if (convert) {
loop = convert(node, outermostLabeledStatement, convertedLoopBodyStatements);
}
else {
let clone = getMutableClone(node);
// clean statement part
clone.statement = undefined!;
// visit childnodes to transform initializer/condition/incrementor parts
clone = visitEachChild(clone, visitor, context);
// set loop statement
clone.statement = createBlock(convertedLoopBodyStatements, /*multiline*/ true);
// reset and re-aggregate the transform flags
clone.transformFlags = 0;
aggregateTransformFlags(clone);
loop = restoreEnclosingLabel(clone, outermostLabeledStatement, convertedLoopState && resetLabel);
}
statements.push(loop);
return statements;
const part = generateCallToConvertedLoop(functionName, currentState, outerState, containsYield);
return { functionName, containsYield, functionDeclaration, part };
}
function copyOutParameter(outParam: LoopOutParameter, copyDirection: CopyDirection): BinaryExpression {
@ -2873,14 +3150,26 @@ namespace ts {
return createBinary(target, SyntaxKind.EqualsToken, source);
}
function copyOutParameters(outParams: LoopOutParameter[], copyDirection: CopyDirection, statements: Statement[]): void {
function copyOutParameters(outParams: LoopOutParameter[], partFlags: LoopOutParameterFlags, copyDirection: CopyDirection, statements: Statement[]): void {
for (const outParam of outParams) {
statements.push(createExpressionStatement(copyOutParameter(outParam, copyDirection)));
if (outParam.flags & partFlags) {
statements.push(createExpressionStatement(copyOutParameter(outParam, copyDirection)));
}
}
}
function generateCallToConvertedLoop(loopFunctionExpressionName: Identifier, parameters: ParameterDeclaration[], state: ConvertedLoopState, isAsyncBlockContainingAwait: boolean): Statement[] {
const outerConvertedLoopState = convertedLoopState;
function generateCallToConvertedLoopInitializer(initFunctionExpressionName: Identifier, containsYield: boolean): Statement {
const call = createCall(initFunctionExpressionName, /*typeArguments*/ undefined, []);
const callResult = containsYield
? createYield(
createToken(SyntaxKind.AsteriskToken),
setEmitFlags(call, EmitFlags.Iterator)
)
: call;
return createStatement(callResult);
}
function generateCallToConvertedLoop(loopFunctionExpressionName: Identifier, state: ConvertedLoopState, outerState: ConvertedLoopState | undefined, containsYield: boolean): Statement[] {
const statements: Statement[] = [];
// loop is considered simple if it does not have any return statements or break\continue that transfer control outside of the loop
@ -2891,8 +3180,8 @@ namespace ts {
!state.labeledNonLocalBreaks &&
!state.labeledNonLocalContinues;
const call = createCall(loopFunctionExpressionName, /*typeArguments*/ undefined, map(parameters, p => <Identifier>p.name));
const callResult = isAsyncBlockContainingAwait
const call = createCall(loopFunctionExpressionName, /*typeArguments*/ undefined, map(state.loopParameters, p => <Identifier>p.name));
const callResult = containsYield
? createYield(
createToken(SyntaxKind.AsteriskToken),
setEmitFlags(call, EmitFlags.Iterator)
@ -2900,7 +3189,7 @@ namespace ts {
: call;
if (isSimpleLoop) {
statements.push(createExpressionStatement(callResult));
copyOutParameters(state.loopOutParameters!, CopyDirection.ToOriginal, statements);
copyOutParameters(state.loopOutParameters, LoopOutParameterFlags.Body, CopyDirection.ToOriginal, statements);
}
else {
const loopResultName = createUniqueName("state");
@ -2911,12 +3200,12 @@ namespace ts {
)
);
statements.push(stateVariable);
copyOutParameters(state.loopOutParameters!, CopyDirection.ToOriginal, statements);
copyOutParameters(state.loopOutParameters, LoopOutParameterFlags.Body, CopyDirection.ToOriginal, statements);
if (state.nonLocalJumps! & Jump.Return) {
let returnStatement: ReturnStatement;
if (outerConvertedLoopState) {
outerConvertedLoopState.nonLocalJumps! |= Jump.Return;
if (outerState) {
outerState.nonLocalJumps! |= Jump.Return;
returnStatement = createReturn(loopResultName);
}
else {
@ -2949,8 +3238,8 @@ namespace ts {
if (state.labeledNonLocalBreaks || state.labeledNonLocalContinues) {
const caseClauses: CaseClause[] = [];
processLabeledJumps(state.labeledNonLocalBreaks!, /*isBreak*/ true, loopResultName, outerConvertedLoopState, caseClauses);
processLabeledJumps(state.labeledNonLocalContinues!, /*isBreak*/ false, loopResultName, outerConvertedLoopState, caseClauses);
processLabeledJumps(state.labeledNonLocalBreaks!, /*isBreak*/ true, loopResultName, outerState, caseClauses);
processLabeledJumps(state.labeledNonLocalContinues!, /*isBreak*/ false, loopResultName, outerState, caseClauses);
statements.push(
createSwitch(
loopResultName,
@ -2998,20 +3287,28 @@ namespace ts {
});
}
function processLoopVariableDeclaration(decl: VariableDeclaration | BindingElement, loopParameters: ParameterDeclaration[], loopOutParameters: LoopOutParameter[]) {
function processLoopVariableDeclaration(container: IterationStatement, decl: VariableDeclaration | BindingElement, loopParameters: ParameterDeclaration[], loopOutParameters: LoopOutParameter[], hasCapturedBindingsInForInitializer: boolean) {
const name = decl.name;
if (isBindingPattern(name)) {
for (const element of name.elements) {
if (!isOmittedExpression(element)) {
processLoopVariableDeclaration(element, loopParameters, loopOutParameters);
processLoopVariableDeclaration(container, element, loopParameters, loopOutParameters, hasCapturedBindingsInForInitializer);
}
}
}
else {
loopParameters.push(createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, name));
if (resolver.getNodeCheckFlags(decl) & NodeCheckFlags.NeedsLoopOutParameter) {
const checkFlags = resolver.getNodeCheckFlags(decl);
if (checkFlags & NodeCheckFlags.NeedsLoopOutParameter || hasCapturedBindingsInForInitializer) {
const outParamName = createUniqueName("out_" + idText(name));
loopOutParameters.push({ originalName: name, outParamName });
let flags: LoopOutParameterFlags = 0;
if (checkFlags & NodeCheckFlags.NeedsLoopOutParameter) {
flags |= LoopOutParameterFlags.Body;
}
if (isForStatement(container) && container.initializer && resolver.isBindingCapturedByNode(container.initializer, decl)) {
flags |= LoopOutParameterFlags.Initializer;
}
loopOutParameters.push({ flags, originalName: name, outParamName });
}
}
}

View file

@ -3420,6 +3420,7 @@ namespace ts {
getJsxFactoryEntity(location?: Node): EntityName | undefined;
getAllAccessorDeclarations(declaration: AccessorDeclaration): AllAccessorDeclarations;
getSymbolOfExternalModuleSpecifier(node: StringLiteralLike): Symbol | undefined;
isBindingCapturedByNode(node: Node, decl: VariableDeclaration | BindingElement): boolean;
}
export const enum SymbolFlags {
@ -3668,14 +3669,15 @@ namespace ts {
EnumValuesComputed = 0x00004000, // Values for enum members have been computed, and any errors have been reported for them.
LexicalModuleMergesWithClass = 0x00008000, // Instantiated lexical module declaration is merged with a previous class declaration.
LoopWithCapturedBlockScopedBinding = 0x00010000, // Loop that contains block scoped variable captured in closure
CapturedBlockScopedBinding = 0x00020000, // Block-scoped binding that is captured in some function
BlockScopedBindingInLoop = 0x00040000, // Block-scoped binding with declaration nested inside iteration statement
ClassWithBodyScopedClassBinding = 0x00080000, // Decorated class that contains a binding to itself inside of the class body.
BodyScopedClassBinding = 0x00100000, // Binding to a decorated class inside of the class's body.
NeedsLoopOutParameter = 0x00200000, // Block scoped binding whose value should be explicitly copied outside of the converted loop
AssignmentsMarked = 0x00400000, // Parameter assignments have been marked
ClassWithConstructorReference = 0x00800000, // Class that contains a binding to its constructor inside of the class body.
ConstructorReferenceInClass = 0x01000000, // Binding to a class constructor inside of the class's body.
ContainsCapturedBlockScopeBinding = 0x00020000, // Part of a loop that contains block scoped variable captured in closure
CapturedBlockScopedBinding = 0x00040000, // Block-scoped binding that is captured in some function
BlockScopedBindingInLoop = 0x00080000, // Block-scoped binding with declaration nested inside iteration statement
ClassWithBodyScopedClassBinding = 0x00100000, // Decorated class that contains a binding to itself inside of the class body.
BodyScopedClassBinding = 0x00200000, // Binding to a decorated class inside of the class's body.
NeedsLoopOutParameter = 0x00400000, // Block scoped binding whose value should be explicitly copied outside of the converted loop
AssignmentsMarked = 0x00800000, // Parameter assignments have been marked
ClassWithConstructorReference = 0x01000000, // Class that contains a binding to its constructor inside of the class body.
ConstructorReferenceInClass = 0x02000000, // Binding to a class constructor inside of the class's body.
}
/* @internal */
@ -3700,6 +3702,7 @@ namespace ts {
switchTypes?: Type[]; // Cached array of switch case expression types
jsxNamespace?: Symbol | false; // Resolved jsx namespace symbol for this node
contextFreeType?: Type; // Cached context-free type used by the first pass of inference; used when a function's return is partially contextually sensitive
capturedBlockScopeBindings?: Symbol[]; // Block-scoped bindings captured beneath this part of an IterationStatement
}
export const enum TypeFlags {

View file

@ -1,4 +1,6 @@
//// [capturedLetConstInLoop1.ts]
declare function use(x: any): any;
//==== let
for (let x in {}) {
(function() { return x});
@ -56,6 +58,19 @@ for (let y = 0; y < 1; ++y) {
(() => x + y);
}
for (let y = (use(() => y), 0); y < 1; ++y) {
}
for (let y = 0; use(() => y), y < 1; ++y) {
}
for (let y = 0; y < 1; use(() => y), ++y) {
}
for (let y = (use(() => y), 0); use(() => y), y < 1; use(() => y), ++y) {
use(() => y);
}
//=========const
for (const x in {}) {
(function() { return x});
@ -202,93 +217,145 @@ var _loop_10 = function (y) {
for (var y = 0; y < 1; ++y) {
_loop_10(y);
}
var _loop_11 = function (x) {
var _loop_init_1 = function () {
var y = (use(function () { return y; }), 0);
out_y_1 = y;
};
var out_y_1;
_loop_init_1();
for (var y = out_y_1; y < 1; ++y) {
}
var _loop_11 = function (y) {
if (inc_1)
++y;
else
inc_1 = true;
if (!(use(function () { return y; }), y < 1))
return "break";
};
var inc_1 = false;
for (var y = 0;;) {
var state_1 = _loop_11(y);
if (state_1 === "break")
break;
}
var _loop_12 = function (y) {
if (inc_2)
use(function () { return y; }), ++y;
else
inc_2 = true;
};
var inc_2 = false;
for (var y = 0; y < 1;) {
_loop_12(y);
}
var _loop_init_2 = function () {
var y = (use(function () { return y; }), 0);
out_y_2 = y;
};
var _loop_13 = function (y) {
if (inc_3)
use(function () { return y; }), ++y;
else
inc_3 = true;
if (!(use(function () { return y; }), y < 1))
return out_y_2 = y, "break";
use(function () { return y; });
};
var out_y_2, inc_3 = false;
_loop_init_2();
for (var y = out_y_2;;) {
var state_2 = _loop_13(y);
if (state_2 === "break")
break;
}
var _loop_14 = function (x) {
(function () { return x; });
(function () { return x; });
};
//=========const
for (var x in {}) {
_loop_11(x);
_loop_14(x);
}
var _loop_12 = function (x) {
var _loop_15 = function (x) {
(function () { return x; });
(function () { return x; });
};
for (var _b = 0, _c = []; _b < _c.length; _b++) {
var x = _c[_b];
_loop_12(x);
_loop_15(x);
}
var _loop_13 = function (x) {
var _loop_16 = function (x) {
(function () { return x; });
(function () { return x; });
};
for (var x = 0; x < 1;) {
_loop_13(x);
_loop_16(x);
}
var _loop_14 = function () {
var _loop_17 = function () {
var x = 1;
(function () { return x; });
(function () { return x; });
};
while (1 === 1) {
_loop_14();
_loop_17();
}
var _loop_15 = function () {
var _loop_18 = function () {
var x = 1;
(function () { return x; });
(function () { return x; });
};
do {
_loop_15();
_loop_18();
} while (1 === 1);
var _loop_16 = function (y) {
var _loop_19 = function (y) {
var x = 1;
(function () { return x; });
(function () { return x; });
};
for (var y = 0; y < 1;) {
_loop_16(y);
_loop_19(y);
}
var _loop_17 = function (x, y) {
var _loop_20 = function (x, y) {
(function () { return x + y; });
(function () { return x + y; });
};
for (var x = 0, y = 1; x < 1;) {
_loop_17(x, y);
_loop_20(x, y);
}
var _loop_18 = function () {
var _loop_21 = function () {
var x = 1, y = 1;
(function () { return x + y; });
(function () { return x + y; });
};
while (1 === 1) {
_loop_18();
_loop_21();
}
var _loop_19 = function () {
var _loop_22 = function () {
var x = 1, y = 1;
(function () { return x + y; });
(function () { return x + y; });
};
do {
_loop_19();
_loop_22();
} while (1 === 1);
var _loop_20 = function (y) {
var _loop_23 = function (y) {
var x = 1;
(function () { return x + y; });
(function () { return x + y; });
};
for (var y = 0; y < 1;) {
_loop_20(y);
_loop_23(y);
}
var _loop_21 = function (sx) {
var _loop_24 = function (sx) {
(function () { return sobj[sx]; });
};
for (var sx in sobj) {
_loop_21(sx);
_loop_24(sx);
}
var _loop_22 = function (ix) {
var _loop_25 = function (ix) {
(function () { return iobj[ix]; });
};
for (var ix in iobj) {
_loop_22(ix);
_loop_25(ix);
}

View file

@ -1,286 +1,330 @@
=== tests/cases/compiler/capturedLetConstInLoop1.ts ===
declare function use(x: any): any;
>use : Symbol(use, Decl(capturedLetConstInLoop1.ts, 0, 0))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 0, 21))
//==== let
for (let x in {}) {
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 1, 8))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 3, 8))
(function() { return x});
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 1, 8))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 3, 8))
(() => x);
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 1, 8))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 3, 8))
}
for (let x of []) {
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 6, 8))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 8, 8))
(function() { return x});
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 6, 8))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 8, 8))
(() => x);
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 6, 8))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 8, 8))
}
for (let x = 0; x < 1; ++x) {
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 11, 8))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 11, 8))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 11, 8))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 13, 8))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 13, 8))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 13, 8))
(function() { return x});
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 11, 8))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 13, 8))
(() => x);
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 11, 8))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 13, 8))
}
while (1 === 1) {
let x;
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 17, 7))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 19, 7))
(function() { return x});
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 17, 7))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 19, 7))
(() => x);
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 17, 7))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 19, 7))
}
do {
let x;
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 23, 7))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 25, 7))
(function() { return x});
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 23, 7))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 25, 7))
(() => x);
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 23, 7))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 25, 7))
} while (1 === 1)
for (let y = 0; y < 1; ++y) {
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 28, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 28, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 28, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 30, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 30, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 30, 8))
let x = 1;
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 29, 7))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 31, 7))
(function() { return x});
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 29, 7))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 31, 7))
(() => x);
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 29, 7))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 31, 7))
}
for (let x = 0, y = 1; x < 1; ++x) {
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 34, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 34, 15))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 34, 8))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 34, 8))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 36, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 36, 15))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 36, 8))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 36, 8))
(function() { return x + y});
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 34, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 34, 15))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 36, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 36, 15))
(() => x + y);
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 34, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 34, 15))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 36, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 36, 15))
}
while (1 === 1) {
let x, y;
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 40, 7))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 40, 10))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 42, 7))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 42, 10))
(function() { return x + y});
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 40, 7))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 40, 10))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 42, 7))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 42, 10))
(() => x + y);
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 40, 7))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 40, 10))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 42, 7))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 42, 10))
}
do {
let x, y;
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 46, 7))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 46, 10))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 48, 7))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 48, 10))
(function() { return x + y});
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 46, 7))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 46, 10))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 48, 7))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 48, 10))
(() => x + y);
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 46, 7))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 46, 10))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 48, 7))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 48, 10))
} while (1 === 1)
for (let y = 0; y < 1; ++y) {
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 51, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 51, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 51, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 53, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 53, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 53, 8))
let x = 1;
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 52, 7))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 54, 7))
(function() { return x + y});
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 52, 7))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 51, 8))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 54, 7))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 53, 8))
(() => x + y);
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 52, 7))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 51, 8))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 54, 7))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 53, 8))
}
for (let y = (use(() => y), 0); y < 1; ++y) {
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 59, 8))
>use : Symbol(use, Decl(capturedLetConstInLoop1.ts, 0, 0))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 59, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 59, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 59, 8))
}
for (let y = 0; use(() => y), y < 1; ++y) {
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 62, 8))
>use : Symbol(use, Decl(capturedLetConstInLoop1.ts, 0, 0))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 62, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 62, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 62, 8))
}
for (let y = 0; y < 1; use(() => y), ++y) {
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 65, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 65, 8))
>use : Symbol(use, Decl(capturedLetConstInLoop1.ts, 0, 0))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 65, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 65, 8))
}
for (let y = (use(() => y), 0); use(() => y), y < 1; use(() => y), ++y) {
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 68, 8))
>use : Symbol(use, Decl(capturedLetConstInLoop1.ts, 0, 0))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 68, 8))
>use : Symbol(use, Decl(capturedLetConstInLoop1.ts, 0, 0))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 68, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 68, 8))
>use : Symbol(use, Decl(capturedLetConstInLoop1.ts, 0, 0))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 68, 8))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 68, 8))
use(() => y);
>use : Symbol(use, Decl(capturedLetConstInLoop1.ts, 0, 0))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 68, 8))
}
//=========const
for (const x in {}) {
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 58, 10))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 73, 10))
(function() { return x});
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 58, 10))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 73, 10))
(() => x);
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 58, 10))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 73, 10))
}
for (const x of []) {
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 63, 10))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 78, 10))
(function() { return x});
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 63, 10))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 78, 10))
(() => x);
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 63, 10))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 78, 10))
}
for (const x = 0; x < 1;) {
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 68, 10))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 68, 10))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 83, 10))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 83, 10))
(function() { return x});
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 68, 10))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 83, 10))
(() => x);
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 68, 10))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 83, 10))
}
while (1 === 1) {
const x = 1;
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 74, 9))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 89, 9))
(function() { return x});
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 74, 9))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 89, 9))
(() => x);
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 74, 9))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 89, 9))
}
do {
const x = 1;
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 80, 9))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 95, 9))
(function() { return x});
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 80, 9))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 95, 9))
(() => x);
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 80, 9))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 95, 9))
} while (1 === 1)
for (const y = 0; y < 1;) {
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 85, 10))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 85, 10))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 100, 10))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 100, 10))
const x = 1;
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 86, 9))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 101, 9))
(function() { return x});
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 86, 9))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 101, 9))
(() => x);
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 86, 9))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 101, 9))
}
for (const x = 0, y = 1; x < 1;) {
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 91, 10))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 91, 17))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 91, 10))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 106, 10))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 106, 17))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 106, 10))
(function() { return x + y});
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 91, 10))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 91, 17))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 106, 10))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 106, 17))
(() => x + y);
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 91, 10))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 91, 17))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 106, 10))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 106, 17))
}
while (1 === 1) {
const x = 1, y = 1;
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 97, 9))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 97, 16))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 112, 9))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 112, 16))
(function() { return x + y});
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 97, 9))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 97, 16))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 112, 9))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 112, 16))
(() => x + y);
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 97, 9))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 97, 16))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 112, 9))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 112, 16))
}
do {
const x = 1, y = 1;
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 103, 9))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 103, 16))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 118, 9))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 118, 16))
(function() { return x + y});
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 103, 9))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 103, 16))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 118, 9))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 118, 16))
(() => x + y);
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 103, 9))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 103, 16))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 118, 9))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 118, 16))
} while (1 === 1)
for (const y = 0; y < 1;) {
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 108, 10))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 108, 10))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 123, 10))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 123, 10))
const x = 1;
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 109, 9))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 124, 9))
(function() { return x + y});
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 109, 9))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 108, 10))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 124, 9))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 123, 10))
(() => x + y);
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 109, 9))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 108, 10))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 124, 9))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 123, 10))
}
// https://github.com/Microsoft/TypeScript/issues/20594
declare const sobj: { [x: string]: any };
>sobj : Symbol(sobj, Decl(capturedLetConstInLoop1.ts, 115, 13))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 115, 23))
>sobj : Symbol(sobj, Decl(capturedLetConstInLoop1.ts, 130, 13))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 130, 23))
for (let sx in sobj) {
>sx : Symbol(sx, Decl(capturedLetConstInLoop1.ts, 116, 8))
>sobj : Symbol(sobj, Decl(capturedLetConstInLoop1.ts, 115, 13))
>sx : Symbol(sx, Decl(capturedLetConstInLoop1.ts, 131, 8))
>sobj : Symbol(sobj, Decl(capturedLetConstInLoop1.ts, 130, 13))
(() => sobj[sx]);
>sobj : Symbol(sobj, Decl(capturedLetConstInLoop1.ts, 115, 13))
>sx : Symbol(sx, Decl(capturedLetConstInLoop1.ts, 116, 8))
>sobj : Symbol(sobj, Decl(capturedLetConstInLoop1.ts, 130, 13))
>sx : Symbol(sx, Decl(capturedLetConstInLoop1.ts, 131, 8))
}
declare const iobj: { [x: number]: any };
>iobj : Symbol(iobj, Decl(capturedLetConstInLoop1.ts, 119, 13))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 119, 23))
>iobj : Symbol(iobj, Decl(capturedLetConstInLoop1.ts, 134, 13))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 134, 23))
for (let ix in iobj) {
>ix : Symbol(ix, Decl(capturedLetConstInLoop1.ts, 120, 8))
>iobj : Symbol(iobj, Decl(capturedLetConstInLoop1.ts, 119, 13))
>ix : Symbol(ix, Decl(capturedLetConstInLoop1.ts, 135, 8))
>iobj : Symbol(iobj, Decl(capturedLetConstInLoop1.ts, 134, 13))
(() => iobj[ix]);
>iobj : Symbol(iobj, Decl(capturedLetConstInLoop1.ts, 119, 13))
>ix : Symbol(ix, Decl(capturedLetConstInLoop1.ts, 120, 8))
>iobj : Symbol(iobj, Decl(capturedLetConstInLoop1.ts, 134, 13))
>ix : Symbol(ix, Decl(capturedLetConstInLoop1.ts, 135, 8))
}

View file

@ -1,4 +1,8 @@
=== tests/cases/compiler/capturedLetConstInLoop1.ts ===
declare function use(x: any): any;
>use : (x: any) => any
>x : any
//==== let
for (let x in {}) {
>x : string
@ -214,6 +218,84 @@ for (let y = 0; y < 1; ++y) {
>y : number
}
for (let y = (use(() => y), 0); y < 1; ++y) {
>y : number
>(use(() => y), 0) : 0
>use(() => y), 0 : 0
>use(() => y) : any
>use : (x: any) => any
>() => y : () => number
>y : number
>0 : 0
>y < 1 : boolean
>y : number
>1 : 1
>++y : number
>y : number
}
for (let y = 0; use(() => y), y < 1; ++y) {
>y : number
>0 : 0
>use(() => y), y < 1 : boolean
>use(() => y) : any
>use : (x: any) => any
>() => y : () => number
>y : number
>y < 1 : boolean
>y : number
>1 : 1
>++y : number
>y : number
}
for (let y = 0; y < 1; use(() => y), ++y) {
>y : number
>0 : 0
>y < 1 : boolean
>y : number
>1 : 1
>use(() => y), ++y : number
>use(() => y) : any
>use : (x: any) => any
>() => y : () => number
>y : number
>++y : number
>y : number
}
for (let y = (use(() => y), 0); use(() => y), y < 1; use(() => y), ++y) {
>y : number
>(use(() => y), 0) : 0
>use(() => y), 0 : 0
>use(() => y) : any
>use : (x: any) => any
>() => y : () => number
>y : number
>0 : 0
>use(() => y), y < 1 : boolean
>use(() => y) : any
>use : (x: any) => any
>() => y : () => number
>y : number
>y < 1 : boolean
>y : number
>1 : 1
>use(() => y), ++y : number
>use(() => y) : any
>use : (x: any) => any
>() => y : () => number
>y : number
>++y : number
>y : number
use(() => y);
>use(() => y) : any
>use : (x: any) => any
>() => y : () => number
>y : number
}
//=========const
for (const x in {}) {
>x : string

View file

@ -1,3 +1,5 @@
declare function use(x: any): any;
//==== let
for (let x in {}) {
(function() { return x});
@ -55,6 +57,19 @@ for (let y = 0; y < 1; ++y) {
(() => x + y);
}
for (let y = (use(() => y), 0); y < 1; ++y) {
}
for (let y = 0; use(() => y), y < 1; ++y) {
}
for (let y = 0; y < 1; use(() => y), ++y) {
}
for (let y = (use(() => y), 0); use(() => y), y < 1; use(() => y), ++y) {
use(() => y);
}
//=========const
for (const x in {}) {
(function() { return x});