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
@@ -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.