Replaced std::exit() with return Carbon::ErrorOr for expected errors like invalid syntax (#1120)

* Replaced std::exit() with return llvm::Expected/llvm::Error<T> for expected errors like invalid syntax.

* Use llvm::formatv() for formatting lexer error messages.
x

* Addresed merge errors.

* Fixed impl scope.

* Made ErrorBuilder::operator<< nodiscard, to catch code forgetting 'return' in 'return FATAL_COMPILATION_ERROR()'.

* FatalComplationError() -> ParseAndLexContext::RecordLexerError().
Other usages of ERROR_TOKEN in lexer.lpp were actually supposed to be END_OF_FILE.

* Update executable_semantics/syntax/parse_and_lex_context.h

Co-authored-by: Jon Meow <jperkins@google.com>

* Code review fixes.

* Update executable_semantics/syntax/parser.ypp

Co-authored-by: Jon Meow <jperkins@google.com>

* More code review fixes.

* Update executable_semantics/interpreter/type_checker.h

Co-authored-by: Jon Meow <jperkins@google.com>

* Yet more code review fixes...

* Update executable_semantics/syntax/lexer.lpp

Co-authored-by: Jon Meow <jperkins@google.com>

* code review comments

* Update executable_semantics/interpreter/interpreter.cpp

Co-authored-by: Geoff Romer <gromer@google.com>

* Apply suggestions from code review

Co-authored-by: Jon Meow <jperkins@google.com>

* Update executable_semantics/syntax/lexer.lpp

Co-authored-by: Jon Meow <jperkins@google.com>

* code review

* code review

* Apply suggestions from code review

Co-authored-by: Jon Meow <jperkins@google.com>

* formatted code

* review comments

* Switched to the new ErrorOr<V> error implementation

* code review comments

* fixed comment

* restored ostream.h as #976 makes the change unnecesary

* review comments

Co-authored-by: Jon Meow <jperkins@google.com>
Co-authored-by: Geoff Romer <gromer@google.com>
This commit is contained in:
pk19604014
2022-03-22 16:17:04 -04:00
committed by GitHub
co-authored by Jon Meow Geoff Romer
parent e5a87af6fe
commit aa8a5f174d
43 changed files with 1324 additions and 873 deletions
+1
View File
@@ -14,6 +14,7 @@ cc_binary(
srcs = ["main.cpp"],
data = ["data/prelude.carbon"],
deps = [
"//common:error",
"//executable_semantics/common:arena",
"//executable_semantics/common:nonnull",
"//executable_semantics/interpreter:exec_program",
+2
View File
@@ -203,8 +203,10 @@ cc_library(
":source_location",
":value_category",
"//common:check",
"//common:error",
"//executable_semantics/common:error",
"//executable_semantics/common:nonnull",
"@llvm-project//llvm:Support",
],
)
+20 -11
View File
@@ -112,29 +112,38 @@ void ReturnTerm::Print(llvm::raw_ostream& out) const {
}
}
// Look for the `me` parameter in the `deduced_parameters_`
// and put it in the `me_pattern_`.
void FunctionDeclaration::ResolveDeducedAndReceiver(
const std::vector<Nonnull<AstNode*>>& deduced_params) {
auto FunctionDeclaration::Create(
Nonnull<Arena*> arena, SourceLocation source_loc, std::string name,
std::vector<Nonnull<AstNode*>> deduced_params,
std::optional<Nonnull<BindingPattern*>> me_pattern,
Nonnull<TuplePattern*> param_pattern, ReturnTerm return_term,
std::optional<Nonnull<Block*>> body)
-> ErrorOr<Nonnull<FunctionDeclaration*>> {
std::vector<Nonnull<GenericBinding*>> resolved_params;
// Look for the `me` parameter in the `deduced_parameters`
// and put it in the `me_pattern`.
for (Nonnull<AstNode*> param : deduced_params) {
switch (param->kind()) {
case AstNodeKind::GenericBinding:
deduced_parameters_.push_back(&cast<GenericBinding>(*param));
resolved_params.push_back(&cast<GenericBinding>(*param));
break;
case AstNodeKind::BindingPattern: {
Nonnull<BindingPattern*> bp = &cast<BindingPattern>(*param);
if (me_pattern_.has_value() || bp->name() != "me") {
FATAL_COMPILATION_ERROR(source_loc())
<< "illegal binding pattern in implicit parameter list";
if (me_pattern.has_value() || bp->name() != "me") {
return FATAL_COMPILATION_ERROR(source_loc)
<< "illegal binding pattern in implicit parameter list";
}
me_pattern_ = bp;
me_pattern = bp;
break;
}
default:
FATAL_COMPILATION_ERROR(source_loc())
<< "illegal AST node in implicit parameter list";
return FATAL_COMPILATION_ERROR(source_loc)
<< "illegal AST node in implicit parameter list";
}
}
return arena->New<FunctionDeclaration>(source_loc, name, resolved_params,
me_pattern, param_pattern, return_term,
body);
}
void FunctionDeclaration::PrintDepth(int depth, llvm::raw_ostream& out) const {
+13 -5
View File
@@ -82,20 +82,29 @@ class FunctionDeclaration : public Declaration {
public:
using ImplementsCarbonValueNode = void;
static auto Create(Nonnull<Arena*> arena, SourceLocation source_loc,
std::string name,
std::vector<Nonnull<AstNode*>> deduced_params,
std::optional<Nonnull<BindingPattern*>> me_pattern,
Nonnull<TuplePattern*> param_pattern,
ReturnTerm return_term,
std::optional<Nonnull<Block*>> body)
-> ErrorOr<Nonnull<FunctionDeclaration*>>;
// Use `Create()` instead. This is public only so Arena::New() can call it.
FunctionDeclaration(SourceLocation source_loc, std::string name,
std::vector<Nonnull<AstNode*>> deduced_params,
std::vector<Nonnull<GenericBinding*>> deduced_params,
std::optional<Nonnull<BindingPattern*>> me_pattern,
Nonnull<TuplePattern*> param_pattern,
ReturnTerm return_term,
std::optional<Nonnull<Block*>> body)
: Declaration(AstNodeKind::FunctionDeclaration, source_loc),
name_(std::move(name)),
deduced_parameters_(std::move(deduced_params)),
me_pattern_(me_pattern),
param_pattern_(param_pattern),
return_term_(return_term),
body_(body) {
ResolveDeducedAndReceiver(deduced_params);
}
body_(body) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromFunctionDeclaration(node->kind());
@@ -135,7 +144,6 @@ class FunctionDeclaration : public Declaration {
auto is_method() const -> bool { return me_pattern_.has_value(); }
private:
void ResolveDeducedAndReceiver(const std::vector<Nonnull<AstNode*>>&);
std::string name_;
std::vector<Nonnull<GenericBinding*>> deduced_parameters_;
std::optional<Nonnull<BindingPattern*>> me_pattern_;
+3 -2
View File
@@ -20,13 +20,14 @@ using llvm::isa;
auto IntrinsicExpression::FindIntrinsic(std::string_view name,
SourceLocation source_loc)
-> Intrinsic {
-> ErrorOr<Intrinsic> {
static const auto& intrinsic_map =
*new std::map<std::string_view, Intrinsic>({{"print", Intrinsic::Print}});
name.remove_prefix(std::strlen("__intrinsic_"));
auto it = intrinsic_map.find(name);
if (it == intrinsic_map.end()) {
FATAL_COMPILATION_ERROR(source_loc) << "Unknown intrinsic '" << name << "'";
return FATAL_COMPILATION_ERROR(source_loc)
<< "Unknown intrinsic '" << name << "'";
}
return it->second;
}
+7 -8
View File
@@ -465,11 +465,15 @@ class IntrinsicExpression : public Expression {
Print,
};
explicit IntrinsicExpression(std::string_view intrinsic_name,
Nonnull<TupleLiteral*> args,
// Returns the enumerator corresponding to the intrinsic named `name`,
// or raises a fatal compile error if there is no such enumerator.
static auto FindIntrinsic(std::string_view name, SourceLocation source_loc)
-> ErrorOr<Intrinsic>;
explicit IntrinsicExpression(Intrinsic intrinsic, Nonnull<TupleLiteral*> args,
SourceLocation source_loc)
: Expression(AstNodeKind::IntrinsicExpression, source_loc),
intrinsic_(FindIntrinsic(intrinsic_name, source_loc)),
intrinsic_(intrinsic),
args_(args) {}
static auto classof(const AstNode* node) -> bool {
@@ -481,11 +485,6 @@ class IntrinsicExpression : public Expression {
auto args() -> TupleLiteral& { return *args_; }
private:
// Returns the enumerator corresponding to the intrinsic named `name`,
// or raises a fatal compile error if there is no such enumerator.
static auto FindIntrinsic(std::string_view name, SourceLocation source_loc)
-> Intrinsic;
Intrinsic intrinsic_;
Nonnull<TupleLiteral*> args_;
};
+5 -13
View File
@@ -108,23 +108,15 @@ auto TuplePatternFromParenContents(Nonnull<Arena*> arena,
// Used by AlternativePattern for constructor initialization. Produces a helpful
// error for incorrect expressions, rather than letting a default cast error
// apply.
static auto RequireFieldAccess(Nonnull<Expression*> alternative)
-> FieldAccessExpression& {
auto AlternativePattern::RequireFieldAccess(Nonnull<Expression*> alternative)
-> ErrorOr<Nonnull<FieldAccessExpression*>> {
if (alternative->kind() != ExpressionKind::FieldAccessExpression) {
FATAL_PROGRAM_ERROR(alternative->source_loc())
<< "Alternative pattern must have the form of a field access.";
return FATAL_PROGRAM_ERROR(alternative->source_loc())
<< "Alternative pattern must have the form of a field access.";
}
return cast<FieldAccessExpression>(*alternative);
return &cast<FieldAccessExpression>(*alternative);
}
AlternativePattern::AlternativePattern(SourceLocation source_loc,
Nonnull<Expression*> alternative,
Nonnull<TuplePattern*> arguments)
: Pattern(AstNodeKind::AlternativePattern, source_loc),
choice_type_(&RequireFieldAccess(alternative).aggregate()),
alternative_name_(RequireFieldAccess(alternative).field()),
arguments_(arguments) {}
auto ParenExpressionToParenPattern(Nonnull<Arena*> arena,
const ParenContents<Expression>& contents)
-> ParenContents<Pattern> {
+16 -6
View File
@@ -218,6 +218,19 @@ auto ParenExpressionToParenPattern(Nonnull<Arena*> arena,
// A pattern that matches an alternative of a choice type.
class AlternativePattern : public Pattern {
public:
// Constructs an AlternativePattern that matches the alternative specified
// by `alternative`, if its arguments match `arguments`.
static auto Create(Nonnull<Arena*> arena, SourceLocation source_loc,
Nonnull<Expression*> alternative,
Nonnull<TuplePattern*> arguments)
-> ErrorOr<Nonnull<AlternativePattern*>> {
ASSIGN_OR_RETURN(Nonnull<FieldAccessExpression*> field_access,
RequireFieldAccess(alternative));
return arena->New<AlternativePattern>(source_loc,
&field_access->aggregate(),
field_access->field(), arguments);
}
// Constructs an AlternativePattern that matches a value of the type
// specified by choice_type if it represents an alternative named
// alternative_name, and its arguments match `arguments`.
@@ -230,12 +243,6 @@ class AlternativePattern : public Pattern {
alternative_name_(std::move(alternative_name)),
arguments_(arguments) {}
// Constructs an AlternativePattern that matches the alternative specified
// by `alternative`, if its arguments match `arguments`.
AlternativePattern(SourceLocation source_loc,
Nonnull<Expression*> alternative,
Nonnull<TuplePattern*> arguments);
static auto classof(const AstNode* node) -> bool {
return InheritsFromAlternativePattern(node->kind());
}
@@ -249,6 +256,9 @@ class AlternativePattern : public Pattern {
auto arguments() -> TuplePattern& { return *arguments_; }
private:
static auto RequireFieldAccess(Nonnull<Expression*> alternative)
-> ErrorOr<Nonnull<FieldAccessExpression*>>;
Nonnull<Expression*> choice_type_;
std::string alternative_name_;
Nonnull<TuplePattern*> arguments_;
+22 -15
View File
@@ -5,43 +5,50 @@
#include "executable_semantics/ast/static_scope.h"
#include "executable_semantics/common/error.h"
#include "llvm/Support/Error.h"
namespace Carbon {
void StaticScope::Add(std::string name, ValueNodeView entity) {
auto StaticScope::Add(std::string name, ValueNodeView entity)
-> ErrorOr<Success> {
auto [it, success] = declared_names_.insert({name, entity});
if (!success && it->second != entity) {
FATAL_COMPILATION_ERROR(entity.base().source_loc())
<< "Duplicate name `" << name << "` also found at "
<< it->second.base().source_loc();
return FATAL_COMPILATION_ERROR(entity.base().source_loc())
<< "Duplicate name `" << name << "` also found at "
<< it->second.base().source_loc();
}
return Success();
}
auto StaticScope::Resolve(const std::string& name,
SourceLocation source_loc) const -> ValueNodeView {
std::optional<ValueNodeView> result = TryResolve(name, source_loc);
if (!result.has_value()) {
FATAL_COMPILATION_ERROR(source_loc) << "could not resolve '" << name << "'";
SourceLocation source_loc) const
-> ErrorOr<ValueNodeView> {
ASSIGN_OR_RETURN(std::optional<ValueNodeView> result,
TryResolve(name, source_loc));
if (!result) {
return FATAL_COMPILATION_ERROR(source_loc)
<< "could not resolve '" << name << "'";
}
return *result;
}
auto StaticScope::TryResolve(const std::string& name,
SourceLocation source_loc) const
-> std::optional<ValueNodeView> {
-> ErrorOr<std::optional<ValueNodeView>> {
auto it = declared_names_.find(name);
if (it != declared_names_.end()) {
return it->second;
return std::make_optional(it->second);
}
std::optional<ValueNodeView> result;
for (Nonnull<const StaticScope*> parent : parent_scopes_) {
auto parent_result = parent->TryResolve(name, source_loc);
ASSIGN_OR_RETURN(std::optional<ValueNodeView> parent_result,
parent->TryResolve(name, source_loc));
if (parent_result.has_value() && result.has_value() &&
*parent_result != *result) {
FATAL_COMPILATION_ERROR(source_loc)
<< "'" << name << "' is ambiguous between "
<< result->base().source_loc() << " and "
<< parent_result->base().source_loc();
return FATAL_COMPILATION_ERROR(source_loc)
<< "'" << name << "' is ambiguous between "
<< result->base().source_loc() << " and "
<< parent_result->base().source_loc();
}
result = parent_result;
}
+5 -3
View File
@@ -12,10 +12,12 @@
#include <vector>
#include "common/check.h"
#include "common/error.h"
#include "executable_semantics/ast/ast_node.h"
#include "executable_semantics/ast/source_location.h"
#include "executable_semantics/ast/value_category.h"
#include "executable_semantics/common/nonnull.h"
#include "llvm/Support/Error.h"
namespace Carbon {
@@ -133,7 +135,7 @@ class StaticScope {
public:
// Defines `name` to be `entity` in this scope, or reports a compilation error
// if `name` is already defined to be a different entity in this scope.
void Add(std::string name, ValueNodeView entity);
auto Add(std::string name, ValueNodeView entity) -> ErrorOr<Success>;
// Make `parent` a parent of this scope.
// REQUIRES: `parent` is not already a parent of this scope.
@@ -145,14 +147,14 @@ class StaticScope {
// scope, or reports a compilation error at `source_loc` there isn't exactly
// one such definition.
auto Resolve(const std::string& name, SourceLocation source_loc) const
-> ValueNodeView;
-> ErrorOr<ValueNodeView>;
private:
// Equivalent to Resolve, but returns `nullopt` instead of raising an error
// if no definition can be found. Still raises a compilation error if more
// than one definition is found.
auto TryResolve(const std::string& name, SourceLocation source_loc) const
-> std::optional<ValueNodeView>;
-> ErrorOr<std::optional<ValueNodeView>>;
// Maps locally declared names to their entities.
std::unordered_map<std::string, ValueNodeView> declared_names_;
+4
View File
@@ -17,6 +17,9 @@ cc_library(
hdrs = ["error.h"],
deps = [
"//common:check",
"//common:error",
"//executable_semantics/ast:source_location",
"@llvm-project//llvm:Support",
],
)
@@ -26,6 +29,7 @@ cc_test(
deps = [
":error",
"@com_google_googletest//:gtest_main",
"@llvm-project//llvm:Support",
],
)
+73 -9
View File
@@ -5,38 +5,102 @@
#ifndef EXECUTABLE_SEMANTICS_COMMON_ERROR_H_
#define EXECUTABLE_SEMANTICS_COMMON_ERROR_H_
#include <optional>
#include "common/check.h"
#include "common/error.h"
#include "executable_semantics/ast/source_location.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
namespace Carbon {
// Prints an error and exits. This should be used for non-recoverable errors
// with user input.
// A helper class for accumulating error message and converting to
// `Carbon::Error`/`Carbon::ErrorOr<T>`.
class ErrorBuilder {
public:
explicit ErrorBuilder(std::optional<SourceLocation> loc = std::nullopt)
: out_(message_) {
if (loc.has_value()) {
out_ << *loc << ": ";
}
}
// Accumulates string message.
template <typename T>
[[nodiscard]] auto operator<<(const T& message) -> ErrorBuilder& {
out_ << message;
return *this;
}
operator Error() { return Error(message_); }
template <typename V>
operator ErrorOr<V>() {
return Error(message_);
}
std::string message_;
llvm::raw_string_ostream out_;
};
// Builds a Carbon::Error instance with the specified message. This should be
// used for non-recoverable errors with user input.
//
// For example:
// FATAL_PROGRAM_ERROR(line_num) << "Line is bad!";
// FATAL_PROGRAM_ERROR_NO_LINE() << "Application is bad!";
// return FATAL_PROGRAM_ERROR(line_num) << "Line is bad!";
// return FATAL_PROGRAM_ERROR_NO_LINE() << "Application is bad!";
//
// Where possible, try to identify the error as a compilation or
// runtime error. Use CHECK/FATAL for internal errors. The generic program error
// option is provided as a fallback for cases that don't fit those
// runtime error. Use CHECK/FATAL for internal errors. The generic program
// error option is provided as a fallback for cases that don't fit those
// classifications.
//
// TODO: replace below macro invocations with direct `return ErrorBuilder() <<
// xx` calls.
#define FATAL_PROGRAM_ERROR_NO_LINE() RAW_EXITING_STREAM() << "PROGRAM ERROR: "
#define FATAL_PROGRAM_ERROR_NO_LINE() \
Carbon::ErrorBuilder() << "PROGRAM ERROR: "
#define FATAL_PROGRAM_ERROR(line) \
FATAL_PROGRAM_ERROR_NO_LINE() << (line) << ": "
#define FATAL_COMPILATION_ERROR_NO_LINE() \
RAW_EXITING_STREAM() << "COMPILATION ERROR: "
Carbon::ErrorBuilder() << "COMPILATION ERROR: "
#define FATAL_COMPILATION_ERROR(line) \
FATAL_COMPILATION_ERROR_NO_LINE() << (line) << ": "
#define FATAL_RUNTIME_ERROR_NO_LINE() RAW_EXITING_STREAM() << "RUNTIME ERROR: "
#define FATAL_RUNTIME_ERROR_NO_LINE() \
Carbon::ErrorBuilder() << "RUNTIME ERROR: "
#define FATAL_RUNTIME_ERROR(line) \
FATAL_RUNTIME_ERROR_NO_LINE() << (line) << ": "
// Macro hackery to get a unique variable name.
#define MAKE_UNIQUE_NAME_IMPL(a, b, c) a##b##c
#define MAKE_UNIQUE_NAME(a, b, c) MAKE_UNIQUE_NAME_IMPL(a, b, c)
#define RETURN_IF_ERROR_IMPL(unique_name, expr) \
if (auto unique_name = (expr); !unique_name.ok()) { \
return std::move(unique_name).error(); \
}
#define RETURN_IF_ERROR(expr) \
RETURN_IF_ERROR_IMPL( \
MAKE_UNIQUE_NAME(_llvm_error_line, __LINE__, __COUNTER__), expr)
#define ASSIGN_OR_RETURN_IMPL(unique_name, var, expr) \
auto unique_name = (expr); \
if (!unique_name.ok()) { \
return std::move(unique_name).error(); \
} \
var = std::move(*unique_name);
#define ASSIGN_OR_RETURN(var, expr) \
ASSIGN_OR_RETURN_IMPL( \
MAKE_UNIQUE_NAME(_llvm_expected_line, __LINE__, __COUNTER__), var, expr)
} // namespace Carbon
#endif // EXECUTABLE_SEMANTICS_COMMON_ERROR_H_
+78 -8
View File
@@ -9,24 +9,94 @@
namespace Carbon::Testing {
namespace {
using ::testing::Eq;
auto MakeSuccess() -> ErrorOr<Success> { return Success(); }
auto MakeError(std::string_view message) -> ErrorOr<Success> {
return Error(message);
}
auto MakeInt(int value) -> ErrorOr<int> { return value; }
auto MakeFailedInt(std::string_view message) -> ErrorOr<int> {
return Error(message);
}
auto ErrorToString(const Error& e) -> std::string { return e.message(); }
template <typename V>
auto ErrorToString(const ErrorOr<V>& e) -> std::string {
return e.error().message();
}
TEST(ErrorTest, FatalProgramError) {
ASSERT_DEATH({ FATAL_PROGRAM_ERROR_NO_LINE() << "test"; },
"^PROGRAM ERROR: test\n");
EXPECT_EQ(ErrorToString(FATAL_PROGRAM_ERROR_NO_LINE() << "test"),
"PROGRAM ERROR: test");
}
TEST(ErrorTest, FatalRuntimeError) {
ASSERT_DEATH({ FATAL_RUNTIME_ERROR_NO_LINE() << "test"; },
"^RUNTIME ERROR: test\n");
EXPECT_EQ(ErrorToString(FATAL_RUNTIME_ERROR_NO_LINE() << "test"),
"RUNTIME ERROR: test");
}
TEST(ErrorTest, FatalCompilationError) {
ASSERT_DEATH({ FATAL_COMPILATION_ERROR_NO_LINE() << "test"; },
"^COMPILATION ERROR: test\n");
EXPECT_EQ(ErrorToString(FATAL_COMPILATION_ERROR_NO_LINE() << "test"),
"COMPILATION ERROR: test");
}
TEST(ErrorTest, FatalProgramErrorLine) {
ASSERT_DEATH({ FATAL_PROGRAM_ERROR(1) << "test"; },
"^PROGRAM ERROR: 1: test\n");
EXPECT_EQ(ErrorToString(FATAL_PROGRAM_ERROR(1) << "test"),
"PROGRAM ERROR: 1: test");
}
TEST(ErrorTest, ReturnIfErrorNoError) {
auto result = []() -> ErrorOr<Success> {
RETURN_IF_ERROR(MakeSuccess());
RETURN_IF_ERROR(MakeSuccess());
return Success();
}();
EXPECT_TRUE(result.ok());
}
TEST(ErrorTest, ReturnIfErrorHasError) {
auto result = []() -> ErrorOr<Success> {
RETURN_IF_ERROR(MakeSuccess());
RETURN_IF_ERROR(MakeError("error"));
return Success();
}();
ASSERT_FALSE(result.ok());
EXPECT_EQ(ErrorToString(result), "error");
}
TEST(ErrorTest, AssignOrReturnNoError) {
auto result = []() -> ErrorOr<int> {
RETURN_IF_ERROR(MakeSuccess());
ASSIGN_OR_RETURN(int a, MakeInt(1));
ASSIGN_OR_RETURN(const int b, MakeInt(2));
int c = 0;
ASSIGN_OR_RETURN(c, MakeInt(3));
return a + b + c;
}();
ASSERT_TRUE(result.ok());
EXPECT_EQ(6, *result);
}
TEST(ErrorTest, AssignOrReturnHasDirectError) {
auto result = []() -> ErrorOr<int> {
RETURN_IF_ERROR(MakeError("error"));
return 0;
}();
ASSERT_FALSE(result.ok());
}
TEST(ErrorTest, AssignOrReturnHasErrorInExpected) {
auto result = []() -> ErrorOr<int> {
ASSIGN_OR_RETURN(int a, MakeFailedInt("error"));
return a;
}();
ASSERT_FALSE(result.ok());
EXPECT_EQ(ErrorToString(result), "error");
}
} // namespace
+1
View File
@@ -77,6 +77,7 @@ cc_library(
"//common:ostream",
"//executable_semantics/ast",
"//executable_semantics/common:arena",
"@llvm-project//llvm:Support",
],
)
@@ -7,6 +7,7 @@
#include "executable_semantics/interpreter/action.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Error.h"
namespace Carbon {
@@ -49,7 +50,7 @@ void ActionStack::Initialize(ValueNodeView value_node,
auto ActionStack::ValueOfNode(ValueNodeView value_node,
SourceLocation source_loc) const
-> Nonnull<const Value*> {
-> ErrorOr<Nonnull<const Value*>> {
if (std::optional<Nonnull<const Value*>> constant_value =
value_node.constant_value();
constant_value.has_value()) {
@@ -74,9 +75,9 @@ auto ActionStack::ValueOfNode(ValueNodeView value_node,
return *result;
}
}
// TODO: Move these errors to name resolution and explain them more clearly.
FATAL_RUNTIME_ERROR(source_loc)
<< "could not find `" << value_node.base() << "`";
// TODO: Move these errors to compile time and explain them more clearly.
return FATAL_RUNTIME_ERROR(source_loc)
<< "could not find `" << value_node.base() << "`";
}
void ActionStack::MergeScope(RuntimeScope scope) {
@@ -110,7 +111,7 @@ void ActionStack::InitializeFragment(ContinuationValue::StackFragment& fragment,
fragment.StoreReversed(std::move(reversed_todo));
}
void ActionStack::FinishAction() {
auto ActionStack::FinishAction() -> ErrorOr<Success> {
std::unique_ptr<Action> act = todo_.Pop();
switch (act->kind()) {
case Action::Kind::ExpressionAction:
@@ -123,9 +124,11 @@ void ActionStack::FinishAction() {
case Action::Kind::DeclarationAction:
PopScopes();
}
return Success();
}
void ActionStack::FinishAction(Nonnull<const Value*> result) {
auto ActionStack::FinishAction(Nonnull<const Value*> result)
-> ErrorOr<Success> {
std::unique_ptr<Action> act = todo_.Pop();
switch (act->kind()) {
case Action::Kind::StatementAction:
@@ -139,27 +142,33 @@ void ActionStack::FinishAction(Nonnull<const Value*> result) {
PopScopes();
SetResult(result);
}
return Success();
}
void ActionStack::Spawn(std::unique_ptr<Action> child) {
auto ActionStack::Spawn(std::unique_ptr<Action> child) -> ErrorOr<Success> {
Action& action = *todo_.Top();
action.set_pos(action.pos() + 1);
todo_.Push(std::move(child));
return Success();
}
void ActionStack::Spawn(std::unique_ptr<Action> child, RuntimeScope scope) {
auto ActionStack::Spawn(std::unique_ptr<Action> child, RuntimeScope scope)
-> ErrorOr<Success> {
Action& action = *todo_.Top();
action.set_pos(action.pos() + 1);
todo_.Push(std::make_unique<ScopeAction>(std::move(scope)));
todo_.Push(std::move(child));
return Success();
}
void ActionStack::RunAgain() {
auto ActionStack::RunAgain() -> ErrorOr<Success> {
Action& action = *todo_.Top();
action.set_pos(action.pos() + 1);
return Success();
}
void ActionStack::UnwindTo(Nonnull<const Statement*> ast_node) {
auto ActionStack::UnwindTo(Nonnull<const Statement*> ast_node)
-> ErrorOr<Success> {
while (true) {
if (const auto* statement_action =
llvm::dyn_cast<StatementAction>(todo_.Top().get());
@@ -169,24 +178,30 @@ void ActionStack::UnwindTo(Nonnull<const Statement*> ast_node) {
}
todo_.Pop();
}
return Success();
}
void ActionStack::UnwindPast(Nonnull<const Statement*> ast_node) {
UnwindTo(ast_node);
auto ActionStack::UnwindPast(Nonnull<const Statement*> ast_node)
-> ErrorOr<Success> {
RETURN_IF_ERROR(UnwindTo(ast_node));
todo_.Pop();
PopScopes();
return Success();
}
void ActionStack::UnwindPast(Nonnull<const Statement*> ast_node,
Nonnull<const Value*> result) {
UnwindPast(ast_node);
auto ActionStack::UnwindPast(Nonnull<const Statement*> ast_node,
Nonnull<const Value*> result) -> ErrorOr<Success> {
RETURN_IF_ERROR(UnwindPast(ast_node));
SetResult(result);
return Success();
}
void ActionStack::Resume(Nonnull<const ContinuationValue*> continuation) {
auto ActionStack::Resume(Nonnull<const ContinuationValue*> continuation)
-> ErrorOr<Success> {
Action& action = *todo_.Top();
action.set_pos(action.pos() + 1);
continuation->stack().RestoreTo(todo_);
return Success();
}
static auto IsRunAction(const Action& action) -> bool {
@@ -194,7 +209,7 @@ static auto IsRunAction(const Action& action) -> bool {
return statement != nullptr && llvm::isa<Run>(statement->statement());
}
void ActionStack::Suspend() {
auto ActionStack::Suspend() -> ErrorOr<Success> {
// Pause the current continuation
todo_.Pop();
std::vector<std::unique_ptr<Action>> paused;
@@ -205,6 +220,7 @@ void ActionStack::Suspend() {
llvm::cast<const ContinuationValue>(*todo_.Top()->results()[0]);
// Update the continuation with the paused stack.
continuation.stack().StoreReversed(std::move(paused));
return Success();
}
void ActionStack::PopScopes() {
+17 -15
View File
@@ -49,7 +49,7 @@ class ActionStack {
// Returns the value bound to `value_node`. If `value_node` is a local
// variable, this will be an LValue.
auto ValueOfNode(ValueNodeView value_node, SourceLocation source_loc) const
-> Nonnull<const Value*>;
-> ErrorOr<Nonnull<const Value*>>;
// Merges `scope` into the innermost scope currently on the stack.
void MergeScope(RuntimeScope scope);
@@ -70,40 +70,42 @@ class ActionStack {
// invoke exactly one transition method, as the very last operation. This is a
// matter of safety as well as convention: most transition methods modify the
// state of the current action, and some of them destroy it. To help enforce
// this requirement, we have a convention of calling these methods as part of
// return statements, e.g. `return todo_.FinishAction()`, even though they
// return void.
// this requirement, we have a convention of making these methods return an
// ErrorOr<Success> even when a method can't actually fail, and calling the
// methods as part of return statements, e.g. `return todo_.FinishAction()`.
// Finishes execution of the current Action. If `result` is specified, it
// represents the result of that Action.
void FinishAction();
void FinishAction(Nonnull<const Value*> result);
auto FinishAction() -> ErrorOr<Success>;
auto FinishAction(Nonnull<const Value*> result) -> ErrorOr<Success>;
// Advances the current action one step, and push `child` onto the stack.
// If `scope` is specified, `child` will be executed in that scope.
void Spawn(std::unique_ptr<Action> child);
void Spawn(std::unique_ptr<Action> child, RuntimeScope scope);
auto Spawn(std::unique_ptr<Action> child) -> ErrorOr<Success>;
auto Spawn(std::unique_ptr<Action> child, RuntimeScope scope)
-> ErrorOr<Success>;
// Advances the current action one step.
void RunAgain();
auto RunAgain() -> ErrorOr<Success>;
// Unwinds Actions from the stack until the StatementAction associated with
// `ast_node` is at the top of the stack.
void UnwindTo(Nonnull<const Statement*> ast_node);
auto UnwindTo(Nonnull<const Statement*> ast_node) -> ErrorOr<Success>;
// Unwinds Actions from the stack until the StatementAction associated with
// `ast_node` has been removed from the stack. If `result` is specified,
// it represents the result of that Action (StatementActions normally cannot
// produce results, but the body of a function can).
void UnwindPast(Nonnull<const Statement*> ast_node);
void UnwindPast(Nonnull<const Statement*> ast_node,
Nonnull<const Value*> result);
auto UnwindPast(Nonnull<const Statement*> ast_node) -> ErrorOr<Success>;
auto UnwindPast(Nonnull<const Statement*> ast_node,
Nonnull<const Value*> result) -> ErrorOr<Success>;
// Resumes execution of a suspended continuation.
void Resume(Nonnull<const ContinuationValue*> continuation);
auto Resume(Nonnull<const ContinuationValue*> continuation)
-> ErrorOr<Success>;
// Suspends execution of the currently-executing continuation.
void Suspend();
auto Suspend() -> ErrorOr<Success>;
private:
// Pop any ScopeActions from the top of the stack, propagating results as
@@ -13,10 +13,11 @@
#include "executable_semantics/interpreter/resolve_control_flow.h"
#include "executable_semantics/interpreter/resolve_names.h"
#include "executable_semantics/interpreter/type_checker.h"
#include "llvm/Support/Error.h"
namespace Carbon {
void ExecProgram(Nonnull<Arena*> arena, AST ast, bool trace) {
auto ExecProgram(Nonnull<Arena*> arena, AST ast, bool trace) -> ErrorOr<int> {
if (trace) {
llvm::outs() << "********** source program **********\n";
for (const auto decl : ast.declarations) {
@@ -32,15 +33,15 @@ void ExecProgram(Nonnull<Arena*> arena, AST ast, bool trace) {
if (trace) {
llvm::outs() << "********** resolving names **********\n";
}
ResolveNames(ast);
RETURN_IF_ERROR(ResolveNames(ast));
if (trace) {
llvm::outs() << "********** resolving control flow **********\n";
}
ResolveControlFlow(ast);
RETURN_IF_ERROR(ResolveControlFlow(ast));
if (trace) {
llvm::outs() << "********** type checking **********\n";
}
TypeChecker(arena, trace).TypeCheck(ast);
RETURN_IF_ERROR(TypeChecker(arena, trace).TypeCheck(ast));
if (trace) {
llvm::outs() << "\n";
llvm::outs() << "********** type checking complete **********\n";
@@ -49,8 +50,9 @@ void ExecProgram(Nonnull<Arena*> arena, AST ast, bool trace) {
}
llvm::outs() << "********** starting execution **********\n";
}
int result = InterpProgram(ast, arena, trace);
ASSIGN_OR_RETURN(const int result, InterpProgram(ast, arena, trace));
llvm::outs() << "result: " << result << "\n";
return result;
}
} // namespace Carbon
@@ -14,7 +14,7 @@
namespace Carbon {
// Runs the top-level declaration list.
void ExecProgram(Nonnull<Arena*> arena, AST ast, bool trace);
auto ExecProgram(Nonnull<Arena*> arena, AST ast, bool trace) -> ErrorOr<int>;
} // namespace Carbon
+16 -12
View File
@@ -6,6 +6,7 @@
#include "executable_semantics/common/error.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Error.h"
namespace Carbon {
@@ -21,26 +22,29 @@ auto Heap::AllocateValue(Nonnull<const Value*> v) -> AllocationId {
}
auto Heap::Read(const Address& a, SourceLocation source_loc) const
-> Nonnull<const Value*> {
this->CheckAlive(a.allocation_, source_loc);
-> ErrorOr<Nonnull<const Value*>> {
RETURN_IF_ERROR(this->CheckAlive(a.allocation_, source_loc));
return values_[a.allocation_.index_]->GetField(arena_, a.field_path_,
source_loc);
}
void Heap::Write(const Address& a, Nonnull<const Value*> v,
SourceLocation source_loc) {
this->CheckAlive(a.allocation_, source_loc);
values_[a.allocation_.index_] = values_[a.allocation_.index_]->SetField(
arena_, a.field_path_, v, source_loc);
auto Heap::Write(const Address& a, Nonnull<const Value*> v,
SourceLocation source_loc) -> ErrorOr<Success> {
RETURN_IF_ERROR(this->CheckAlive(a.allocation_, source_loc));
ASSIGN_OR_RETURN(values_[a.allocation_.index_],
values_[a.allocation_.index_]->SetField(
arena_, a.field_path_, v, source_loc));
return Success();
}
void Heap::CheckAlive(AllocationId allocation,
SourceLocation source_loc) const {
auto Heap::CheckAlive(AllocationId allocation, SourceLocation source_loc) const
-> ErrorOr<Success> {
if (!alive_[allocation.index_]) {
FATAL_RUNTIME_ERROR(source_loc)
<< "undefined behavior: access to dead value "
<< *values_[allocation.index_];
return FATAL_RUNTIME_ERROR(source_loc)
<< "undefined behavior: access to dead value "
<< *values_[allocation.index_];
}
return Success();
}
void Heap::Deallocate(AllocationId allocation) {
+5 -4
View File
@@ -28,12 +28,12 @@ class Heap : public HeapAllocationInterface {
// Returns the value at the given address in the heap after
// checking that it is alive.
auto Read(const Address& a, SourceLocation source_loc) const
-> Nonnull<const Value*>;
-> ErrorOr<Nonnull<const Value*>>;
// Writes the given value at the address in the heap after
// checking that the address is alive.
void Write(const Address& a, Nonnull<const Value*> v,
SourceLocation source_loc);
auto Write(const Address& a, Nonnull<const Value*> v,
SourceLocation source_loc) -> ErrorOr<Success>;
// Put the given value on the heap and mark it as alive.
auto AllocateValue(Nonnull<const Value*> v) -> AllocationId override;
@@ -50,7 +50,8 @@ class Heap : public HeapAllocationInterface {
private:
// Signal an error if the allocation is no longer alive.
void CheckAlive(AllocationId allocation, SourceLocation source_loc) const;
auto CheckAlive(AllocationId allocation, SourceLocation source_loc) const
-> ErrorOr<Success>;
Nonnull<Arena*> arena_;
std::vector<Nonnull<const Value*>> values_;
@@ -23,12 +23,14 @@ void ImplScope::AddParent(Nonnull<const ImplScope*> parent) {
auto ImplScope::Resolve(Nonnull<const Value*> iface_type,
Nonnull<const Value*> type,
SourceLocation source_loc) const -> ValueNodeView {
std::optional<ValueNodeView> result =
TryResolve(iface_type, type, source_loc);
SourceLocation source_loc) const
-> ErrorOr<ValueNodeView> {
ASSIGN_OR_RETURN(std::optional<ValueNodeView> result,
TryResolve(iface_type, type, source_loc));
if (!result.has_value()) {
FATAL_COMPILATION_ERROR(source_loc) << "could not find implementation of "
<< *iface_type << " for " << *type;
return FATAL_COMPILATION_ERROR(source_loc)
<< "could not find implementation of " << *iface_type << " for "
<< *type;
}
return *result;
}
@@ -36,18 +38,20 @@ auto ImplScope::Resolve(Nonnull<const Value*> iface_type,
auto ImplScope::TryResolve(Nonnull<const Value*> iface_type,
Nonnull<const Value*> type,
SourceLocation source_loc) const
-> std::optional<ValueNodeView> {
-> ErrorOr<std::optional<ValueNodeView>> {
std::optional<ValueNodeView> result =
ResolveHere(iface_type, type, source_loc);
if (result.has_value()) {
return result;
}
for (Nonnull<const ImplScope*> parent : parent_scopes_) {
auto parent_result = parent->TryResolve(iface_type, type, source_loc);
ASSIGN_OR_RETURN(auto parent_result,
parent->TryResolve(iface_type, type, source_loc));
if (parent_result.has_value() && result.has_value() &&
*parent_result != *result) {
FATAL_COMPILATION_ERROR(source_loc)
<< "ambiguous implementations of " << *iface_type << " for " << *type;
return FATAL_COMPILATION_ERROR(source_loc)
<< "ambiguous implementations of " << *iface_type << " for "
<< *type;
}
result = parent_result;
}
@@ -50,12 +50,12 @@ class ImplScope {
// the ancestor graph of this scope, or reports a compilation error
// at `source_loc` there isn't exactly one matching impl.
auto Resolve(Nonnull<const Value*> iface, Nonnull<const Value*> type,
SourceLocation source_loc) const -> ValueNodeView;
SourceLocation source_loc) const -> ErrorOr<ValueNodeView>;
private:
auto TryResolve(Nonnull<const Value*> iface_type, Nonnull<const Value*> type,
SourceLocation source_loc) const
-> std::optional<ValueNodeView>;
-> ErrorOr<std::optional<ValueNodeView>>;
auto ResolveHere(Nonnull<const Value*> iface_type,
Nonnull<const Value*> impl_type,
SourceLocation source_loc) const
@@ -21,6 +21,7 @@
#include "executable_semantics/interpreter/stack.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Error.h"
using llvm::cast;
using llvm::dyn_cast;
@@ -58,7 +59,8 @@ class Interpreter {
~Interpreter();
// Runs all the steps of `action`.
void RunAllSteps(std::unique_ptr<Action> action);
// It's not safe to call `RunAllSteps()` or `result()` after an error.
auto RunAllSteps(std::unique_ptr<Action> action) -> ErrorOr<Success>;
// The result produced by the `action` argument of the most recent
// RunAllSteps call. Cannot be called if `action` was an action that doesn't
@@ -66,25 +68,25 @@ class Interpreter {
auto result() const -> Nonnull<const Value*> { return todo_.result(); }
private:
void Step();
auto Step() -> ErrorOr<Success>;
// State transitions for expressions.
void StepExp();
auto StepExp() -> ErrorOr<Success>;
// State transitions for lvalues.
void StepLvalue();
auto StepLvalue() -> ErrorOr<Success>;
// State transitions for patterns.
void StepPattern();
auto StepPattern() -> ErrorOr<Success>;
// State transition for statements.
void StepStmt();
auto StepStmt() -> ErrorOr<Success>;
// State transition for declarations.
void StepDeclaration();
auto StepDeclaration() -> ErrorOr<Success>;
auto CreateStruct(const std::vector<FieldInitializer>& fields,
const std::vector<Nonnull<const Value*>>& values)
-> Nonnull<const Value*>;
auto EvalPrim(Operator op, const std::vector<Nonnull<const Value*>>& args,
SourceLocation source_loc) -> Nonnull<const Value*>;
SourceLocation source_loc) -> ErrorOr<Nonnull<const Value*>>;
// Returns the result of converting `value` to type `destination_type`.
auto Convert(Nonnull<const Value*> value,
@@ -129,7 +131,8 @@ void Interpreter::PrintState(llvm::raw_ostream& out) {
auto Interpreter::EvalPrim(Operator op,
const std::vector<Nonnull<const Value*>>& args,
SourceLocation source_loc) -> Nonnull<const Value*> {
SourceLocation source_loc)
-> ErrorOr<Nonnull<const Value*>> {
switch (op) {
case Operator::Neg:
return arena_->New<IntValue>(-cast<IntValue>(*args[0]).value());
@@ -257,7 +260,7 @@ auto PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
}
}
void Interpreter::StepLvalue() {
auto Interpreter::StepLvalue() -> ErrorOr<Success> {
Action& act = todo_.CurrentAction();
const Expression& exp = cast<LValAction>(act).expression();
if (trace_) {
@@ -268,8 +271,10 @@ void Interpreter::StepLvalue() {
case ExpressionKind::IdentifierExpression: {
// { {x :: C, E, F} :: S, H}
// -> { {E(x) :: C, E, F} :: S, H}
Nonnull<const Value*> value = todo_.ValueOfNode(
cast<IdentifierExpression>(exp).value_node(), exp.source_loc());
ASSIGN_OR_RETURN(
Nonnull<const Value*> value,
todo_.ValueOfNode(cast<IdentifierExpression>(exp).value_node(),
exp.source_loc()));
CHECK(isa<LValue>(value)) << *value;
return todo_.FinishAction(value);
}
@@ -418,7 +423,7 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
}
}
void Interpreter::StepExp() {
auto Interpreter::StepExp() -> ErrorOr<Success> {
Action& act = todo_.CurrentAction();
const Expression& exp = cast<ExpressionAction>(act).expression();
if (trace_) {
@@ -441,8 +446,8 @@ void Interpreter::StepExp() {
const auto& tuple = cast<TupleValue>(*act.results()[0]);
int i = cast<IntValue>(*act.results()[1]).value();
if (i < 0 || i >= static_cast<int>(tuple.elements().size())) {
FATAL_RUNTIME_ERROR_NO_LINE()
<< "index " << i << " out of range in " << tuple;
return FATAL_RUNTIME_ERROR_NO_LINE()
<< "index " << i << " out of range in " << tuple;
}
return todo_.FinishAction(tuple.elements()[i]);
}
@@ -495,15 +500,19 @@ void Interpreter::StepExp() {
// -> { { v_f :: C, E, F} : S, H}
std::optional<Nonnull<const Witness*>> witness = std::nullopt;
if (access.impl().has_value()) {
auto witness_addr =
todo_.ValueOfNode(*access.impl(), access.source_loc());
witness = cast<Witness>(
ASSIGN_OR_RETURN(
auto witness_addr,
todo_.ValueOfNode(*access.impl(), access.source_loc()));
ASSIGN_OR_RETURN(
Nonnull<const Value*> witness_value,
heap_.Read(llvm::cast<LValue>(witness_addr)->address(),
access.source_loc()));
witness = cast<Witness>(witness_value);
}
FieldPath::Component field(access.field(), witness);
Nonnull<const Value*> member = act.results()[0]->GetField(
arena_, FieldPath(field), exp.source_loc());
ASSIGN_OR_RETURN(Nonnull<const Value*> member,
act.results()[0]->GetField(arena_, FieldPath(field),
exp.source_loc()));
return todo_.FinishAction(member);
}
}
@@ -511,10 +520,12 @@ void Interpreter::StepExp() {
CHECK(act.pos() == 0);
const auto& ident = cast<IdentifierExpression>(exp);
// { {x :: C, E, F} :: S, H} -> { {H(E(x)) :: C, E, F} :: S, H}
Nonnull<const Value*> value =
todo_.ValueOfNode(ident.value_node(), ident.source_loc());
ASSIGN_OR_RETURN(
Nonnull<const Value*> value,
todo_.ValueOfNode(ident.value_node(), ident.source_loc()));
if (const auto* lvalue = dyn_cast<LValue>(value)) {
value = heap_.Read(lvalue->address(), exp.source_loc());
ASSIGN_OR_RETURN(value,
heap_.Read(lvalue->address(), exp.source_loc()));
}
return todo_.FinishAction(value);
}
@@ -542,8 +553,9 @@ void Interpreter::StepExp() {
} else {
// { {v :: op(vs,[]) :: C, E, F} :: S, H}
// -> { {eval_prim(op, (vs,v)) :: C, E, F} :: S, H}
return todo_.FinishAction(
EvalPrim(op.op(), act.results(), exp.source_loc()));
ASSIGN_OR_RETURN(Nonnull<const Value*> value,
EvalPrim(op.op(), act.results(), exp.source_loc()));
return todo_.FinishAction(value);
}
}
case ExpressionKind::CallExpression:
@@ -576,11 +588,12 @@ void Interpreter::StepExp() {
// Bring the impl witness tables into scope.
for (const auto& [impl_bind, impl_node] :
cast<CallExpression>(exp).impls()) {
Nonnull<const Value*> witness =
todo_.ValueOfNode(impl_node, exp.source_loc());
ASSIGN_OR_RETURN(Nonnull<const Value*> witness,
todo_.ValueOfNode(impl_node, exp.source_loc()));
if (witness->kind() == Value::Kind::LValue) {
const auto& lval = cast<LValue>(*witness);
witness = heap_.Read(lval.address(), exp.source_loc());
ASSIGN_OR_RETURN(witness,
heap_.Read(lval.address(), exp.source_loc()));
}
function_scope.Initialize(impl_bind, witness);
}
@@ -610,8 +623,8 @@ void Interpreter::StepExp() {
std::move(method_scope));
}
default:
FATAL_RUNTIME_ERROR(exp.source_loc())
<< "in call, expected a function, not " << *act.results()[0];
return FATAL_RUNTIME_ERROR(exp.source_loc())
<< "in call, expected a function, not " << *act.results()[0];
}
} else if (act.pos() == 3) {
if (act.results().size() < 3) {
@@ -701,7 +714,7 @@ void Interpreter::StepExp() {
} // switch (exp->kind)
}
void Interpreter::StepPattern() {
auto Interpreter::StepPattern() -> ErrorOr<Success> {
Action& act = todo_.CurrentAction();
const Pattern& pattern = cast<PatternAction>(act).pattern();
if (trace_) {
@@ -768,7 +781,7 @@ void Interpreter::StepPattern() {
}
}
void Interpreter::StepStmt() {
auto Interpreter::StepStmt() -> ErrorOr<Success> {
Action& act = todo_.CurrentAction();
const Statement& stmt = cast<StatementAction>(act).statement();
if (trace_) {
@@ -901,7 +914,7 @@ void Interpreter::StepStmt() {
const auto& lval = cast<LValue>(*act.results()[0]);
Nonnull<const Value*> rval =
Convert(act.results()[1], &assign.lhs().static_type());
heap_.Write(lval.address(), rval, stmt.source_loc());
RETURN_IF_ERROR(heap_.Write(lval.address(), rval, stmt.source_loc()));
return todo_.FinishAction();
}
}
@@ -977,7 +990,7 @@ void Interpreter::StepStmt() {
}
}
void Interpreter::StepDeclaration() {
auto Interpreter::StepDeclaration() -> ErrorOr<Success> {
Action& act = todo_.CurrentAction();
const Declaration& decl = cast<DeclarationAction>(act).declaration();
if (trace_) {
@@ -1009,72 +1022,79 @@ void Interpreter::StepDeclaration() {
}
// State transition.
void Interpreter::Step() {
auto Interpreter::Step() -> ErrorOr<Success> {
Action& act = todo_.CurrentAction();
switch (act.kind()) {
case Action::Kind::LValAction:
StepLvalue();
RETURN_IF_ERROR(StepLvalue());
break;
case Action::Kind::ExpressionAction:
StepExp();
RETURN_IF_ERROR(StepExp());
break;
case Action::Kind::PatternAction:
StepPattern();
RETURN_IF_ERROR(StepPattern());
break;
case Action::Kind::StatementAction:
StepStmt();
RETURN_IF_ERROR(StepStmt());
break;
case Action::Kind::DeclarationAction:
StepDeclaration();
RETURN_IF_ERROR(StepDeclaration());
break;
case Action::Kind::ScopeAction:
FATAL() << "ScopeAction escaped ActionStack";
} // switch
return Success();
}
void Interpreter::RunAllSteps(std::unique_ptr<Action> action) {
auto Interpreter::RunAllSteps(std::unique_ptr<Action> action)
-> ErrorOr<Success> {
if (trace_) {
PrintState(llvm::outs());
}
todo_.Start(std::move(action));
while (!todo_.IsEmpty()) {
Step();
RETURN_IF_ERROR(Step());
if (trace_) {
PrintState(llvm::outs());
}
}
return Success();
}
auto InterpProgram(const AST& ast, Nonnull<Arena*> arena, bool trace) -> int {
auto InterpProgram(const AST& ast, Nonnull<Arena*> arena, bool trace)
-> ErrorOr<int> {
Interpreter interpreter(Phase::RunTime, arena, trace);
if (trace) {
llvm::outs() << "********** initializing globals **********\n";
}
for (Nonnull<Declaration*> declaration : ast.declarations) {
interpreter.RunAllSteps(std::make_unique<DeclarationAction>(declaration));
RETURN_IF_ERROR(interpreter.RunAllSteps(
std::make_unique<DeclarationAction>(declaration)));
}
if (trace) {
llvm::outs() << "********** calling main function **********\n";
}
interpreter.RunAllSteps(std::make_unique<ExpressionAction>(*ast.main_call));
RETURN_IF_ERROR(interpreter.RunAllSteps(
std::make_unique<ExpressionAction>(*ast.main_call)));
return cast<IntValue>(*interpreter.result()).value();
}
auto InterpExp(Nonnull<const Expression*> e, Nonnull<Arena*> arena, bool trace)
-> Nonnull<const Value*> {
-> ErrorOr<Nonnull<const Value*>> {
Interpreter interpreter(Phase::CompileTime, arena, trace);
interpreter.RunAllSteps(std::make_unique<ExpressionAction>(e));
RETURN_IF_ERROR(
interpreter.RunAllSteps(std::make_unique<ExpressionAction>(e)));
return interpreter.result();
}
auto InterpPattern(Nonnull<const Pattern*> p, Nonnull<Arena*> arena, bool trace)
-> Nonnull<const Value*> {
-> ErrorOr<Nonnull<const Value*>> {
Interpreter interpreter(Phase::CompileTime, arena, trace);
interpreter.RunAllSteps(std::make_unique<PatternAction>(p));
RETURN_IF_ERROR(interpreter.RunAllSteps(std::make_unique<PatternAction>(p)));
return interpreter.result();
}
@@ -23,19 +23,20 @@ namespace Carbon {
// Interprets the program defined by `ast`, allocating values on `arena` and
// printing traces if `trace` is true.
auto InterpProgram(const AST& ast, Nonnull<Arena*> arena, bool trace) -> int;
auto InterpProgram(const AST& ast, Nonnull<Arena*> arena, bool trace)
-> ErrorOr<int>;
// Interprets `e` at compile-time, allocating values on `arena` and
// printing traces if `trace` is true. The caller must ensure that all the
// code this evaluates has been typechecked.
auto InterpExp(Nonnull<const Expression*> e, Nonnull<Arena*> arena, bool trace)
-> Nonnull<const Value*>;
-> ErrorOr<Nonnull<const Value*>>;
// Interprets `p` at compile-time, allocating values on `arena` and
// printing traces if `trace` is true. The caller must ensure that all the
// code this evaluates has been typechecked.
auto InterpPattern(Nonnull<const Pattern*> p, Nonnull<Arena*> arena, bool trace)
-> Nonnull<const Value*>;
-> ErrorOr<Nonnull<const Value*>>;
// Attempts to match `v` against the pattern `p`, returning whether matching
// is successful. If it is, populates **bindings with the variables bound by
@@ -9,6 +9,7 @@
#include "executable_semantics/ast/statement.h"
#include "executable_semantics/common/error.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Error.h"
using llvm::cast;
@@ -31,114 +32,121 @@ struct FunctionData {
// belongs to, and that information may be updated by this call. `function`
// can be nullopt if `statement` does not belong to a function body, for
// example if it is part of a continuation body instead.
static void ResolveControlFlow(Nonnull<Statement*> statement,
static auto ResolveControlFlow(Nonnull<Statement*> statement,
std::optional<Nonnull<const Statement*>> loop,
std::optional<Nonnull<FunctionData*>> function) {
std::optional<Nonnull<FunctionData*>> function)
-> ErrorOr<Success> {
switch (statement->kind()) {
case StatementKind::Return: {
if (!function.has_value()) {
FATAL_COMPILATION_ERROR(statement->source_loc())
<< "return is not within a function body";
return FATAL_COMPILATION_ERROR(statement->source_loc())
<< "return is not within a function body";
}
const ReturnTerm& function_return =
(*function)->declaration->return_term();
if (function_return.is_auto()) {
if ((*function)->saw_return_in_auto) {
FATAL_COMPILATION_ERROR(statement->source_loc())
<< "Only one return is allowed in a function with an `auto` "
"return type.";
return FATAL_COMPILATION_ERROR(statement->source_loc())
<< "Only one return is allowed in a function with an `auto` "
"return type.";
}
(*function)->saw_return_in_auto = true;
}
auto& ret = cast<Return>(*statement);
ret.set_function((*function)->declaration);
if (ret.is_omitted_expression() != function_return.is_omitted()) {
FATAL_COMPILATION_ERROR(ret.source_loc())
<< ret << " should" << (function_return.is_omitted() ? " not" : "")
<< " provide a return value, to match the function's signature.";
return FATAL_COMPILATION_ERROR(ret.source_loc())
<< ret << " should"
<< (function_return.is_omitted() ? " not" : "")
<< " provide a return value, to match the function's signature.";
}
return;
return Success();
}
case StatementKind::Break:
if (!loop.has_value()) {
FATAL_COMPILATION_ERROR(statement->source_loc())
<< "break is not within a loop body";
return FATAL_COMPILATION_ERROR(statement->source_loc())
<< "break is not within a loop body";
}
cast<Break>(*statement).set_loop(*loop);
return;
return Success();
case StatementKind::Continue:
if (!loop.has_value()) {
FATAL_COMPILATION_ERROR(statement->source_loc())
<< "continue is not within a loop body";
return FATAL_COMPILATION_ERROR(statement->source_loc())
<< "continue is not within a loop body";
}
cast<Continue>(*statement).set_loop(*loop);
return;
return Success();
case StatementKind::If: {
auto& if_stmt = cast<If>(*statement);
ResolveControlFlow(&if_stmt.then_block(), loop, function);
RETURN_IF_ERROR(
ResolveControlFlow(&if_stmt.then_block(), loop, function));
if (if_stmt.else_block().has_value()) {
ResolveControlFlow(*if_stmt.else_block(), loop, function);
RETURN_IF_ERROR(
ResolveControlFlow(*if_stmt.else_block(), loop, function));
}
return;
return Success();
}
case StatementKind::Block: {
auto& block = cast<Block>(*statement);
for (auto* block_statement : block.statements()) {
ResolveControlFlow(block_statement, loop, function);
RETURN_IF_ERROR(ResolveControlFlow(block_statement, loop, function));
}
return;
return Success();
}
case StatementKind::While:
ResolveControlFlow(&cast<While>(*statement).body(), statement, function);
return;
RETURN_IF_ERROR(ResolveControlFlow(&cast<While>(*statement).body(),
statement, function));
return Success();
case StatementKind::Match: {
auto& match = cast<Match>(*statement);
for (Match::Clause& clause : match.clauses()) {
ResolveControlFlow(&clause.statement(), loop, function);
RETURN_IF_ERROR(
ResolveControlFlow(&clause.statement(), loop, function));
}
return;
return Success();
}
case StatementKind::Continuation:
ResolveControlFlow(&cast<Continuation>(*statement).body(), std::nullopt,
std::nullopt);
return;
RETURN_IF_ERROR(ResolveControlFlow(&cast<Continuation>(*statement).body(),
std::nullopt, std::nullopt));
return Success();
case StatementKind::ExpressionStatement:
case StatementKind::Assign:
case StatementKind::VariableDefinition:
case StatementKind::Run:
case StatementKind::Await:
return;
return Success();
}
}
void ResolveControlFlow(Nonnull<Declaration*> declaration) {
auto ResolveControlFlow(Nonnull<Declaration*> declaration) -> ErrorOr<Success> {
switch (declaration->kind()) {
case DeclarationKind::FunctionDeclaration: {
auto& function = cast<FunctionDeclaration>(*declaration);
if (function.body().has_value()) {
FunctionData data = {.declaration = &function};
ResolveControlFlow(*function.body(), std::nullopt, &data);
RETURN_IF_ERROR(
ResolveControlFlow(*function.body(), std::nullopt, &data));
}
break;
}
case DeclarationKind::ClassDeclaration: {
auto& class_decl = cast<ClassDeclaration>(*declaration);
for (Nonnull<Declaration*> member : class_decl.members()) {
ResolveControlFlow(member);
RETURN_IF_ERROR(ResolveControlFlow(member));
}
break;
}
case DeclarationKind::InterfaceDeclaration: {
auto& iface_decl = cast<InterfaceDeclaration>(*declaration);
for (Nonnull<Declaration*> member : iface_decl.members()) {
ResolveControlFlow(member);
RETURN_IF_ERROR(ResolveControlFlow(member));
}
break;
}
case DeclarationKind::ImplDeclaration: {
auto& impl_decl = cast<ImplDeclaration>(*declaration);
for (Nonnull<Declaration*> member : impl_decl.members()) {
ResolveControlFlow(member);
RETURN_IF_ERROR(ResolveControlFlow(member));
}
break;
}
@@ -147,12 +155,14 @@ void ResolveControlFlow(Nonnull<Declaration*> declaration) {
// do nothing
break;
}
return Success();
}
void ResolveControlFlow(AST& ast) {
auto ResolveControlFlow(AST& ast) -> ErrorOr<Success> {
for (auto declaration : ast.declarations) {
ResolveControlFlow(declaration);
RETURN_IF_ERROR(ResolveControlFlow(declaration));
}
return Success();
}
} // namespace Carbon
@@ -12,7 +12,9 @@ namespace Carbon {
// Resolves non-local control-flow edges, such as `break` and `return`, in the
// given AST.
void ResolveControlFlow(AST& ast);
// On failure, `ast` is left in a partial state and should not be further
// processed.
auto ResolveControlFlow(AST& ast) -> ErrorOr<Success>;
} // namespace Carbon
@@ -12,21 +12,22 @@
#include "executable_semantics/ast/statement.h"
#include "executable_semantics/ast/static_scope.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Error.h"
using llvm::cast;
namespace Carbon {
// Adds the names exposed by the given AST node to enclosing_scope.
static void AddExposedNames(const Declaration& declaration,
StaticScope& enclosing_scope);
static auto AddExposedNames(const Declaration& declaration,
StaticScope& enclosing_scope) -> ErrorOr<Success>;
static void AddExposedNames(const Declaration& declaration,
StaticScope& enclosing_scope) {
static auto AddExposedNames(const Declaration& declaration,
StaticScope& enclosing_scope) -> ErrorOr<Success> {
switch (declaration.kind()) {
case DeclarationKind::InterfaceDeclaration: {
auto& iface_decl = cast<InterfaceDeclaration>(declaration);
enclosing_scope.Add(iface_decl.name(), &iface_decl);
RETURN_IF_ERROR(enclosing_scope.Add(iface_decl.name(), &iface_decl));
break;
}
case DeclarationKind::ImplDeclaration: {
@@ -35,26 +36,28 @@ static void AddExposedNames(const Declaration& declaration,
}
case DeclarationKind::FunctionDeclaration: {
auto& func = cast<FunctionDeclaration>(declaration);
enclosing_scope.Add(func.name(), &func);
RETURN_IF_ERROR(enclosing_scope.Add(func.name(), &func));
break;
}
case DeclarationKind::ClassDeclaration: {
auto& class_decl = cast<ClassDeclaration>(declaration);
enclosing_scope.Add(class_decl.name(), &class_decl);
RETURN_IF_ERROR(enclosing_scope.Add(class_decl.name(), &class_decl));
break;
}
case DeclarationKind::ChoiceDeclaration: {
auto& choice = cast<ChoiceDeclaration>(declaration);
enclosing_scope.Add(choice.name(), &choice);
RETURN_IF_ERROR(enclosing_scope.Add(choice.name(), &choice));
break;
}
case DeclarationKind::VariableDeclaration:
auto& var = cast<VariableDeclaration>(declaration);
if (var.binding().name() != AnonymousName) {
enclosing_scope.Add(var.binding().name(), &var.binding());
RETURN_IF_ERROR(
enclosing_scope.Add(var.binding().name(), &var.binding()));
}
return;
break;
}
return Success();
}
// Traverses the sub-AST rooted at the given node, resolving all names within
@@ -67,76 +70,85 @@ static void AddExposedNames(const Declaration& declaration,
// calling AddExposedNames on each element of the scope to populate a
// StaticScope, and then calling ResolveNames on each element, passing it the
// already-populated StaticScope.
static void ResolveNames(Expression& expression,
const StaticScope& enclosing_scope);
static void ResolveNames(Pattern& pattern, StaticScope& enclosing_scope);
static void ResolveNames(Statement& statement, StaticScope& enclosing_scope);
static void ResolveNames(Declaration& declaration,
StaticScope& enclosing_scope);
static auto ResolveNames(Expression& expression,
const StaticScope& enclosing_scope)
-> ErrorOr<Success>;
static auto ResolveNames(Pattern& pattern, StaticScope& enclosing_scope)
-> ErrorOr<Success>;
static auto ResolveNames(Statement& statement, StaticScope& enclosing_scope)
-> ErrorOr<Success>;
static auto ResolveNames(Declaration& declaration, StaticScope& enclosing_scope)
-> ErrorOr<Success>;
static void ResolveNames(Expression& expression,
const StaticScope& enclosing_scope) {
static auto ResolveNames(Expression& expression,
const StaticScope& enclosing_scope)
-> ErrorOr<Success> {
switch (expression.kind()) {
case ExpressionKind::CallExpression: {
auto& call = cast<CallExpression>(expression);
ResolveNames(call.function(), enclosing_scope);
ResolveNames(call.argument(), enclosing_scope);
RETURN_IF_ERROR(ResolveNames(call.function(), enclosing_scope));
RETURN_IF_ERROR(ResolveNames(call.argument(), enclosing_scope));
break;
}
case ExpressionKind::FunctionTypeLiteral: {
auto& fun_type = cast<FunctionTypeLiteral>(expression);
ResolveNames(fun_type.parameter(), enclosing_scope);
ResolveNames(fun_type.return_type(), enclosing_scope);
RETURN_IF_ERROR(ResolveNames(fun_type.parameter(), enclosing_scope));
RETURN_IF_ERROR(ResolveNames(fun_type.return_type(), enclosing_scope));
break;
}
case ExpressionKind::FieldAccessExpression:
ResolveNames(cast<FieldAccessExpression>(expression).aggregate(),
enclosing_scope);
RETURN_IF_ERROR(
ResolveNames(cast<FieldAccessExpression>(expression).aggregate(),
enclosing_scope));
break;
case ExpressionKind::IndexExpression: {
auto& index = cast<IndexExpression>(expression);
ResolveNames(index.aggregate(), enclosing_scope);
ResolveNames(index.offset(), enclosing_scope);
RETURN_IF_ERROR(ResolveNames(index.aggregate(), enclosing_scope));
RETURN_IF_ERROR(ResolveNames(index.offset(), enclosing_scope));
break;
}
case ExpressionKind::PrimitiveOperatorExpression:
for (Nonnull<Expression*> operand :
cast<PrimitiveOperatorExpression>(expression).arguments()) {
ResolveNames(*operand, enclosing_scope);
RETURN_IF_ERROR(ResolveNames(*operand, enclosing_scope));
}
break;
case ExpressionKind::TupleLiteral:
for (Nonnull<Expression*> field :
cast<TupleLiteral>(expression).fields()) {
ResolveNames(*field, enclosing_scope);
RETURN_IF_ERROR(ResolveNames(*field, enclosing_scope));
}
break;
case ExpressionKind::StructLiteral:
for (FieldInitializer& init : cast<StructLiteral>(expression).fields()) {
ResolveNames(init.expression(), enclosing_scope);
RETURN_IF_ERROR(ResolveNames(init.expression(), enclosing_scope));
}
break;
case ExpressionKind::StructTypeLiteral:
for (FieldInitializer& init :
cast<StructTypeLiteral>(expression).fields()) {
ResolveNames(init.expression(), enclosing_scope);
RETURN_IF_ERROR(ResolveNames(init.expression(), enclosing_scope));
}
break;
case ExpressionKind::IdentifierExpression: {
auto& identifier = cast<IdentifierExpression>(expression);
identifier.set_value_node(
ASSIGN_OR_RETURN(
const auto value_node,
enclosing_scope.Resolve(identifier.name(), identifier.source_loc()));
identifier.set_value_node(value_node);
break;
}
case ExpressionKind::IntrinsicExpression:
ResolveNames(cast<IntrinsicExpression>(expression).args(),
enclosing_scope);
RETURN_IF_ERROR(ResolveNames(cast<IntrinsicExpression>(expression).args(),
enclosing_scope));
break;
case ExpressionKind::IfExpression: {
auto& if_expr = cast<IfExpression>(expression);
ResolveNames(*if_expr.condition(), enclosing_scope);
ResolveNames(*if_expr.then_expression(), enclosing_scope);
ResolveNames(*if_expr.else_expression(), enclosing_scope);
RETURN_IF_ERROR(ResolveNames(*if_expr.condition(), enclosing_scope));
RETURN_IF_ERROR(
ResolveNames(*if_expr.then_expression(), enclosing_scope));
RETURN_IF_ERROR(
ResolveNames(*if_expr.else_expression(), enclosing_scope));
break;
}
case ExpressionKind::BoolTypeLiteral:
@@ -151,140 +163,149 @@ static void ResolveNames(Expression& expression,
case ExpressionKind::UnimplementedExpression:
FATAL() << "Unimplemented";
}
return Success();
}
static void ResolveNames(Pattern& pattern, StaticScope& enclosing_scope) {
static auto ResolveNames(Pattern& pattern, StaticScope& enclosing_scope)
-> ErrorOr<Success> {
switch (pattern.kind()) {
case PatternKind::BindingPattern: {
auto& binding = cast<BindingPattern>(pattern);
ResolveNames(binding.type(), enclosing_scope);
RETURN_IF_ERROR(ResolveNames(binding.type(), enclosing_scope));
if (binding.name() != AnonymousName) {
enclosing_scope.Add(binding.name(), &binding);
RETURN_IF_ERROR(enclosing_scope.Add(binding.name(), &binding));
}
break;
}
case PatternKind::TuplePattern:
for (Nonnull<Pattern*> field : cast<TuplePattern>(pattern).fields()) {
ResolveNames(*field, enclosing_scope);
RETURN_IF_ERROR(ResolveNames(*field, enclosing_scope));
}
break;
case PatternKind::AlternativePattern: {
auto& alternative = cast<AlternativePattern>(pattern);
ResolveNames(alternative.choice_type(), enclosing_scope);
ResolveNames(alternative.arguments(), enclosing_scope);
RETURN_IF_ERROR(ResolveNames(alternative.choice_type(), enclosing_scope));
RETURN_IF_ERROR(ResolveNames(alternative.arguments(), enclosing_scope));
break;
}
case PatternKind::ExpressionPattern:
ResolveNames(cast<ExpressionPattern>(pattern).expression(),
enclosing_scope);
RETURN_IF_ERROR(ResolveNames(
cast<ExpressionPattern>(pattern).expression(), enclosing_scope));
break;
case PatternKind::AutoPattern:
break;
case PatternKind::VarPattern:
ResolveNames(cast<VarPattern>(pattern).pattern(), enclosing_scope);
RETURN_IF_ERROR(
ResolveNames(cast<VarPattern>(pattern).pattern(), enclosing_scope));
break;
}
return Success();
}
static void ResolveNames(Statement& statement, StaticScope& enclosing_scope) {
static auto ResolveNames(Statement& statement, StaticScope& enclosing_scope)
-> ErrorOr<Success> {
switch (statement.kind()) {
case StatementKind::ExpressionStatement:
ResolveNames(cast<ExpressionStatement>(statement).expression(),
enclosing_scope);
RETURN_IF_ERROR(ResolveNames(
cast<ExpressionStatement>(statement).expression(), enclosing_scope));
break;
case StatementKind::Assign: {
auto& assign = cast<Assign>(statement);
ResolveNames(assign.lhs(), enclosing_scope);
ResolveNames(assign.rhs(), enclosing_scope);
RETURN_IF_ERROR(ResolveNames(assign.lhs(), enclosing_scope));
RETURN_IF_ERROR(ResolveNames(assign.rhs(), enclosing_scope));
break;
}
case StatementKind::VariableDefinition: {
auto& def = cast<VariableDefinition>(statement);
ResolveNames(def.init(), enclosing_scope);
ResolveNames(def.pattern(), enclosing_scope);
RETURN_IF_ERROR(ResolveNames(def.init(), enclosing_scope));
RETURN_IF_ERROR(ResolveNames(def.pattern(), enclosing_scope));
break;
}
case StatementKind::If: {
auto& if_stmt = cast<If>(statement);
ResolveNames(if_stmt.condition(), enclosing_scope);
ResolveNames(if_stmt.then_block(), enclosing_scope);
RETURN_IF_ERROR(ResolveNames(if_stmt.condition(), enclosing_scope));
RETURN_IF_ERROR(ResolveNames(if_stmt.then_block(), enclosing_scope));
if (if_stmt.else_block().has_value()) {
ResolveNames(**if_stmt.else_block(), enclosing_scope);
RETURN_IF_ERROR(ResolveNames(**if_stmt.else_block(), enclosing_scope));
}
break;
}
case StatementKind::Return:
ResolveNames(cast<Return>(statement).expression(), enclosing_scope);
RETURN_IF_ERROR(
ResolveNames(cast<Return>(statement).expression(), enclosing_scope));
break;
case StatementKind::Block: {
auto& block = cast<Block>(statement);
StaticScope block_scope;
block_scope.AddParent(&enclosing_scope);
for (Nonnull<Statement*> sub_statement : block.statements()) {
ResolveNames(*sub_statement, block_scope);
RETURN_IF_ERROR(ResolveNames(*sub_statement, block_scope));
}
break;
}
case StatementKind::While: {
auto& while_stmt = cast<While>(statement);
ResolveNames(while_stmt.condition(), enclosing_scope);
ResolveNames(while_stmt.body(), enclosing_scope);
RETURN_IF_ERROR(ResolveNames(while_stmt.condition(), enclosing_scope));
RETURN_IF_ERROR(ResolveNames(while_stmt.body(), enclosing_scope));
break;
}
case StatementKind::Match: {
auto& match = cast<Match>(statement);
ResolveNames(match.expression(), enclosing_scope);
RETURN_IF_ERROR(ResolveNames(match.expression(), enclosing_scope));
for (Match::Clause& clause : match.clauses()) {
StaticScope clause_scope;
clause_scope.AddParent(&enclosing_scope);
ResolveNames(clause.pattern(), clause_scope);
ResolveNames(clause.statement(), clause_scope);
RETURN_IF_ERROR(ResolveNames(clause.pattern(), clause_scope));
RETURN_IF_ERROR(ResolveNames(clause.statement(), clause_scope));
}
break;
}
case StatementKind::Continuation: {
auto& continuation = cast<Continuation>(statement);
enclosing_scope.Add(continuation.name(), &continuation);
RETURN_IF_ERROR(enclosing_scope.Add(continuation.name(), &continuation));
StaticScope continuation_scope;
continuation_scope.AddParent(&enclosing_scope);
ResolveNames(cast<Continuation>(statement).body(), continuation_scope);
RETURN_IF_ERROR(ResolveNames(cast<Continuation>(statement).body(),
continuation_scope));
break;
}
case StatementKind::Run:
ResolveNames(cast<Run>(statement).argument(), enclosing_scope);
RETURN_IF_ERROR(
ResolveNames(cast<Run>(statement).argument(), enclosing_scope));
break;
case StatementKind::Await:
case StatementKind::Break:
case StatementKind::Continue:
break;
}
return Success();
}
static void ResolveNames(Declaration& declaration,
StaticScope& enclosing_scope) {
static auto ResolveNames(Declaration& declaration, StaticScope& enclosing_scope)
-> ErrorOr<Success> {
switch (declaration.kind()) {
case DeclarationKind::InterfaceDeclaration: {
auto& iface = cast<InterfaceDeclaration>(declaration);
StaticScope iface_scope;
iface_scope.AddParent(&enclosing_scope);
iface_scope.Add("Self", iface.self());
RETURN_IF_ERROR(iface_scope.Add("Self", iface.self()));
for (Nonnull<Declaration*> member : iface.members()) {
AddExposedNames(*member, iface_scope);
RETURN_IF_ERROR(AddExposedNames(*member, iface_scope));
}
for (Nonnull<Declaration*> member : iface.members()) {
ResolveNames(*member, iface_scope);
RETURN_IF_ERROR(ResolveNames(*member, iface_scope));
}
break;
}
case DeclarationKind::ImplDeclaration: {
auto& impl = cast<ImplDeclaration>(declaration);
ResolveNames(impl.interface(), enclosing_scope);
ResolveNames(*impl.impl_type(), enclosing_scope);
RETURN_IF_ERROR(ResolveNames(impl.interface(), enclosing_scope));
RETURN_IF_ERROR(ResolveNames(*impl.impl_type(), enclosing_scope));
for (Nonnull<Declaration*> member : impl.members()) {
AddExposedNames(*member, enclosing_scope);
RETURN_IF_ERROR(AddExposedNames(*member, enclosing_scope));
}
for (Nonnull<Declaration*> member : impl.members()) {
ResolveNames(*member, enclosing_scope);
RETURN_IF_ERROR(ResolveNames(*member, enclosing_scope));
}
break;
}
@@ -293,19 +314,19 @@ static void ResolveNames(Declaration& declaration,
StaticScope function_scope;
function_scope.AddParent(&enclosing_scope);
for (Nonnull<GenericBinding*> binding : function.deduced_parameters()) {
function_scope.Add(binding->name(), binding);
ResolveNames(binding->type(), function_scope);
RETURN_IF_ERROR(function_scope.Add(binding->name(), binding));
RETURN_IF_ERROR(ResolveNames(binding->type(), function_scope));
}
if (function.is_method()) {
ResolveNames(function.me_pattern(), function_scope);
RETURN_IF_ERROR(ResolveNames(function.me_pattern(), function_scope));
}
ResolveNames(function.param_pattern(), function_scope);
RETURN_IF_ERROR(ResolveNames(function.param_pattern(), function_scope));
if (function.return_term().type_expression().has_value()) {
ResolveNames(**function.return_term().type_expression(),
function_scope);
RETURN_IF_ERROR(ResolveNames(**function.return_term().type_expression(),
function_scope));
}
if (function.body().has_value()) {
ResolveNames(**function.body(), function_scope);
RETURN_IF_ERROR(ResolveNames(**function.body(), function_scope));
}
break;
}
@@ -313,12 +334,12 @@ static void ResolveNames(Declaration& declaration,
auto& class_decl = cast<ClassDeclaration>(declaration);
StaticScope class_scope;
class_scope.AddParent(&enclosing_scope);
class_scope.Add(class_decl.name(), &class_decl);
RETURN_IF_ERROR(class_scope.Add(class_decl.name(), &class_decl));
for (Nonnull<Declaration*> member : class_decl.members()) {
AddExposedNames(*member, class_scope);
RETURN_IF_ERROR(AddExposedNames(*member, class_scope));
}
for (Nonnull<Declaration*> member : class_decl.members()) {
ResolveNames(*member, class_scope);
RETURN_IF_ERROR(ResolveNames(*member, class_scope));
}
break;
}
@@ -329,35 +350,37 @@ static void ResolveNames(Declaration& declaration,
// need to check for duplicates.
std::set<std::string_view> alternative_names;
for (Nonnull<AlternativeSignature*> alternative : choice.alternatives()) {
ResolveNames(alternative->signature(), enclosing_scope);
RETURN_IF_ERROR(
ResolveNames(alternative->signature(), enclosing_scope));
if (!alternative_names.insert(alternative->name()).second) {
FATAL_COMPILATION_ERROR(alternative->source_loc())
<< "Duplicate name `" << alternative->name()
<< "` in choice type";
return FATAL_COMPILATION_ERROR(alternative->source_loc())
<< "Duplicate name `" << alternative->name()
<< "` in choice type";
}
}
break;
}
case DeclarationKind::VariableDeclaration: {
auto& var = cast<VariableDeclaration>(declaration);
ResolveNames(var.binding(), enclosing_scope);
RETURN_IF_ERROR(ResolveNames(var.binding(), enclosing_scope));
if (var.has_initializer()) {
ResolveNames(var.initializer(), enclosing_scope);
RETURN_IF_ERROR(ResolveNames(var.initializer(), enclosing_scope));
}
break;
}
}
return Success();
}
void ResolveNames(AST& ast) {
auto ResolveNames(AST& ast) -> ErrorOr<Success> {
StaticScope file_scope;
for (auto declaration : ast.declarations) {
AddExposedNames(*declaration, file_scope);
RETURN_IF_ERROR(AddExposedNames(*declaration, file_scope));
}
for (auto declaration : ast.declarations) {
ResolveNames(*declaration, file_scope);
RETURN_IF_ERROR(ResolveNames(*declaration, file_scope));
}
ResolveNames(**ast.main_call, file_scope);
return ResolveNames(**ast.main_call, file_scope);
}
} // namespace Carbon
@@ -11,7 +11,9 @@
namespace Carbon {
// Resolves names (IdentifierExpressions) in the AST.
void ResolveNames(AST& ast);
// On failure, `ast` is left in a partial state and should not be further
// processed.
auto ResolveNames(AST& ast) -> ErrorOr<Success>;
} // namespace Carbon
File diff suppressed because it is too large Load Diff
+50 -34
View File
@@ -23,7 +23,11 @@ class TypeChecker {
explicit TypeChecker(Nonnull<Arena*> arena, bool trace)
: arena_(arena), trace_(trace) {}
void TypeCheck(AST& ast);
// Type-checks `ast` and sets properties such as `static_type`, as documented
// on the individual nodes.
// On failure, `ast` is left in a partial state and should not be further
// processed.
auto TypeCheck(AST& ast) -> ErrorOr<Success>;
private:
// Perform type argument deduction, matching the parameter type `param`
@@ -32,32 +36,35 @@ class TypeChecker {
// inside the argument type.
// The `deduced` parameter is an accumulator, that is, it holds the
// results so-far.
static void ArgumentDeduction(SourceLocation source_loc, BindingMap& deduced,
static auto ArgumentDeduction(SourceLocation source_loc, BindingMap& deduced,
Nonnull<const Value*> param,
Nonnull<const Value*> arg);
Nonnull<const Value*> arg) -> ErrorOr<Success>;
// Traverses the AST rooted at `e`, populating the static_type() of all nodes
// and ensuring they follow Carbon's typing rules.
//
// `values` maps variable names to their compile-time values. It is not
// directly used in this function but is passed to InterExp.
void TypeCheckExp(Nonnull<Expression*> e, const ImplScope& impl_scope);
auto TypeCheckExp(Nonnull<Expression*> e, const ImplScope& impl_scope)
-> ErrorOr<Success>;
// Equivalent to TypeCheckExp, but operates on the AST rooted at `p`.
//
// `expected` is the type that this pattern is expected to have, if the
// surrounding context gives us that information. Otherwise, it is
// nullopt.
void TypeCheckPattern(Nonnull<Pattern*> p,
auto TypeCheckPattern(Nonnull<Pattern*> p,
std::optional<Nonnull<const Value*>> expected,
const ImplScope& impl_scope,
ValueCategory enclosing_value_category);
ValueCategory enclosing_value_category)
-> ErrorOr<Success>;
// Equivalent to TypeCheckExp, but operates on the AST rooted at `s`.
//
// REQUIRES: f.return_term().has_static_type() || f.return_term().is_auto(),
// where `f` is nearest enclosing FunctionDeclaration of `s`.
void TypeCheckStmt(Nonnull<Statement*> s, const ImplScope& impl_scope);
auto TypeCheckStmt(Nonnull<Statement*> s, const ImplScope& impl_scope)
-> ErrorOr<Success>;
// Establish the `static_type` and `constant_value` of the
// declaration and all of its nested declarations. This involves the
@@ -65,59 +72,68 @@ class TypeChecker {
// declaration. It does not involve type checking statements and
// (runtime) expressions, as in the body of a function or a method.
// Dispatches to one of the following functions.
void DeclareDeclaration(Nonnull<Declaration*> d, ImplScope& enclosing_scope);
auto DeclareDeclaration(Nonnull<Declaration*> d, ImplScope& enclosing_scope)
-> ErrorOr<Success>;
void DeclareFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
const ImplScope& enclosing_scope);
auto DeclareFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
const ImplScope& enclosing_scope)
-> ErrorOr<Success>;
void DeclareClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
ImplScope& enclosing_scope);
auto DeclareClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
ImplScope& enclosing_scope) -> ErrorOr<Success>;
void DeclareInterfaceDeclaration(Nonnull<InterfaceDeclaration*> iface_decl,
ImplScope& enclosing_scope);
auto DeclareInterfaceDeclaration(Nonnull<InterfaceDeclaration*> iface_decl,
ImplScope& enclosing_scope)
-> ErrorOr<Success>;
void DeclareImplDeclaration(Nonnull<ImplDeclaration*> impl_decl,
ImplScope& enclosing_scope);
auto DeclareImplDeclaration(Nonnull<ImplDeclaration*> impl_decl,
ImplScope& enclosing_scope) -> ErrorOr<Success>;
void DeclareChoiceDeclaration(Nonnull<ChoiceDeclaration*> choice,
const ImplScope& enclosing_scope);
auto DeclareChoiceDeclaration(Nonnull<ChoiceDeclaration*> choice,
const ImplScope& enclosing_scope)
-> ErrorOr<Success>;
// Checks the statements and (runtime) expressions within the
// declaration, such as the body of a function.
// Dispatches to one of the following functions.
// Assumes that DeclareDeclaration has already been invoked on `d`.
void TypeCheckDeclaration(Nonnull<Declaration*> d,
const ImplScope& impl_scope);
auto TypeCheckDeclaration(Nonnull<Declaration*> d,
const ImplScope& impl_scope) -> ErrorOr<Success>;
// Type check the body of the function.
void TypeCheckFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
const ImplScope& impl_scope);
auto TypeCheckFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
const ImplScope& impl_scope)
-> ErrorOr<Success>;
// Type check all the members of the class.
void TypeCheckClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
const ImplScope& impl_scope);
auto TypeCheckClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
const ImplScope& impl_scope)
-> ErrorOr<Success>;
// Type check all the members of the interface.
void TypeCheckInterfaceDeclaration(Nonnull<InterfaceDeclaration*> iface_decl,
const ImplScope& impl_scope);
auto TypeCheckInterfaceDeclaration(Nonnull<InterfaceDeclaration*> iface_decl,
const ImplScope& impl_scope)
-> ErrorOr<Success>;
// Type check all the members of the implementation.
void TypeCheckImplDeclaration(Nonnull<ImplDeclaration*> impl_decl,
const ImplScope& impl_scope);
auto TypeCheckImplDeclaration(Nonnull<ImplDeclaration*> impl_decl,
const ImplScope& impl_scope)
-> ErrorOr<Success>;
// This currently does nothing, but perhaps that will change in the future.
void TypeCheckChoiceDeclaration(Nonnull<ChoiceDeclaration*> choice,
const ImplScope& impl_scope);
auto TypeCheckChoiceDeclaration(Nonnull<ChoiceDeclaration*> choice,
const ImplScope& impl_scope)
-> ErrorOr<Success>;
// Verifies that opt_stmt holds a statement, and it is structurally impossible
// for control flow to leave that statement except via a `return`.
void ExpectReturnOnAllPaths(std::optional<Nonnull<Statement*>> opt_stmt,
SourceLocation source_loc);
auto ExpectReturnOnAllPaths(std::optional<Nonnull<Statement*>> opt_stmt,
SourceLocation source_loc) -> ErrorOr<Success>;
// Verifies that *value represents a concrete type, as opposed to a
// type pattern or a non-type value.
void ExpectIsConcreteType(SourceLocation source_loc,
Nonnull<const Value*> value);
auto ExpectIsConcreteType(SourceLocation source_loc,
Nonnull<const Value*> value) -> ErrorOr<Success>;
auto Substitute(const std::map<Nonnull<const GenericBinding*>,
Nonnull<const Value*>>& dict,
+31 -22
View File
@@ -12,6 +12,7 @@
#include "executable_semantics/interpreter/action.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Error.h"
namespace Carbon {
@@ -29,7 +30,8 @@ auto StructValue::FindField(const std::string& name) const
static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
const FieldPath::Component& field,
SourceLocation source_loc) -> Nonnull<const Value*> {
SourceLocation source_loc)
-> ErrorOr<Nonnull<const Value*>> {
const std::string& f = field.name();
if (field.witness().has_value()) {
@@ -42,8 +44,8 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
const auto& fun_decl = cast<FunctionDeclaration>(**mem_decl);
return arena->New<BoundMethodValue>(&fun_decl, v);
} else {
FATAL_COMPILATION_ERROR(source_loc)
<< "member " << f << " not in " << *witness;
return FATAL_COMPILATION_ERROR(source_loc)
<< "member " << f << " not in " << *witness;
}
}
default:
@@ -55,7 +57,8 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
std::optional<Nonnull<const Value*>> field =
cast<StructValue>(*v).FindField(f);
if (field == std::nullopt) {
FATAL_RUNTIME_ERROR(source_loc) << "member " << f << " not in " << *v;
return FATAL_RUNTIME_ERROR(source_loc)
<< "member " << f << " not in " << *v;
}
return *field;
}
@@ -70,8 +73,9 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
std::optional<Nonnull<const FunctionValue*>> func =
class_type.FindFunction(f);
if (func == std::nullopt) {
FATAL_RUNTIME_ERROR(source_loc)
<< "member " << f << " not in " << *v << " or its " << class_type;
return FATAL_RUNTIME_ERROR(source_loc)
<< "member " << f << " not in " << *v << " or its "
<< class_type;
} else if ((*func)->declaration().is_method()) {
// Found a method. Turn it into a bound method.
const auto& m = cast<FunctionValue>(**func);
@@ -86,8 +90,8 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
case Value::Kind::ChoiceType: {
const auto& choice = cast<ChoiceType>(*v);
if (!choice.FindAlternative(f)) {
FATAL_RUNTIME_ERROR(source_loc)
<< "alternative " << f << " not in " << *v;
return FATAL_RUNTIME_ERROR(source_loc)
<< "alternative " << f << " not in " << *v;
}
return arena->New<AlternativeConstructorValue>(f, choice.name());
}
@@ -96,8 +100,8 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
std::optional<Nonnull<const FunctionValue*>> fun =
class_type.FindFunction(f);
if (fun == std::nullopt) {
FATAL_RUNTIME_ERROR(source_loc)
<< "class function " << f << " not in " << *v;
return FATAL_RUNTIME_ERROR(source_loc)
<< "class function " << f << " not in " << *v;
}
return *fun;
}
@@ -107,10 +111,11 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
}
auto Value::GetField(Nonnull<Arena*> arena, const FieldPath& path,
SourceLocation source_loc) const -> Nonnull<const Value*> {
SourceLocation source_loc) const
-> ErrorOr<Nonnull<const Value*>> {
Nonnull<const Value*> value(this);
for (const FieldPath::Component& field : path.components_) {
value = GetMember(arena, value, field, source_loc);
ASSIGN_OR_RETURN(value, GetMember(arena, value, field, source_loc));
}
return value;
}
@@ -120,7 +125,7 @@ static auto SetFieldImpl(
std::vector<FieldPath::Component>::const_iterator path_begin,
std::vector<FieldPath::Component>::const_iterator path_end,
Nonnull<const Value*> field_value, SourceLocation source_loc)
-> Nonnull<const Value*> {
-> ErrorOr<Nonnull<const Value*>> {
if (path_begin == path_end) {
return field_value;
}
@@ -132,11 +137,12 @@ static auto SetFieldImpl(
return element.name == (*path_begin).name();
});
if (it == elements.end()) {
FATAL_RUNTIME_ERROR(source_loc)
<< "field " << (*path_begin).name() << " not in " << *value;
return FATAL_RUNTIME_ERROR(source_loc)
<< "field " << (*path_begin).name() << " not in " << *value;
}
it->value = SetFieldImpl(arena, it->value, path_begin + 1, path_end,
field_value, source_loc);
ASSIGN_OR_RETURN(it->value,
SetFieldImpl(arena, it->value, path_begin + 1, path_end,
field_value, source_loc));
return arena->New<StructValue>(elements);
}
case Value::Kind::NominalClassValue: {
@@ -149,11 +155,13 @@ static auto SetFieldImpl(
// TODO(geoffromer): update FieldPath to hold integers as well as strings.
int index = std::stoi((*path_begin).name());
if (index < 0 || static_cast<size_t>(index) >= elements.size()) {
FATAL_RUNTIME_ERROR(source_loc) << "index " << (*path_begin).name()
<< " out of range in " << *value;
return FATAL_RUNTIME_ERROR(source_loc)
<< "index " << (*path_begin).name() << " out of range in "
<< *value;
}
elements[index] = SetFieldImpl(arena, elements[index], path_begin + 1,
path_end, field_value, source_loc);
ASSIGN_OR_RETURN(elements[index],
SetFieldImpl(arena, elements[index], path_begin + 1,
path_end, field_value, source_loc));
return arena->New<TupleValue>(elements);
}
default:
@@ -163,7 +171,8 @@ static auto SetFieldImpl(
auto Value::SetField(Nonnull<Arena*> arena, const FieldPath& path,
Nonnull<const Value*> field_value,
SourceLocation source_loc) const -> Nonnull<const Value*> {
SourceLocation source_loc) const
-> ErrorOr<Nonnull<const Value*>> {
return SetFieldImpl(arena, Nonnull<const Value*>(this),
path.components_.begin(), path.components_.end(),
field_value, source_loc);
+4 -2
View File
@@ -76,13 +76,15 @@ class Value {
// Returns the sub-Value specified by `path`, which must be a valid field
// path for *this.
auto GetField(Nonnull<Arena*> arena, const FieldPath& path,
SourceLocation source_loc) const -> Nonnull<const Value*>;
SourceLocation source_loc) const
-> ErrorOr<Nonnull<const Value*>>;
// Returns a copy of *this, but with the sub-Value specified by `path`
// set to `field_value`. `path` must be a valid field path for *this.
auto SetField(Nonnull<Arena*> arena, const FieldPath& path,
Nonnull<const Value*> field_value,
SourceLocation source_loc) const -> Nonnull<const Value*>;
SourceLocation source_loc) const
-> ErrorOr<Nonnull<const Value*>>;
// Returns the enumerator corresponding to the most-derived type of this
// object.
+22 -15
View File
@@ -10,6 +10,7 @@
#include <string>
#include <vector>
#include "common/error.h"
#include "executable_semantics/common/arena.h"
#include "executable_semantics/common/nonnull.h"
#include "executable_semantics/interpreter/exec_program.h"
@@ -21,18 +22,26 @@
static void AddPrelude(
std::string_view prelude_file_name, Carbon::Nonnull<Carbon::Arena*> arena,
std::vector<Carbon::Nonnull<Carbon::Declaration*>>* declarations) {
std::variant<Carbon::AST, Carbon::SyntaxErrorCode> parse_result =
Carbon::ErrorOr<Carbon::AST> parse_result =
Carbon::Parse(arena, prelude_file_name, false);
if (std::holds_alternative<Carbon::SyntaxErrorCode>(parse_result)) {
if (!parse_result.ok()) {
// Try again with tracing, to help diagnose the problem.
Carbon::Parse(arena, prelude_file_name, true);
FATAL() << "Failed to parse prelude.";
Carbon::ErrorOr<Carbon::AST> trace_parse_result =
Carbon::Parse(arena, prelude_file_name, true);
FATAL() << "Failed to parse prelude: "
<< trace_parse_result.error().message();
}
const auto& prelude = std::get<Carbon::AST>(parse_result);
const auto& prelude = *parse_result;
declarations->insert(declarations->begin(), prelude.declarations.begin(),
prelude.declarations.end());
}
// Prints an error message and returns error code value.
auto PrintError(const Carbon::Error& error) -> int {
llvm::errs() << error.message() << "\n";
return EXIT_FAILURE;
}
auto main(int argc, char* argv[]) -> int {
llvm::setBugReportMsg(
"Please report issues to "
@@ -56,18 +65,16 @@ auto main(int argc, char* argv[]) -> int {
llvm::cl::ParseCommandLineOptions(argc, argv);
Carbon::Arena arena;
std::variant<Carbon::AST, Carbon::SyntaxErrorCode> ast_or_error =
Carbon::ErrorOr<Carbon::AST> ast =
Carbon::Parse(&arena, input_file_name, trace_option);
if (auto* error = std::get_if<Carbon::SyntaxErrorCode>(&ast_or_error)) {
// Diagnostic already reported to std::cerr; this is just a return code.
return *error;
if (!ast.ok()) {
return PrintError(ast.error());
}
auto& ast = std::get<Carbon::AST>(ast_or_error);
AddPrelude(prelude_file_name, &arena, &ast.declarations);
AddPrelude(prelude_file_name, &arena, &ast->declarations);
// Typecheck and run the parsed program.
Carbon::ExecProgram(&arena, std::get<Carbon::AST>(ast_or_error),
trace_option);
Carbon::ErrorOr<int> result = Carbon::ExecProgram(&arena, *ast, trace_option);
if (!result.ok()) {
return PrintError(result.error());
}
}
+2
View File
@@ -30,6 +30,7 @@ cc_library(
deps = [
":syntax",
"@com_google_googletest//:gtest",
"@llvm-project//llvm:Support",
],
)
@@ -57,6 +58,7 @@ cc_library(
deps = [
":bison_wrap",
"//common:check",
"//common:error",
"//common:ostream",
"//common:string_helpers",
"//executable_semantics/ast",
+38 -22
View File
@@ -8,20 +8,15 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include <cstdlib>
#include "common/check.h"
#include "common/error.h"
#include "common/string_helpers.h"
#include "executable_semantics/syntax/parse_and_lex_context.h"
#include "executable_semantics/syntax/parser.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/FormatVariadic.h"
// Prints a newline in trace mode because trace prints an incomplete line
// "Reading a token: " which can prevent LIT from finding expected patterns.
#define FATAL_SYNTAX_ERROR(context) \
RAW_EXITING_STREAM() << (context.trace() ? "\n" : "") \
<< "COMPILATION ERROR: " << (context.source_loc()) \
<< ": "
// Reads and returns a single character. Fails on EOF.
char ReadChar(yyscan_t yyscanner, const Carbon::ParseAndLexContext& context);
// Reads and returns a single character. Reports an error on EOF.
auto ReadChar(yyscan_t yyscanner, Carbon::ParseAndLexContext& context) -> int;
%}
/* Turn off legacy bits we don't need. */
@@ -258,7 +253,13 @@ string_literal \"([^\\\"\n\v\f\r]|\\.)*\"
{intrinsic_identifier} {
BEGIN(AFTER_OPERAND);
return ARG_TOKEN(intrinsic_identifier, yytext);
Carbon::ErrorOr<Carbon::IntrinsicExpression::Intrinsic> intrinsic =
Carbon::IntrinsicExpression::FindIntrinsic(yytext, context.source_loc());
if (intrinsic.ok()) {
return ARG_TOKEN(intrinsic_identifier, *intrinsic);
} else {
return context.RecordSyntaxError(intrinsic.error().message());
}
}
{identifier} {
@@ -270,7 +271,8 @@ string_literal \"([^\\\"\n\v\f\r]|\\.)*\"
BEGIN(AFTER_OPERAND);
int val = 0;
if (!llvm::to_integer(yytext, val)) {
FATAL_SYNTAX_ERROR(context) << "Invalid integer literal: " << yytext;
return context.RecordSyntaxError(
llvm::formatv("Invalid integer literal: {0}", yytext));
}
return ARG_TOKEN(integer_literal, val);
}
@@ -280,7 +282,8 @@ string_literal \"([^\\\"\n\v\f\r]|\\.)*\"
CHECK(str.consume_front("\"") && str.consume_back("\""));
std::optional<std::string> unescaped = Carbon::UnescapeStringLiteral(str);
if (unescaped == std::nullopt) {
FATAL_SYNTAX_ERROR(context) << "Invalid escaping in string: " << yytext;
return context.RecordSyntaxError(
llvm::formatv("Invalid escaping in string: {0}", yytext));
}
return ARG_TOKEN(string_literal, *unescaped);
}
@@ -291,24 +294,37 @@ string_literal \"([^\\\"\n\v\f\r]|\\.)*\"
// Scans for the closing """, checking for possible escape sequences
// like \""".
for (;;) {
char c = ReadChar(yyscanner, context);
int c = ReadChar(yyscanner, context);
if (c <= 0) {
return SIMPLE_TOKEN(END_OF_FILE);
}
s.push_back(c);
if (c != '"' && c != '\\') {
continue;
}
if (c == '\\') {
// \" in \""" is not a terminator.
s.push_back(ReadChar(yyscanner, context));
c = ReadChar(yyscanner, context);
if (c <= 0) {
return SIMPLE_TOKEN(END_OF_FILE);
}
s.push_back(c);
continue;
}
c = ReadChar(yyscanner, context);
if (c <= 0) {
return SIMPLE_TOKEN(END_OF_FILE);
}
s.push_back(c);
if (c != '"') {
continue;
}
c = ReadChar(yyscanner, context);
if (c <= 0) {
return SIMPLE_TOKEN(END_OF_FILE);
}
s.push_back(c);
if (c == '"') {
break;
@@ -317,8 +333,8 @@ string_literal \"([^\\\"\n\v\f\r]|\\.)*\"
Carbon::ErrorOr<std::string> block_string =
Carbon::ParseBlockStringLiteral(s);
if (!block_string.ok()) {
FATAL_SYNTAX_ERROR(context)
<< "Invalid block string: " << block_string.error();
return context.RecordSyntaxError(llvm::formatv(
"Invalid block string: {0}", block_string.error().message()));
}
return ARG_TOKEN(string_literal, *block_string);
}
@@ -345,17 +361,17 @@ string_literal \"([^\\\"\n\v\f\r]|\\.)*\"
}
. {
FATAL_SYNTAX_ERROR(context)
<< "invalid character '\\x" << llvm::toHex(llvm::StringRef(yytext, 1))
<< "' in source file.";
return context.RecordSyntaxError(
llvm::formatv("invalid character '\\x{0}' in source file.",
llvm::toHex(llvm::StringRef(yytext, 1))));
}
%%
char ReadChar(yyscan_t yyscanner, const Carbon::ParseAndLexContext& context) {
auto ReadChar(yyscan_t yyscanner, Carbon::ParseAndLexContext& context) -> int {
const int c = yyinput(yyscanner);
if (c == EOF) {
FATAL_SYNTAX_ERROR(context) << "Unexpected end of file";
if (c <= 0) {
context.RecordSyntaxError("Unexpected end of file");
}
return c;
}
+14 -14
View File
@@ -5,16 +5,17 @@
#include "executable_semantics/syntax/parse.h"
#include "common/check.h"
#include "common/error.h"
#include "executable_semantics/common/error.h"
#include "executable_semantics/syntax/lexer.h"
#include "executable_semantics/syntax/parse_and_lex_context.h"
#include "executable_semantics/syntax/parser.h"
#include "llvm/Support/Error.h"
namespace Carbon {
auto ParseImpl(yyscan_t scanner, Nonnull<Arena*> arena,
std::string_view input_file_name, bool trace)
-> std::variant<AST, SyntaxErrorCode> {
std::string_view input_file_name, bool trace) -> ErrorOr<AST> {
// Prepare other parser arguments.
std::optional<AST> ast = std::nullopt;
ParseAndLexContext context(arena->New<std::string>(input_file_name), trace);
@@ -24,11 +25,12 @@ auto ParseImpl(yyscan_t scanner, Nonnull<Arena*> arena,
if (trace) {
parser.set_debug_level(1);
}
auto syntax_error_code = parser();
// Return an error if appropriate.
if (syntax_error_code != 0) {
return syntax_error_code;
if (auto syntax_error_code = parser(); syntax_error_code != 0) {
const std::string error_message = context.error_messages().empty()
? "Unknown parser error"
: context.error_messages()[0];
return Error(error_message);
}
// Return parse results.
@@ -38,11 +40,11 @@ auto ParseImpl(yyscan_t scanner, Nonnull<Arena*> arena,
}
auto Parse(Nonnull<Arena*> arena, std::string_view input_file_name, bool trace)
-> std::variant<AST, SyntaxErrorCode> {
-> ErrorOr<AST> {
FILE* input_file = fopen(std::string(input_file_name).c_str(), "r");
if (input_file == nullptr) {
FATAL_PROGRAM_ERROR_NO_LINE() << "Error opening '" << input_file_name
<< "': " << std::strerror(errno);
return FATAL_PROGRAM_ERROR_NO_LINE() << "Error opening '" << input_file_name
<< "': " << std::strerror(errno);
}
// Prepare the lexer.
@@ -51,8 +53,7 @@ auto Parse(Nonnull<Arena*> arena, std::string_view input_file_name, bool trace)
auto buffer = yy_create_buffer(input_file, YY_BUF_SIZE, scanner);
yy_switch_to_buffer(buffer, scanner);
std::variant<AST, SyntaxErrorCode> result =
ParseImpl(scanner, arena, input_file_name, trace);
ErrorOr<AST> result = ParseImpl(scanner, arena, input_file_name, trace);
// Clean up the lexer.
yy_delete_buffer(buffer, scanner);
@@ -64,7 +65,7 @@ auto Parse(Nonnull<Arena*> arena, std::string_view input_file_name, bool trace)
auto ParseFromString(Nonnull<Arena*> arena, std::string_view input_file_name,
std::string_view file_contents, bool trace)
-> std::variant<Carbon::AST, SyntaxErrorCode> {
-> ErrorOr<AST> {
// Prepare the lexer.
yyscan_t scanner;
yylex_init(&scanner);
@@ -72,8 +73,7 @@ auto ParseFromString(Nonnull<Arena*> arena, std::string_view input_file_name,
yy_scan_bytes(file_contents.data(), file_contents.size(), scanner);
yy_switch_to_buffer(buffer, scanner);
std::variant<AST, SyntaxErrorCode> result =
ParseImpl(scanner, arena, input_file_name, trace);
ErrorOr<AST> result = ParseImpl(scanner, arena, input_file_name, trace);
// Clean up the lexer.
yy_delete_buffer(buffer, scanner);
+2 -5
View File
@@ -13,20 +13,17 @@
namespace Carbon {
// This is the code given us by Bison, for now.
using SyntaxErrorCode = int;
// Returns the AST representing the contents of the named file, or an error code
// if parsing fails. Allocations go into the provided arena.
auto Parse(Nonnull<Arena*> arena, std::string_view input_file_name, bool trace)
-> std::variant<Carbon::AST, SyntaxErrorCode>;
-> ErrorOr<Carbon::AST>;
// Equivalent to `Parse`, but parses the contents of `file_contents`.
// `input_file_name` is used only for reporting source locations, and does
// not need to name a real file.
auto ParseFromString(Nonnull<Arena*> arena, std::string_view input_file_name,
std::string_view file_contents, bool trace)
-> std::variant<Carbon::AST, SyntaxErrorCode>;
-> ErrorOr<Carbon::AST>;
} // namespace Carbon
@@ -6,11 +6,21 @@
namespace Carbon {
auto ParseAndLexContext::PrintDiagnostic(const std::string& message) -> void {
// TODO: Do we really want this to be fatal? It makes the comment and the
// name a lie, and renders some of the other yyparse() result propagation code
// moot.
FATAL_COMPILATION_ERROR(source_loc()) << message;
auto ParseAndLexContext::RecordSyntaxError(const std::string& message,
bool prefix_with_newline)
-> Parser::symbol_type {
// Optionally adds a newline in trace mode because trace prints an incomplete
// line "Reading a token: " which can prevent LIT from finding expected
// patterns.
// TODO: support formatting of `SourceLocation` instances with formatv().
std::string full_message;
llvm::raw_string_ostream(full_message)
<< (prefix_with_newline && trace() ? "\n" : "")
<< "COMPILATION ERROR: " << source_loc() << ": " << message;
error_messages_.push_back(full_message);
// TODO: use `YYerror` token once bison is upgraded to at least 3.5.
return Parser::make_END_OF_FILE(current_token_position);
}
} // namespace Carbon
@@ -20,8 +20,11 @@ class ParseAndLexContext {
ParseAndLexContext(Nonnull<const std::string*> input_file_name, bool trace)
: input_file_name_(input_file_name), trace_(trace) {}
// Writes a syntax error diagnostic containing message to standard error.
auto PrintDiagnostic(const std::string& message) -> void;
// Formats ands records a lexer error. Returns an error token as a
// convenience.
auto RecordSyntaxError(const std::string& message,
bool prefix_with_newline = false)
-> Parser::symbol_type;
auto source_loc() const -> SourceLocation {
return SourceLocation(input_file_name_,
@@ -33,12 +36,18 @@ class ParseAndLexContext {
// The source range of the token being (or just) lex'd.
location current_token_position;
auto error_messages() const -> const std::vector<std::string> {
return error_messages_;
}
private:
// A path to the file processed, relative to the current working directory
// when *this is called.
Nonnull<const std::string*> input_file_name_;
bool trace_;
std::vector<std::string> error_messages_;
};
} // namespace Carbon
+3 -3
View File
@@ -23,10 +23,10 @@ fn Foo() {}
TEST(ParseTest, ParseFromString) {
Arena arena;
std::variant<AST, SyntaxErrorCode> parse_result =
ErrorOr<AST> parse_result =
ParseFromString(&arena, "file.carbon", FileContents, /*trace=*/false);
ASSERT_TRUE(std::holds_alternative<AST>(parse_result));
EXPECT_EQ(std::get<AST>(parse_result).declarations.size(), 1);
ASSERT_TRUE(parse_result.ok());
EXPECT_EQ(parse_result->declarations.size(), 1);
}
} // namespace
@@ -12,6 +12,8 @@
#include <variant>
#include "executable_semantics/syntax/parse.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/raw_ostream.h"
namespace Carbon::TestingInternal {
@@ -32,14 +34,14 @@ class ParsedAsMatcher {
DescribeToImpl(out, /*negated=*/true);
}
auto MatchAndExplain(const std::variant<AST, SyntaxErrorCode>& result,
auto MatchAndExplain(const ErrorOr<AST>& result,
::testing::MatchResultListener* listener) const -> bool {
if (std::holds_alternative<SyntaxErrorCode>(result)) {
*listener << "holds error code " << std::get<SyntaxErrorCode>(result);
if (!result.ok()) {
*listener << "is a failed parse with error: " << result.error().message();
return false;
} else {
*listener << "is a successful parse whose ";
return ast_matcher_.MatchAndExplain(std::get<AST>(result), listener);
return ast_matcher_.MatchAndExplain(*result, listener);
}
}
+29 -7
View File
@@ -62,6 +62,7 @@
#include "common/check.h"
#include "executable_semantics/syntax/parse_and_lex_context.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/raw_ostream.h"
} // %code top
%code requires {
@@ -86,13 +87,13 @@
%code {
void Carbon::Parser::error(const location_type&, const std::string& message) {
context.PrintDiagnostic(message);
context.RecordSyntaxError(message);
}
} // %code
%token <int> integer_literal
%token <std::string> identifier
%token <std::string> intrinsic_identifier
%token <IntrinsicExpression::Intrinsic> intrinsic_identifier
%token <std::string> sized_type_literal
%token <std::string> string_literal
%type <std::string> designator
@@ -582,7 +583,16 @@ non_expression_pattern:
| paren_pattern
{ $$ = $1; }
| postfix_expression tuple_pattern
{ $$ = arena->New<AlternativePattern>(context.source_loc(), $1, $2); }
{
ErrorOr<Nonnull<AlternativePattern*>> alternative_pattern =
AlternativePattern::Create(arena, context.source_loc(), $1, $2);
if (alternative_pattern.ok()) {
$$ = *alternative_pattern;
} else {
context.RecordSyntaxError(alternative_pattern.error().message());
YYERROR;
}
}
| VAR non_expression_pattern
{ $$ = arena->New<VarPattern>(context.source_loc(), $2); }
;
@@ -804,13 +814,25 @@ receiver:
function_declaration:
FN identifier deduced_params receiver maybe_empty_tuple_pattern return_term block
{
$$ = arena->New<FunctionDeclaration>(context.source_loc(), $2, $3, $4, $5,
$6, $7);
ErrorOr<FunctionDeclaration*> fn = FunctionDeclaration::Create(
arena, context.source_loc(), $2, $3, $4, $5, $6, $7);
if (fn.ok()) {
$$ = *fn;
} else {
context.RecordSyntaxError(fn.error().message());
YYERROR;
}
}
| FN identifier deduced_params receiver maybe_empty_tuple_pattern return_term SEMICOLON
{
$$ = arena->New<FunctionDeclaration>(context.source_loc(), $2, $3, $4, $5,
$6, std::nullopt);
ErrorOr<FunctionDeclaration*> fn = FunctionDeclaration::Create(
arena, context.source_loc(), $2, $3, $4, $5, $6, std::nullopt);
if (fn.ok()) {
$$ = *fn;
} else {
context.RecordSyntaxError(fn.error().message());
YYERROR;
}
}
;
variable_declaration: identifier COLON pattern