Browse Source

Factor out check-related logic from SemIR::File (#3174)

This is necessary for a clean split of the SemIR and Check namespaces.
My design intent with check.h is that check/check.h provides the factory
functions necessary to construct a SemIR, which is the main reason I
have the MakeBuiltins wrapper there.

I don't think File's constructors are great, but it felt good enough to
me in that context. I considered factory functions, or something like an
enum as discriminator, but it felt like more burden than improvement.
Jon Ross-Perkins 2 years ago
parent
commit
15f0818888

+ 1 - 0
toolchain/driver/BUILD

@@ -23,6 +23,7 @@ cc_library(
         "//toolchain/lexer:tokenized_buffer",
         "//toolchain/lower",
         "//toolchain/parser:parse_tree",
+        "//toolchain/semantics:check",
         "//toolchain/semantics:semantics_ir",
         "//toolchain/semantics:semantics_ir_formatter",
         "//toolchain/source:source_buffer",

+ 3 - 3
toolchain/driver/driver.cpp

@@ -18,7 +18,7 @@
 #include "toolchain/lexer/tokenized_buffer.h"
 #include "toolchain/lower/lower.h"
 #include "toolchain/parser/parse_tree.h"
-#include "toolchain/semantics/semantics_ir.h"
+#include "toolchain/semantics/check.h"
 #include "toolchain/semantics/semantics_ir_formatter.h"
 #include "toolchain/source/source_buffer.h"
 
@@ -436,9 +436,9 @@ auto Driver::Compile(const CompileOptions& options) -> bool {
     return !has_errors;
   }
 
-  const SemIR::File builtin_ir = SemIR::File::MakeBuiltinIR();
+  const SemIR::File builtin_ir = Check::MakeBuiltins();
   CARBON_VLOG() << "*** SemanticsIR::MakeFromParseTree ***\n";
-  const SemIR::File semantics_ir = SemIR::File::MakeFromParseTree(
+  const SemIR::File semantics_ir = Check::CheckParseTree(
       builtin_ir, tokenized_source, parse_tree, *consumer, vlog_stream_);
 
   // We've finished all steps that can produce diagnostics. Emit the

+ 20 - 11
toolchain/semantics/BUILD

@@ -59,11 +59,11 @@ cc_library(
 )
 
 cc_library(
-    name = "semantics_ir",
+    name = "check",
     srcs = [
+        "check.cpp",
         "semantics_context.cpp",
         "semantics_declaration_name_stack.cpp",
-        "semantics_ir.cpp",
         "semantics_node_block_stack.cpp",
     ] +
     # Glob handler files to avoid missing any.
@@ -71,13 +71,14 @@ cc_library(
         "semantics_handle*.cpp",
     ]),
     hdrs = [
+        "check.h",
         "semantics_context.h",
         "semantics_declaration_name_stack.h",
-        "semantics_ir.h",
         "semantics_node_block_stack.h",
     ],
     deps = [
         ":semantics_builtin_kind",
+        ":semantics_ir",
         ":semantics_node",
         ":semantics_node_kind",
         ":semantics_node_stack",
@@ -85,9 +86,8 @@ cc_library(
         "//common:ostream",
         "//common:vlog",
         "//toolchain/base:pretty_stack_trace_function",
+        "//toolchain/diagnostics:diagnostic_emitter",
         "//toolchain/diagnostics:diagnostic_kind",
-        "//toolchain/lexer:numeric_literal",
-        "//toolchain/lexer:token_kind",
         "//toolchain/lexer:tokenized_buffer",
         "//toolchain/parser:parse_node_kind",
         "//toolchain/parser:parse_tree",
@@ -97,13 +97,22 @@ cc_library(
 )
 
 cc_library(
-    name = "semantics_ir_formatter",
-    srcs = [
-        "semantics_ir_formatter.cpp",
-    ],
-    hdrs = [
-        "semantics_ir_formatter.h",
+    name = "semantics_ir",
+    srcs = ["semantics_ir.cpp"],
+    hdrs = ["semantics_ir.h"],
+    deps = [
+        ":semantics_builtin_kind",
+        ":semantics_node",
+        ":semantics_node_kind",
+        "//common:check",
+        "@llvm-project//llvm:Support",
     ],
+)
+
+cc_library(
+    name = "semantics_ir_formatter",
+    srcs = ["semantics_ir_formatter.cpp"],
+    hdrs = ["semantics_ir_formatter.h"],
     deps = [
         ":semantics_ir",
         ":semantics_node_kind",

+ 71 - 0
toolchain/semantics/check.cpp

@@ -0,0 +1,71 @@
+// 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 "common/check.h"
+
+#include "toolchain/base/pretty_stack_trace_function.h"
+#include "toolchain/parser/parse_tree_node_location_translator.h"
+#include "toolchain/semantics/semantics_context.h"
+#include "toolchain/semantics/semantics_ir.h"
+
+namespace Carbon::Check {
+
+auto CheckParseTree(const SemIR::File& builtin_ir,
+                    const TokenizedBuffer& tokens,
+                    const Parse::Tree& parse_tree, DiagnosticConsumer& consumer,
+                    llvm::raw_ostream* vlog_stream) -> SemIR::File {
+  auto semantics_ir = SemIR::File(&builtin_ir);
+
+  Parse::NodeLocationTranslator translator(&tokens, &parse_tree);
+  ErrorTrackingDiagnosticConsumer err_tracker(consumer);
+  DiagnosticEmitter<Parse::Node> emitter(translator, err_tracker);
+
+  Check::Context context(tokens, emitter, parse_tree, semantics_ir,
+                         vlog_stream);
+  PrettyStackTraceFunction context_dumper(
+      [&](llvm::raw_ostream& output) { context.PrintForStackDump(output); });
+
+  // Add a block for the Parse::Tree.
+  context.node_block_stack().Push();
+  context.PushScope();
+
+  // Loops over all nodes in the tree. On some errors, this may return early,
+  // for example if an unrecoverable state is encountered.
+  for (auto parse_node : parse_tree.postorder()) {
+    // clang warns on unhandled enum values; clang-tidy is incorrect here.
+    // NOLINTNEXTLINE(bugprone-switch-missing-default-case)
+    switch (auto parse_kind = parse_tree.node_kind(parse_node)) {
+#define CARBON_PARSE_NODE_KIND(Name)                                         \
+  case Parse::NodeKind::Name: {                                              \
+    if (!Check::Handle##Name(context, parse_node)) {                         \
+      CARBON_CHECK(err_tracker.seen_error())                                 \
+          << "Handle" #Name " returned false without printing a diagnostic"; \
+      semantics_ir.set_has_errors(true);                                     \
+      return semantics_ir;                                                   \
+    }                                                                        \
+    break;                                                                   \
+  }
+#include "toolchain/parser/parse_node_kind.def"
+    }
+  }
+
+  // Pop information for the file-level scope.
+  semantics_ir.set_top_node_block_id(context.node_block_stack().Pop());
+  context.PopScope();
+
+  context.VerifyOnFinish();
+
+  semantics_ir.set_has_errors(err_tracker.seen_error());
+
+#ifndef NDEBUG
+  if (auto verify = semantics_ir.Verify(); !verify.ok()) {
+    CARBON_FATAL() << semantics_ir
+                   << "Built invalid semantics IR: " << verify.error() << "\n";
+  }
+#endif
+
+  return semantics_ir;
+}
+
+}  // namespace Carbon::Check

+ 29 - 0
toolchain/semantics/check.h

@@ -0,0 +1,29 @@
+// 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 CARBON_TOOLCHAIN_SEMANTICS_CHECK_H_
+#define CARBON_TOOLCHAIN_SEMANTICS_CHECK_H_
+
+#include "common/ostream.h"
+#include "toolchain/diagnostics/diagnostic_emitter.h"
+#include "toolchain/lexer/tokenized_buffer.h"
+#include "toolchain/parser/parse_tree.h"
+#include "toolchain/semantics/semantics_ir.h"
+
+namespace Carbon::Check {
+
+// Constructs builtins. A single instance should be reused with CheckParseTree
+// calls associated with a given compilation.
+inline auto MakeBuiltins() -> SemIR::File { return SemIR::File(); }
+
+// Produces and checks the IR for the provided Parse::Tree.
+extern auto CheckParseTree(const SemIR::File& builtin_ir,
+                           const TokenizedBuffer& tokens,
+                           const Parse::Tree& parse_tree,
+                           DiagnosticConsumer& consumer,
+                           llvm::raw_ostream* vlog_stream) -> SemIR::File;
+
+}  // namespace Carbon::Check
+
+#endif  // CARBON_TOOLCHAIN_SEMANTICS_CHECK_H_

+ 28 - 80
toolchain/semantics/semantics_ir.cpp

@@ -5,105 +5,53 @@
 #include "toolchain/semantics/semantics_ir.h"
 
 #include "common/check.h"
-#include "llvm/ADT/Sequence.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallVector.h"
-#include "toolchain/base/pretty_stack_trace_function.h"
-#include "toolchain/parser/parse_tree_node_location_translator.h"
 #include "toolchain/semantics/semantics_builtin_kind.h"
-#include "toolchain/semantics/semantics_context.h"
 #include "toolchain/semantics/semantics_node.h"
 #include "toolchain/semantics/semantics_node_kind.h"
 
 namespace Carbon::SemIR {
 
-auto File::MakeBuiltinIR() -> File {
-  File semantics_ir(/*builtin_ir=*/nullptr);
-  semantics_ir.nodes_.reserve(BuiltinKind::ValidCount);
+File::File()
+    // Builtins are always the first IR, even when self-referential.
+    : cross_reference_irs_({this}),
+      // Default entry for NodeBlockId::Empty.
+      node_blocks_(1) {
+  nodes_.reserve(BuiltinKind::ValidCount);
 
   // Error uses a self-referential type so that it's not accidentally treated as
   // a normal type. Every other builtin is a type, including the
   // self-referential TypeType.
-#define CARBON_SEMANTICS_BUILTIN_KIND(Name, ...)                 \
-  semantics_ir.nodes_.push_back(Node::Builtin::Make(             \
-      BuiltinKind::Name, BuiltinKind::Name == BuiltinKind::Error \
-                             ? TypeId::Error                     \
-                             : TypeId::TypeType));
+#define CARBON_SEMANTICS_BUILTIN_KIND(Name, ...)                               \
+  nodes_.push_back(Node::Builtin::Make(BuiltinKind::Name,                      \
+                                       BuiltinKind::Name == BuiltinKind::Error \
+                                           ? TypeId::Error                     \
+                                           : TypeId::TypeType));
 #include "toolchain/semantics/semantics_builtin_kind.def"
 
-  CARBON_CHECK(semantics_ir.node_blocks_.size() == 1)
-      << "BuildBuiltins should only have the empty block, actual: "
-      << semantics_ir.node_blocks_.size();
-  CARBON_CHECK(semantics_ir.nodes_.size() == BuiltinKind::ValidCount)
-      << "BuildBuiltins should produce " << BuiltinKind::ValidCount
-      << " nodes, actual: " << semantics_ir.nodes_.size();
-  return semantics_ir;
+  CARBON_CHECK(nodes_.size() == BuiltinKind::ValidCount)
+      << "Builtins should produce " << BuiltinKind::ValidCount
+      << " nodes, actual: " << nodes_.size();
 }
 
-auto File::MakeFromParseTree(const File& builtin_ir,
-                             const TokenizedBuffer& tokens,
-                             const Parse::Tree& parse_tree,
-                             DiagnosticConsumer& consumer,
-                             llvm::raw_ostream* vlog_stream) -> File {
-  File semantics_ir(&builtin_ir);
+File::File(const File* builtins)
+    // Builtins are always the first IR.
+    : cross_reference_irs_({builtins}),
+      // Default entry for NodeBlockId::Empty.
+      node_blocks_(1) {
+  CARBON_CHECK(builtins != nullptr);
+  CARBON_CHECK(builtins->cross_reference_irs_[0] == builtins)
+      << "Not called with builtins!";
 
   // Copy builtins over.
-  semantics_ir.nodes_.resize_for_overwrite(BuiltinKind::ValidCount);
+  nodes_.reserve(BuiltinKind::ValidCount);
   static constexpr auto BuiltinIR = CrossReferenceIRId(0);
-  for (int i : llvm::seq(BuiltinKind::ValidCount)) {
-    // We can reuse the type node ID because the offsets of cross-references
-    // will be the same in this IR.
-    auto type = builtin_ir.nodes_[i].type_id();
-    semantics_ir.nodes_[i] =
-        Node::CrossReference::Make(type, BuiltinIR, NodeId(i));
+  for (auto [i, node] : llvm::enumerate(builtins->nodes_)) {
+    // We can reuse builtin type IDs because they're special-cased values.
+    nodes_.push_back(Node::CrossReference::Make(node.type_id(), BuiltinIR,
+                                                SemIR::NodeId(i)));
   }
-
-  Parse::NodeLocationTranslator translator(&tokens, &parse_tree);
-  ErrorTrackingDiagnosticConsumer err_tracker(consumer);
-  DiagnosticEmitter<Parse::Node> emitter(translator, err_tracker);
-
-  Check::Context context(tokens, emitter, parse_tree, semantics_ir,
-                         vlog_stream);
-  PrettyStackTraceFunction context_dumper(
-      [&](llvm::raw_ostream& output) { context.PrintForStackDump(output); });
-
-  // Add a block for the Parse::Tree.
-  context.node_block_stack().Push();
-  context.PushScope();
-
-  // Loops over all nodes in the tree. On some errors, this may return early,
-  // for example if an unrecoverable state is encountered.
-  for (auto parse_node : parse_tree.postorder()) {
-    // clang warns on unhandled enum values; clang-tidy is incorrect here.
-    // NOLINTNEXTLINE(bugprone-switch-missing-default-case)
-    switch (auto parse_kind = parse_tree.node_kind(parse_node)) {
-#define CARBON_PARSE_NODE_KIND(Name)                 \
-  case Parse::NodeKind::Name: {                      \
-    if (!Check::Handle##Name(context, parse_node)) { \
-      semantics_ir.has_errors_ = true;               \
-      return semantics_ir;                           \
-    }                                                \
-    break;                                           \
-  }
-#include "toolchain/parser/parse_node_kind.def"
-    }
-  }
-
-  // Pop information for the file-level scope.
-  semantics_ir.top_node_block_id_ = context.node_block_stack().Pop();
-  context.PopScope();
-
-  context.VerifyOnFinish();
-
-  semantics_ir.has_errors_ = err_tracker.seen_error();
-
-#ifndef NDEBUG
-  if (auto verify = semantics_ir.Verify(); !verify.ok()) {
-    CARBON_FATAL() << semantics_ir
-                   << "Built invalid semantics IR: " << verify.error() << "\n";
-  }
-#endif
-
-  return semantics_ir;
 }
 
 auto File::Verify() const -> ErrorOr<Success> {

+ 8 - 15
toolchain/semantics/semantics_ir.h

@@ -9,7 +9,6 @@
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/iterator_range.h"
 #include "llvm/Support/FormatVariadic.h"
-#include "toolchain/parser/parse_tree.h"
 #include "toolchain/semantics/semantics_node.h"
 
 namespace Carbon::SemIR {
@@ -68,15 +67,11 @@ struct RealLiteral : public Printable<RealLiteral> {
 // Provides semantic analysis on a Parse::Tree.
 class File : public Printable<File> {
  public:
-  // Produces the builtins.
-  static auto MakeBuiltinIR() -> File;
+  // Produces a file for the builtins.
+  explicit File();
 
-  // Adds the IR for the provided Parse::Tree.
-  static auto MakeFromParseTree(const File& builtin_ir,
-                                const TokenizedBuffer& tokens,
-                                const Parse::Tree& parse_tree,
-                                DiagnosticConsumer& consumer,
-                                llvm::raw_ostream* vlog_stream) -> File;
+  // Starts a new file for Check::CheckParseTree. Builtins are required.
+  explicit File(const File* builtins);
 
   // Verifies that invariants of the semantics IR hold.
   auto Verify() const -> ErrorOr<Success>;
@@ -286,17 +281,15 @@ class File : public Printable<File> {
   }
 
   auto top_node_block_id() const -> NodeBlockId { return top_node_block_id_; }
+  auto set_top_node_block_id(NodeBlockId block_id) -> void {
+    top_node_block_id_ = block_id;
+  }
 
   // Returns true if there were errors creating the semantics IR.
   auto has_errors() const -> bool { return has_errors_; }
+  auto set_has_errors(bool has_errors) -> void { has_errors_ = has_errors; }
 
  private:
-  explicit File(const File* builtin_ir)
-      : cross_reference_irs_({builtin_ir == nullptr ? this : builtin_ir}) {
-    // For NodeBlockId::Empty.
-    node_blocks_.resize(1);
-  }
-
   bool has_errors_ = false;
 
   // Storage for callable objects.