diff --git a/verilog/analysis/symbol_table.cc b/verilog/analysis/symbol_table.cc index 8fd7df0067..b29e3530ee 100644 --- a/verilog/analysis/symbol_table.cc +++ b/verilog/analysis/symbol_table.cc @@ -78,6 +78,7 @@ static const verible::EnumNameMap kSymbolMetaTypeNames({ {"function", SymbolMetaType::kFunction}, {"task", SymbolMetaType::kTask}, {"struct", SymbolMetaType::kStruct}, + {"enum", SymbolMetaType::kEnum}, {"interface", SymbolMetaType::kInterface}, {"", SymbolMetaType::kUnspecified}, {"", SymbolMetaType::kCallable}, @@ -261,6 +262,9 @@ class SymbolTable::Builder : public TreeContextVisitor { case NodeEnum::kStructType: DescendStructType(node); break; + case NodeEnum::kEnumType: + DescendEnumType(node); + break; default: Descend(node); break; @@ -455,6 +459,29 @@ class SymbolTable::Builder : public TreeContextVisitor { } } + void DescendEnumType(const SyntaxTreeNode& enum_type) { + CHECK(enum_type.MatchesTag(NodeEnum::kEnumType)); + const absl::string_view anon_name = + current_scope_->Value().CreateAnonymousScope("enum"); + SymbolTableNode& new_enum = DeclareScopedElementAndDescend( + enum_type, anon_name, SymbolMetaType::kEnum); + + const ReferenceComponent anon_type_ref{ + .identifier = anon_name, + .ref_type = ReferenceType::kImmediate, + .required_metatype = SymbolMetaType::kEnum, + // pre-resolve this symbol immediately + .resolved_symbol = &new_enum, + }; + + const CaptureDependentReference capture(this); + capture.Ref().PushReferenceComponent(anon_type_ref); + + if (declaration_type_info_ != nullptr) { + declaration_type_info_->user_defined_type = capture.Ref().LastLeaf(); + } + } + void HandleIdentifier(const SyntaxTreeLeaf& leaf) { const absl::string_view text = leaf.get().text(); VLOG(2) << __FUNCTION__ << ": " << text; @@ -546,6 +573,13 @@ class SymbolTable::Builder : public TreeContextVisitor { return; } + if (Context().DirectParentsAre({NodeEnum::kEnumName, + NodeEnum::kEnumNameList})) { + EmplaceTypedElementInCurrentScope( + leaf, text, SymbolMetaType::kUnspecified); + return; + } + // In DeclareInstance(), we already planted a self-reference that is // resolved to the instance being declared. if (Context().DirectParentIs(NodeEnum::kGateInstance)) return; diff --git a/verilog/analysis/symbol_table.h b/verilog/analysis/symbol_table.h index a339dd17f8..34d840183b 100644 --- a/verilog/analysis/symbol_table.h +++ b/verilog/analysis/symbol_table.h @@ -54,6 +54,7 @@ enum class SymbolMetaType { kFunction, kTask, kStruct, + kEnum, kInterface, // The following enums represent classes/groups of the above types, diff --git a/verilog/analysis/symbol_table_test.cc b/verilog/analysis/symbol_table_test.cc index 9bc78f3738..4fa9d6f3b5 100644 --- a/verilog/analysis/symbol_table_test.cc +++ b/verilog/analysis/symbol_table_test.cc @@ -6132,6 +6132,79 @@ TEST(BuildSymbolTableTest, AnonymousStructTypeNestedMemberReference) { } } +TEST(BuildSymbolTableTest, AnonymousEnumTypeData) { + TestVerilogSourceFile src("enumy.sv", + "enum {\n" + " idle, busy\n" + "} data;\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); + + // Expect one anonymous enum definition and reference. + EXPECT_EQ(root_symbol.Value().anonymous_scope_names.size(), 1); + ASSERT_EQ(root_symbol.Children().size(), 2); + // Find the symbol that is a enum (anon), which is not "data". + const auto found = + std::find_if(root_symbol.Children().begin(), root_symbol.Children().end(), + [](const SymbolTableNode::key_value_type& p) { + return p.first != "data"; + }); + ASSERT_NE(found, root_symbol.Children().end()); + const SymbolTableNode& anon_enum(found->second); + const SymbolInfo& anon_enum_info(anon_enum.Value()); + EXPECT_EQ(anon_enum_info.metatype, SymbolMetaType::kEnum); + EXPECT_TRUE(anon_enum_info.local_references_to_bind.empty()); + + // Enum has two members. + MUST_ASSIGN_LOOKUP_SYMBOL(idle, anon_enum, "idle"); + EXPECT_EQ(idle_info.metatype, SymbolMetaType::kUnspecified); + EXPECT_EQ(idle_info.file_origin, &src); + //EXPECT_EQ( + // verible::StringSpanOfSymbol( + // *ABSL_DIE_IF_NULL(idle_info.declared_type.syntax_origin)), + // "idle"); + //EXPECT_EQ(idle_info.declared_type.user_defined_type, nullptr); + + MUST_ASSIGN_LOOKUP_SYMBOL(busy, anon_enum, "busy"); + EXPECT_EQ(busy_info.metatype, SymbolMetaType::kUnspecified); + EXPECT_EQ(busy_info.file_origin, &src); + //EXPECT_EQ( + // verible::StringSpanOfSymbol( + // *ABSL_DIE_IF_NULL(busy_info.declared_type.syntax_origin)), + // "idle"); + //EXPECT_EQ(busy_info.declared_type.user_defined_type, nullptr); + + // Expect to bind anonymous enum immediately. + ASSERT_EQ(root_symbol.Value().local_references_to_bind.size(), 1); + const DependentReferences& anon_enum_ref( + root_symbol.Value().local_references_to_bind.front()); + const ReferenceComponent& anon_enum_ref_comp( + anon_enum_ref.components->Value()); + EXPECT_EQ(anon_enum_ref_comp.ref_type, ReferenceType::kImmediate); + EXPECT_EQ(anon_enum_ref_comp.required_metatype, SymbolMetaType::kEnum); + EXPECT_EQ(anon_enum_ref_comp.resolved_symbol, &anon_enum); + + // "data"'s type is the (internal) anonymous enum type reference. + MUST_ASSIGN_LOOKUP_SYMBOL(data, root_symbol, "data"); + EXPECT_EQ(data_info.metatype, SymbolMetaType::kDataNetVariableInstance); + EXPECT_EQ(data_info.file_origin, &src); + EXPECT_EQ(data_info.declared_type.user_defined_type, + anon_enum_ref.LastLeaf()); + + { + std::vector resolve_diagnostics; + symbol_table.Resolve(&resolve_diagnostics); + EXPECT_EMPTY_STATUSES(resolve_diagnostics); + + EXPECT_EQ(anon_enum_ref_comp.resolved_symbol, &anon_enum); // unchanged + } +} + TEST(BuildSymbolTableTest, TypedefPrimitive) { TestVerilogSourceFile src("typedef.sv", "typedef int number;\n"