Просмотр исходного кода

Adding some shape to toolchain semantic analysis (#1092)

Jon Meow 4 лет назад
Родитель
Сommit
bf7159f841

+ 1 - 1
.clang-tidy

@@ -8,7 +8,7 @@
 # - performance-unnecessary-value-param is disabled because it duplicate
 #   modernize-pass-by-value.
 Checks:
-  -*, bugprone-*, -bugprone-easily-swappable-parameters,
+  -*, bugprone-*, -bugprone-branch-clone, -bugprone-easily-swappable-parameters,
   -bugprone-narrowing-conversions, google-*, -google-readability-todo,
   misc-definitions-in-headers, misc-misplaced-const, misc-redundant-expression,
   misc-static-assert, misc-unconventional-assign-operator,

+ 33 - 7
toolchain/semantics/BUILD

@@ -5,19 +5,45 @@
 package(default_visibility = ["//visibility:public"])
 
 cc_library(
-    name = "semantics",
-    srcs = ["semantics.cpp"],
-    hdrs = ["semantics.h"],
+    name = "function",
+    hdrs = ["function.h"],
     deps = ["//toolchain/parser:parse_tree"],
 )
 
+cc_library(
+    name = "semantics_ir",
+    srcs = ["semantics_ir.cpp"],
+    hdrs = ["semantics_ir.h"],
+    deps = [
+        ":function",
+        "//common:check",
+        "//toolchain/lexer:tokenized_buffer",
+        "//toolchain/parser:parse_tree",
+        "@llvm-project//llvm:Support",
+    ],
+)
+
+cc_library(
+    name = "semantics_ir_factory",
+    srcs = ["semantics_ir_factory.cpp"],
+    hdrs = ["semantics_ir_factory.h"],
+    deps = [
+        ":semantics_ir",
+        "//common:check",
+        "//toolchain/lexer:tokenized_buffer",
+        "//toolchain/parser:parse_node_kind",
+        "//toolchain/parser:parse_tree",
+        "@llvm-project//llvm:Support",
+    ],
+)
+
 cc_test(
-    name = "semantics_test",
+    name = "semantics_ir_factory_test",
     size = "small",
-    srcs = ["semantics_test.cpp"],
+    srcs = ["semantics_ir_factory_test.cpp"],
     deps = [
-        ":semantics",
-        "//toolchain/diagnostics:diagnostic_emitter",
+        ":semantics_ir_factory",
+        "//toolchain/diagnostics:mocks",
         "//toolchain/lexer:tokenized_buffer",
         "//toolchain/parser:parse_tree",
         "//toolchain/source:source_buffer",

+ 31 - 0
toolchain/semantics/function.h

@@ -0,0 +1,31 @@
+// 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 TOOLCHAIN_SEMANTICS_FUNCTION_H_
+#define TOOLCHAIN_SEMANTICS_FUNCTION_H_
+
+#include "toolchain/parser/parse_tree.h"
+
+namespace Carbon::Semantics {
+
+// Semantic information for a function.
+class Function {
+ public:
+  Function(ParseTree::Node decl_node, ParseTree::Node name_node)
+      : decl_node_(decl_node), name_node_(name_node) {}
+
+  auto decl_node() const -> ParseTree::Node { return decl_node_; }
+  auto name_node() const -> ParseTree::Node { return name_node_; }
+
+ private:
+  // The FunctionDeclaration node.
+  ParseTree::Node decl_node_;
+
+  // The function's DeclaredName node.
+  ParseTree::Node name_node_;
+};
+
+}  // namespace Carbon::Semantics
+
+#endif  // TOOLCHAIN_SEMANTICS_FUNCTION_H_

+ 0 - 15
toolchain/semantics/semantics.cpp

@@ -1,15 +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 "toolchain/semantics/semantics.h"
-
-namespace Carbon {
-
-Semantics Semantics::Analyze(const ParseTree& parse_tree,
-                             DiagnosticConsumer& consumer) {
-  Semantics semantics;
-  return semantics;
-}
-
-}  // namespace Carbon

+ 0 - 25
toolchain/semantics/semantics.h

@@ -1,25 +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 TOOLCHAIN_SEMANTICS_SEMANTICS_H_
-#define TOOLCHAIN_SEMANTICS_SEMANTICS_H_
-
-#include "toolchain/parser/parse_tree.h"
-
-namespace Carbon {
-
-// Provides semantic analysis on a ParseTree.
-class Semantics {
- public:
-  // Analyzes a parse tree and returns the constructed semantic information.
-  static auto Analyze(const ParseTree& parse_tree, DiagnosticConsumer& consumer)
-      -> Semantics;
-
- private:
-  Semantics() = default;
-};
-
-}  // namespace Carbon
-
-#endif  // TOOLCHAIN_SEMANTICS_SEMANTICS_H_

+ 28 - 0
toolchain/semantics/semantics_ir.cpp

@@ -0,0 +1,28 @@
+// 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 "toolchain/semantics/semantics_ir.h"
+
+#include "common/check.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "toolchain/lexer/tokenized_buffer.h"
+
+namespace Carbon {
+
+void SemanticsIR::Block::Add(llvm::StringRef name, Node named_entity) {
+  ordering_.push_back(named_entity);
+  name_lookup_.insert({name, named_entity});
+}
+
+auto SemanticsIR::AddFunction(Block& block, ParseTree::Node decl_node,
+                              ParseTree::Node name_node)
+    -> Semantics::Function& {
+  int32_t index = functions_.size();
+  functions_.push_back(Semantics::Function(decl_node, name_node));
+  block.Add(parse_tree_->GetNodeText(name_node),
+            Node(Node::Kind::Function, index));
+  return functions_[index];
+}
+
+}  // namespace Carbon

+ 78 - 0
toolchain/semantics/semantics_ir.h

@@ -0,0 +1,78 @@
+// 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 TOOLCHAIN_SEMANTICS_SEMANTICS_IR_H_
+#define TOOLCHAIN_SEMANTICS_SEMANTICS_IR_H_
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
+#include "toolchain/parser/parse_tree.h"
+#include "toolchain/semantics/function.h"
+
+namespace Carbon {
+
+// Provides semantic analysis on a ParseTree.
+class SemanticsIR {
+ public:
+  // Provides a link back to a semantic node in a name scope.
+  class Node {
+   public:
+    Node() : Node(Kind::Invalid, -1) {}
+
+   private:
+    friend class SemanticsIR;
+
+    // The kind of token. These correspond to the lists on SemanticsIR which
+    // will be indexed into.
+    enum class Kind {
+      Invalid,
+      Function,
+    };
+
+    Node(Kind kind, int32_t index) : kind_(kind), index_(index) {
+      // TODO: kind_ and index_ are currently unused, this suppresses the
+      // warning.
+      kind_ = kind;
+      index_ = index;
+    }
+
+    Kind kind_;
+
+    // The index of the named entity within its list.
+    int32_t index_;
+  };
+
+  struct Block {
+   public:
+    void Add(llvm::StringRef name, Node named_entity);
+
+   private:
+    llvm::SmallVector<Node> ordering_;
+    llvm::StringMap<Node> name_lookup_;
+  };
+
+ private:
+  friend class SemanticsIRFactory;
+
+  explicit SemanticsIR(const ParseTree& parse_tree)
+      : parse_tree_(&parse_tree) {}
+
+  // Creates a function, adds it to the enclosing scope, and returns a reference
+  // for further mutations. On a name collision, it will not be added to the
+  // scope, but will still be returned.
+  auto AddFunction(Block& block, ParseTree::Node decl_node,
+                   ParseTree::Node name_node) -> Semantics::Function&;
+
+  // Indexed by Token::Function.
+  llvm::SmallVector<Semantics::Function, 0> functions_;
+
+  // The file-level block.
+  Block root_block_;
+
+  const ParseTree* parse_tree_;
+};
+
+}  // namespace Carbon
+
+#endif  // TOOLCHAIN_SEMANTICS_SEMANTICS_IR_H_

+ 58 - 0
toolchain/semantics/semantics_ir_factory.cpp

@@ -0,0 +1,58 @@
+// 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 "toolchain/semantics/semantics_ir_factory.h"
+
+#include "common/check.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "toolchain/lexer/tokenized_buffer.h"
+#include "toolchain/parser/parse_node_kind.h"
+
+namespace Carbon {
+
+auto SemanticsIRFactory::Build(const ParseTree& parse_tree) -> SemanticsIR {
+  SemanticsIRFactory builder(parse_tree);
+  builder.ProcessRoots();
+  return builder.semantics_;
+}
+
+void SemanticsIRFactory::ProcessRoots() {
+  for (ParseTree::Node node : semantics_.parse_tree_->roots()) {
+    switch (semantics_.parse_tree_->node_kind(node)) {
+      case ParseNodeKind::FunctionDeclaration():
+        ProcessFunctionNode(semantics_.root_block_, node);
+        break;
+      case ParseNodeKind::FileEnd():
+        // No action needed.
+        break;
+      default:
+        FATAL() << "Unhandled node kind: "
+                << semantics_.parse_tree_->node_kind(node).name();
+    }
+  }
+}
+
+void SemanticsIRFactory::ProcessFunctionNode(SemanticsIR::Block& block,
+                                             ParseTree::Node decl_node) {
+  llvm::Optional<Semantics::Function> fn;
+  for (ParseTree::Node node : semantics_.parse_tree_->children(decl_node)) {
+    switch (semantics_.parse_tree_->node_kind(node)) {
+      case ParseNodeKind::DeclaredName():
+        fn = semantics_.AddFunction(block, decl_node, node);
+        break;
+      case ParseNodeKind::ParameterList():
+        // TODO: Maybe something like Semantics::AddVariable passed to
+        // Function::AddParameter.
+        break;
+      case ParseNodeKind::CodeBlock():
+        // TODO: Should accumulate the definition into the code block.
+        break;
+      default:
+        FATAL() << "Unhandled node kind: "
+                << semantics_.parse_tree_->node_kind(node).name();
+    }
+  }
+}
+
+}  // namespace Carbon

+ 40 - 0
toolchain/semantics/semantics_ir_factory.h

@@ -0,0 +1,40 @@
+// 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 TOOLCHAIN_SEMANTICS_SEMANTICS_IR_FACTORY_H_
+#define TOOLCHAIN_SEMANTICS_SEMANTICS_IR_FACTORY_H_
+
+#include <optional>
+
+#include "llvm/ADT/StringMap.h"
+#include "toolchain/parser/parse_tree.h"
+#include "toolchain/semantics/semantics_ir.h"
+
+namespace Carbon {
+
+// The main semantic analysis entry.
+class SemanticsIRFactory {
+ public:
+  // Builds the SemanticsIR without doing any substantial semantic analysis.
+  static auto Build(const ParseTree& parse_tree) -> SemanticsIR;
+
+ private:
+  explicit SemanticsIRFactory(const ParseTree& parse_tree)
+      : semantics_(parse_tree) {}
+
+  // Processes the roots of the ParseTree into semantics_, transitively
+  // handling children.
+  void ProcessRoots();
+
+  // Turns a function node from the parse tree into a semantic function node,
+  // adding it to the containing scope.
+  void ProcessFunctionNode(SemanticsIR::Block& block,
+                           ParseTree::Node decl_node);
+
+  SemanticsIR semantics_;
+};
+
+}  // namespace Carbon
+
+#endif  // TOOLCHAIN_SEMANTICS_SEMANTICS_IR_FACTORY_H_

+ 56 - 0
toolchain/semantics/semantics_ir_factory_test.cpp

@@ -0,0 +1,56 @@
+// 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 "toolchain/semantics/semantics_ir_factory.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <optional>
+
+#include "toolchain/diagnostics/mocks.h"
+#include "toolchain/lexer/tokenized_buffer.h"
+#include "toolchain/parser/parse_tree.h"
+#include "toolchain/source/source_buffer.h"
+
+namespace Carbon::Testing {
+namespace {
+
+using ::testing::_;
+
+class SemanticsIRFactoryTest : public ::testing::Test {
+ protected:
+  auto Analyze(llvm::Twine t) -> SemanticsIR {
+    source_buffer.emplace(std::move(*SourceBuffer::CreateFromText(t.str())));
+    tokenized_buffer = TokenizedBuffer::Lex(*source_buffer, consumer);
+    EXPECT_FALSE(tokenized_buffer->has_errors());
+    parse_tree = ParseTree::Parse(*tokenized_buffer, consumer);
+    EXPECT_FALSE(parse_tree->has_errors());
+    return SemanticsIRFactory::Build(*parse_tree);
+  }
+
+  std::optional<SourceBuffer> source_buffer;
+  std::optional<TokenizedBuffer> tokenized_buffer;
+  std::optional<ParseTree> parse_tree;
+  MockDiagnosticConsumer consumer;
+};
+
+TEST_F(SemanticsIRFactoryTest, Empty) {
+  EXPECT_CALL(consumer, HandleDiagnostic(_)).Times(0);
+  Analyze("");
+}
+
+TEST_F(SemanticsIRFactoryTest, FunctionBasic) {
+  EXPECT_CALL(consumer, HandleDiagnostic(_)).Times(0);
+  Analyze("fn Foo() {}");
+}
+
+TEST_F(SemanticsIRFactoryTest, FunctionDuplicate) {
+  Analyze(R"(fn Foo() {}
+             fn Foo() {}
+            )");
+}
+
+}  // namespace
+}  // namespace Carbon::Testing