瀏覽代碼

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>
pk19604014 4 年之前
父節點
當前提交
aa8a5f174d
共有 43 個文件被更改,包括 1163 次插入755 次删除
  1. 5 1
      common/error.h
  2. 1 0
      executable_semantics/BUILD
  3. 2 0
      executable_semantics/ast/BUILD
  4. 20 11
      executable_semantics/ast/declaration.cpp
  5. 13 5
      executable_semantics/ast/declaration.h
  6. 3 2
      executable_semantics/ast/expression.cpp
  7. 7 8
      executable_semantics/ast/expression.h
  8. 5 13
      executable_semantics/ast/pattern.cpp
  9. 16 6
      executable_semantics/ast/pattern.h
  10. 22 15
      executable_semantics/ast/static_scope.cpp
  11. 5 3
      executable_semantics/ast/static_scope.h
  12. 4 0
      executable_semantics/common/BUILD
  13. 73 9
      executable_semantics/common/error.h
  14. 78 8
      executable_semantics/common/error_test.cpp
  15. 1 0
      executable_semantics/interpreter/BUILD
  16. 33 17
      executable_semantics/interpreter/action_stack.cpp
  17. 17 15
      executable_semantics/interpreter/action_stack.h
  18. 7 5
      executable_semantics/interpreter/exec_program.cpp
  19. 1 1
      executable_semantics/interpreter/exec_program.h
  20. 16 12
      executable_semantics/interpreter/heap.cpp
  21. 5 4
      executable_semantics/interpreter/heap.h
  22. 13 9
      executable_semantics/interpreter/impl_scope.cpp
  23. 2 2
      executable_semantics/interpreter/impl_scope.h
  24. 69 49
      executable_semantics/interpreter/interpreter.cpp
  25. 4 3
      executable_semantics/interpreter/interpreter.h
  26. 47 37
      executable_semantics/interpreter/resolve_control_flow.cpp
  27. 3 1
      executable_semantics/interpreter/resolve_control_flow.h
  28. 117 94
      executable_semantics/interpreter/resolve_names.cpp
  29. 3 1
      executable_semantics/interpreter/resolve_names.h
  30. 344 289
      executable_semantics/interpreter/type_checker.cpp
  31. 50 34
      executable_semantics/interpreter/type_checker.h
  32. 31 22
      executable_semantics/interpreter/value.cpp
  33. 4 2
      executable_semantics/interpreter/value.h
  34. 22 15
      executable_semantics/main.cpp
  35. 2 0
      executable_semantics/syntax/BUILD
  36. 38 22
      executable_semantics/syntax/lexer.lpp
  37. 14 14
      executable_semantics/syntax/parse.cpp
  38. 2 5
      executable_semantics/syntax/parse.h
  39. 15 5
      executable_semantics/syntax/parse_and_lex_context.cpp
  40. 11 2
      executable_semantics/syntax/parse_and_lex_context.h
  41. 3 3
      executable_semantics/syntax/parse_test.cpp
  42. 6 4
      executable_semantics/syntax/parse_test_matchers_internal.h
  43. 29 7
      executable_semantics/syntax/parser.ypp

+ 5 - 1
common/error.h

