diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b31bb6673da12..c7fb04a1a3c6b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6580,7 +6580,7 @@ namespace ts { while (current && !nodeStartsNewLexicalEnvironment(current)) { if (isIterationStatement(current, /*lookInLabeledStatements*/ false)) { if (inFunction) { - grammarErrorOnFirstToken(current, Diagnostics.Loop_contains_block_scoped_variable_0_referenced_by_a_function_in_the_loop_This_is_only_supported_in_ECMAScript_6_or_higher, declarationNameToString(node)); + getNodeLinks(current).flags |= NodeCheckFlags.LoopWithBlockScopedBindingCapturedInFunction; } // mark value declaration so during emit they can have a special handling getNodeLinks(symbol.valueDeclaration).flags |= NodeCheckFlags.BlockScopedBindingInLoop; @@ -14553,6 +14553,10 @@ namespace ts { // Emitter support + function isArgumentsLocalBinding(node: Identifier): boolean { + return getReferencedValueSymbol(node) === argumentsSymbol; + } + // When resolved as an expression identifier, if the given node references an exported entity, return the declaration // node of the exported entity's container. Otherwise, return undefined. function getReferencedExportContainer(node: Identifier): SourceFile | ModuleDeclaration | EnumDeclaration { @@ -14857,7 +14861,8 @@ namespace ts { collectLinkedAliases, getReferencedValueDeclaration, getTypeReferenceSerializationKind, - isOptionalParameter + isOptionalParameter, + isArgumentsLocalBinding }; } @@ -15684,21 +15689,6 @@ namespace ts { } } - function isIterationStatement(node: Node, lookInLabeledStatements: boolean): boolean { - switch (node.kind) { - case SyntaxKind.ForStatement: - case SyntaxKind.ForInStatement: - case SyntaxKind.ForOfStatement: - case SyntaxKind.DoStatement: - case SyntaxKind.WhileStatement: - return true; - case SyntaxKind.LabeledStatement: - return lookInLabeledStatements && isIterationStatement((node).statement, lookInLabeledStatements); - } - - return false; - } - function checkGrammarBreakOrContinueStatement(node: BreakOrContinueStatement): boolean { let current: Node = node; while (current) { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index b2c1e6f0d5ec0..00c82ab15b801 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2000,10 +2000,6 @@ "category": "Error", "code": 4082 }, - "Loop contains block-scoped variable '{0}' referenced by a function in the loop. This is only supported in ECMAScript 6 or higher.": { - "category": "Error", - "code": 4091 - }, "The current host does not support the '{0}' option.": { "category": "Error", "code": 5001 diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index f2468e7244d1b..85ccbf5d8db99 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -379,6 +379,105 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi return true; } + const enum Jump { + Break = 1 << 1, + Continue = 1 << 2, + Return = 1 << 3 + } + + interface ConvertedLoopState { + /* + * set of labels that occured inside the converted loop + * used to determine if labeled jump can be emitted as is or it should be dispatched to calling code + */ + labels?: Map; + /* + * collection of labeled jumps that transfer control outside the converted loop. + * maps store association 'label -> labelMarker' where + * - label - value of label as it apprear in code + * - label marker - return value that should be interpreted by calling code as 'jump to