浏览代码

Switch executable semantics to use ErrorBuilder directly and relocate macros (#1184)

Jon Meow 4 年之前
父节点
当前提交
87e58f5db3
共有 37 个文件被更改,包括 327 次插入376 次删除
  1. 53 0
      common/error.h
  2. 48 0
      common/error_test.cpp
  3. 2 2
      executable_semantics/README.md
  4. 13 22
      executable_semantics/ast/BUILD
  5. 1 1
      executable_semantics/ast/ast_node.h
  6. 2 2
      executable_semantics/ast/declaration.cpp
  7. 1 1
      executable_semantics/ast/declaration.h
  8. 2 3
      executable_semantics/ast/expression.cpp
  9. 1 1
      executable_semantics/ast/expression.h
  10. 2 2
      executable_semantics/ast/paren_contents.h
  11. 2 2
      executable_semantics/ast/pattern.cpp
  12. 1 1
      executable_semantics/ast/pattern.h
  13. 1 1
      executable_semantics/ast/return_term.h
  14. 1 1
      executable_semantics/ast/statement.h
  15. 4 5
      executable_semantics/ast/static_scope.cpp
  16. 1 1
      executable_semantics/ast/static_scope.h
  17. 16 10
      executable_semantics/common/BUILD
  18. 0 109
      executable_semantics/common/error.h
  19. 44 0
      executable_semantics/common/error_builders.h
  20. 30 0
      executable_semantics/common/error_builders_test.cpp
  21. 0 101
      executable_semantics/common/error_test.cpp
  22. 3 3
      executable_semantics/common/source_location.h
  23. 1 1
      executable_semantics/fuzzing/BUILD
  24. 1 1
      executable_semantics/fuzzing/fuzzverter.cpp
  25. 6 6
      executable_semantics/interpreter/BUILD
  26. 1 1
      executable_semantics/interpreter/action_stack.cpp
  27. 2 2
      executable_semantics/interpreter/heap.cpp
  28. 1 1
      executable_semantics/interpreter/heap.h
  29. 5 7
      executable_semantics/interpreter/impl_scope.cpp
  30. 3 3
      executable_semantics/interpreter/interpreter.cpp
  31. 6 6
      executable_semantics/interpreter/resolve_control_flow.cpp
  32. 2 3
      executable_semantics/interpreter/resolve_names.cpp
  33. 54 58
      executable_semantics/interpreter/type_checker.cpp
  34. 10 13
      executable_semantics/interpreter/value.cpp
  35. 1 1
      executable_semantics/syntax/BUILD
  36. 5 4
      executable_semantics/syntax/parse.cpp
  37. 1 1
      executable_semantics/testdata/array/fail_index.carbon

+ 53 - 0
common/error.h

@@ -104,6 +104,59 @@ class [[nodiscard]] ErrorOr {
   std::variant<Error, T> val_;
 };
 
+// A helper class for accumulating error message and converting to
+// `Error` and `ErrorOr<T>`.
+class ErrorBuilder {
+ public:
+  ErrorBuilder() : out_(std::make_unique<llvm::raw_string_ostream>(message_)) {}
+
+  // Accumulates string message.
+  template <typename T>
+  [[nodiscard]] auto operator<<(const T& message) -> ErrorBuilder& {
+    *out_ << message;
+    return *this;
+  }
+
+  // NOLINTNEXTLINE(google-explicit-constructor): Implicit cast for returns.
+  operator Error() { return Error(message_); }
+
+  template <typename T>
+  // NOLINTNEXTLINE(google-explicit-constructor): Implicit cast for returns.
+  operator ErrorOr<T>() {
+    return Error(message_);
+  }
+
+ private:
+  std::string message_;
+  // Use a pointer to allow move construction.
+  std::unique_ptr<llvm::raw_string_ostream> out_;
+};
+
 }  // namespace Carbon
 
+// 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); /* NOLINT(bugprone-macro-parentheses) */ \
+      !(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); /* NOLINT(bugprone-macro-parentheses) */ \
+  if (!(unique_name).ok()) {                                          \
+    return std::move(unique_name).error();                            \
+  }                                                                   \
+  var = std::move(*(unique_name)); /* NOLINT(bugprone-macro-parentheses) */
+
+#define ASSIGN_OR_RETURN(var, expr) \
+  ASSIGN_OR_RETURN_IMPL(            \
+      MAKE_UNIQUE_NAME(_llvm_expected_line, __LINE__, __COUNTER__), var, expr)
+
 #endif  // COMMON_ERROR_H_

+ 48 - 0
common/error_test.cpp

@@ -49,5 +49,53 @@ TEST(ErrorTest, IndirectErrorOrSuccess) {
   EXPECT_TRUE(IndirectErrorOrSuccessTest().ok());
 }
 
+TEST(ErrorTest, ReturnIfErrorNoError) {
+  auto result = []() -> ErrorOr<Success> {
+    RETURN_IF_ERROR(ErrorOr<Success>(Success()));
+    RETURN_IF_ERROR(ErrorOr<Success>(Success()));
+    return Success();
+  }();
+  EXPECT_TRUE(result.ok());
+}
+
+TEST(ErrorTest, ReturnIfErrorHasError) {
+  auto result = []() -> ErrorOr<Success> {
+    RETURN_IF_ERROR(ErrorOr<Success>(Success()));
+    RETURN_IF_ERROR(ErrorOr<Success>(Error("error")));
+    return Success();
+  }();
+  ASSERT_FALSE(result.ok());
+  EXPECT_EQ(result.error().message(), "error");
+}
+
+TEST(ErrorTest, AssignOrReturnNoError) {
+  auto result = []() -> ErrorOr<int> {
+    ASSIGN_OR_RETURN(int a, ErrorOr<int>(1));
+    ASSIGN_OR_RETURN(const int b, ErrorOr<int>(2));
+    int c = 0;
+    ASSIGN_OR_RETURN(c, ErrorOr<int>(3));
+    return a + b + c;
+  }();
+  ASSERT_TRUE(result.ok());
+  EXPECT_EQ(6, *result);
+}
+
+TEST(ErrorTest, AssignOrReturnHasDirectError) {
+  auto result = []() -> ErrorOr<int> {
+    RETURN_IF_ERROR(ErrorOr<int>(Error("error")));
+    return 0;
+  }();
+  ASSERT_FALSE(result.ok());
+}
+
+TEST(ErrorTest, AssignOrReturnHasErrorInExpected) {
+  auto result = []() -> ErrorOr<int> {
+    ASSIGN_OR_RETURN(int a, ErrorOr<int>(Error("error")));
+    return a;
+  }();
+  ASSERT_FALSE(result.ok());
+  EXPECT_EQ(result.error().message(), "error");
+}
+
 }  // namespace
 }  // namespace Carbon::Testing

+ 2 - 2
executable_semantics/README.md

@@ -54,8 +54,8 @@ lifetime groups in the future.
 
 For simplicity, `executable_semantics` generally treats all errors as fatal.
 Errors caused by bugs in the user-provided Carbon code should be reported with
-the macros in [`error.h`](common/error.h). Errors caused by bugs in
-`executable_semantics` itself should be reported with
+the error builders in [`error_builders.h`](common/error_builders.h). Errors
+caused by bugs in `executable_semantics` itself should be reported with
 [`CHECK` or `FATAL`](../common/check.h).
 
 ## Example Programs (Regression Tests)

+ 13 - 22
executable_semantics/ast/BUILD

@@ -22,7 +22,7 @@ cc_library(
         "ast_rtti.h",
     ],
     deps = [
-        ":source_location",
+        "//executable_semantics/common:source_location",
         "@llvm-project//llvm:Support",
     ],
 )
