Emit of rest parameter for loop uses unique temporary variable name

This commit is contained in:
Anders Hejlsberg 2014-11-28 18:25:27 -08:00
parent cf3e3ac6b8
commit 28a73bc936
2 changed files with 14 additions and 84 deletions

View File

@ -1714,7 +1714,7 @@ module ts {
function getTypeFromBindingPattern(pattern: BindingPattern): Type {
if (pattern.kind === SyntaxKind.ArrayBindingPattern) {
return createTupleType(map(pattern.elements, getTypeFromBindingElement));
return createTupleType(map(pattern.elements, e => e.kind === SyntaxKind.OmittedExpression ? anyType : getTypeFromBindingElement(e)));
}
var members: SymbolTable = {};
forEach(pattern.elements, e => {
@ -4628,7 +4628,6 @@ module ts {
checkCollisionWithCapturedSuperVariable(node, node);
checkCollisionWithCapturedThisVariable(node, node);
checkCollisionWithIndexVariableInGeneratedCode(node, node);
return getNarrowedTypeOfSymbol(getExportSymbolOfValueSymbolIfExported(symbol), node);
}
@ -6759,7 +6758,6 @@ module ts {
if (isBindingPattern(node.name)) {
return;
}
checkCollisionWithIndexVariableInGeneratedCode(node, <Identifier>node.name);
var func = getContainingFunction(node);
if (node.flags & (NodeFlags.Public | NodeFlags.Private | NodeFlags.Protected) &&
!(func.kind === SyntaxKind.Constructor && func.body)) {
@ -7418,85 +7416,6 @@ module ts {
});
}
function checkCollisionWithIndexVariableInGeneratedCode(node: Node, name: Identifier) {
if (!(name && name.text === "_i")) {
return;
}
if (node.kind === SyntaxKind.Parameter) {
// report error if parameter has name '_i' when:
// - function has implementation (not a signature)
// - function has rest parameters
// - context is not ambient (otherwise no codegen impact)
var func = getContainingFunction(node);
if (func.body && hasRestParameters(func) && !isInAmbientContext(node)) {
error(node, Diagnostics.Duplicate_identifier_i_Compiler_uses_i_to_initialize_rest_parameter);
}
return;
}
var symbol = getNodeLinks(node).resolvedSymbol;
if (symbol === unknownSymbol) {
return;
}
// we would like to discover cases like one below:
//
// var _i = "!";
// function foo(...a) {
// function bar() {
// var x = { get baz() { return _i; } }
// }
// }
//
// at runtime '_i' referenced in getter will be resolved to the generated index variable '_i' used to initialize rest parameters.
// legitimate case: when '_i' is defined inside the function declaration with rest parameters.
//
// function foo(...a) {
// var _i = "!";
// function bar() {
// var x = { get baz() { return _i; } }
// }
// }
//// if resolved symbol for node has more than one declaration - this is definitely an error
//// (there is nothing value-like in the language that can be nested in function and consists of multiple declarations)
//if (symbol.declarations.length > 1) {
// error(node, Diagnostics.Expression_resolves_to_variable_declaration_i_that_compiler_uses_to_initialize_rest_parameter);
// return;
//}
// short gist of the check:
// - otherwise
// - walk to the top of the tree starting from the 'node'
// - at every step check if 'current' node contains any declaration of original node
// yes - return
// no - check if current declaration is function with rest parameters
// yes - report error since '_i' from this function will shadow '_i' defined in the outer scope
// no - go up to the next level
var current = node;
while (current) {
var definedOnCurrentLevel = forEach(symbol.declarations, d => getContainingFunction(d) === current);
if (definedOnCurrentLevel) {
return;
}
switch (current.kind) {
// all kinds that might have rest parameters
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.FunctionExpression:
case SyntaxKind.Method:
case SyntaxKind.ArrowFunction:
case SyntaxKind.Constructor:
if (hasRestParameters(<FunctionLikeDeclaration>current)) {
error(node, Diagnostics.Expression_resolves_to_variable_declaration_i_that_compiler_uses_to_initialize_rest_parameter);
return;
}
break;
}
current = current.parent;
}
}
// TODO(jfreeman): Decide what to do for computed properties
function needCollisionCheckForIdentifier(node: Node, identifier: DeclarationName, name: string): boolean {
if (!(identifier && (<Identifier>identifier).text === name)) {

View File

@ -1503,6 +1503,8 @@ module ts {
emitEnd(node.name);
}
// Rewrites a destructuring variable declaration into a sequence of regular variable declarations.
// Nodes generated by the syntactic rewrite have no parent references.
function rewriteDestructuringDeclaration(node: VariableDeclaration): NodeArray<VariableDeclaration> {
var declarations = <NodeArray<VariableDeclaration>>[];
rewriteDeclaration(node, undefined);
@ -1533,9 +1535,12 @@ module ts {
}
function createDefaultValueCheck(value: Expression, defaultValue: Expression): Expression {
// The value expression will be evaluated twice, so for anything but a simple identifier
// we need to generate a temporary variable
if (value.kind !== SyntaxKind.Identifier) {
value = createTemporaryVariable(value);
}
// Return the expression 'value === void 0 ? defaultValue : value'
var equals = <BinaryExpression>createNode(SyntaxKind.BinaryExpression);
equals.left = value;
equals.operator = SyntaxKind.EqualsEqualsEqualsToken;
@ -1581,24 +1586,30 @@ module ts {
function rewriteDeclaration(target: BindingElement, value: Expression) {
if (target.initializer) {
// Combine value and initializer
value = value ? createDefaultValueCheck(value, target.initializer) : target.initializer;
}
else if (!value) {
// Use 'void 0' in absence of value and initializer
value = createVoidZero();
}
if (isBindingPattern(target.name)) {
var pattern = <BindingPattern>target.name;
var elements = pattern.elements;
if (elements.length !== 1) {
// For anything but a single element destructuring we need to generate a temporary
// to ensure value is evaluated exactly once.
value = createTemporaryVariable(value);
}
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
if (pattern.kind === SyntaxKind.ObjectBindingPattern) {
// Rewrite element to a declaration with an initializer that fetches property
var propName = element.propertyName || <Identifier>element.name;
rewriteDeclaration(element, createPropertyAccess(value, propName));
}
else {
else if (element.kind !== SyntaxKind.OmittedExpression) {
// Rewrite element to a declaration that accesses array element at index i
rewriteDeclaration(element, createIndexedAccess(value, createNumericLiteral(i)));
}
}
@ -1690,7 +1701,7 @@ module ts {
if (hasRestParameters(node)) {
var restIndex = node.parameters.length - 1;
var restParam = node.parameters[restIndex];
var tempName = "_i";
var tempName = resolver.isUnknownIdentifier(node, "_i") ? "_i" : getTempVariableName(node);
writeLine();
emitLeadingComments(restParam);
emitStart(restParam);