diff --git a/.clang-tidy b/.clang-tidy index 6ee896f3a0c9..fb96cd2f60df 100644 --- a/.clang-tidy +++ b/.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, diff --git a/toolchain/semantics/BUILD b/toolchain/semantics/BUILD index eede08fd1f32..4ebd609f4a2b 100644 --- a/toolchain/semantics/BUILD +++ b/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_test( - name = "semantics_test", - size = "small", - srcs = ["semantics_test.cpp"], +cc_library( + name = "semantics_ir", + srcs = ["semantics_ir.cpp"], + hdrs = ["semantics_ir.h"], deps = [ - ":semantics", - "//toolchain/diagnostics:diagnostic_emitter", + ":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_ir_factory_test", + size = "small", + srcs = ["semantics_ir_factory_test.cpp"], + deps = [ + ":semantics_ir_factory", + "//toolchain/diagnostics:mocks", "//toolchain/lexer:tokenized_buffer", "//toolchain/parser:parse_tree", "//toolchain/source:source_buffer", diff --git a/toolchain/semantics/function.h b/toolchain/semantics/function.h new file mode 100644 index 000000000000..ece60cff3ff8 --- /dev/null +++ b/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_ diff --git a/toolchain/semantics/semantics.cpp b/toolchain/semantics/semantics.cpp deleted file mode 100644 index 31989108962c..000000000000 --- a/toolchain/semantics/semantics.cpp +++ /dev/null @@ -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 diff --git a/toolchain/semantics/semantics.h b/toolchain/semantics/semantics.h deleted file mode 100644 index 37639b6893b4..000000000000 --- a/toolchain/semantics/semantics.h +++ /dev/null @@ -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_ diff --git a/toolchain/semantics/semantics_ir.cpp b/toolchain/semantics/semantics_ir.cpp new file mode 100644 index 000000000000..13c837aa10a6 --- /dev/null +++ b/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 diff --git a/toolchain/semantics/semantics_ir.h b/toolchain/semantics/semantics_ir.h new file mode 100644 index 000000000000..b23d14f57c2f --- /dev/null +++ b/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 ordering_; + llvm::StringMap 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 functions_; + + // The file-level block. + Block root_block_; + + const ParseTree* parse_tree_; +}; + +} // namespace Carbon + +#endif // TOOLCHAIN_SEMANTICS_SEMANTICS_IR_H_ diff --git a/toolchain/semantics/semantics_ir_factory.cpp b/toolchain/semantics/semantics_ir_factory.cpp new file mode 100644 index 000000000000..59fad4152361 --- /dev/null +++ b/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 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 diff --git a/toolchain/semantics/semantics_ir_factory.h b/toolchain/semantics/semantics_ir_factory.h new file mode 100644 index 000000000000..bf3b1c9b5c8d --- /dev/null +++ b/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 + +#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_ diff --git a/toolchain/semantics/semantics_ir_factory_test.cpp b/toolchain/semantics/semantics_ir_factory_test.cpp new file mode 100644 index 000000000000..bb470469c4cb --- /dev/null +++ b/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 +#include + +#include + +#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 source_buffer; + std::optional tokenized_buffer; + std::optional 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