@@ -77,11 +77,11 @@ cc_library(
     deps = [
         ":ast_node",
         ":pattern",
-        ":source_location",
         ":value_category",
         "//common:check",
         "//common:ostream",
         "//executable_semantics/common:nonnull",
+        "//executable_semantics/common:source_location",
         "@llvm-project//llvm:Support",
     ],
 )
@@ -97,12 +97,12 @@ cc_library(
         ":impl_binding",
         ":pattern",
         ":return_term",
-        ":source_location",
         ":statement",
         ":static_scope",
         ":value_category",
         "//common:ostream",
         "//executable_semantics/common:nonnull",
+        "//executable_semantics/common:source_location",
         "@llvm-project//llvm:Support",
     ],
 )
@@ -112,10 +112,10 @@ cc_library(
     hdrs = ["return_term.h"],
     deps = [
         ":expression",
-        ":source_location",
         "//common:check",
         "//common:ostream",
         "//executable_semantics/common:nonnull",
+        "//executable_semantics/common:source_location",
         "@llvm-project//llvm:Support",
     ],
 )
@@ -127,13 +127,13 @@ cc_library(
     deps = [
         ":ast_node",
         ":paren_contents",
-        ":source_location",
         ":static_scope",
         ":value_category",
         "//common:indirect_value",
         "//common:ostream",
         "//executable_semantics/common:arena",
-        "//executable_semantics/common:error",
+        "//executable_semantics/common:error_builders",
+        "//executable_semantics/common:source_location",
         "@llvm-project//llvm:Support",
     ],
 )
@@ -159,8 +159,8 @@ cc_library(
     name = "paren_contents",
     hdrs = ["paren_contents.h"],
     deps = [
-        ":source_location",
-        "//executable_semantics/common:error",
+        "//executable_semantics/common:error_builders",
+        "//executable_semantics/common:source_location",
     ],
 )
 
@@ -171,12 +171,12 @@ cc_library(
     deps = [
         ":ast_node",
         ":expression",
-        ":source_location",
         ":static_scope",
         ":value_category",
         "//common:ostream",
         "//executable_semantics/common:arena",
-        "//executable_semantics/common:error",
+        "//executable_semantics/common:error_builders",
+        "//executable_semantics/common:source_location",
         "@llvm-project//llvm:Support",
     ],
 )
@@ -200,25 +200,16 @@ cc_library(
     hdrs = ["static_scope.h"],
     deps = [
         ":ast_node",
-        ":source_location",
         ":value_category",
         "//common:check",
         "//common:error",
-        "//executable_semantics/common:error",
+        "//executable_semantics/common:error_builders",
         "//executable_semantics/common:nonnull",
+        "//executable_semantics/common:source_location",
         "@llvm-project//llvm:Support",
     ],
 )
 
-cc_library(
-    name = "source_location",
-    hdrs = ["source_location.h"],
-    deps = [
-        "//common:ostream",
-        "//executable_semantics/common:nonnull",
-    ],
-)
-
 cc_library(
     name = "statement",
     srcs = ["statement.cpp"],
@@ -228,12 +219,12 @@ cc_library(
         ":expression",
         ":pattern",
         ":return_term",
-        ":source_location",
         ":static_scope",
         ":value_category",
         "//common:check",
         "//common:ostream",
         "//executable_semantics/common:arena",
+        "//executable_semantics/common:source_location",
         "@llvm-project//llvm:Support",
     ],
 )

+ 1 - 1
executable_semantics/ast/ast_node.h

@@ -6,7 +6,7 @@
 #define EXECUTABLE_SEMANTICS_AST_AST_NODE_H_
 
 #include "executable_semantics/ast/ast_rtti.h"
-#include "executable_semantics/ast/source_location.h"
+#include "executable_semantics/common/source_location.h"
 #include "llvm/Support/Casting.h"
 
 namespace Carbon {

+ 2 - 2
executable_semantics/ast/declaration.cpp

@@ -175,14 +175,14 @@ auto FunctionDeclaration::Create(
       case AstNodeKind::BindingPattern: {
         Nonnull<BindingPattern*> bp = &cast<BindingPattern>(*param);
         if (me_pattern.has_value() || bp->name() != "me") {
-          return FATAL_COMPILATION_ERROR(source_loc)
+          return CompilationError(source_loc)
                  << "illegal binding pattern in implicit parameter list";
         }
         me_pattern = bp;
         break;
       }
       default:
-        return FATAL_COMPILATION_ERROR(source_loc)
+        return CompilationError(source_loc)
                << "illegal AST node in implicit parameter list";
     }
   }

+ 1 - 1
executable_semantics/ast/declaration.h

@@ -14,11 +14,11 @@
 #include "executable_semantics/ast/impl_binding.h"
 #include "executable_semantics/ast/pattern.h"
 #include "executable_semantics/ast/return_term.h"
-#include "executable_semantics/ast/source_location.h"
 #include "executable_semantics/ast/statement.h"
 #include "executable_semantics/ast/static_scope.h"
 #include "executable_semantics/ast/value_category.h"
 #include "executable_semantics/common/nonnull.h"
+#include "executable_semantics/common/source_location.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/Support/Compiler.h"
 

+ 2 - 3
executable_semantics/ast/expression.cpp

@@ -8,7 +8,7 @@
 #include <optional>
 
 #include "executable_semantics/common/arena.h"
-#include "executable_semantics/common/error.h"
+#include "executable_semantics/common/error_builders.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/raw_ostream.h"
@@ -26,8 +26,7 @@ auto IntrinsicExpression::FindIntrinsic(std::string_view name,
   name.remove_prefix(std::strlen("__intrinsic_"));
   auto it = intrinsic_map.find(name);
   if (it == intrinsic_map.end()) {
-    return FATAL_COMPILATION_ERROR(source_loc)
-           << "Unknown intrinsic '" << name << "'";
+    return CompilationError(source_loc) << "Unknown intrinsic '" << name << "'";
   }
   return it->second;
 }

+ 1 - 1
executable_semantics/ast/expression.h

@@ -14,10 +14,10 @@
 #include "common/ostream.h"
 #include "executable_semantics/ast/ast_node.h"
 #include "executable_semantics/ast/paren_contents.h"
-#include "executable_semantics/ast/source_location.h"
 #include "executable_semantics/ast/static_scope.h"
 #include "executable_semantics/ast/value_category.h"
 #include "executable_semantics/common/arena.h"
+#include "executable_semantics/common/source_location.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/Support/Compiler.h"
 

+ 2 - 2
executable_semantics/ast/paren_contents.h

@@ -9,8 +9,8 @@
 #include <string>
 #include <vector>
 
