Skip to content

Commit

Permalink
[SymbolTable]: initial support for implicit declarations
Browse files Browse the repository at this point in the history
Signed-off-by: Lukasz Dalek <[email protected]>
  • Loading branch information
Lukasz Dalek committed Apr 5, 2021
1 parent d46e152 commit b9bbc81
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 0 deletions.
53 changes: 53 additions & 0 deletions verilog/analysis/symbol_table.cc
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ static absl::Status DiagnoseMemberSymbolResolutionFailure(
context_name, "."));
}

static const SymbolTableNode* LookupSymbolUpwards(
const SymbolTableNode& context, absl::string_view symbol);

class SymbolTable::Builder : public TreeContextVisitor {
public:
Builder(const VerilogSourceFile& source, SymbolTable* symbol_table,
Expand Down Expand Up @@ -261,6 +264,9 @@ class SymbolTable::Builder : public TreeContextVisitor {
case NodeEnum::kStructType:
DescendStructType(node);
break;
case NodeEnum::kLPValue:
HandlePossibleImplicitDeclaration(node);
break;
default:
Descend(node);
break;
Expand Down Expand Up @@ -455,6 +461,26 @@ class SymbolTable::Builder : public TreeContextVisitor {
}
}

void HandlePossibleImplicitDeclaration(const SyntaxTreeNode& node) {
VLOG(2) << __FUNCTION__;

// Only left-hand side of continuous assignment statements are allowed to
// implicitly declare nets (LRM 6.10: Implicit declarations).
if (Context().DirectParentsAre(
{NodeEnum::kNetVariableAssignment, NodeEnum::kAssignmentList,
NodeEnum::kContinuousAssignmentStatement})) {
CHECK(node.MatchesTag(NodeEnum::kLPValue));

DeclarationTypeInfo decl_type_info;
const ValueSaver<DeclarationTypeInfo*> save_type(&declaration_type_info_,
&decl_type_info);
declaration_type_info_->implicit = true;
Descend(node);
} else {
Descend(node);
}
}

void HandleIdentifier(const SyntaxTreeLeaf& leaf) {
const absl::string_view text = leaf.get().text();
VLOG(2) << __FUNCTION__ << ": " << text;
Expand Down Expand Up @@ -582,6 +608,29 @@ class SymbolTable::Builder : public TreeContextVisitor {
return;
}

// Handle possible implicit declarations here
if (declaration_type_info_ != nullptr && declaration_type_info_->implicit) {
const SymbolTableNode* resolved =
LookupSymbolUpwards(*ABSL_DIE_IF_NULL(current_scope_), text);
if (resolved == nullptr) {
// No explicit declaration found, declare here
SymbolTableNode& implicit_declaration =
EmplaceTypedElementInCurrentScope(
leaf, text, SymbolMetaType::kDataNetVariableInstance);

const ReferenceComponent implicit_ref{
.identifier = text,
.ref_type = InferReferenceType(),
.required_metatype = InferMetaType(),
// pre-resolve
.resolved_symbol = &implicit_declaration,
};

ref.PushReferenceComponent(implicit_ref);
return;
}
}

// For all other cases, grow the reference chain deeper.
// For type references, which may contained named parameters,
// when encountering the first unqualified reference, establish its
Expand Down Expand Up @@ -1697,6 +1746,10 @@ std::ostream& operator<<(std::ostream& stream,
stream << "(primitive)";
}

if (decl_type_info.implicit) {
stream << ", implicit";
}

return stream << " }";
}

Expand Down
5 changes: 5 additions & 0 deletions verilog/analysis/symbol_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,11 @@ struct DeclarationTypeInfo {
// advance, and only ever moving ReferenceComponents, never copying them.
const ReferenceComponentNode* user_defined_type = nullptr;

// Indicates that this is implicit declaration.
// FIXME(ldk): Check if this could be replaced by user_defined_type pointing
// to default implicit type.
bool implicit = false;

public:
DeclarationTypeInfo() = default;

Expand Down
132 changes: 132 additions & 0 deletions verilog/analysis/symbol_table_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2183,6 +2183,138 @@ TEST(BuildSymbolTableTest, ModuleDeclarationLocalsDependOnParameter) {
}
}

TEST(BuildSymbolTableTest, ModuleSingleImplicitDeclaration) {
TestVerilogSourceFile src("foo.sv",
"module m;"
"assign a = 1'b0;"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode& root_symbol(symbol_table.Root());

const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);

MUST_ASSIGN_LOOKUP_SYMBOL(module_m, root_symbol, "m");
EXPECT_EQ(module_m_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_m_info.file_origin, &src);
EXPECT_EQ(module_m_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type

MUST_ASSIGN_LOOKUP_SYMBOL(a_variable, module_m, "a");
EXPECT_EQ(a_variable_info.metatype, SymbolMetaType::kDataNetVariableInstance);
const ReferenceComponentNode* a_type_ref =
a_variable_info.declared_type.user_defined_type;
EXPECT_EQ(a_type_ref, nullptr); // implicit type is primitive type

EXPECT_EQ(module_m_info.local_references_to_bind.size(), 1);
const auto ref_map(module_m_info.LocalReferencesMapViewForTesting());

ASSIGN_MUST_FIND(a_refs, ref_map, "a");
ASSERT_EQ(a_refs.size(), 1); // all references to "N" parameter
for (const auto& a_ref : a_refs) {
const ReferenceComponent& a_ref_comp(a_ref->components->Value());
EXPECT_EQ(a_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(a_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(a_ref_comp.identifier, "a");
EXPECT_EQ(a_ref_comp.resolved_symbol, &a_variable); // pre-resolved
}

{
std::vector<absl::Status> resolve_diagnostics;
// Resolve mustn't break anything
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);

// All references to "a" resolved.
for (const auto& a_ref : a_refs) {
const ReferenceComponent& a_ref_comp(a_ref->components->Value());
EXPECT_EQ(a_ref_comp.resolved_symbol,
&a_variable); // resolved successfully
}
}
}

TEST(BuildSymbolTableTest, ModuleReferenceToImplicitDeclaration) {
TestVerilogSourceFile src("foo.sv",
"module m;"
"assign a = 1'b0;"
"assign a = 1'b1;"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);
const SymbolTableNode& root_symbol(symbol_table.Root());

const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EMPTY_STATUSES(build_diagnostics);

MUST_ASSIGN_LOOKUP_SYMBOL(module_m, root_symbol, "m");
EXPECT_EQ(module_m_info.metatype, SymbolMetaType::kModule);
EXPECT_EQ(module_m_info.file_origin, &src);
EXPECT_EQ(module_m_info.declared_type.syntax_origin,
nullptr); // there is no module meta-type

MUST_ASSIGN_LOOKUP_SYMBOL(a_variable, module_m, "a");
EXPECT_EQ(a_variable_info.metatype, SymbolMetaType::kDataNetVariableInstance);
const ReferenceComponentNode* a_type_ref =
a_variable_info.declared_type.user_defined_type;
EXPECT_EQ(a_type_ref, nullptr); // implicit type is primitive type

EXPECT_EQ(module_m_info.local_references_to_bind.size(), 2);
const auto ref_map(module_m_info.LocalReferencesMapViewForTesting());

ASSIGN_MUST_FIND(a_refs, ref_map, "a");
ASSERT_EQ(a_refs.size(), 2); // all references to "N" parameter
{
const auto& a_ref = *a_refs.begin();
const ReferenceComponent& a_ref_comp(a_ref->components->Value());
EXPECT_EQ(a_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(a_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(a_ref_comp.identifier, "a");
EXPECT_EQ(a_ref_comp.resolved_symbol, &a_variable); // pre-resolved
}
{
const auto& a_ref = *std::next(a_refs.begin());
const ReferenceComponent& a_ref_comp(a_ref->components->Value());
EXPECT_EQ(a_ref_comp.ref_type, ReferenceType::kUnqualified);
EXPECT_EQ(a_ref_comp.required_metatype, SymbolMetaType::kUnspecified);
EXPECT_EQ(a_ref_comp.identifier, "a");
EXPECT_EQ(a_ref_comp.resolved_symbol, nullptr); // pre-resolved
}

{
std::vector<absl::Status> resolve_diagnostics;
symbol_table.Resolve(&resolve_diagnostics);
EXPECT_EMPTY_STATUSES(resolve_diagnostics);

// All references to "a" resolved.
for (const auto& a_ref : a_refs) {
const ReferenceComponent& a_ref_comp(a_ref->components->Value());
EXPECT_EQ(a_ref_comp.resolved_symbol,
&a_variable); // resolved successfully
}
}
}

TEST(BuildSymbolTableTest, ModuleImplicitRedeclared) {
TestVerilogSourceFile src("foo.sv",
"module m;"
"assign a = 1'b0;"
"wire a;"
"endmodule\n");
const auto status = src.Parse();
ASSERT_TRUE(status.ok()) << status.message();
SymbolTable symbol_table(nullptr);

const auto build_diagnostics = BuildSymbolTable(src, &symbol_table);
EXPECT_EQ(build_diagnostics.size(), 1);
EXPECT_FALSE(build_diagnostics.front().ok());
EXPECT_EQ(build_diagnostics.front().message(),
"Symbol \"a\" is already defined in the $root::m scope.");
}

TEST(BuildSymbolTableTest, ClassDeclarationSingle) {
TestVerilogSourceFile src("foobar.sv", "class ccc;\nendclass\n");
const auto status = src.Parse();
Expand Down

0 comments on commit b9bbc81

Please sign in to comment.