Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions tslang/lib/TypeScript/MLIRGenFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include "MLIRGenImpl.h"

#include "llvm/ADT/ScopeExit.h"

namespace typescript
{
namespace mlirgen
Expand Down Expand Up @@ -1633,6 +1635,11 @@ namespace mlirgen
SymbolTableScopeT varScope(symbolTable);
BoundRefCacheScopeT boundRefCacheScope(boundRefMaterializedCache);

// the labels around a function are not the body's to jump to
auto outerLabels = std::move(activeLabels);
activeLabels.clear();
auto restoreLabels = llvm::make_scope_exit([&]() { activeLabels = std::move(outerLabels); });

auto location = loc(functionLikeDeclarationBaseAST);

// Debug Info
Expand Down
5 changes: 5 additions & 0 deletions tslang/lib/TypeScript/MLIRGenImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -14384,6 +14384,11 @@ class MLIRGenImpl

Stages stage;

// The labels of the statements around the code being generated, innermost last: a labeled
// `break` or `continue` has to name one of them. A function body starts with none: a jump
// can't leave a function.
SmallVector<std::string> activeLabels;

private:
std::string label;
mlir::Block* tempEntryBlock;
Expand Down
17 changes: 17 additions & 0 deletions tslang/lib/TypeScript/MLIRGenStatements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

#include "mlir/Dialect/Async/IR/Async.h"

#include "llvm/ADT/ScopeExit.h"

#undef DEBUG_TYPE
#define DEBUG_TYPE "mlir"

Expand Down Expand Up @@ -996,6 +998,9 @@ namespace mlirgen
kind == SyntaxKind::ForStatement || kind == SyntaxKind::ForInStatement ||
kind == SyntaxKind::ForOfStatement;

activeLabels.push_back(label);
auto popLabel = llvm::make_scope_exit([&]() { activeLabels.pop_back(); });

if (noLabelOp)
{
return mlirGen(labeledStatementAST->statement, genContext);
Expand Down Expand Up @@ -1029,6 +1034,13 @@ namespace mlirgen
auto location = loc(continueStatementAST);

auto label = MLIRHelper::getName(continueStatementAST->label);
// a `continue` to the label of a block (not a loop) starts the block again: an extension
// TypeScript doesn't have (tests/00break_continue.ts)
if (!label.empty() && !llvm::is_contained(activeLabels, label))
{
emitError(location, "A 'continue' statement can only jump to a label of an enclosing statement");
return mlir::failure();
}

EXIT_IF_FAILED(mlirGenScopeExit(location, DisposeDepth::LoopScope, label, &genContext));

Expand All @@ -1041,6 +1053,11 @@ namespace mlirgen
auto location = loc(breakStatementAST);

auto label = MLIRHelper::getName(breakStatementAST->label);
if (!label.empty() && !llvm::is_contained(activeLabels, label))
{
emitError(location, "A 'break' statement can only jump to a label of an enclosing statement");
return mlir::failure();
}

EXIT_IF_FAILED(mlirGenScopeExit(location, DisposeDepth::LoopScope, label, &genContext));

Expand Down
17 changes: 17 additions & 0 deletions tslang/test/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2698,6 +2698,23 @@ foreach(call_arity_case ${call_arity_cases})
FAIL_REGULAR_EXPRESSION "Stack dump|Assertion failed")
endforeach()

# A `continue` or `break` naming a label that no statement around it has is an error (a label
# outside the function doesn't count); it compiled to a jump with nowhere to go.
add_test(NAME test-compile-diagnostic-location-continue-label
COMMAND $<TARGET_FILE:tslang> --emit=obj --no-default-lib -mm=none
"${PROJECT_SOURCE_DIR}/test/tester/diagnostic-location/continue_label.ts"
-o "${CMAKE_CURRENT_BINARY_DIR}/diagnostic-location-continue-label.obj")
set_tests_properties(test-compile-diagnostic-location-continue-label
PROPERTIES PASS_REGULAR_EXPRESSION "continue_label\\.ts:7:13: error: A 'continue' statement can only jump to a label of an enclosing statement"
FAIL_REGULAR_EXPRESSION "Stack dump|Assertion failed|continue_label\\.ts:6:")
add_test(NAME test-compile-diagnostic-location-break-label
COMMAND $<TARGET_FILE:tslang> --emit=obj --no-default-lib -mm=none
"${PROJECT_SOURCE_DIR}/test/tester/diagnostic-location/break_label.ts"
-o "${CMAKE_CURRENT_BINARY_DIR}/diagnostic-location-break-label.obj")
set_tests_properties(test-compile-diagnostic-location-break-label
PROPERTIES PASS_REGULAR_EXPRESSION "break_label\\.ts:7:17: error: A 'break' statement can only jump to a label of an enclosing statement"
FAIL_REGULAR_EXPRESSION "Stack dump|Assertion failed|break_label\\.ts:10:")

# Storing a fraction into a variable whose type was inferred from an integer literal (`let s = 0`
# is an s32) drops it, and is warned about; the inference itself stays.
foreach(truncation_case "global|3:1" "local|5:9")
Expand Down
12 changes: 12 additions & 0 deletions tslang/test/tester/diagnostic-location/break_label.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// A continue or break naming a label no statement around it has compiled with no error.

function main() {
outer: for (let i = 0; i < 3; i++) {
const f = () => {
for (;;) {
break outer;
}
};
break outer;
}
}
10 changes: 10 additions & 0 deletions tslang/test/tester/diagnostic-location/continue_label.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// A continue or break naming a label no statement around it has compiled with no error.

function main() {
outer: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (j == 1) continue outer;
continue nolabel;
}
}
}
Loading