Adding some shape to toolchain semantic analysis (#1092)

This commit is contained in:
Jon Meow
2022-03-16 16:04:39 -07:00
committed by GitHub
parent 50d9fc5189
commit bf7159f841
10 changed files with 327 additions and 50 deletions
+1 -1
View File
@@ -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,
+35 -9
View File
@@ -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",
+31
View File
@@ -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_
-15
View File
@@ -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
-25
View File
@@ -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
View File
@@ -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
View File
@@ -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_
@@ -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
@@ -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_
@@ -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