-#include "executable_semantics/ast/source_location.h"
-#include "executable_semantics/common/error.h"
+#include "executable_semantics/common/error_builders.h"
+#include "executable_semantics/common/source_location.h"
 
 namespace Carbon {
 

+ 2 - 2
executable_semantics/ast/pattern.cpp

@@ -9,7 +9,7 @@
 #include "common/ostream.h"
 #include "executable_semantics/ast/expression.h"
 #include "executable_semantics/common/arena.h"
-#include "executable_semantics/common/error.h"
+#include "executable_semantics/common/error_builders.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/Casting.h"
 
@@ -151,7 +151,7 @@ auto TuplePatternFromParenContents(Nonnull<Arena*> arena,
 auto AlternativePattern::RequireFieldAccess(Nonnull<Expression*> alternative)
     -> ErrorOr<Nonnull<FieldAccessExpression*>> {
   if (alternative->kind() != ExpressionKind::FieldAccessExpression) {
-    return FATAL_PROGRAM_ERROR(alternative->source_loc())
+    return ProgramError(alternative->source_loc())
            << "Alternative pattern must have the form of a field access.";
   }
   return &cast<FieldAccessExpression>(*alternative);

+ 1 - 1
executable_semantics/ast/pattern.h

@@ -13,9 +13,9 @@
 #include "executable_semantics/ast/ast_node.h"
 #include "executable_semantics/ast/ast_rtti.h"
 #include "executable_semantics/ast/expression.h"
-#include "executable_semantics/ast/source_location.h"
 #include "executable_semantics/ast/static_scope.h"
 #include "executable_semantics/ast/value_category.h"
+#include "executable_semantics/common/source_location.h"
 #include "llvm/ADT/ArrayRef.h"
 
 namespace Carbon {

+ 1 - 1
executable_semantics/ast/return_term.h

@@ -11,8 +11,8 @@
 #include "common/check.h"
 #include "common/ostream.h"
 #include "executable_semantics/ast/expression.h"
-#include "executable_semantics/ast/source_location.h"
 #include "executable_semantics/common/nonnull.h"
+#include "executable_semantics/common/source_location.h"
 
 namespace Carbon {
 

+ 1 - 1
executable_semantics/ast/statement.h

@@ -13,10 +13,10 @@
 #include "executable_semantics/ast/expression.h"
 #include "executable_semantics/ast/pattern.h"
 #include "executable_semantics/ast/return_term.h"
-#include "executable_semantics/ast/source_location.h"
 #include "executable_semantics/ast/static_scope.h"
 #include "executable_semantics/ast/value_category.h"
 #include "executable_semantics/common/arena.h"
+#include "executable_semantics/common/source_location.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/Support/Compiler.h"
 

+ 4 - 5
executable_semantics/ast/static_scope.cpp

@@ -4,7 +4,7 @@
 
 #include "executable_semantics/ast/static_scope.h"
 
-#include "executable_semantics/common/error.h"
+#include "executable_semantics/common/error_builders.h"
 #include "llvm/Support/Error.h"
 
 namespace Carbon {
@@ -13,7 +13,7 @@ auto StaticScope::Add(std::string name, ValueNodeView entity)
     -> ErrorOr<Success> {
   auto [it, success] = declared_names_.insert({name, entity});
   if (!success && it->second != entity) {
-    return FATAL_COMPILATION_ERROR(entity.base().source_loc())
+    return CompilationError(entity.base().source_loc())
            << "Duplicate name `" << name << "` also found at "
            << it->second.base().source_loc();
   }
@@ -26,8 +26,7 @@ auto StaticScope::Resolve(const std::string& name,
   ASSIGN_OR_RETURN(std::optional<ValueNodeView> result,
                    TryResolve(name, source_loc));
   if (!result) {
-    return FATAL_COMPILATION_ERROR(source_loc)
-           << "could not resolve '" << name << "'";
+    return CompilationError(source_loc) << "could not resolve '" << name << "'";
   }
   return *result;
 }
@@ -45,7 +44,7 @@ auto StaticScope::TryResolve(const std::string& name,
                      parent->TryResolve(name, source_loc));
     if (parent_result.has_value() && result.has_value() &&
         *parent_result != *result) {
-      return FATAL_COMPILATION_ERROR(source_loc)
+      return CompilationError(source_loc)
              << "'" << name << "' is ambiguous between "
              << result->base().source_loc() << " and "
              << parent_result->base().source_loc();

+ 1 - 1
executable_semantics/ast/static_scope.h

@@ -14,9 +14,9 @@
 #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 "executable_semantics/common/source_location.h"
 #include "llvm/Support/Error.h"
 
 namespace Carbon {

+ 16 - 10
executable_semantics/common/BUILD

@@ -13,23 +13,21 @@ cc_library(
 )
 
 cc_library(
-    name = "error",
-    hdrs = ["error.h"],
+    name = "error_builders",
+    hdrs = ["error_builders.h"],
     deps = [
-        "//common:check",
+        ":source_location",
         "//common:error",
-        "//executable_semantics/ast:source_location",
-        "@llvm-project//llvm:Support",
     ],
 )
 
 cc_test(
-    name = "error_test",
-    srcs = ["error_test.cpp"],
+    name = "error_builders_test",
+    srcs = ["error_builders_test.cpp"],
     deps = [
-        ":error",
+        ":error_builders",
+        ":source_location",
         "@com_google_googletest//:gtest_main",
-        "@llvm-project//llvm:Support",
     ],
 )
 
@@ -38,6 +36,14 @@ cc_library(
     hdrs = ["nonnull.h"],
     deps = [
         "//common:check",
-        "@llvm-project//llvm:Support",
+    ],
+)
+
+cc_library(
+    name = "source_location",
+    hdrs = ["source_location.h"],
+    deps = [
+        ":nonnull",
+        "//common:ostream",
     ],
 )

+ 0 - 109
executable_semantics/common/error.h

@@ -1,109 +0,0 @@
-// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
-// Exceptions. See /LICENSE for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-#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 {
-
-// 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;
-  }
-
-  // NOLINTNEXTLINE(google-explicit-constructor): Implicit cast for returns.
-  operator Error() { return Error(message_); }
-
-  template <typename V>
-  // NOLINTNEXTLINE(google-explicit-constructor): Implicit cast for returns.
-  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:
-//   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
-// classifications.
-//
-// TODO: replace below macro invocations with direct `return ErrorBuilder() <<
-// xx` calls.
-
-#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() \
-  Carbon::ErrorBuilder() << "COMPILATION ERROR: "
-
-#define FATAL_COMPILATION_ERROR(line) \
-  FATAL_COMPILATION_ERROR_NO_LINE() << (line) << ": "
-
-#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); /* NOLINT(bugprone-macro-parentheses) */ \
-      !(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); /* NOLINT(bugprone-macro-parentheses) */ \
-  if (!(unique_name).ok()) {                                          \
-    return std::move(unique_name).error();                            \
-  }                                                                   \
-  var = std::move(*(unique_name)); /* NOLINT(bugprone-macro-parentheses) */
-
-#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_

+ 44 - 0
executable_semantics/common/error_builders.h

@@ -0,0 +1,44 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef EXECUTABLE_SEMANTICS_COMMON_ERROR_BUILDERS_H_
+#define EXECUTABLE_SEMANTICS_COMMON_ERROR_BUILDERS_H_
+
+#include "common/error.h"
+#include "executable_semantics/common/source_location.h"
+
+namespace Carbon {
+
+// Builds an Error instance with the specified message. This should be used for
+// non-recoverable errors with user input.
+//
+// For example:
+//   return ProgramError(line_num) << "Line is bad!";
+//   return ProgramError() << "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 classifications.
+
+inline auto CompilationError(SourceLocation loc) -> ErrorBuilder {
+  ErrorBuilder builder;
+  (void)(builder << "COMPILATION ERROR: " << loc << ": ");
+  return builder;
+}
+
+inline auto ProgramError(SourceLocation loc) -> ErrorBuilder {
+  ErrorBuilder builder;
+  (void)(builder << "PROGRAM ERROR: " << loc << ": ");
+  return builder;
+}
+
+inline auto RuntimeError(SourceLocation loc) -> ErrorBuilder {
+  ErrorBuilder builder;
+  (void)(builder << "RUNTIME ERROR: " << loc << ": ");
+  return builder;
+}
+
+}  // namespace Carbon
+
+#endif  // EXECUTABLE_SEMANTICS_COMMON_ERROR_BUILDERS_H_

+ 30 - 0
executable_semantics/common/error_builders_test.cpp

@@ -0,0 +1,30 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "executable_semantics/common/error_builders.h"
+
+#include <gtest/gtest.h>
+
+#include "executable_semantics/common/source_location.h"
+
+namespace Carbon::Testing {
+namespace {
+
+TEST(ErrorBuildersTest, CompilationError) {
+  Error err = CompilationError(SourceLocation("x", 1)) << "test";
+  EXPECT_EQ(err.message(), "COMPILATION ERROR: x:1: test");
+}
+
+TEST(ErrorBuildersTest, ProgramError) {
+  Error err = ProgramError(SourceLocation("x", 1)) << "test";
+  EXPECT_EQ(err.message(), "PROGRAM ERROR: x:1: test");
+}
+
+TEST(ErrorBuildersTest, RuntimeError) {
+  Error err = RuntimeError(SourceLocation("x", 1)) << "test";
+  EXPECT_EQ(err.message(), "RUNTIME ERROR: x:1: test");
+}
+
+}  // namespace
+}  // namespace Carbon::Testing

+ 0 - 101
executable_semantics/common/error_test.cpp

@@ -1,101 +0,0 @@
-// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
-// Exceptions. See /LICENSE for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-#include "executable_semantics/common/error.h"
-
-#include <gtest/gtest.h>
-
-namespace Carbon::Testing {
-namespace {
-
-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) {
-  EXPECT_EQ(ErrorToString(FATAL_PROGRAM_ERROR_NO_LINE() << "test"),
-            "PROGRAM ERROR: test");
-}
-
-TEST(ErrorTest, FatalRuntimeError) {
-  EXPECT_EQ(ErrorToString(FATAL_RUNTIME_ERROR_NO_LINE() << "test"),
-            "RUNTIME ERROR: test");
-}
-
-TEST(ErrorTest, FatalCompilationError) {
-  EXPECT_EQ(ErrorToString(FATAL_COMPILATION_ERROR_NO_LINE() << "test"),
-            "COMPILATION ERROR: test");
-}
-
-TEST(ErrorTest, FatalProgramErrorLine) {
-  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
-}  // namespace Carbon::Testing

+ 3 - 3
executable_semantics/ast/source_location.h → executable_semantics/common/source_location.h

@@ -2,8 +2,8 @@
 // Exceptions. See /LICENSE for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
-#ifndef EXECUTABLE_SEMANTICS_AST_SOURCE_LOCATION_H_
-#define EXECUTABLE_SEMANTICS_AST_SOURCE_LOCATION_H_
+#ifndef EXECUTABLE_SEMANTICS_COMMON_SOURCE_LOCATION_H_
+#define EXECUTABLE_SEMANTICS_COMMON_SOURCE_LOCATION_H_
 
 #include <string>
 #include <string_view>
@@ -42,4 +42,4 @@ class SourceLocation {
 
 }  // namespace Carbon
 
-#endif  // EXECUTABLE_SEMANTICS_AST_SOURCE_LOCATION_H_
+#endif  // EXECUTABLE_SEMANTICS_COMMON_SOURCE_LOCATION_H_

+ 1 - 1
executable_semantics/fuzzing/BUILD

@@ -58,7 +58,7 @@ cc_binary(
         ":fuzzer_util",
         "//common:error",
         "//common/fuzzing:carbon_cc_proto",
-        "//executable_semantics/common:error",
+        "//executable_semantics/common:error_builders",
         "//executable_semantics/common:nonnull",
         "//executable_semantics/syntax",
         "@com_google_protobuf//:protobuf_headers",

+ 1 - 1
executable_semantics/fuzzing/fuzzverter.cpp

@@ -19,7 +19,7 @@
 
 #include "common/error.h"
 #include "common/fuzzing/carbon.pb.h"
-#include "executable_semantics/common/error.h"
+#include "executable_semantics/common/error_builders.h"
 #include "executable_semantics/fuzzing/ast_to_proto.h"
 #include "executable_semantics/fuzzing/fuzzer_util.h"
 #include "executable_semantics/syntax/parse.h"

+ 6 - 6
executable_semantics/interpreter/BUILD

@@ -29,7 +29,7 @@ cc_library(
         "//executable_semantics/ast:pattern",
         "//executable_semantics/ast:statement",
         "//executable_semantics/common:arena",
-        "//executable_semantics/common:error",
+        "//executable_semantics/common:error_builders",
         "//executable_semantics/common:nonnull",
         "@llvm-project//llvm:Support",
     ],
@@ -105,9 +105,9 @@ cc_library(
         ":address",
         ":heap_allocation_interface",
         "//common:ostream",
-        "//executable_semantics/ast:source_location",
-        "//executable_semantics/common:error",
+        "//executable_semantics/common:error_builders",
         "//executable_semantics/common:nonnull",
+        "//executable_semantics/common:source_location",
         "@llvm-project//llvm:Support",
     ],
 )
@@ -143,7 +143,7 @@ cc_library(
         "//executable_semantics/ast:expression",
         "//executable_semantics/ast:pattern",
         "//executable_semantics/common:arena",
-        "//executable_semantics/common:error",
+        "//executable_semantics/common:error_builders",
         "@llvm-project//llvm:Support",
     ],
 )
@@ -158,7 +158,7 @@ cc_library(
         "//executable_semantics/ast:declaration",
         "//executable_semantics/ast:return_term",
         "//executable_semantics/ast:statement",
-        "//executable_semantics/common:error",
+        "//executable_semantics/common:error_builders",
         "//executable_semantics/common:nonnull",
         "@llvm-project//llvm:Support",
     ],
@@ -208,7 +208,7 @@ cc_library(
         "//executable_semantics/ast:expression",
         "//executable_semantics/ast:statement",
         "//executable_semantics/common:arena",
-        "//executable_semantics/common:error",
+        "//executable_semantics/common:error_builders",
         "//executable_semantics/common:nonnull",
         "@llvm-project//llvm:Support",
     ],

+ 1 - 1
executable_semantics/interpreter/action_stack.cpp

@@ -77,7 +77,7 @@ auto ActionStack::ValueOfNode(ValueNodeView value_node,
     }
   }
   // TODO: Move these errors to compile time and explain them more clearly.
-  return FATAL_RUNTIME_ERROR(source_loc)
+  return RuntimeError(source_loc)
          << "could not find `" << value_node.base() << "`";
 }
 

+ 2 - 2
executable_semantics/interpreter/heap.cpp

@@ -4,7 +4,7 @@
 
 #include "executable_semantics/interpreter/heap.h"
 
-#include "executable_semantics/common/error.h"
+#include "executable_semantics/common/error_builders.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/Error.h"
 
@@ -40,7 +40,7 @@ auto Heap::Write(const Address& a, Nonnull<const Value*> v,
 auto Heap::CheckAlive(AllocationId allocation, SourceLocation source_loc) const
     -> ErrorOr<Success> {
   if (!alive_[allocation.index_]) {
-    return FATAL_RUNTIME_ERROR(source_loc)
+    return RuntimeError(source_loc)
            << "undefined behavior: access to dead value "
            << *values_[allocation.index_];
   }

+ 1 - 1
executable_semantics/interpreter/heap.h

@@ -8,8 +8,8 @@
 #include <vector>
 
 #include "common/ostream.h"
-#include "executable_semantics/ast/source_location.h"
 #include "executable_semantics/common/nonnull.h"
+#include "executable_semantics/common/source_location.h"
 #include "executable_semantics/interpreter/address.h"
 #include "executable_semantics/interpreter/heap_allocation_interface.h"
 #include "executable_semantics/interpreter/value.h"

+ 5 - 7
executable_semantics/interpreter/impl_scope.cpp

@@ -4,7 +4,7 @@
 
 #include "executable_semantics/interpreter/impl_scope.h"
 
-#include "executable_semantics/common/error.h"
+#include "executable_semantics/common/error_builders.h"
 #include "executable_semantics/interpreter/value.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/Casting.h"
@@ -29,9 +29,8 @@ auto ImplScope::Resolve(Nonnull<const Value*> iface_type,
   ASSIGN_OR_RETURN(std::optional<ValueNodeView> result,
                    TryResolve(iface_type, type, source_loc));
   if (!result.has_value()) {
-    return FATAL_COMPILATION_ERROR(source_loc)
-           << "could not find implementation of " << *iface_type << " for "
-           << *type;
+    return CompilationError(source_loc) << "could not find implementation of "
+                                        << *iface_type << " for " << *type;
   }
   return *result;
 }
@@ -50,9 +49,8 @@ auto ImplScope::TryResolve(Nonnull<const Value*> iface_type,
                      parent->TryResolve(iface_type, type, source_loc));
     if (parent_result.has_value() && result.has_value() &&
         *parent_result != *result) {
-      return FATAL_COMPILATION_ERROR(source_loc)
-             << "ambiguous implementations of " << *iface_type << " for "
-             << *type;
+      return CompilationError(source_loc) << "ambiguous implementations of "
+                                          << *iface_type << " for " << *type;
     }
     result = parent_result;
   }

+ 3 - 3
executable_semantics/interpreter/interpreter.cpp

@@ -15,7 +15,7 @@
 #include "executable_semantics/ast/declaration.h"
 #include "executable_semantics/ast/expression.h"
 #include "executable_semantics/common/arena.h"
-#include "executable_semantics/common/error.h"
+#include "executable_semantics/common/error_builders.h"
 #include "executable_semantics/interpreter/action.h"
 #include "executable_semantics/interpreter/action_stack.h"
 #include "executable_semantics/interpreter/stack.h"
@@ -560,7 +560,7 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
         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())) {
-          return FATAL_RUNTIME_ERROR_NO_LINE()
+          return RuntimeError(exp.source_loc())
                  << "index " << i << " out of range in " << tuple;
         }
         return todo_.FinishAction(tuple.elements()[i]);
@@ -813,7 +813,7 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
             }
           }
           default:
-            return FATAL_RUNTIME_ERROR(exp.source_loc())
+            return RuntimeError(exp.source_loc())
                    << "in call, expected a function, not " << *act.results()[0];
         }
       } else if (act.pos() == 3) {

+ 6 - 6
executable_semantics/interpreter/resolve_control_flow.cpp

@@ -7,7 +7,7 @@
 #include "executable_semantics/ast/declaration.h"
 #include "executable_semantics/ast/return_term.h"
 #include "executable_semantics/ast/statement.h"
-#include "executable_semantics/common/error.h"
+#include "executable_semantics/common/error_builders.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/Error.h"
 
@@ -39,14 +39,14 @@ static auto ResolveControlFlow(Nonnull<Statement*> statement,
   switch (statement->kind()) {
     case StatementKind::Return: {
       if (!function.has_value()) {
-        return FATAL_COMPILATION_ERROR(statement->source_loc())
+        return CompilationError(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) {
-          return FATAL_COMPILATION_ERROR(statement->source_loc())
+          return CompilationError(statement->source_loc())
                  << "Only one return is allowed in a function with an `auto` "
                     "return type.";
         }
@@ -55,7 +55,7 @@ static auto ResolveControlFlow(Nonnull<Statement*> statement,
       auto& ret = cast<Return>(*statement);
       ret.set_function((*function)->declaration);
       if (ret.is_omitted_expression() != function_return.is_omitted()) {
-        return FATAL_COMPILATION_ERROR(ret.source_loc())
+        return CompilationError(ret.source_loc())
                << ret << " should"
                << (function_return.is_omitted() ? " not" : "")
                << " provide a return value, to match the function's signature.";
@@ -64,14 +64,14 @@ static auto ResolveControlFlow(Nonnull<Statement*> statement,
     }
     case StatementKind::Break:
       if (!loop.has_value()) {
-        return FATAL_COMPILATION_ERROR(statement->source_loc())
+        return CompilationError(statement->source_loc())
                << "break is not within a loop body";
       }
       cast<Break>(*statement).set_loop(*loop);
       return Success();
     case StatementKind::Continue:
       if (!loop.has_value()) {
-        return FATAL_COMPILATION_ERROR(statement->source_loc())
+        return CompilationError(statement->source_loc())
                << "continue is not within a loop body";
       }
       cast<Continue>(*statement).set_loop(*loop);

+ 2 - 3
executable_semantics/interpreter/resolve_names.cpp

@@ -167,8 +167,7 @@ static auto ResolveNames(Expression& expression,
     case ExpressionKind::TypeTypeLiteral:
       break;
     case ExpressionKind::UnimplementedExpression:
-      return FATAL_COMPILATION_ERROR(expression.source_loc())
-             << "Unimplemented";
+      return CompilationError(expression.source_loc()) << "Unimplemented";
   }
   return Success();
 }
@@ -376,7 +375,7 @@ static auto ResolveNames(Declaration& declaration, StaticScope& enclosing_scope)
         RETURN_IF_ERROR(
             ResolveNames(alternative->signature(), enclosing_scope));
         if (!alternative_names.insert(alternative->name()).second) {
-          return FATAL_COMPILATION_ERROR(alternative->source_loc())
+          return CompilationError(alternative->source_loc())
                  << "Duplicate name `" << alternative->name()
                  << "` in choice type";
         }

+ 54 - 58
executable_semantics/interpreter/type_checker.cpp

@@ -13,7 +13,7 @@
 #include "common/ostream.h"
 #include "executable_semantics/ast/declaration.h"
 #include "executable_semantics/common/arena.h"
-#include "executable_semantics/common/error.h"
+#include "executable_semantics/common/error_builders.h"
 #include "executable_semantics/interpreter/impl_scope.h"
 #include "executable_semantics/interpreter/interpreter.h"
 #include "executable_semantics/interpreter/value.h"
@@ -42,10 +42,9 @@ static auto ExpectExactType(SourceLocation source_loc,
                             Nonnull<const Value*> expected,
                             Nonnull<const Value*> actual) -> ErrorOr<Success> {
   if (!TypeEqual(expected, actual)) {
-    return FATAL_COMPILATION_ERROR(source_loc)
-           << "type error in " << context << "\n"
-           << "expected: " << *expected << "\n"
-           << "actual: " << *actual;
+    return CompilationError(source_loc) << "type error in " << context << "\n"
+                                        << "expected: " << *expected << "\n"
+                                        << "actual: " << *actual;
   }
   return Success();
 }
@@ -55,10 +54,9 @@ static auto ExpectPointerType(SourceLocation source_loc,
                               Nonnull<const Value*> actual)
     -> ErrorOr<Success> {
   if (actual->kind() != Value::Kind::PointerType) {
-    return FATAL_COMPILATION_ERROR(source_loc)
-           << "type error in " << context << "\n"
-           << "expected a pointer type\n"
-           << "actual: " << *actual;
+    return CompilationError(source_loc) << "type error in " << context << "\n"
+                                        << "expected a pointer type\n"
+                                        << "actual: " << *actual;
   }
   return Success();
 }
@@ -116,7 +114,7 @@ auto TypeChecker::ExpectIsConcreteType(SourceLocation source_loc,
                                        Nonnull<const Value*> value)
     -> ErrorOr<Success> {
   if (!IsConcreteType(value)) {
-    return FATAL_COMPILATION_ERROR(source_loc)
+    return CompilationError(source_loc)
            << "Expected a type, but got " << *value;
   } else {
     return Success();
@@ -230,7 +228,7 @@ auto TypeChecker::ExpectType(SourceLocation source_loc,
                              Nonnull<const Value*> expected,
                              Nonnull<const Value*> actual) -> ErrorOr<Success> {
   if (!IsImplicitlyConvertible(actual, expected)) {
-    return FATAL_COMPILATION_ERROR(source_loc)
+    return CompilationError(source_loc)
            << "type error in " << context << ": "
            << "'" << *actual << "' is not implicitly convertible to '"
            << *expected << "'";
@@ -257,7 +255,7 @@ auto TypeChecker::ArgumentDeduction(SourceLocation source_loc,
     }
     case Value::Kind::TupleValue: {
       if (arg_type->kind() != Value::Kind::TupleValue) {
-        return FATAL_COMPILATION_ERROR(source_loc)
+        return CompilationError(source_loc)
                << "type error in argument deduction\n"
                << "expected: " << *param_type << "\n"
                << "actual: " << *arg_type;
@@ -265,7 +263,7 @@ auto TypeChecker::ArgumentDeduction(SourceLocation source_loc,
       const auto& param_tup = cast<TupleValue>(*param_type);
       const auto& arg_tup = cast<TupleValue>(*arg_type);
       if (param_tup.elements().size() != arg_tup.elements().size()) {
-        return FATAL_COMPILATION_ERROR(source_loc)
+        return CompilationError(source_loc)
                << "mismatch in tuple sizes, expected "
                << param_tup.elements().size() << " but got "
                << arg_tup.elements().size();
@@ -279,7 +277,7 @@ auto TypeChecker::ArgumentDeduction(SourceLocation source_loc,
     }
     case Value::Kind::StructType: {
       if (arg_type->kind() != Value::Kind::StructType) {
-        return FATAL_COMPILATION_ERROR(source_loc)
+        return CompilationError(source_loc)
                << "type error in argument deduction\n"
                << "expected: " << *param_type << "\n"
                << "actual: " << *arg_type;
@@ -287,14 +285,14 @@ auto TypeChecker::ArgumentDeduction(SourceLocation source_loc,
       const auto& param_struct = cast<StructType>(*param_type);
       const auto& arg_struct = cast<StructType>(*arg_type);
       if (param_struct.fields().size() != arg_struct.fields().size()) {
-        return FATAL_COMPILATION_ERROR(source_loc)
+        return CompilationError(source_loc)
                << "mismatch in struct field counts, expected "
                << param_struct.fields().size() << " but got "
                << arg_struct.fields().size();
       }
       for (size_t i = 0; i < param_struct.fields().size(); ++i) {
         if (param_struct.fields()[i].name != arg_struct.fields()[i].name) {
-          return FATAL_COMPILATION_ERROR(source_loc)
+          return CompilationError(source_loc)
                  << "mismatch in field names, " << param_struct.fields()[i].name
                  << " != " << arg_struct.fields()[i].name;
         }
@@ -306,7 +304,7 @@ auto TypeChecker::ArgumentDeduction(SourceLocation source_loc,
     }
     case Value::Kind::FunctionType: {
       if (arg_type->kind() != Value::Kind::FunctionType) {
-        return FATAL_COMPILATION_ERROR(source_loc)
+        return CompilationError(source_loc)
                << "type error in argument deduction\n"
                << "expected: " << *param_type << "\n"
                << "actual: " << *arg_type;
@@ -322,7 +320,7 @@ auto TypeChecker::ArgumentDeduction(SourceLocation source_loc,
     }
     case Value::Kind::PointerType: {
       if (arg_type->kind() != Value::Kind::PointerType) {
-        return FATAL_COMPILATION_ERROR(source_loc)
+        return CompilationError(source_loc)
                << "type error in argument deduction\n"
                << "expected: " << *param_type << "\n"
                << "actual: " << *arg_type;
@@ -349,7 +347,7 @@ auto TypeChecker::ArgumentDeduction(SourceLocation source_loc,
           return Success();
         }
       }
-      return FATAL_COMPILATION_ERROR(source_loc)
+      return CompilationError(source_loc)
              << "type error in argument deduction\n"
              << "expected: " << *param_type << "\n"
              << "actual: " << *arg_type;
@@ -497,7 +495,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
                            InterpExp(&index.offset(), arena_, trace_));
           int i = cast<IntValue>(*offset_value).value();
           if (i < 0 || i >= static_cast<int>(tuple_type.elements().size())) {
-            return FATAL_COMPILATION_ERROR(e->source_loc())
+            return CompilationError(e->source_loc())
                    << "index " << i << " is out of range for type "
                    << tuple_type;
           }
@@ -515,7 +513,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
           return Success();
         }
         default:
-          return FATAL_COMPILATION_ERROR(e->source_loc()) << "expected a tuple";
+          return CompilationError(e->source_loc()) << "expected a tuple";
       }
     }
     case ExpressionKind::TupleLiteral: {
@@ -573,7 +571,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
               return Success();
             }
           }
-          return FATAL_COMPILATION_ERROR(access.source_loc())
+          return CompilationError(access.source_loc())
                  << "struct " << struct_type << " does not have a field named "
                  << access.field();
         }
@@ -599,7 +597,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
             }
             return Success();
           } else {
-            return FATAL_COMPILATION_ERROR(e->source_loc())
+            return CompilationError(e->source_loc())
                    << "class " << t_class.declaration().name()
                    << " does not have a field named " << access.field();
           }
@@ -610,7 +608,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
           std::optional<Nonnull<const Value*>> parameter_types =
               choice.FindAlternative(access.field());
           if (!parameter_types.has_value()) {
-            return FATAL_COMPILATION_ERROR(e->source_loc())
+            return CompilationError(e->source_loc())
                    << "choice " << choice.name()
                    << " does not have a field named " << access.field();
           }
@@ -641,10 +639,10 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
               default:
                 break;
             }
-            return FATAL_COMPILATION_ERROR(access.source_loc())
+            return CompilationError(access.source_loc())
                    << access.field() << " is not a class function";
           } else {
-            return FATAL_COMPILATION_ERROR(access.source_loc())
+            return CompilationError(access.source_loc())
                    << class_type << " does not have a class function named "
                    << access.field();
           }
@@ -674,14 +672,14 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
                 access.set_impl(*var_type.binding().impl_binding());
                 return Success();
               } else {
-                return FATAL_COMPILATION_ERROR(e->source_loc())
+                return CompilationError(e->source_loc())
                        << "field access, " << access.field() << " not in "
                        << iface_decl.name();
               }
               break;
             }
             default:
-              return FATAL_COMPILATION_ERROR(e->source_loc())
+              return CompilationError(e->source_loc())
                      << "field access, unexpected " << aggregate_type
                      << " of non-interface type " << typeof_var << " in " << *e;
           }
@@ -708,14 +706,14 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
             access.set_impl(*var_type.binding().impl_binding());
             return Success();
           } else {
-            return FATAL_COMPILATION_ERROR(e->source_loc())
+            return CompilationError(e->source_loc())
                    << "field access, " << access.field() << " not in "
                    << iface_decl.name();
           }
           break;
         }
         default:
-          return FATAL_COMPILATION_ERROR(e->source_loc())
+          return CompilationError(e->source_loc())
                  << "field access, unexpected " << aggregate_type << " in "
                  << *e;
       }
@@ -728,7 +726,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
             cast<FunctionDeclaration>(ident.value_node().base());
         if (!function.has_static_type()) {
           CHECK(function.return_term().is_auto());
-          return FATAL_COMPILATION_ERROR(ident.source_loc())
+          return CompilationError(ident.source_loc())
                  << "Function calls itself, but has a deduced return type";
         }
       }
@@ -822,7 +820,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
           return Success();
         case Operator::AddressOf:
           if (op.arguments()[0]->value_category() != ValueCategory::Var) {
-            return FATAL_COMPILATION_ERROR(op.arguments()[0]->source_loc())
+            return CompilationError(op.arguments()[0]->source_loc())
                    << "Argument to " << ToString(op.op())
                    << " should be an lvalue.";
           }
@@ -853,7 +851,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
               // has been added to the type checking of function signatures.
               if (auto it = deduced_type_args.find(deduced_param);
                   it == deduced_type_args.end()) {
-                return FATAL_COMPILATION_ERROR(e->source_loc())
+                return CompilationError(e->source_loc())
                        << "could not deduce type argument for type parameter "
                        << deduced_param->name() << "\n"
                        << "in " << call;
@@ -880,7 +878,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
                 case Value::Kind::TypeType:
                   break;
                 default:
-                  return FATAL_COMPILATION_ERROR(e->source_loc())
+                  return CompilationError(e->source_loc())
                          << "unexpected type of deduced parameter "
                          << *impl_binding->interface();
               }
@@ -913,7 +911,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
             CHECK(PatternMatch(&(*class_decl.type_params())->value(), arg,
                                call.source_loc(), std::nullopt, generic_args));
           } else {
-            return FATAL_COMPILATION_ERROR(call.source_loc())
+            return CompilationError(call.source_loc())
                    << "attempt to instantiate a non-generic class: " << *e;
           }
           // Find impls for all the impl bindings of the class.
@@ -934,7 +932,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
                 case Value::Kind::TypeType:
                   break;
                 default:
-                  return FATAL_COMPILATION_ERROR(e->source_loc())
+                  return CompilationError(e->source_loc())
                          << "unexpected type of deduced parameter "
                          << *impl_binding->interface();
               }
@@ -948,7 +946,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
           return Success();
         }
         default: {
-          return FATAL_COMPILATION_ERROR(e->source_loc())
+          return CompilationError(e->source_loc())
                  << "in call, expected a function\n"
                  << *e << "\nnot an operator of type "
                  << call.function().static_type() << "\n";
@@ -980,7 +978,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
       switch (cast<IntrinsicExpression>(*e).intrinsic()) {
         case IntrinsicExpression::Intrinsic::Print:
           if (intrinsic_exp.args().fields().size() != 1) {
-            return FATAL_COMPILATION_ERROR(e->source_loc())
+            return CompilationError(e->source_loc())
                    << "__intrinsic_print takes 1 argument";
           }
           RETURN_IF_ERROR(
@@ -1040,8 +1038,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
           Nonnull<const Value*> size_value,
           InterpExp(&array_literal.size_expression(), arena_, trace_));
       if (cast<IntValue>(size_value)->value() < 0) {
-        return FATAL_COMPILATION_ERROR(
-                   array_literal.size_expression().source_loc())
+        return CompilationError(array_literal.size_expression().source_loc())
                << "Array size cannot be negative";
       }
       array_literal.set_static_type(arena_->New<TypeType>());
@@ -1107,7 +1104,7 @@ auto TypeChecker::TypeCheckPattern(
     case PatternKind::BindingPattern: {
       auto& binding = cast<BindingPattern>(*p);
       if (!GetBindings(binding.type()).empty()) {
-        return FATAL_COMPILATION_ERROR(binding.type().source_loc())
+        return CompilationError(binding.type().source_loc())
                << "The type of a binding pattern cannot contain bindings.";
       }
       RETURN_IF_ERROR(TypeCheckPattern(&binding.type(), std::nullopt,
@@ -1122,7 +1119,7 @@ auto TypeChecker::TypeCheckPattern(
           BindingMap generic_args;
           if (!PatternMatch(type, *expected, binding.type().source_loc(),
                             std::nullopt, generic_args)) {
-            return FATAL_COMPILATION_ERROR(binding.type().source_loc())
+            return CompilationError(binding.type().source_loc())
                    << "Type pattern '" << *type
                    << "' does not match actual type '" << **expected << "'";
           }
@@ -1146,7 +1143,7 @@ auto TypeChecker::TypeCheckPattern(
       ASSIGN_OR_RETURN(Nonnull<const Value*> type,
                        InterpExp(&binding.type(), arena_, trace_));
       if (expected) {
-        return FATAL_COMPILATION_ERROR(binding.type().source_loc())
+        return CompilationError(binding.type().source_loc())
                << "Generic binding may not occur in pattern with expected "
                   "type: "
                << binding;
@@ -1165,12 +1162,11 @@ auto TypeChecker::TypeCheckPattern(
       auto& tuple = cast<TuplePattern>(*p);
       std::vector<Nonnull<const Value*>> field_types;
       if (expected && (*expected)->kind() != Value::Kind::TupleValue) {
-        return FATAL_COMPILATION_ERROR(p->source_loc())
-               << "didn't expect a tuple";
+        return CompilationError(p->source_loc()) << "didn't expect a tuple";
       }
       if (expected && tuple.fields().size() !=
                           cast<TupleValue>(**expected).elements().size()) {
-        return FATAL_COMPILATION_ERROR(tuple.source_loc())
+        return CompilationError(tuple.source_loc())
                << "tuples of different length";
       }
       for (size_t i = 0; i < tuple.fields().size(); ++i) {
@@ -1197,7 +1193,7 @@ auto TypeChecker::TypeCheckPattern(
       RETURN_IF_ERROR(TypeCheckExp(&alternative.choice_type(), impl_scope));
       if (alternative.choice_type().static_type().kind() !=
           Value::Kind::TypeOfChoiceType) {
-        return FATAL_COMPILATION_ERROR(alternative.source_loc())
+        return CompilationError(alternative.source_loc())
                << "alternative pattern does not name a choice type.";
       }
       if (expected) {
@@ -1212,7 +1208,7 @@ auto TypeChecker::TypeCheckPattern(
           cast<ChoiceType>(choice_type)
               .FindAlternative(alternative.alternative_name());
       if (parameter_types == std::nullopt) {
-        return FATAL_COMPILATION_ERROR(alternative.source_loc())
+        return CompilationError(alternative.source_loc())
                << "'" << alternative.alternative_name()
                << "' is not an alternative of " << choice_type;
       }
@@ -1302,7 +1298,7 @@ auto TypeChecker::TypeCheckStmt(Nonnull<Statement*> s,
                                  &assign.lhs().static_type(),
                                  &assign.rhs().static_type()));
       if (assign.lhs().value_category() != ValueCategory::Var) {
-        return FATAL_COMPILATION_ERROR(assign.source_loc())
+        return CompilationError(assign.source_loc())
                << "Cannot assign to rvalue '" << assign.lhs() << "'";
       }
       return Success();
@@ -1377,7 +1373,7 @@ auto TypeChecker::ExpectReturnOnAllPaths(
     std::optional<Nonnull<Statement*>> opt_stmt, SourceLocation source_loc)
     -> ErrorOr<Success> {
   if (!opt_stmt) {
-    return FATAL_COMPILATION_ERROR(source_loc)
+    return CompilationError(source_loc)
            << "control-flow reaches end of function that provides a `->` "
               "return "
               "type without reaching a return statement";
@@ -1387,7 +1383,7 @@ auto TypeChecker::ExpectReturnOnAllPaths(
     case StatementKind::Match: {
       auto& match = cast<Match>(*stmt);
       if (!IsExhaustive(match)) {
-        return FATAL_COMPILATION_ERROR(source_loc)
+        return CompilationError(source_loc)
                << "non-exhaustive match may allow control-flow to reach the "
                   "end "
                   "of a function that provides a `->` return type";
@@ -1402,7 +1398,7 @@ auto TypeChecker::ExpectReturnOnAllPaths(
     case StatementKind::Block: {
       auto& block = cast<Block>(*stmt);
       if (block.statements().empty()) {
-        return FATAL_COMPILATION_ERROR(stmt->source_loc())
+        return CompilationError(stmt->source_loc())
                << "control-flow reaches end of function that provides a `->` "
                   "return type without reaching a return statement";
       }
@@ -1431,7 +1427,7 @@ auto TypeChecker::ExpectReturnOnAllPaths(
     case StatementKind::Break:
     case StatementKind::Continue:
     case StatementKind::VariableDefinition:
-      return FATAL_COMPILATION_ERROR(stmt->source_loc())
+      return CompilationError(stmt->source_loc())
              << "control-flow reaches end of function that provides a `->` "
                 "return type without reaching a return statement";
   }
@@ -1497,7 +1493,7 @@ auto TypeChecker::DeclareFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
   } else {
     // We have to type-check the body in order to determine the return type.
     if (!f->body().has_value()) {
-      return FATAL_COMPILATION_ERROR(f->return_term().source_loc())
+      return CompilationError(f->return_term().source_loc())
              << "Function declaration has deduced return type but no body";
     }
     RETURN_IF_ERROR(TypeCheckStmt(*f->body(), function_scope));
@@ -1515,7 +1511,7 @@ auto TypeChecker::DeclareFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
 
   if (f->name() == "Main") {
     if (!f->return_term().type_expression().has_value()) {
-      return FATAL_COMPILATION_ERROR(f->return_term().source_loc())
+      return CompilationError(f->return_term().source_loc())
              << "`Main` must have an explicit return type";
     }
     RETURN_IF_ERROR(ExpectExactType(
@@ -1696,7 +1692,7 @@ auto TypeChecker::DeclareImplDeclaration(Nonnull<ImplDeclaration*> impl_decl,
                                    "member of implementation", iface_mem_type,
                                    &(*mem)->static_type()));
       } else {
-        return FATAL_COMPILATION_ERROR(impl_decl->source_loc())
+        return CompilationError(impl_decl->source_loc())
                << "implementation missing " << *mem_name;
       }
     }
@@ -1793,7 +1789,7 @@ auto TypeChecker::TypeCheckDeclaration(Nonnull<Declaration*> d,
           dyn_cast<ExpressionPattern>(&var.binding().type());
       if (binding_type == nullptr) {
         // TODO: consider adding support for `auto`
-        return FATAL_COMPILATION_ERROR(var.source_loc())
+        return CompilationError(var.source_loc())
                << "Type of a top-level variable must be an expression.";
       }
       if (var.has_initializer()) {
@@ -1845,7 +1841,7 @@ auto TypeChecker::DeclareDeclaration(Nonnull<Declaration*> d,
       // Associate the variable name with it's declared type in the
       // compile-time symbol table.
       if (!llvm::isa<ExpressionPattern>(var.binding().type())) {
-        return FATAL_COMPILATION_ERROR(var.binding().type().source_loc())
+        return CompilationError(var.binding().type().source_loc())
                << "Expected expression for variable type";
       }
       Expression& type =

+ 10 - 13
executable_semantics/interpreter/value.cpp

@@ -8,7 +8,7 @@
 
 #include "common/check.h"
 #include "executable_semantics/common/arena.h"
-#include "executable_semantics/common/error.h"
+#include "executable_semantics/common/error_builders.h"
 #include "executable_semantics/interpreter/action.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/Casting.h"
@@ -49,7 +49,7 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
             return *fun_decl.constant_value();
           }
         } else {
-          return FATAL_COMPILATION_ERROR(source_loc)
+          return CompilationError(source_loc)
                  << "member " << f << " not in " << *witness;
         }
       }
@@ -62,8 +62,7 @@ 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) {
-        return FATAL_RUNTIME_ERROR(source_loc)
-               << "member " << f << " not in " << *v;
+        return RuntimeError(source_loc) << "member " << f << " not in " << *v;
       }
       return *field;
     }
@@ -80,9 +79,8 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
         std::optional<Nonnull<const FunctionValue*>> func =
             class_type.FindFunction(f);
         if (func == std::nullopt) {
-          return FATAL_RUNTIME_ERROR(source_loc)
-                 << "member " << f << " not in " << *v << " or its "
-                 << class_type;
+          return RuntimeError(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 FunctionValue& m = cast<FunctionValue>(**func);
@@ -101,7 +99,7 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
     case Value::Kind::ChoiceType: {
       const auto& choice = cast<ChoiceType>(*v);
       if (!choice.FindAlternative(f)) {
-        return FATAL_RUNTIME_ERROR(source_loc)
+        return RuntimeError(source_loc)
                << "alternative " << f << " not in " << *v;
       }
       return arena->New<AlternativeConstructorValue>(f, choice.name());
@@ -112,7 +110,7 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
       std::optional<Nonnull<const FunctionValue*>> fun =
           class_type.FindFunction(f);
       if (fun == std::nullopt) {
-        return FATAL_RUNTIME_ERROR(source_loc)
+        return RuntimeError(source_loc)
                << "class function " << f << " not in " << *v;
       }
       return arena->New<FunctionValue>(&(*fun)->declaration(),
@@ -151,7 +149,7 @@ static auto SetFieldImpl(
                                return element.name == (*path_begin).name();
                              });
       if (it == elements.end()) {
-        return FATAL_RUNTIME_ERROR(source_loc)
+        return RuntimeError(source_loc)
                << "field " << (*path_begin).name() << " not in " << *value;
       }
       ASSIGN_OR_RETURN(it->value,
@@ -169,9 +167,8 @@ 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()) {
-        return FATAL_RUNTIME_ERROR(source_loc)
-               << "index " << (*path_begin).name() << " out of range in "
-               << *value;
+        return RuntimeError(source_loc) << "index " << (*path_begin).name()
+                                        << " out of range in " << *value;
       }
       ASSIGN_OR_RETURN(elements[index],
                        SetFieldImpl(arena, elements[index], path_begin + 1,

+ 1 - 1
executable_semantics/syntax/BUILD

@@ -85,7 +85,7 @@ cc_library(
         "//executable_semantics/ast:pattern",
         "//executable_semantics/ast:value_category",
         "//executable_semantics/common:arena",
-        "//executable_semantics/common:error",
+        "//executable_semantics/common:error_builders",
         "//executable_semantics/common:nonnull",
         "@llvm-project//llvm:Support",
     ],

+ 5 - 4
executable_semantics/syntax/parse.cpp

@@ -6,7 +6,7 @@
 
 #include "common/check.h"
 #include "common/error.h"
-#include "executable_semantics/common/error.h"
+#include "executable_semantics/common/error_builders.h"
 #include "executable_semantics/syntax/lexer.h"
 #include "executable_semantics/syntax/parse_and_lex_context.h"
 #include "executable_semantics/syntax/parser.h"
@@ -41,10 +41,11 @@ auto ParseImpl(yyscan_t scanner, Nonnull<Arena*> arena,
 
 auto Parse(Nonnull<Arena*> arena, std::string_view input_file_name, bool trace)
     -> ErrorOr<AST> {
-  FILE* input_file = fopen(std::string(input_file_name).c_str(), "r");
+  std::string name_str(input_file_name);
+  FILE* input_file = fopen(name_str.c_str(), "r");
   if (input_file == nullptr) {
-    return FATAL_PROGRAM_ERROR_NO_LINE() << "Error opening '" << input_file_name
-                                         << "': " << std::strerror(errno);
+    return ProgramError(SourceLocation(name_str.c_str(), 0))
+           << "Error opening file: " << std::strerror(errno);
   }
 
   // Prepare the lexer.

+ 1 - 1
executable_semantics/testdata/array/fail_index.carbon

@@ -7,7 +7,7 @@
 // RUN: %{not} %{executable_semantics} --trace %s 2>&1 | \
 // RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
 // AUTOUPDATE: %{executable_semantics} %s
-// CHECK: RUNTIME ERROR: index 2 out of range in (0, 1)
+// CHECK: RUNTIME ERROR: {{.*}}/executable_semantics/testdata/array/fail_index.carbon:16: index 2 out of range in (0, 1)
 
 package ExecutableSemanticsTest api;