mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
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:
committed by
GitHub
co-authored by
Jon Meow
Geoff Romer
parent
e5a87af6fe
commit
aa8a5f174d
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user