@@ -62,10 +62,14 @@ class [[nodiscard]] ErrorOr {
 
   // Returns the contained error.
   // REQUIRES: `ok()` is false.
-  auto error() const -> const Error& {
+  auto error() const& -> const Error& {
     CHECK(!ok());
     return std::get<Error>(val_);
   }
+  auto error() && -> Error {
+    CHECK(!ok());
+    return std::get<Error>(std::move(val_));
+  }
 
   // Returns the contained value.
   // REQUIRES: `ok()` is true.

+ 1 - 0
executable_semantics/BUILD

@@ -14,6 +14,7 @@ cc_binary(
     srcs = ["main.cpp"],
     data = ["data/prelude.carbon"],
     deps = [
+        "//common:error",
         "//executable_semantics/common:arena",
         "//executable_semantics/common:nonnull",
         "//executable_semantics/interpreter:exec_program",

+ 2 - 0
executable_semantics/ast/BUILD

@@ -203,8 +203,10 @@ cc_library(
         ":source_location",
         ":value_category",
         "//common:check",
+        "//common:error",
         "//executable_semantics/common:error",
         "//executable_semantics/common:nonnull",
+        "@llvm-project//llvm:Support",
     ],
 )
 

+ 20 - 11
executable_semantics/ast/declaration.cpp

@@ -112,29 +112,38 @@ void ReturnTerm::Print(llvm::raw_ostream& out) const {
   }
 }
 
-// Look for the `me` parameter in the `deduced_parameters_`
-// and put it in the `me_pattern_`.
-void FunctionDeclaration::ResolveDeducedAndReceiver(
-    const std::vector<Nonnull<AstNode*>>& deduced_params) {
+auto FunctionDeclaration::Create(
+    Nonnull<Arena*> arena, SourceLocation source_loc, std::string name,
+    std::vector<Nonnull<AstNode*>> deduced_params,
+    std::optional<Nonnull<BindingPattern*>> me_pattern,
+    Nonnull<TuplePattern*> param_pattern, ReturnTerm return_term,
+    std::optional<Nonnull<Block*>> body)
+    -> ErrorOr<Nonnull<FunctionDeclaration*>> {
+  std::vector<Nonnull<GenericBinding*>> resolved_params;
+  // Look for the `me` parameter in the `deduced_parameters`
+  // and put it in the `me_pattern`.
   for (Nonnull<AstNode*> param : deduced_params) {
     switch (param->kind()) {
       case AstNodeKind::GenericBinding:
-        deduced_parameters_.push_back(&cast<GenericBinding>(*param));
+        resolved_params.push_back(&cast<GenericBinding>(*param));
         break;
       case AstNodeKind::BindingPattern: {
         Nonnull<BindingPattern*> bp = &cast<BindingPattern>(*param);
-        if (me_pattern_.has_value() || bp->name() != "me") {
-          FATAL_COMPILATION_ERROR(source_loc())
-              << "illegal binding pattern in implicit parameter list";
+        if (me_pattern.has_value() || bp->name() != "me") {
+          return FATAL_COMPILATION_ERROR(source_loc)
+                 << "illegal binding pattern in implicit parameter list";
         }
-        me_pattern_ = bp;
+        me_pattern = bp;
         break;
       }
       default:
-        FATAL_COMPILATION_ERROR(source_loc())
-            << "illegal AST node in implicit parameter list";
+        return FATAL_COMPILATION_ERROR(source_loc)
+               << "illegal AST node in implicit parameter list";
     }
   }
+  return arena->New<FunctionDeclaration>(source_loc, name, resolved_params,
+                                         me_pattern, param_pattern, return_term,
+                                         body);
 }
 
 void FunctionDeclaration::PrintDepth(int depth, llvm::raw_ostream& out) const {

+ 13 - 5
executable_semantics/ast/declaration.h

@@ -82,20 +82,29 @@ class FunctionDeclaration : public Declaration {
  public:
   using ImplementsCarbonValueNode = void;
 
+  static auto Create(Nonnull<Arena*> arena, SourceLocation source_loc,
+                     std::string name,
+                     std::vector<Nonnull<AstNode*>> deduced_params,
+                     std::optional<Nonnull<BindingPattern*>> me_pattern,
+                     Nonnull<TuplePattern*> param_pattern,
+                     ReturnTerm return_term,
+                     std::optional<Nonnull<Block*>> body)
+      -> ErrorOr<Nonnull<FunctionDeclaration*>>;
+
+  // Use `Create()` instead. This is public only so Arena::New() can call it.
   FunctionDeclaration(SourceLocation source_loc, std::string name,
-                      std::vector<Nonnull<AstNode*>> deduced_params,
+                      std::vector<Nonnull<GenericBinding*>> deduced_params,
                       std::optional<Nonnull<BindingPattern*>> me_pattern,
                       Nonnull<TuplePattern*> param_pattern,
                       ReturnTerm return_term,
                       std::optional<Nonnull<Block*>> body)
       : Declaration(AstNodeKind::FunctionDeclaration, source_loc),
         name_(std::move(name)),
+        deduced_parameters_(std::move(deduced_params)),
         me_pattern_(me_pattern),
         param_pattern_(param_pattern),
         return_term_(return_term),
-        body_(body) {
-    ResolveDeducedAndReceiver(deduced_params);
-  }
+        body_(body) {}
 
   static auto classof(const AstNode* node) -> bool {
     return InheritsFromFunctionDeclaration(node->kind());
@@ -135,7 +144,6 @@ class FunctionDeclaration : public Declaration {
   auto is_method() const -> bool { return me_pattern_.has_value(); }
 
  private:
-  void ResolveDeducedAndReceiver(const std::vector<Nonnull<AstNode*>>&);
   std::string name_;
   std::vector<Nonnull<GenericBinding*>> deduced_parameters_;
   std::optional<Nonnull<BindingPattern*>> me_pattern_;

+ 3 - 2
executable_semantics/ast/expression.cpp

@@ -20,13 +20,14 @@ using llvm::isa;
 
 auto IntrinsicExpression::FindIntrinsic(std::string_view name,
                                         SourceLocation source_loc)
-    -> Intrinsic {
+    -> ErrorOr<Intrinsic> {
   static const auto& intrinsic_map =
       *new std::map<std::string_view, Intrinsic>({{"print", Intrinsic::Print}});
   name.remove_prefix(std::strlen("__intrinsic_"));
   auto it = intrinsic_map.find(name);
   if (it == intrinsic_map.end()) {
-    FATAL_COMPILATION_ERROR(source_loc) << "Unknown intrinsic '" << name << "'";
+    return FATAL_COMPILATION_ERROR(source_loc)
+           << "Unknown intrinsic '" << name << "'";
   }
   return it->second;
 }

+ 7 - 8
executable_semantics/ast/expression.h

@@ -465,11 +465,15 @@ class IntrinsicExpression : public Expression {
     Print,
   };
 
-  explicit IntrinsicExpression(std::string_view intrinsic_name,
-                               Nonnull<TupleLiteral*> args,
+  // Returns the enumerator corresponding to the intrinsic named `name`,
+  // or raises a fatal compile error if there is no such enumerator.
+  static auto FindIntrinsic(std::string_view name, SourceLocation source_loc)
+      -> ErrorOr<Intrinsic>;
+
+  explicit IntrinsicExpression(Intrinsic intrinsic, Nonnull<TupleLiteral*> args,
                                SourceLocation source_loc)
       : Expression(AstNodeKind::IntrinsicExpression, source_loc),
-        intrinsic_(FindIntrinsic(intrinsic_name, source_loc)),
+        intrinsic_(intrinsic),
         args_(args) {}
 
   static auto classof(const AstNode* node) -> bool {
@@ -481,11 +485,6 @@ class IntrinsicExpression : public Expression {
   auto args() -> TupleLiteral& { return *args_; }
 
  private:
-  // Returns the enumerator corresponding to the intrinsic named `name`,
-  // or raises a fatal compile error if there is no such enumerator.
-  static auto FindIntrinsic(std::string_view name, SourceLocation source_loc)
-      -> Intrinsic;
-
   Intrinsic intrinsic_;
   Nonnull<TupleLiteral*> args_;
 };

+ 5 - 13
executable_semantics/ast/pattern.cpp

@@ -108,23 +108,15 @@ auto TuplePatternFromParenContents(Nonnull<Arena*> arena,
 // Used by AlternativePattern for constructor initialization. Produces a helpful
 // error for incorrect expressions, rather than letting a default cast error
 // apply.
-static auto RequireFieldAccess(Nonnull<Expression*> alternative)
-    -> FieldAccessExpression& {
+auto AlternativePattern::RequireFieldAccess(Nonnull<Expression*> alternative)
+    -> ErrorOr<Nonnull<FieldAccessExpression*>> {
   if (alternative->kind() != ExpressionKind::FieldAccessExpression) {
-    FATAL_PROGRAM_ERROR(alternative->source_loc())
-        << "Alternative pattern must have the form of a field access.";
+    return FATAL_PROGRAM_ERROR(alternative->source_loc())
+           << "Alternative pattern must have the form of a field access.";
   }
-  return cast<FieldAccessExpression>(*alternative);
+  return &cast<FieldAccessExpression>(*alternative);
 }
 
-AlternativePattern::AlternativePattern(SourceLocation source_loc,
-                                       Nonnull<Expression*> alternative,
-                                       Nonnull<TuplePattern*> arguments)
-    : Pattern(AstNodeKind::AlternativePattern, source_loc),
-      choice_type_(&RequireFieldAccess(alternative).aggregate()),
-      alternative_name_(RequireFieldAccess(alternative).field()),
-      arguments_(arguments) {}
-
 auto ParenExpressionToParenPattern(Nonnull<Arena*> arena,
                                    const ParenContents<Expression>& contents)
     -> ParenContents<Pattern> {

+ 16 - 6
executable_semantics/ast/pattern.h

@@ -218,6 +218,19 @@ auto ParenExpressionToParenPattern(Nonnull<Arena*> arena,
 // A pattern that matches an alternative of a choice type.
 class AlternativePattern : public Pattern {
  public:
+  // Constructs an AlternativePattern that matches the alternative specified
+  // by `alternative`, if its arguments match `arguments`.
+  static auto Create(Nonnull<Arena*> arena, SourceLocation source_loc,
+                     Nonnull<Expression*> alternative,
+                     Nonnull<TuplePattern*> arguments)
+      -> ErrorOr<Nonnull<AlternativePattern*>> {
+    ASSIGN_OR_RETURN(Nonnull<FieldAccessExpression*> field_access,
+                     RequireFieldAccess(alternative));
+    return arena->New<AlternativePattern>(source_loc,
+                                          &field_access->aggregate(),
+                                          field_access->field(), arguments);
+  }
+
   // Constructs an AlternativePattern that matches a value of the type
   // specified by choice_type if it represents an alternative named
   // alternative_name, and its arguments match `arguments`.
@@ -230,12 +243,6 @@ class AlternativePattern : public Pattern {
         alternative_name_(std::move(alternative_name)),
         arguments_(arguments) {}
 
-  // Constructs an AlternativePattern that matches the alternative specified
-  // by `alternative`, if its arguments match `arguments`.
-  AlternativePattern(SourceLocation source_loc,
-                     Nonnull<Expression*> alternative,
-                     Nonnull<TuplePattern*> arguments);
-
   static auto classof(const AstNode* node) -> bool {
     return InheritsFromAlternativePattern(node->kind());
   }
@@ -249,6 +256,9 @@ class AlternativePattern : public Pattern {
   auto arguments() -> TuplePattern& { return *arguments_; }
 
  private:
+  static auto RequireFieldAccess(Nonnull<Expression*> alternative)
+      -> ErrorOr<Nonnull<FieldAccessExpression*>>;
+
   Nonnull<Expression*> choice_type_;
   std::string alternative_name_;
   Nonnull<TuplePattern*> arguments_;

+ 22 - 15
executable_semantics/ast/static_scope.cpp

@@ -5,43 +5,50 @@
 #include "executable_semantics/ast/static_scope.h"
 
 #include "executable_semantics/common/error.h"
+#include "llvm/Support/Error.h"
 
 namespace Carbon {
 
-void StaticScope::Add(std::string name, ValueNodeView entity) {
+auto StaticScope::Add(std::string name, ValueNodeView entity)
+    -> ErrorOr<Success> {
   auto [it, success] = declared_names_.insert({name, entity});
   if (!success && it->second != entity) {
-    FATAL_COMPILATION_ERROR(entity.base().source_loc())
-        << "Duplicate name `" << name << "` also found at "
-        << it->second.base().source_loc();
+    return FATAL_COMPILATION_ERROR(entity.base().source_loc())
+           << "Duplicate name `" << name << "` also found at "
+           << it->second.base().source_loc();
   }
+  return Success();
 }
 
 auto StaticScope::Resolve(const std::string& name,
-                          SourceLocation source_loc) const -> ValueNodeView {
-  std::optional<ValueNodeView> result = TryResolve(name, source_loc);
-  if (!result.has_value()) {
-    FATAL_COMPILATION_ERROR(source_loc) << "could not resolve '" << name << "'";
+                          SourceLocation source_loc) const
+    -> ErrorOr<ValueNodeView> {
+  ASSIGN_OR_RETURN(std::optional<ValueNodeView> result,
+                   TryResolve(name, source_loc));
+  if (!result) {
+    return FATAL_COMPILATION_ERROR(source_loc)
+           << "could not resolve '" << name << "'";
   }
   return *result;
 }
 
 auto StaticScope::TryResolve(const std::string& name,
                              SourceLocation source_loc) const
-    -> std::optional<ValueNodeView> {
+    -> ErrorOr<std::optional<ValueNodeView>> {
   auto it = declared_names_.find(name);
   if (it != declared_names_.end()) {
-    return it->second;
+    return std::make_optional(it->second);
   }
   std::optional<ValueNodeView> result;
   for (Nonnull<const StaticScope*> parent : parent_scopes_) {
-    auto parent_result = parent->TryResolve(name, source_loc);
+    ASSIGN_OR_RETURN(std::optional<ValueNodeView> parent_result,
+                     parent->TryResolve(name, source_loc));
     if (parent_result.has_value() && result.has_value() &&
         *parent_result != *result) {
-      FATAL_COMPILATION_ERROR(source_loc)
-          << "'" << name << "' is ambiguous between "
-          << result->base().source_loc() << " and "
-          << parent_result->base().source_loc();
+      return FATAL_COMPILATION_ERROR(source_loc)
+             << "'" << name << "' is ambiguous between "
+             << result->base().source_loc() << " and "
+             << parent_result->base().source_loc();
     }
     result = parent_result;
   }

+ 5 - 3
executable_semantics/ast/static_scope.h

@@ -12,10 +12,12 @@
 #include <vector>
 
 #include "common/check.h"
+#include "common/error.h"
 #include "executable_semantics/ast/ast_node.h"
 #include "executable_semantics/ast/source_location.h"
 #include "executable_semantics/ast/value_category.h"
 #include "executable_semantics/common/nonnull.h"
+#include "llvm/Support/Error.h"
 
 namespace Carbon {
 
@@ -133,7 +135,7 @@ class StaticScope {
  public:
   // Defines `name` to be `entity` in this scope, or reports a compilation error
   // if `name` is already defined to be a different entity in this scope.
-  void Add(std::string name, ValueNodeView entity);
+  auto Add(std::string name, ValueNodeView entity) -> ErrorOr<Success>;
 
   // Make `parent` a parent of this scope.
   // REQUIRES: `parent` is not already a parent of this scope.
@@ -145,14 +147,14 @@ class StaticScope {
   // scope, or reports a compilation error at `source_loc` there isn't exactly
   // one such definition.
   auto Resolve(const std::string& name, SourceLocation source_loc) const
-      -> ValueNodeView;
+      -> ErrorOr<ValueNodeView>;
 
  private:
   // Equivalent to Resolve, but returns `nullopt` instead of raising an error
   // if no definition can be found. Still raises a compilation error if more
   // than one definition is found.
   auto TryResolve(const std::string& name, SourceLocation source_loc) const
-      -> std::optional<ValueNodeView>;
+      -> ErrorOr<std::optional<ValueNodeView>>;
 
   // Maps locally declared names to their entities.
   std::unordered_map<std::string, ValueNodeView> declared_names_;

+ 4 - 0
executable_semantics/common/BUILD

@@ -17,6 +17,9 @@ cc_library(
     hdrs = ["error.h"],
     deps = [
         "//common:check",
+        "//common:error",
+        "//executable_semantics/ast:source_location",
+        "@llvm-project//llvm:Support",
     ],
 )
 
@@ -26,6 +29,7 @@ cc_test(
     deps = [
         ":error",
         "@com_google_googletest//:gtest_main",
+        "@llvm-project//llvm:Support",
     ],
 )
 

+ 73 - 9
executable_semantics/common/error.h

@@ -5,38 +5,102 @@
 #ifndef EXECUTABLE_SEMANTICS_COMMON_ERROR_H_
 #define EXECUTABLE_SEMANTICS_COMMON_ERROR_H_
 
+#include <optional>
+
 #include "common/check.h"
+#include "common/error.h"
+#include "executable_semantics/ast/source_location.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/raw_ostream.h"
 
 namespace Carbon {
 
-// Prints an error and exits. This should be used for non-recoverable errors
-// with user input.
+// A helper class for accumulating error message and converting to
+// `Carbon::Error`/`Carbon::ErrorOr<T>`.
+class ErrorBuilder {
+ public:
+  explicit ErrorBuilder(std::optional<SourceLocation> loc = std::nullopt)
+      : out_(message_) {
+    if (loc.has_value()) {
+      out_ << *loc << ": ";
+    }
+  }
+
+  // Accumulates string message.
+  template <typename T>
+  [[nodiscard]] auto operator<<(const T& message) -> ErrorBuilder& {
+    out_ << message;
+    return *this;
+  }
+
+  operator Error() { return Error(message_); }
+
+  template <typename V>
+  operator ErrorOr<V>() {
+    return Error(message_);
+  }
+
+  std::string message_;
+  llvm::raw_string_ostream out_;
+};
+
+// Builds a Carbon::Error instance with the specified message. This should be
+// used for non-recoverable errors with user input.
 //
 // For example:
-//   FATAL_PROGRAM_ERROR(line_num) << "Line is bad!";
-//   FATAL_PROGRAM_ERROR_NO_LINE() << "Application is bad!";
+//   return FATAL_PROGRAM_ERROR(line_num) << "Line is bad!";
+//   return FATAL_PROGRAM_ERROR_NO_LINE() << "Application is bad!";
 //
 // Where possible, try to identify the error as a compilation or
-// runtime error. Use CHECK/FATAL for internal errors. The generic program error
-// option is provided as a fallback for cases that don't fit those
+// runtime error. Use CHECK/FATAL for internal errors. The generic program
+// error option is provided as a fallback for cases that don't fit those
 // classifications.
+//
+// TODO: replace below macro invocations with direct `return ErrorBuilder() <<
+// xx` calls.
 
-#define FATAL_PROGRAM_ERROR_NO_LINE() RAW_EXITING_STREAM() << "PROGRAM ERROR: "
+#define FATAL_PROGRAM_ERROR_NO_LINE() \
+  Carbon::ErrorBuilder() << "PROGRAM ERROR: "
 
 #define FATAL_PROGRAM_ERROR(line) \
   FATAL_PROGRAM_ERROR_NO_LINE() << (line) << ": "
 
 #define FATAL_COMPILATION_ERROR_NO_LINE() \
-  RAW_EXITING_STREAM() << "COMPILATION ERROR: "
+  Carbon::ErrorBuilder() << "COMPILATION ERROR: "
 
 #define FATAL_COMPILATION_ERROR(line) \
   FATAL_COMPILATION_ERROR_NO_LINE() << (line) << ": "
 
-#define FATAL_RUNTIME_ERROR_NO_LINE() RAW_EXITING_STREAM() << "RUNTIME ERROR: "
+#define FATAL_RUNTIME_ERROR_NO_LINE() \
+  Carbon::ErrorBuilder() << "RUNTIME ERROR: "
 
 #define FATAL_RUNTIME_ERROR(line) \
   FATAL_RUNTIME_ERROR_NO_LINE() << (line) << ": "
 
+// Macro hackery to get a unique variable name.
+#define MAKE_UNIQUE_NAME_IMPL(a, b, c) a##b##c
+#define MAKE_UNIQUE_NAME(a, b, c) MAKE_UNIQUE_NAME_IMPL(a, b, c)
+
+#define RETURN_IF_ERROR_IMPL(unique_name, expr)       \
+  if (auto unique_name = (expr); !unique_name.ok()) { \
+    return std::move(unique_name).error();            \
+  }
+
+#define RETURN_IF_ERROR(expr) \
+  RETURN_IF_ERROR_IMPL(       \
+      MAKE_UNIQUE_NAME(_llvm_error_line, __LINE__, __COUNTER__), expr)
+
+#define ASSIGN_OR_RETURN_IMPL(unique_name, var, expr) \
+  auto unique_name = (expr);                          \
+  if (!unique_name.ok()) {                            \
+    return std::move(unique_name).error();            \
+  }                                                   \
+  var = std::move(*unique_name);
+
+#define ASSIGN_OR_RETURN(var, expr) \
+  ASSIGN_OR_RETURN_IMPL(            \
+      MAKE_UNIQUE_NAME(_llvm_expected_line, __LINE__, __COUNTER__), var, expr)
+
 }  // namespace Carbon
 
 #endif  // EXECUTABLE_SEMANTICS_COMMON_ERROR_H_

+ 78 - 8
executable_semantics/common/error_test.cpp

@@ -9,24 +9,94 @@
 namespace Carbon::Testing {
 namespace {
 
+using ::testing::Eq;
+
+auto MakeSuccess() -> ErrorOr<Success> { return Success(); }
+
+auto MakeError(std::string_view message) -> ErrorOr<Success> {
+  return Error(message);
+}
+
+auto MakeInt(int value) -> ErrorOr<int> { return value; }
+
+auto MakeFailedInt(std::string_view message) -> ErrorOr<int> {
+  return Error(message);
+}
+
+auto ErrorToString(const Error& e) -> std::string { return e.message(); }
+
+template <typename V>
+auto ErrorToString(const ErrorOr<V>& e) -> std::string {
+  return e.error().message();
+}
+
 TEST(ErrorTest, FatalProgramError) {
-  ASSERT_DEATH({ FATAL_PROGRAM_ERROR_NO_LINE() << "test"; },
-               "^PROGRAM ERROR: test\n");
+  EXPECT_EQ(ErrorToString(FATAL_PROGRAM_ERROR_NO_LINE() << "test"),
+            "PROGRAM ERROR: test");
 }
 
 TEST(ErrorTest, FatalRuntimeError) {
-  ASSERT_DEATH({ FATAL_RUNTIME_ERROR_NO_LINE() << "test"; },
-               "^RUNTIME ERROR: test\n");
+  EXPECT_EQ(ErrorToString(FATAL_RUNTIME_ERROR_NO_LINE() << "test"),
+            "RUNTIME ERROR: test");
 }
 
 TEST(ErrorTest, FatalCompilationError) {
-  ASSERT_DEATH({ FATAL_COMPILATION_ERROR_NO_LINE() << "test"; },
-               "^COMPILATION ERROR: test\n");
+  EXPECT_EQ(ErrorToString(FATAL_COMPILATION_ERROR_NO_LINE() << "test"),
+            "COMPILATION ERROR: test");
 }
 
 TEST(ErrorTest, FatalProgramErrorLine) {
-  ASSERT_DEATH({ FATAL_PROGRAM_ERROR(1) << "test"; },
-               "^PROGRAM ERROR: 1: test\n");
+  EXPECT_EQ(ErrorToString(FATAL_PROGRAM_ERROR(1) << "test"),
+            "PROGRAM ERROR: 1: test");
+}
+
+TEST(ErrorTest, ReturnIfErrorNoError) {
+  auto result = []() -> ErrorOr<Success> {
+    RETURN_IF_ERROR(MakeSuccess());
+    RETURN_IF_ERROR(MakeSuccess());
+    return Success();
+  }();
+  EXPECT_TRUE(result.ok());
+}
+
+TEST(ErrorTest, ReturnIfErrorHasError) {
+  auto result = []() -> ErrorOr<Success> {
+    RETURN_IF_ERROR(MakeSuccess());
+    RETURN_IF_ERROR(MakeError("error"));
+    return Success();
+  }();
+  ASSERT_FALSE(result.ok());
+  EXPECT_EQ(ErrorToString(result), "error");
+}
+
+TEST(ErrorTest, AssignOrReturnNoError) {
+  auto result = []() -> ErrorOr<int> {
+    RETURN_IF_ERROR(MakeSuccess());
+    ASSIGN_OR_RETURN(int a, MakeInt(1));
+    ASSIGN_OR_RETURN(const int b, MakeInt(2));
+    int c = 0;
+    ASSIGN_OR_RETURN(c, MakeInt(3));
+    return a + b + c;
+  }();
+  ASSERT_TRUE(result.ok());
+  EXPECT_EQ(6, *result);
+}
+
+TEST(ErrorTest, AssignOrReturnHasDirectError) {
+  auto result = []() -> ErrorOr<int> {
+    RETURN_IF_ERROR(MakeError("error"));
+    return 0;
+  }();
+  ASSERT_FALSE(result.ok());
+}
+
+TEST(ErrorTest, AssignOrReturnHasErrorInExpected) {
+  auto result = []() -> ErrorOr<int> {
+    ASSIGN_OR_RETURN(int a, MakeFailedInt("error"));
+    return a;
+  }();
+  ASSERT_FALSE(result.ok());
+  EXPECT_EQ(ErrorToString(result), "error");
 }
 
 }  // namespace

+ 1 - 0
executable_semantics/interpreter/BUILD

@@ -77,6 +77,7 @@ cc_library(
         "//common:ostream",
         "//executable_semantics/ast",
         "//executable_semantics/common:arena",
+        "@llvm-project//llvm:Support",
     ],
 )
 

+ 33 - 17
executable_semantics/interpreter/action_stack.cpp

@@ -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
executable_semantics/interpreter/action_stack.h

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

+ 7 - 5
executable_semantics/interpreter/exec_program.cpp

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

+ 1 - 1
executable_semantics/interpreter/exec_program.h

@@ -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
executable_semantics/interpreter/heap.cpp

@@ -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
executable_semantics/interpreter/heap.h

@@ -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_;

+ 13 - 9
executable_semantics/interpreter/impl_scope.cpp

@@ -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;
   }

+ 2 - 2
executable_semantics/interpreter/impl_scope.h

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

+ 69 - 49
executable_semantics/interpreter/interpreter.cpp

@@ -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();
 }
 

+ 4 - 3
executable_semantics/interpreter/interpreter.h

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

+ 47 - 37
executable_semantics/interpreter/resolve_control_flow.cpp

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

+ 3 - 1
executable_semantics/interpreter/resolve_control_flow.h

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

+ 117 - 94
executable_semantics/interpreter/resolve_names.cpp

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

+ 3 - 1
executable_semantics/interpreter/resolve_names.h

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

文件差異過大導致無法顯示
+ 344 - 289
executable_semantics/interpreter/type_checker.cpp


+ 50 - 34
executable_semantics/interpreter/type_checker.h

@@ -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
executable_semantics/interpreter/value.cpp

@@ -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
executable_semantics/interpreter/value.h

@@ -76,13 +76,15 @@ class Value {
   // Returns the sub-Value specified by `path`, which must be a valid field
   // path for *this.
   auto GetField(Nonnull<Arena*> arena, const FieldPath& path,
-                SourceLocation source_loc) const -> Nonnull<const Value*>;
+                SourceLocation source_loc) const
+      -> ErrorOr<Nonnull<const Value*>>;
 
   // Returns a copy of *this, but with the sub-Value specified by `path`
   // set to `field_value`. `path` must be a valid field path for *this.
   auto SetField(Nonnull<Arena*> arena, const FieldPath& path,
                 Nonnull<const Value*> field_value,
-                SourceLocation source_loc) const -> Nonnull<const Value*>;
+                SourceLocation source_loc) const
+      -> ErrorOr<Nonnull<const Value*>>;
 
   // Returns the enumerator corresponding to the most-derived type of this
   // object.

+ 22 - 15
executable_semantics/main.cpp

@@ -10,6 +10,7 @@
 #include <string>
 #include <vector>
 
+#include "common/error.h"
 #include "executable_semantics/common/arena.h"
 #include "executable_semantics/common/nonnull.h"
 #include "executable_semantics/interpreter/exec_program.h"
@@ -21,18 +22,26 @@
 static void AddPrelude(
     std::string_view prelude_file_name, Carbon::Nonnull<Carbon::Arena*> arena,
     std::vector<Carbon::Nonnull<Carbon::Declaration*>>* declarations) {
-  std::variant<Carbon::AST, Carbon::SyntaxErrorCode> parse_result =
+  Carbon::ErrorOr<Carbon::AST> parse_result =
       Carbon::Parse(arena, prelude_file_name, false);
-  if (std::holds_alternative<Carbon::SyntaxErrorCode>(parse_result)) {
+  if (!parse_result.ok()) {
     // Try again with tracing, to help diagnose the problem.
-    Carbon::Parse(arena, prelude_file_name, true);
-    FATAL() << "Failed to parse prelude.";
+    Carbon::ErrorOr<Carbon::AST> trace_parse_result =
+        Carbon::Parse(arena, prelude_file_name, true);
+    FATAL() << "Failed to parse prelude: "
+            << trace_parse_result.error().message();
   }
-  const auto& prelude = std::get<Carbon::AST>(parse_result);
+  const auto& prelude = *parse_result;
   declarations->insert(declarations->begin(), prelude.declarations.begin(),
                        prelude.declarations.end());
 }
 
+// Prints an error message and returns error code value.
+auto PrintError(const Carbon::Error& error) -> int {
+  llvm::errs() << error.message() << "\n";
+  return EXIT_FAILURE;
+}
+
 auto main(int argc, char* argv[]) -> int {
   llvm::setBugReportMsg(
       "Please report issues to "
@@ -56,18 +65,16 @@ auto main(int argc, char* argv[]) -> int {
   llvm::cl::ParseCommandLineOptions(argc, argv);
 
   Carbon::Arena arena;
-  std::variant<Carbon::AST, Carbon::SyntaxErrorCode> ast_or_error =
+  Carbon::ErrorOr<Carbon::AST> ast =
       Carbon::Parse(&arena, input_file_name, trace_option);
-
-  if (auto* error = std::get_if<Carbon::SyntaxErrorCode>(&ast_or_error)) {
-    // Diagnostic already reported to std::cerr; this is just a return code.
-    return *error;
+  if (!ast.ok()) {
+    return PrintError(ast.error());
   }
-  auto& ast = std::get<Carbon::AST>(ast_or_error);
-
-  AddPrelude(prelude_file_name, &arena, &ast.declarations);
+  AddPrelude(prelude_file_name, &arena, &ast->declarations);
 
   // Typecheck and run the parsed program.
-  Carbon::ExecProgram(&arena, std::get<Carbon::AST>(ast_or_error),
-                      trace_option);
+  Carbon::ErrorOr<int> result = Carbon::ExecProgram(&arena, *ast, trace_option);
+  if (!result.ok()) {
+    return PrintError(result.error());
+  }
 }

+ 2 - 0
executable_semantics/syntax/BUILD

@@ -30,6 +30,7 @@ cc_library(
     deps = [
         ":syntax",
         "@com_google_googletest//:gtest",
+        "@llvm-project//llvm:Support",
     ],
 )
 
@@ -57,6 +58,7 @@ cc_library(
     deps = [
         ":bison_wrap",
         "//common:check",
+        "//common:error",
         "//common:ostream",
         "//common:string_helpers",
         "//executable_semantics/ast",

+ 38 - 22
executable_semantics/syntax/lexer.lpp

@@ -8,20 +8,15 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
   #include <cstdlib>
 
   #include "common/check.h"
+  #include "common/error.h"
   #include "common/string_helpers.h"
   #include "executable_semantics/syntax/parse_and_lex_context.h"
   #include "executable_semantics/syntax/parser.h"
   #include "llvm/ADT/StringExtras.h"
+  #include "llvm/Support/FormatVariadic.h"
 
-  // Prints a newline in trace mode because trace prints an incomplete line
-  // "Reading a token: " which can prevent LIT from finding expected patterns.
-  #define FATAL_SYNTAX_ERROR(context)                                       \
-    RAW_EXITING_STREAM() << (context.trace() ? "\n" : "")                   \
-                         << "COMPILATION ERROR: " << (context.source_loc()) \
-                         << ": "
-
-  // Reads and returns a single character. Fails on EOF.
-  char ReadChar(yyscan_t yyscanner, const Carbon::ParseAndLexContext& context);
+  // Reads and returns a single character. Reports an error on EOF.
+  auto ReadChar(yyscan_t yyscanner, Carbon::ParseAndLexContext& context) -> int;
 %}
 
 /* Turn off legacy bits we don't need. */
@@ -258,7 +253,13 @@ string_literal        \"([^\\\"\n\v\f\r]|\\.)*\"
 
 {intrinsic_identifier} {
   BEGIN(AFTER_OPERAND);
-  return ARG_TOKEN(intrinsic_identifier, yytext);
+  Carbon::ErrorOr<Carbon::IntrinsicExpression::Intrinsic> intrinsic =
+      Carbon::IntrinsicExpression::FindIntrinsic(yytext, context.source_loc());
+  if (intrinsic.ok()) {
+    return ARG_TOKEN(intrinsic_identifier, *intrinsic);
+  } else {
+    return context.RecordSyntaxError(intrinsic.error().message());
+  }
 }
 
 {identifier} {
@@ -270,7 +271,8 @@ string_literal        \"([^\\\"\n\v\f\r]|\\.)*\"
   BEGIN(AFTER_OPERAND);
   int val = 0;
   if (!llvm::to_integer(yytext, val)) {
-    FATAL_SYNTAX_ERROR(context) << "Invalid integer literal: " << yytext;
+    return context.RecordSyntaxError(
+        llvm::formatv("Invalid integer literal: {0}", yytext));
   }
   return ARG_TOKEN(integer_literal, val);
 }
@@ -280,7 +282,8 @@ string_literal        \"([^\\\"\n\v\f\r]|\\.)*\"
   CHECK(str.consume_front("\"") && str.consume_back("\""));
   std::optional<std::string> unescaped = Carbon::UnescapeStringLiteral(str);
   if (unescaped == std::nullopt) {
-    FATAL_SYNTAX_ERROR(context) << "Invalid escaping in string: " << yytext;
+    return context.RecordSyntaxError(
+        llvm::formatv("Invalid escaping in string: {0}", yytext));
   }
   return ARG_TOKEN(string_literal, *unescaped);
 }
@@ -291,24 +294,37 @@ string_literal        \"([^\\\"\n\v\f\r]|\\.)*\"
   // Scans for the closing """, checking for possible escape sequences
   // like \""".
   for (;;) {
-    char c = ReadChar(yyscanner, context);
+    int c = ReadChar(yyscanner, context);
+    if (c <= 0) {
+      return SIMPLE_TOKEN(END_OF_FILE);
+    }
     s.push_back(c);
     if (c != '"' && c != '\\') {
       continue;
     }
     if (c == '\\') {
       // \" in \""" is not a terminator.
-      s.push_back(ReadChar(yyscanner, context));
+      c = ReadChar(yyscanner, context);
+      if (c <= 0) {
+        return SIMPLE_TOKEN(END_OF_FILE);
+      }
+      s.push_back(c);
       continue;
     }
 
     c = ReadChar(yyscanner, context);
+    if (c <= 0) {
+      return SIMPLE_TOKEN(END_OF_FILE);
+    }
     s.push_back(c);
     if (c != '"') {
       continue;
     }
 
     c = ReadChar(yyscanner, context);
+    if (c <= 0) {
+      return SIMPLE_TOKEN(END_OF_FILE);
+    }
     s.push_back(c);
     if (c == '"') {
       break;
@@ -317,8 +333,8 @@ string_literal        \"([^\\\"\n\v\f\r]|\\.)*\"
   Carbon::ErrorOr<std::string> block_string =
       Carbon::ParseBlockStringLiteral(s);
   if (!block_string.ok()) {
-    FATAL_SYNTAX_ERROR(context)
-        << "Invalid block string: " << block_string.error();
+    return context.RecordSyntaxError(llvm::formatv(
+        "Invalid block string: {0}", block_string.error().message()));
   }
   return ARG_TOKEN(string_literal, *block_string);
 }
@@ -345,17 +361,17 @@ string_literal        \"([^\\\"\n\v\f\r]|\\.)*\"
 }
 
 . {
-  FATAL_SYNTAX_ERROR(context)
-      << "invalid character '\\x" << llvm::toHex(llvm::StringRef(yytext, 1))
-      << "' in source file.";
+  return context.RecordSyntaxError(
+      llvm::formatv("invalid character '\\x{0}' in source file.",
+                    llvm::toHex(llvm::StringRef(yytext, 1))));
 }
 
 %%
 
-char ReadChar(yyscan_t yyscanner, const Carbon::ParseAndLexContext& context) {
+auto ReadChar(yyscan_t yyscanner, Carbon::ParseAndLexContext& context) -> int {
   const int c = yyinput(yyscanner);
-  if (c == EOF) {
-    FATAL_SYNTAX_ERROR(context) << "Unexpected end of file";
+  if (c <= 0) {
+    context.RecordSyntaxError("Unexpected end of file");
   }
   return c;
 }

+ 14 - 14
executable_semantics/syntax/parse.cpp

@@ -5,16 +5,17 @@
 #include "executable_semantics/syntax/parse.h"
 
 #include "common/check.h"
+#include "common/error.h"
 #include "executable_semantics/common/error.h"
 #include "executable_semantics/syntax/lexer.h"
 #include "executable_semantics/syntax/parse_and_lex_context.h"
 #include "executable_semantics/syntax/parser.h"
+#include "llvm/Support/Error.h"
 
 namespace Carbon {
 
 auto ParseImpl(yyscan_t scanner, Nonnull<Arena*> arena,
-               std::string_view input_file_name, bool trace)
-    -> std::variant<AST, SyntaxErrorCode> {
+               std::string_view input_file_name, bool trace) -> ErrorOr<AST> {
   // Prepare other parser arguments.
   std::optional<AST> ast = std::nullopt;
   ParseAndLexContext context(arena->New<std::string>(input_file_name), trace);
@@ -24,11 +25,12 @@ auto ParseImpl(yyscan_t scanner, Nonnull<Arena*> arena,
   if (trace) {
     parser.set_debug_level(1);
   }
-  auto syntax_error_code = parser();
 
-  // Return an error if appropriate.
-  if (syntax_error_code != 0) {
-    return syntax_error_code;
+  if (auto syntax_error_code = parser(); syntax_error_code != 0) {
+    const std::string error_message = context.error_messages().empty()
+                                          ? "Unknown parser error"
+                                          : context.error_messages()[0];
+    return Error(error_message);
   }
 
   // Return parse results.
@@ -38,11 +40,11 @@ auto ParseImpl(yyscan_t scanner, Nonnull<Arena*> arena,
 }
 
 auto Parse(Nonnull<Arena*> arena, std::string_view input_file_name, bool trace)
-    -> std::variant<AST, SyntaxErrorCode> {
+    -> ErrorOr<AST> {
   FILE* input_file = fopen(std::string(input_file_name).c_str(), "r");
   if (input_file == nullptr) {
-    FATAL_PROGRAM_ERROR_NO_LINE() << "Error opening '" << input_file_name
-                                  << "': " << std::strerror(errno);
+    return FATAL_PROGRAM_ERROR_NO_LINE() << "Error opening '" << input_file_name
+                                         << "': " << std::strerror(errno);
   }
 
   // Prepare the lexer.
@@ -51,8 +53,7 @@ auto Parse(Nonnull<Arena*> arena, std::string_view input_file_name, bool trace)
   auto buffer = yy_create_buffer(input_file, YY_BUF_SIZE, scanner);
   yy_switch_to_buffer(buffer, scanner);
 
-  std::variant<AST, SyntaxErrorCode> result =
-      ParseImpl(scanner, arena, input_file_name, trace);
+  ErrorOr<AST> result = ParseImpl(scanner, arena, input_file_name, trace);
 
   // Clean up the lexer.
   yy_delete_buffer(buffer, scanner);
@@ -64,7 +65,7 @@ auto Parse(Nonnull<Arena*> arena, std::string_view input_file_name, bool trace)
 
 auto ParseFromString(Nonnull<Arena*> arena, std::string_view input_file_name,
                      std::string_view file_contents, bool trace)
-    -> std::variant<Carbon::AST, SyntaxErrorCode> {
+    -> ErrorOr<AST> {
   // Prepare the lexer.
   yyscan_t scanner;
   yylex_init(&scanner);
@@ -72,8 +73,7 @@ auto ParseFromString(Nonnull<Arena*> arena, std::string_view input_file_name,
       yy_scan_bytes(file_contents.data(), file_contents.size(), scanner);
   yy_switch_to_buffer(buffer, scanner);
 
-  std::variant<AST, SyntaxErrorCode> result =
-      ParseImpl(scanner, arena, input_file_name, trace);
+  ErrorOr<AST> result = ParseImpl(scanner, arena, input_file_name, trace);
 
   // Clean up the lexer.
   yy_delete_buffer(buffer, scanner);

+ 2 - 5
executable_semantics/syntax/parse.h

@@ -13,20 +13,17 @@
 
 namespace Carbon {
 
-// This is the code given us by Bison, for now.
-using SyntaxErrorCode = int;
-
 // Returns the AST representing the contents of the named file, or an error code
 // if parsing fails. Allocations go into the provided arena.
 auto Parse(Nonnull<Arena*> arena, std::string_view input_file_name, bool trace)
-    -> std::variant<Carbon::AST, SyntaxErrorCode>;
+    -> ErrorOr<Carbon::AST>;
 
 // Equivalent to `Parse`, but parses the contents of `file_contents`.
 // `input_file_name` is used only for reporting source locations, and does
 // not need to name a real file.
 auto ParseFromString(Nonnull<Arena*> arena, std::string_view input_file_name,
                      std::string_view file_contents, bool trace)
-    -> std::variant<Carbon::AST, SyntaxErrorCode>;
+    -> ErrorOr<Carbon::AST>;
 
 }  // namespace Carbon
 

+ 15 - 5
executable_semantics/syntax/parse_and_lex_context.cpp

@@ -6,11 +6,21 @@
 
 namespace Carbon {
 
-auto ParseAndLexContext::PrintDiagnostic(const std::string& message) -> void {
-  // TODO: Do we really want this to be fatal?  It makes the comment and the
-  // name a lie, and renders some of the other yyparse() result propagation code
-  // moot.
-  FATAL_COMPILATION_ERROR(source_loc()) << message;
+auto ParseAndLexContext::RecordSyntaxError(const std::string& message,
+                                           bool prefix_with_newline)
+    -> Parser::symbol_type {
+  // Optionally adds a newline in trace mode because trace prints an incomplete
+  // line "Reading a token: " which can prevent LIT from finding expected
+  // patterns.
+  // TODO: support formatting of `SourceLocation` instances with formatv().
+  std::string full_message;
+  llvm::raw_string_ostream(full_message)
+      << (prefix_with_newline && trace() ? "\n" : "")
+      << "COMPILATION ERROR: " << source_loc() << ": " << message;
+  error_messages_.push_back(full_message);
+
+  // TODO: use `YYerror` token once bison is upgraded to at least 3.5.
+  return Parser::make_END_OF_FILE(current_token_position);
 }
 
 }  // namespace Carbon

+ 11 - 2
executable_semantics/syntax/parse_and_lex_context.h

@@ -20,8 +20,11 @@ class ParseAndLexContext {
   ParseAndLexContext(Nonnull<const std::string*> input_file_name, bool trace)
       : input_file_name_(input_file_name), trace_(trace) {}
 
-  // Writes a syntax error diagnostic containing message to standard error.
-  auto PrintDiagnostic(const std::string& message) -> void;
+  // Formats ands records a lexer error. Returns an error token as a
+  // convenience.
+  auto RecordSyntaxError(const std::string& message,
+                         bool prefix_with_newline = false)
+      -> Parser::symbol_type;
 
   auto source_loc() const -> SourceLocation {
     return SourceLocation(input_file_name_,
@@ -33,12 +36,18 @@ class ParseAndLexContext {
   // The source range of the token being (or just) lex'd.
   location current_token_position;
 
+  auto error_messages() const -> const std::vector<std::string> {
+    return error_messages_;
+  }
+
  private:
   // A path to the file processed, relative to the current working directory
   // when *this is called.
   Nonnull<const std::string*> input_file_name_;
 
   bool trace_;
+
+  std::vector<std::string> error_messages_;
 };
 
 }  // namespace Carbon

+ 3 - 3
executable_semantics/syntax/parse_test.cpp

@@ -23,10 +23,10 @@ fn Foo() {}
 
 TEST(ParseTest, ParseFromString) {
   Arena arena;
-  std::variant<AST, SyntaxErrorCode> parse_result =
+  ErrorOr<AST> parse_result =
       ParseFromString(&arena, "file.carbon", FileContents, /*trace=*/false);
-  ASSERT_TRUE(std::holds_alternative<AST>(parse_result));
-  EXPECT_EQ(std::get<AST>(parse_result).declarations.size(), 1);
+  ASSERT_TRUE(parse_result.ok());
+  EXPECT_EQ(parse_result->declarations.size(), 1);
 }
 
 }  // namespace

+ 6 - 4
executable_semantics/syntax/parse_test_matchers_internal.h

@@ -12,6 +12,8 @@
 #include <variant>
 
 #include "executable_semantics/syntax/parse.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
 
 namespace Carbon::TestingInternal {
 
@@ -32,14 +34,14 @@ class ParsedAsMatcher {
     DescribeToImpl(out, /*negated=*/true);
   }
 
-  auto MatchAndExplain(const std::variant<AST, SyntaxErrorCode>& result,
+  auto MatchAndExplain(const ErrorOr<AST>& result,
                        ::testing::MatchResultListener* listener) const -> bool {
-    if (std::holds_alternative<SyntaxErrorCode>(result)) {
-      *listener << "holds error code " << std::get<SyntaxErrorCode>(result);
+    if (!result.ok()) {
+      *listener << "is a failed parse with error: " << result.error().message();
       return false;
     } else {
       *listener << "is a successful parse whose ";
-      return ast_matcher_.MatchAndExplain(std::get<AST>(result), listener);
+      return ast_matcher_.MatchAndExplain(*result, listener);
     }
   }
 

+ 29 - 7
executable_semantics/syntax/parser.ypp

@@ -62,6 +62,7 @@
   #include "common/check.h"
   #include "executable_semantics/syntax/parse_and_lex_context.h"
   #include "llvm/ADT/StringExtras.h"
+  #include "llvm/Support/raw_ostream.h"
 }  // %code top
 
 %code requires {
@@ -86,13 +87,13 @@
 
 %code {
   void Carbon::Parser::error(const location_type&, const std::string& message) {
-    context.PrintDiagnostic(message);
+    context.RecordSyntaxError(message);
   }
 }  // %code
 
 %token <int> integer_literal
 %token <std::string> identifier
-%token <std::string> intrinsic_identifier
+%token <IntrinsicExpression::Intrinsic> intrinsic_identifier
 %token <std::string> sized_type_literal
 %token <std::string> string_literal
 %type <std::string> designator
@@ -582,7 +583,16 @@ non_expression_pattern:
 | paren_pattern
     { $$ = $1; }
 | postfix_expression tuple_pattern
-    { $$ = arena->New<AlternativePattern>(context.source_loc(), $1, $2); }
+    {
+      ErrorOr<Nonnull<AlternativePattern*>> alternative_pattern =
+          AlternativePattern::Create(arena, context.source_loc(), $1, $2);
+      if (alternative_pattern.ok()) {
+        $$ = *alternative_pattern;
+      } else {
+        context.RecordSyntaxError(alternative_pattern.error().message());
+        YYERROR;
+      }
+    }
 | VAR non_expression_pattern
     { $$ = arena->New<VarPattern>(context.source_loc(), $2); }
 ;
@@ -804,13 +814,25 @@ receiver:
 function_declaration:
   FN identifier deduced_params receiver maybe_empty_tuple_pattern return_term block
     {
-      $$ = arena->New<FunctionDeclaration>(context.source_loc(), $2, $3, $4, $5,
-                                           $6, $7);
+      ErrorOr<FunctionDeclaration*> fn = FunctionDeclaration::Create(
+          arena, context.source_loc(), $2, $3, $4, $5, $6, $7);
+      if (fn.ok()) {
+        $$ = *fn;
+      } else {
+        context.RecordSyntaxError(fn.error().message());
+        YYERROR;
+      }
     }
 | FN identifier deduced_params receiver maybe_empty_tuple_pattern return_term SEMICOLON
     {
-      $$ = arena->New<FunctionDeclaration>(context.source_loc(), $2, $3, $4, $5,
-                                           $6, std::nullopt);
+      ErrorOr<FunctionDeclaration*> fn = FunctionDeclaration::Create(
+          arena, context.source_loc(), $2, $3, $4, $5, $6, std::nullopt);
+      if (fn.ok()) {
+        $$ = *fn;
+      } else {
+        context.RecordSyntaxError(fn.error().message());
+        YYERROR;
+      }
     }
 ;
 variable_declaration: identifier COLON pattern

部分文件因文件數量過多而無法顯示