Commit 9cd8995f authored by Daniel Clifford's avatar Daniel Clifford Committed by Commit Bot

[Torque]: add assert(), unreachable and debug statements

- In debug builds, 'assert(<expr>)' evaluates and aborts execution
  if the provided Torque expression is false at runtime.
  assert(<expr>) supports the same set of expressions protocols
  as Toruqe's if statement, i.e. both bool values and BranchIf-
  style tests. Upon failure, the assertion prints the Torque
  source code of the failed expression, not the generated CSA
  code.
- 'unreachable' calls CSA's Unreachable() and signals to Torque
  that code execution cannot continue (i.e. its statement
  returns the 'never' type). In debug builds, the line number
  and position of the statement are printed before breaking.
- 'debug' calls CSA's DebugBreak(). In debug builds, the line
  number and position of the 'debug' are printed before breaking.

Change-Id: I4efd052536bb402c097a0d5f7be56e154b5b3676
Reviewed-on: https://chromium-review.googlesource.com/1042570
Commit-Queue: Daniel Clifford <danno@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52984}
parent 0f772171
......@@ -61,6 +61,9 @@ module array {
a.elements = newElements;
}
// Double check that the array is still in fast elements mode
assert(IsFastSmiElementsKind(a.map.elements_kind));
// Copy over inserted elements.
let k: Smi = actualStart;
if (insertCount > 0) {
......
......@@ -141,10 +141,6 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
// Returns true iff number is NaN.
// TOOD(szuend): Remove when UncheckedCasts are supported in Torque.
TNode<BoolT> NumberIsNaN(TNode<Number> number);
// Always CSA_ASSERTs false.
// TODO(szuend): Remove when Unreachable is supported in Torque.
void AssertUnreachable() { CSA_ASSERT(this, Int32FalseConstant()); }
};
} // namespace internal
......
......@@ -14,7 +14,6 @@ module typed_array {
extern builtin TypedArrayStoreElementFromTagged(
Context, JSTypedArray, Smi, Smi, Object);
extern macro AssertUnreachable();
extern macro NumberIsNaN(Number): bit;
macro CallCompareWithDetachedCheck(
......@@ -255,8 +254,7 @@ module typed_array {
TypedArrayQuickSort(context, array, elements_kind, 0, len, comparefn);
}
label CastError {
// TODO(szuend): Replace with Unreachable() when its supported in Torque.
AssertUnreachable();
unreachable;
}
return array;
}
......
......@@ -34,6 +34,9 @@ TAIL: 'tail';
ISNT: 'isnt';
IS: 'is';
LET: 'let';
ASSERT: 'assert';
UNREACHABLE_TOKEN: 'unreachable';
DEBUG_TOKEN: 'debug';
ASSIGNMENT: '=';
ASSIGNMENT_OPERATOR
......@@ -231,6 +234,8 @@ gotoStatement: GOTO labelReference argumentList?;
handlerWithStatement: (CATCH IDENTIFIER | LABEL labelDeclaration) statementBlock;
tryCatch: TRY statementBlock handlerWithStatement+;
diagnosticStatement: (ASSERT '(' expression ')') | UNREACHABLE_TOKEN | DEBUG_TOKEN;
statement : variableDeclarationWithInitialization ';'
| helperCallStatement ';'
| expressionStatement ';'
......@@ -239,6 +244,7 @@ statement : variableDeclarationWithInitialization ';'
| continueStatement ';'
| gotoStatement ';'
| ifStatement
| diagnosticStatement ';'
| whileLoop
| forOfLoop
| forLoop
......
......@@ -225,6 +225,11 @@ class TorqueBaseListener : public TorqueListener {
void enterTryCatch(TorqueParser::TryCatchContext* /*ctx*/) override {}
void exitTryCatch(TorqueParser::TryCatchContext* /*ctx*/) override {}
void enterDiagnosticStatement(
TorqueParser::DiagnosticStatementContext* /*ctx*/) override {}
void exitDiagnosticStatement(
TorqueParser::DiagnosticStatementContext* /*ctx*/) override {}
void enterStatement(TorqueParser::StatementContext* /*ctx*/) override {}
void exitStatement(TorqueParser::StatementContext* /*ctx*/) override {}
......
......@@ -242,6 +242,11 @@ class TorqueBaseVisitor : public TorqueVisitor {
return visitChildren(ctx);
}
antlrcpp::Any visitDiagnosticStatement(
TorqueParser::DiagnosticStatementContext* ctx) override {
return visitChildren(ctx);
}
antlrcpp::Any visitStatement(TorqueParser::StatementContext* ctx) override {
return visitChildren(ctx);
}
......
This diff is collapsed.
......@@ -59,38 +59,41 @@ class TorqueLexer : public antlr4::Lexer {
ISNT = 44,
IS = 45,
LET = 46,
ASSIGNMENT = 47,
ASSIGNMENT_OPERATOR = 48,
EQUAL = 49,
PLUS = 50,
MINUS = 51,
MULTIPLY = 52,
DIVIDE = 53,
MODULO = 54,
BIT_OR = 55,
BIT_AND = 56,
BIT_NOT = 57,
MAX = 58,
MIN = 59,
NOT_EQUAL = 60,
LESS_THAN = 61,
LESS_THAN_EQUAL = 62,
GREATER_THAN = 63,
GREATER_THAN_EQUAL = 64,
SHIFT_LEFT = 65,
SHIFT_RIGHT = 66,
SHIFT_RIGHT_ARITHMETIC = 67,
VARARGS = 68,
EQUALITY_OPERATOR = 69,
INCREMENT = 70,
DECREMENT = 71,
NOT = 72,
STRING_LITERAL = 73,
IDENTIFIER = 74,
WS = 75,
BLOCK_COMMENT = 76,
LINE_COMMENT = 77,
DECIMAL_LITERAL = 78
ASSERT = 47,
UNREACHABLE_TOKEN = 48,
DEBUG_TOKEN = 49,
ASSIGNMENT = 50,
ASSIGNMENT_OPERATOR = 51,
EQUAL = 52,
PLUS = 53,
MINUS = 54,
MULTIPLY = 55,
DIVIDE = 56,
MODULO = 57,
BIT_OR = 58,
BIT_AND = 59,
BIT_NOT = 60,
MAX = 61,
MIN = 62,
NOT_EQUAL = 63,
LESS_THAN = 64,
LESS_THAN_EQUAL = 65,
GREATER_THAN = 66,
GREATER_THAN_EQUAL = 67,
SHIFT_LEFT = 68,
SHIFT_RIGHT = 69,
SHIFT_RIGHT_ARITHMETIC = 70,
VARARGS = 71,
EQUALITY_OPERATOR = 72,
INCREMENT = 73,
DECREMENT = 74,
NOT = 75,
STRING_LITERAL = 76,
IDENTIFIER = 77,
WS = 78,
BLOCK_COMMENT = 79,
LINE_COMMENT = 80,
DECIMAL_LITERAL = 81
};
explicit TorqueLexer(antlr4::CharStream* input);
......
......@@ -214,6 +214,11 @@ class TorqueListener : public antlr4::tree::ParseTreeListener {
virtual void enterTryCatch(TorqueParser::TryCatchContext* ctx) = 0;
virtual void exitTryCatch(TorqueParser::TryCatchContext* ctx) = 0;
virtual void enterDiagnosticStatement(
TorqueParser::DiagnosticStatementContext* ctx) = 0;
virtual void exitDiagnosticStatement(
TorqueParser::DiagnosticStatementContext* ctx) = 0;
virtual void enterStatement(TorqueParser::StatementContext* ctx) = 0;
virtual void exitStatement(TorqueParser::StatementContext* ctx) = 0;
......
This diff is collapsed.
......@@ -59,38 +59,41 @@ class TorqueParser : public antlr4::Parser {
ISNT = 44,
IS = 45,
LET = 46,
ASSIGNMENT = 47,
ASSIGNMENT_OPERATOR = 48,
EQUAL = 49,
PLUS = 50,
MINUS = 51,
MULTIPLY = 52,
DIVIDE = 53,
MODULO = 54,
BIT_OR = 55,
BIT_AND = 56,
BIT_NOT = 57,
MAX = 58,
MIN = 59,
NOT_EQUAL = 60,
LESS_THAN = 61,
LESS_THAN_EQUAL = 62,
GREATER_THAN = 63,
GREATER_THAN_EQUAL = 64,
SHIFT_LEFT = 65,
SHIFT_RIGHT = 66,
SHIFT_RIGHT_ARITHMETIC = 67,
VARARGS = 68,
EQUALITY_OPERATOR = 69,
INCREMENT = 70,
DECREMENT = 71,
NOT = 72,
STRING_LITERAL = 73,
IDENTIFIER = 74,
WS = 75,
BLOCK_COMMENT = 76,
LINE_COMMENT = 77,
DECIMAL_LITERAL = 78
ASSERT = 47,
UNREACHABLE_TOKEN = 48,
DEBUG_TOKEN = 49,
ASSIGNMENT = 50,
ASSIGNMENT_OPERATOR = 51,
EQUAL = 52,
PLUS = 53,
MINUS = 54,
MULTIPLY = 55,
DIVIDE = 56,
MODULO = 57,
BIT_OR = 58,
BIT_AND = 59,
BIT_NOT = 60,
MAX = 61,
MIN = 62,
NOT_EQUAL = 63,
LESS_THAN = 64,
LESS_THAN_EQUAL = 65,
GREATER_THAN = 66,
GREATER_THAN_EQUAL = 67,
SHIFT_LEFT = 68,
SHIFT_RIGHT = 69,
SHIFT_RIGHT_ARITHMETIC = 70,
VARARGS = 71,
EQUALITY_OPERATOR = 72,
INCREMENT = 73,
DECREMENT = 74,
NOT = 75,
STRING_LITERAL = 76,
IDENTIFIER = 77,
WS = 78,
BLOCK_COMMENT = 79,
LINE_COMMENT = 80,
DECIMAL_LITERAL = 81
};
enum {
......@@ -141,23 +144,24 @@ class TorqueParser : public antlr4::Parser {
RuleGotoStatement = 44,
RuleHandlerWithStatement = 45,
RuleTryCatch = 46,
RuleStatement = 47,
RuleStatementList = 48,
RuleStatementScope = 49,
RuleStatementBlock = 50,
RuleHelperBody = 51,
RuleGeneratesDeclaration = 52,
RuleExtendsDeclaration = 53,
RuleTypeDeclaration = 54,
RuleExternalBuiltin = 55,
RuleExternalMacro = 56,
RuleExternalRuntime = 57,
RuleBuiltinDeclaration = 58,
RuleMacroDeclaration = 59,
RuleConstDeclaration = 60,
RuleDeclaration = 61,
RuleModuleDeclaration = 62,
RuleFile = 63
RuleDiagnosticStatement = 47,
RuleStatement = 48,
RuleStatementList = 49,
RuleStatementScope = 50,
RuleStatementBlock = 51,
RuleHelperBody = 52,
RuleGeneratesDeclaration = 53,
RuleExtendsDeclaration = 54,
RuleTypeDeclaration = 55,
RuleExternalBuiltin = 56,
RuleExternalMacro = 57,
RuleExternalRuntime = 58,
RuleBuiltinDeclaration = 59,
RuleMacroDeclaration = 60,
RuleConstDeclaration = 61,
RuleDeclaration = 62,
RuleModuleDeclaration = 63,
RuleFile = 64
};
explicit TorqueParser(antlr4::TokenStream* input);
......@@ -218,6 +222,7 @@ class TorqueParser : public antlr4::Parser {
class GotoStatementContext;
class HandlerWithStatementContext;
class TryCatchContext;
class DiagnosticStatementContext;
class StatementContext;
class StatementListContext;
class StatementScopeContext;
......@@ -1035,6 +1040,24 @@ class TorqueParser : public antlr4::Parser {
TryCatchContext* tryCatch();
class DiagnosticStatementContext : public antlr4::ParserRuleContext {
public:
DiagnosticStatementContext(antlr4::ParserRuleContext* parent,
size_t invokingState);
size_t getRuleIndex() const override;
antlr4::tree::TerminalNode* ASSERT();
ExpressionContext* expression();
antlr4::tree::TerminalNode* UNREACHABLE_TOKEN();
antlr4::tree::TerminalNode* DEBUG_TOKEN();
void enterRule(antlr4::tree::ParseTreeListener* listener) override;
void exitRule(antlr4::tree::ParseTreeListener* listener) override;
antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor* visitor) override;
};
DiagnosticStatementContext* diagnosticStatement();
class StatementContext : public antlr4::ParserRuleContext {
public:
StatementContext(antlr4::ParserRuleContext* parent, size_t invokingState);
......@@ -1048,6 +1071,7 @@ class TorqueParser : public antlr4::Parser {
ContinueStatementContext* continueStatement();
GotoStatementContext* gotoStatement();
IfStatementContext* ifStatement();
DiagnosticStatementContext* diagnosticStatement();
WhileLoopContext* whileLoop();
ForOfLoopContext* forOfLoop();
ForLoopContext* forLoop();
......
......@@ -159,6 +159,9 @@ class TorqueVisitor : public antlr4::tree::AbstractParseTreeVisitor {
virtual antlrcpp::Any visitTryCatch(
TorqueParser::TryCatchContext* context) = 0;
virtual antlrcpp::Any visitDiagnosticStatement(
TorqueParser::DiagnosticStatementContext* context) = 0;
virtual antlrcpp::Any visitStatement(
TorqueParser::StatementContext* context) = 0;
......
......@@ -613,6 +613,26 @@ antlrcpp::Any AstGenerator::visitConditionalExpression(
return context->logicalORExpression(0)->accept(this);
}
antlrcpp::Any AstGenerator::visitDiagnosticStatement(
TorqueParser::DiagnosticStatementContext* context) {
if (context->ASSERT()) {
size_t a = context->expression()->start->getStartIndex();
size_t b = context->expression()->stop->getStopIndex();
antlr4::misc::Interval interval(a, b);
std::string source = source_file_context_->stream->getText(interval);
return base::implicit_cast<Statement*>(RegisterNode(new AssertStatement{
Pos(context), context->expression()->accept(this).as<Expression*>(),
source}));
} else if (context->UNREACHABLE_TOKEN()) {
return base::implicit_cast<Statement*>(
RegisterNode(new DebugStatement{Pos(context), "unreachable", true}));
} else {
DCHECK(context->DEBUG_TOKEN());
return base::implicit_cast<Statement*>(
RegisterNode(new DebugStatement{Pos(context), "debug", false}));
}
}
void AstGenerator::visitSourceFile(SourceFileContext* context) {
source_file_context_ = context;
current_source_file_ = ast_.AddSource(context->name);
......
......@@ -133,6 +133,9 @@ class AstGenerator : public TorqueBaseVisitor {
antlrcpp::Any visitForOfLoop(
TorqueParser::ForOfLoopContext* context) override;
antlrcpp::Any visitDiagnosticStatement(
TorqueParser::DiagnosticStatementContext* context) override;
antlrcpp::Any aggregateResult(antlrcpp::Any aggregate,
const antlrcpp::Any& nextResult) override {
if (aggregate.isNull())
......
......@@ -49,6 +49,8 @@ struct SourcePosition {
V(BreakStatement) \
V(ContinueStatement) \
V(ReturnStatement) \
V(DebugStatement) \
V(AssertStatement) \
V(TailCallStatement) \
V(VarDeclarationStatement) \
V(GotoStatement) \
......@@ -355,6 +357,22 @@ struct ReturnStatement : Statement {
base::Optional<Expression*> value;
};
struct DebugStatement : Statement {
DEFINE_AST_NODE_LEAF_BOILERPLATE(DebugStatement)
DebugStatement(SourcePosition p, const std::string& r, bool n)
: Statement(kKind, p), reason(r), never_continues(n) {}
std::string reason;
bool never_continues;
};
struct AssertStatement : Statement {
DEFINE_AST_NODE_LEAF_BOILERPLATE(AssertStatement)
AssertStatement(SourcePosition p, Expression* e, const std::string& s)
: Statement(kKind, p), expression(e), source(s) {}
Expression* expression;
std::string source;
};
struct TailCallStatement : Statement {
DEFINE_AST_NODE_LEAF_BOILERPLATE(TailCallStatement)
TailCallStatement(SourcePosition p, CallExpression* c)
......
......@@ -190,6 +190,11 @@ class DeclarationVisitor : public FileVisitor {
void Visit(MacroDeclaration* decl);
void Visit(ReturnStatement* stmt);
void Visit(DebugStatement* stmt) {}
void Visit(AssertStatement* stmt) {
DeclareExpressionForBranch(stmt->expression);
}
void Visit(VarDeclarationStatement* stmt) {
std::string variable_name = stmt->name;
Type type = declarations()->LookupType(stmt->pos, stmt->type);
......
......@@ -49,14 +49,6 @@ struct SourceFileContext {
std::unique_ptr<antlr4::CommonTokenStream> tokens;
std::unique_ptr<TorqueParser> parser;
TorqueParser::FileContext* file;
std::string sourceFileAndLineNumber(antlr4::ParserRuleContext* context) {
antlr4::misc::Interval i = context->getSourceInterval();
auto token = tokens->get(i.a);
size_t line = token->getLine();
size_t pos = token->getCharPositionInLine();
return name + ":" + std::to_string(line) + ":" + std::to_string(pos);
}
};
class GlobalContext {
......
......@@ -565,6 +565,70 @@ Type ImplementationVisitor::Visit(BlockStatement* block) {
return type;
}
Type ImplementationVisitor::Visit(DebugStatement* stmt) {
#if defined(DEBUG)
GenerateIndent();
source_out() << "Print(\""
<< "halting because of '" << stmt->reason << "' at "
<< PositionAsString(stmt->pos) << "\");" << std::endl;
#endif
GenerateIndent();
if (stmt->never_continues) {
source_out() << "Unreachable();" << std::endl;
return GetTypeOracle().GetNeverType();
} else {
source_out() << "DebugBreak();" << std::endl;
return GetTypeOracle().GetVoidType();
}
}
Type ImplementationVisitor::Visit(AssertStatement* stmt) {
#if defined(DEBUG)
// CSA_ASSERT & co. are not used here on purpose for two reasons. First,
// Torque allows and handles two types of expressions in the if protocol
// automagically, ones that return TNode<BoolT> and those that use the
// BranchIf(..., Label* true, Label* false) idiom. Because the machinery to
// handle this is embedded in the expression handling and to it's not possible
// to make the decision to use CSA_ASSERT or CSA_ASSERT_BRANCH isn't trivial
// up-front. Secondly, on failure, the assert text should be the corresponding
// Torque code, not the -gen.cc code, which would be the case when using
// CSA_ASSERT_XXX.
Label* true_label = nullptr;
Label* false_label = nullptr;
Declarations::NodeScopeActivator scope(declarations(), stmt->expression);
true_label =
Label::cast(declarations()->LookupValue(stmt->pos, kTrueLabelName));
GenerateLabelDefinition(true_label);
false_label =
Label::cast(declarations()->LookupValue(stmt->pos, kFalseLabelName));
GenerateLabelDefinition(false_label);
Expression* expression = stmt->expression;
VisitResult expression_result = Visit(stmt->expression);
if (expression_result.type() == GetTypeOracle().GetBitType()) {
GenerateBranch(expression_result, true_label, false_label);
} else {
if (expression_result.type() != GetTypeOracle().GetNeverType()) {
std::stringstream s;
s << "unexpected return type " << expression_result.type()
<< " for branch expression at " << PositionAsString(expression->pos);
ReportError(s.str());
}
}
GenerateLabelBind(false_label);
GenerateIndent();
source_out() << "Print(\""
<< "assert '" << stmt->source << "' failed at "
<< PositionAsString(stmt->pos) << "\");" << std::endl;
GenerateIndent();
source_out() << "Unreachable();" << std::endl;
GenerateLabelBind(true_label);
#endif
return GetTypeOracle().GetVoidType();
}
Type ImplementationVisitor::Visit(ExpressionStatement* stmt) {
Type type = Visit(stmt->expression).type();
return type.IsNever() ? type : GetTypeOracle().GetVoidType();
......
......@@ -131,6 +131,8 @@ class ImplementationVisitor : public FileVisitor {
Type Visit(ForOfLoopStatement* stmt);
Type Visit(BlockStatement* block);
Type Visit(ExpressionStatement* stmt);
Type Visit(DebugStatement* stmt);
Type Visit(AssertStatement* stmt);
Label* GetLabel(SourcePosition pos, const std::string& label);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment