Przeglądaj źródła

Modify the driver to support using for semantics IR testing (#2222)

This unifies `dump-tokens` and `dump-parse-tree` so that we don't keep writing basically the same code repeatedly.

I'll need to modify the output of SemanticsIR::Print more, and may soon unify printing multiple IRs this way (particularly including the builtins SemanticsIR) but this is intended to offer a starting point.

I may eventually try to unify lit.cfg.py files, but I was thinking about whether that works in various contexts we may run in and eventually decided copying the driver/testdata/lit.cfg.py file would be the easiest solution.
Jon Ross-Perkins 3 lat temu
rodzic
commit
80570e1a5b

+ 3 - 0
toolchain/driver/BUILD

@@ -6,6 +6,8 @@ load("//bazel/fuzzing:rules.bzl", "cc_fuzz_test")
 
 package(default_visibility = ["//visibility:public"])
 
+exports_files(["lit.cfg.py"])
+
 cc_library(
     name = "driver",
     srcs = ["driver.cpp"],
@@ -16,6 +18,7 @@ cc_library(
         "//toolchain/diagnostics:sorting_diagnostic_consumer",
         "//toolchain/lexer:tokenized_buffer",
         "//toolchain/parser:parse_tree",
+        "//toolchain/semantics:semantics_ir_factory",
         "//toolchain/source:source_buffer",
         "@llvm-project//llvm:Support",
     ],

+ 50 - 45
toolchain/driver/driver.cpp

@@ -14,6 +14,7 @@
 #include "toolchain/diagnostics/sorting_diagnostic_consumer.h"
 #include "toolchain/lexer/tokenized_buffer.h"
 #include "toolchain/parser/parse_tree.h"
+#include "toolchain/semantics/semantics_ir_factory.h"
 #include "toolchain/source/source_buffer.h"
 
 namespace Carbon {
@@ -36,25 +37,23 @@ auto GetSubcommand(llvm::StringRef name) -> Subcommand {
 }  // namespace
 
 auto Driver::RunFullCommand(llvm::ArrayRef<llvm::StringRef> args) -> bool {
-  if (args.empty()) {
-    error_stream_ << "ERROR: No subcommand specified.\n";
-    return false;
-  }
-
-  llvm::StringRef subcommand_text = args[0];
-  llvm::SmallVector<llvm::StringRef, 16> subcommand_args(
-      std::next(args.begin()), args.end());
-
   DiagnosticConsumer* consumer = &ConsoleDiagnosticConsumer();
   std::unique_ptr<SortingDiagnosticConsumer> sorting_consumer;
-  // TODO: Figure out command-line support (llvm::cl?), this is temporary.
-  if (!subcommand_args.empty() &&
-      subcommand_args[0] == "--print-errors=streamed") {
-    subcommand_args.erase(subcommand_args.begin());
+  // TODO: Figure out a command-line support library, this is temporary.
+  if (!args.empty() && args[0] == "--print-errors=streamed") {
+    args = args.drop_front();
   } else {
     sorting_consumer = std::make_unique<SortingDiagnosticConsumer>(*consumer);
     consumer = sorting_consumer.get();
   }
+
+  if (args.empty()) {
+    error_stream_ << "ERROR: No subcommand specified.\n";
+    return false;
+  }
+
+  llvm::StringRef subcommand_text = args[0];
+  args = args.drop_front();
   switch (GetSubcommand(subcommand_text)) {
     case Subcommand::Unknown:
       error_stream_ << "ERROR: Unknown subcommand '" << subcommand_text
@@ -63,7 +62,7 @@ auto Driver::RunFullCommand(llvm::ArrayRef<llvm::StringRef> args) -> bool {
 
 #define CARBON_SUBCOMMAND(Name, ...) \
   case Subcommand::Name:             \
-    return Run##Name##Subcommand(*consumer, subcommand_args);
+    return Run##Name##Subcommand(*consumer, args);
 #include "toolchain/driver/flags.def"
   }
   llvm_unreachable("All subcommands handled!");
@@ -105,40 +104,27 @@ auto Driver::RunHelpSubcommand(DiagnosticConsumer& /*consumer*/,
   return true;
 }
 
-auto Driver::RunDumpTokensSubcommand(DiagnosticConsumer& consumer,
-                                     llvm::ArrayRef<llvm::StringRef> args)
-    -> bool {
-  if (args.empty()) {
-    error_stream_ << "ERROR: No input file specified.\n";
-    return false;
-  }
+enum class DumpMode { TokenizedBuffer, ParseTree, SemanticsIR, Unknown };
 
-  llvm::StringRef input_file_name = args.front();
-  args = args.drop_front();
-  if (!args.empty()) {
-    ReportExtraArgs("dump-tokens", args);
+auto Driver::RunDumpSubcommand(DiagnosticConsumer& consumer,
+                               llvm::ArrayRef<llvm::StringRef> args) -> bool {
+  if (args.empty()) {
+    error_stream_ << "ERROR: No dump mode specified.\n";
     return false;
   }
 
-  auto source = SourceBuffer::CreateFromFile(input_file_name);
-  if (!source) {
-    error_stream_ << "ERROR: Unable to open input source file: ";
-    llvm::handleAllErrors(source.takeError(),
-                          [&](const llvm::ErrorInfoBase& ei) {
-                            ei.log(error_stream_);
-                            error_stream_ << "\n";
-                          });
+  auto dump_mode = llvm::StringSwitch<DumpMode>(args.front())
+                       .Case("tokens", DumpMode::TokenizedBuffer)
+                       .Case("parse-tree", DumpMode::ParseTree)
+                       .Case("semantics-ir", DumpMode::SemanticsIR)
+                       .Default(DumpMode::Unknown);
+  if (dump_mode == DumpMode::Unknown) {
+    error_stream_ << "ERROR: Dump mode should be one of tokens, parse_tree, or "
+                     "semantics_ir.\n";
     return false;
   }
-  auto tokenized_source = TokenizedBuffer::Lex(*source, consumer);
-  consumer.Flush();
-  tokenized_source.Print(output_stream_);
-  return !tokenized_source.has_errors();
-}
+  args = args.drop_front();
 
-auto Driver::RunDumpParseTreeSubcommand(DiagnosticConsumer& consumer,
-                                        llvm::ArrayRef<llvm::StringRef> args)
-    -> bool {
   if (args.empty()) {
     error_stream_ << "ERROR: No input file specified.\n";
     return false;
@@ -147,7 +133,7 @@ auto Driver::RunDumpParseTreeSubcommand(DiagnosticConsumer& consumer,
   llvm::StringRef input_file_name = args.front();
   args = args.drop_front();
   if (!args.empty()) {
-    ReportExtraArgs("dump-parse-tree", args);
+    ReportExtraArgs("dump", args);
     return false;
   }
 
@@ -161,11 +147,30 @@ auto Driver::RunDumpParseTreeSubcommand(DiagnosticConsumer& consumer,
                           });
     return false;
   }
+
   auto tokenized_source = TokenizedBuffer::Lex(*source, consumer);
+  if (dump_mode == DumpMode::TokenizedBuffer) {
+    consumer.Flush();
+    tokenized_source.Print(output_stream_);
+    return !tokenized_source.has_errors();
+  }
+
   auto parse_tree = ParseTree::Parse(tokenized_source, consumer);
-  consumer.Flush();
-  parse_tree.Print(output_stream_);
-  return !tokenized_source.has_errors() && !parse_tree.has_errors();
+  if (dump_mode == DumpMode::ParseTree) {
+    consumer.Flush();
+    parse_tree.Print(output_stream_);
+    return !tokenized_source.has_errors() && !parse_tree.has_errors();
+  }
+
+  auto semantics_ir = SemanticsIRFactory::Build(tokenized_source, parse_tree);
+  if (dump_mode == DumpMode::SemanticsIR) {
+    consumer.Flush();
+    semantics_ir.Print(output_stream_);
+    // TODO:
+    return !tokenized_source.has_errors() && !parse_tree.has_errors();
+  }
+
+  llvm_unreachable("should handle all dump modes");
 }
 
 auto Driver::ReportExtraArgs(llvm::StringRef subcommand_text,

+ 4 - 14
toolchain/driver/driver.h

@@ -49,7 +49,8 @@ class Driver {
   auto RunHelpSubcommand(DiagnosticConsumer& consumer,
                          llvm::ArrayRef<llvm::StringRef> args) -> bool;
 
-  // Subcommand that dumps the token information for the provided source file.
+  // Subcommand that dumps internal compilation information for the provided
+  // source file.
   //
   // Requires exactly one positional parameter to designate the source file to
   // read. May be `-` to read from stdin.
@@ -57,19 +58,8 @@ class Driver {
   // Returns true if the operation succeeds. If the operation fails, this
   // returns false and any information about the failure is printed to the
   // registered error stream (stderr by default).
-  auto RunDumpTokensSubcommand(DiagnosticConsumer& consumer,
-                               llvm::ArrayRef<llvm::StringRef> args) -> bool;
-
-  // Subcommand that dumps the parse tree for the provided source file.
-  //
-  // Requires exactly one positional parameter to designate the source file to
-  // read. May be `-` to read from stdin.
-  //
-  // Returns true if the operation succeeds. If the operation fails, this
-  // returns false and any information about the failure is printed to the
-  // registered error stream (stderr by default).
-  auto RunDumpParseTreeSubcommand(DiagnosticConsumer& consumer,
-                                  llvm::ArrayRef<llvm::StringRef> args) -> bool;
+  auto RunDumpSubcommand(DiagnosticConsumer& consumer,
+                         llvm::ArrayRef<llvm::StringRef> args) -> bool;
 
  private:
   auto ReportExtraArgs(llvm::StringRef subcommand_text,

+ 16 - 11
toolchain/driver/driver_test.cpp

@@ -127,8 +127,8 @@ TEST(DriverTest, DumpTokens) {
   Driver driver = Driver(test_output_stream, test_error_stream);
 
   auto test_file_path = CreateTestFile("Hello World");
-  EXPECT_TRUE(driver.RunDumpTokensSubcommand(ConsoleDiagnosticConsumer(),
-                                             {test_file_path}));
+  EXPECT_TRUE(driver.RunDumpSubcommand(ConsoleDiagnosticConsumer(),
+                                       {"tokens", test_file_path}));
   EXPECT_THAT(test_error_stream.TakeStr(), StrEq(""));
   auto tokenized_text = test_output_stream.TakeStr();
 
@@ -158,27 +158,32 @@ TEST(DriverTest, DumpTokens) {
                                                {"spelling", ""}}}}));
 
   // Check that the subcommand dispatch works.
-  EXPECT_TRUE(driver.RunFullCommand({"dump-tokens", test_file_path}));
+  EXPECT_TRUE(driver.RunFullCommand({"dump", "tokens", test_file_path}));
   EXPECT_THAT(test_error_stream.TakeStr(), StrEq(""));
   EXPECT_THAT(test_output_stream.TakeStr(), StrEq(tokenized_text));
 }
 
-TEST(DriverTest, DumpTokenErrors) {
+TEST(DriverTest, DumpErrors) {
   RawTestOstream test_output_stream;
   RawTestOstream test_error_stream;
   Driver driver = Driver(test_output_stream, test_error_stream);
 
-  EXPECT_FALSE(driver.RunDumpTokensSubcommand(ConsoleDiagnosticConsumer(), {}));
+  EXPECT_FALSE(driver.RunDumpSubcommand(ConsoleDiagnosticConsumer(), {"foo"}));
   EXPECT_THAT(test_output_stream.TakeStr(), StrEq(""));
   EXPECT_THAT(test_error_stream.TakeStr(), HasSubstr("ERROR"));
 
   EXPECT_FALSE(
-      driver.RunDumpTokensSubcommand(ConsoleDiagnosticConsumer(), {"--xyz"}));
+      driver.RunDumpSubcommand(ConsoleDiagnosticConsumer(), {"--xyz"}));
   EXPECT_THAT(test_output_stream.TakeStr(), StrEq(""));
   EXPECT_THAT(test_error_stream.TakeStr(), HasSubstr("ERROR"));
 
-  EXPECT_FALSE(driver.RunDumpTokensSubcommand(ConsoleDiagnosticConsumer(),
-                                              {"/not/a/real/file/name"}));
+  EXPECT_FALSE(
+      driver.RunDumpSubcommand(ConsoleDiagnosticConsumer(), {"tokens"}));
+  EXPECT_THAT(test_output_stream.TakeStr(), StrEq(""));
+  EXPECT_THAT(test_error_stream.TakeStr(), HasSubstr("ERROR"));
+
+  EXPECT_FALSE(driver.RunDumpSubcommand(ConsoleDiagnosticConsumer(),
+                                        {"tokens", "/not/a/real/file/name"}));
   EXPECT_THAT(test_output_stream.TakeStr(), StrEq(""));
   EXPECT_THAT(test_error_stream.TakeStr(), HasSubstr("ERROR"));
 }
@@ -189,8 +194,8 @@ TEST(DriverTest, DumpParseTree) {
   Driver driver = Driver(test_output_stream, test_error_stream);
 
   auto test_file_path = CreateTestFile("var v: Int = 42;");
-  EXPECT_TRUE(driver.RunDumpParseTreeSubcommand(ConsoleDiagnosticConsumer(),
-                                                {test_file_path}));
+  EXPECT_TRUE(driver.RunDumpSubcommand(ConsoleDiagnosticConsumer(),
+                                       {"parse-tree", test_file_path}));
   EXPECT_THAT(test_error_stream.TakeStr(), StrEq(""));
   auto tokenized_text = test_output_stream.TakeStr();
 
@@ -234,7 +239,7 @@ TEST(DriverTest, DumpParseTree) {
                              {"text", ""}}}));
 
   // Check that the subcommand dispatch works.
-  EXPECT_TRUE(driver.RunFullCommand({"dump-parse-tree", test_file_path}));
+  EXPECT_TRUE(driver.RunFullCommand({"dump", "parse-tree", test_file_path}));
   EXPECT_THAT(test_error_stream.TakeStr(), StrEq(""));
   EXPECT_THAT(test_output_stream.TakeStr(), StrEq(tokenized_text));
 }

+ 2 - 5
toolchain/driver/flags.def

@@ -13,10 +13,7 @@
 CARBON_SUBCOMMAND(Help, "help",
                   "Display help information about the driver options.")
 CARBON_SUBCOMMAND(
-    DumpTokens, "dump-tokens",
-    "Dumps the sequence of tokens lexed out of the input source file.")
-CARBON_SUBCOMMAND(
-    DumpParseTree, "dump-parse-tree",
-    "Dumps the parse tree for the input source file.")
+    Dump, "dump",
+    "Dumps intermediate compilation state for the input source file.")
 
 #undef CARBON_SUBCOMMAND

+ 1 - 1
toolchain/driver/testdata/carbon_test.carbon

@@ -2,7 +2,7 @@
 // Exceptions. See /LICENSE for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
-// RUN: %{carbon} dump-tokens %s 2>&1 | \
+// RUN: %{carbon} dump tokens %s 2>&1 | \
 // RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
 
 fn run(String program) {

+ 1 - 1
toolchain/driver/testdata/errors_sorted_test.carbon

@@ -2,7 +2,7 @@
 // Exceptions. See /LICENSE for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
-// RUN: %{not} %{carbon} dump-tokens %s 2>&1 | \
+// RUN: %{not} %{carbon} dump tokens %s 2>&1 | \
 // RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
 
 fn run(String program) {

+ 1 - 1
toolchain/driver/testdata/errors_streamed_test.carbon

@@ -2,7 +2,7 @@
 // Exceptions. See /LICENSE for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
-// RUN: %{not} %{carbon} dump-tokens --print-errors=streamed %s 2>&1 | \
+// RUN: %{not} %{carbon} --print-errors=streamed dump tokens %s 2>&1 | \
 // RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
 
 fn run(String program) {

+ 9 - 2
toolchain/semantics/semantics_ir.cpp

@@ -10,8 +10,15 @@
 
 namespace Carbon {
 
-void SemanticsIR::Print(llvm::raw_ostream& out,
-                        Semantics::NodeRef node_ref) const {
+auto SemanticsIR::Print(llvm::raw_ostream& out) const -> void {
+  for (const auto& node_ref : root_block()) {
+    Print(out, node_ref);
+    out << "\n";
+  }
+}
+
+auto SemanticsIR::Print(llvm::raw_ostream& out,
+                        Semantics::NodeRef node_ref) const -> void {
   switch (node_ref.kind()) {
     case Semantics::NodeKind::BinaryOperator:
       nodes_.Get<Semantics::BinaryOperator>(node_ref).Print(out);

+ 3 - 0
toolchain/semantics/semantics_ir.h

@@ -24,6 +24,9 @@ class SemanticsIR {
     return root_block_;
   }
 
+  // Prints the full IR.
+  auto Print(llvm::raw_ostream& out) const -> void;
+
   // Prints the node information.
   auto Print(llvm::raw_ostream& out, Semantics::NodeRef node_ref) const -> void;
 

+ 15 - 0
toolchain/semantics/testdata/BUILD

@@ -0,0 +1,15 @@
+# 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
+
+load("//bazel/testing:lit.bzl", "glob_lit_tests")
+
+glob_lit_tests(
+    data = [
+        "//toolchain/driver:carbon",
+        "@llvm-project//llvm:FileCheck",
+        "@llvm-project//llvm:not",
+    ],
+    driver = "lit.cfg.py",
+    test_file_exts = ["carbon"],
+)

+ 35 - 0
toolchain/semantics/testdata/lit.cfg.py

@@ -0,0 +1,35 @@
+__copyright__ = """
+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
+"""
+
+import lit.formats
+import os
+
+
+# This is a provided variable, ignore the undefined name warning.
+config = config  # noqa: F821
+
+
+def fullpath(relative_path):
+    return os.path.join(os.environ["TEST_SRCDIR"], relative_path)
+
+
+config.name = "lit"
+config.suffixes = [".carbon"]
+config.test_format = lit.formats.ShTest()
+
+config.substitutions.append(
+    (
+        "%{carbon}",
+        fullpath("carbon/toolchain/driver/carbon"),
+    )
+)
+config.substitutions.append(("%{not}", fullpath("llvm-project/llvm/not")))
+config.substitutions.append(
+    (
+        "%{FileCheck}",
+        fullpath("llvm-project/llvm/FileCheck --dump-input-filter=all"),
+    )
+)

+ 12 - 0
toolchain/semantics/testdata/zero.carbon

@@ -0,0 +1,12 @@
+// 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
+//
+// RUN: %{carbon} dump semantics-ir %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
+// CHECK: Function(%0, {IntegerLiteral(%1, 0), Return(%1)})
+// CHECK: SetName(`Main`, %0)
+
+fn Main() {
+  return 0;
+}