diff --git a/Changes b/Changes index fea08cd9e..843177436 100644 --- a/Changes +++ b/Changes @@ -32,6 +32,7 @@ Verilator 5.015 devel * Fix internal error on real conversion (#4447). [vdhotre-ventana] * Fix lifetime unknown error on enum.name (#4448). [jwoutersymatra] * Fix error on enum with VARHIDDEN of cell (#4482). [Michail Rontionov] +* Fix reference to extended class in parameterized class (#4466). * Fix display %x formatting of real. * Fix mis-warning on #() in classes' own functions. diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 64a7aab4a..ceb1f2284 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -2251,6 +2251,15 @@ private: if (baseClassp->isInterfaceClass()) importImplementsClass(nodep, srcp, baseClassp); if (!cextp->isImplements()) m_curSymp->importFromClass(m_statep->symsp(), srcp); } + void classExtendImport(AstClass* nodep) { + // A class reference might be to a class that is later in Ast due to + // e.g. parmaeterization or referring to a "class (type T) extends T" + // Resolve it so later Class:: references into its base classes work + VL_RESTORER(m_ds); + VSymEnt* const srcp = m_statep->getNodeSym(nodep); + m_ds.init(srcp); + iterate(nodep); + } bool checkPinRef(AstPin* pinp, VVarType refVarType) { // In instantiations of modules/ifaces, we shouldn't connect port pins to submodule's // parameters or vice versa @@ -2901,12 +2910,14 @@ private: if (nodep->classOrPackagep()) { m_pinSymp = m_statep->getNodeSym(nodep->classOrPackagep()); } + AstClass* const refClassp = VN_CAST(nodep->classOrPackagep(), Class); + // Make sure any extends() are properly imported within referenced class + if (refClassp && !m_statep->forPrimary()) classExtendImport(refClassp); + m_ds.init(m_curSymp); UINFO(4, "(Backto) Link ClassOrPackageRef: " << nodep << endl); - iterateChildren(nodep); - AstClass* const refClassp = VN_CAST(nodep->classOrPackagep(), Class); AstClass* const modClassp = VN_CAST(m_modp, Class); if (m_statep->forPrimary() && refClassp && !nodep->paramsp() && nodep->classOrPackagep()->hasGParam() @@ -3511,6 +3522,8 @@ private: // Already converted. Update symbol table to link unlinked members. // Base class has to be visited in a case if its extends statement // needs to be handled. Recursive inheritance was already checked. + // Must be here instead of in LinkDotParam to handle + // "class (type T) extends T". if (baseClassp == nodep) { cextp->v3error("Attempting to extend class " << nodep->prettyNameQ() << " from itself"); diff --git a/test_regress/t/t_class_param_extends3.pl b/test_regress/t/t_class_param_extends3.pl new file mode 100755 index 000000000..859050d63 --- /dev/null +++ b/test_regress/t/t_class_param_extends3.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2023 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_class_param_extends3.v b/test_regress/t/t_class_param_extends3.v new file mode 100644 index 000000000..3447076ec --- /dev/null +++ b/test_regress/t/t_class_param_extends3.v @@ -0,0 +1,63 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +package u_pkg; + typedef class u_report_object; + typedef class u_callback; + + virtual class u_object; + endclass + + class u_queue #(type T=int) extends u_object; + int m_value = 6; + endclass + + class u_callbacks_base extends u_object; + typedef u_callbacks_base this_type; + endclass + + class u_typed_callbacks#(type T=u_object) extends u_callbacks_base; + typedef u_typed_callbacks#(T) this_type; + static this_type m_t_inst; + static u_queue#(u_callback) m_tw_cb_q; + endclass + + class u_callbacks #(type T=u_object, type CB=u_callback) + extends u_typed_callbacks#(T); + static function bit m_register_pair(); + endfunction + static function void add(u_callback cb); + u_queue#(u_callback) qr; + qr = u_callbacks#(u_report_object,u_callback)::m_t_inst.m_tw_cb_q; //<<<< + if (qr.m_value != 6) $stop; + endfunction + endclass + + class u_callback extends u_object; + endclass + + virtual class u_report_catcher extends u_callback; + static local bit m_register_cb_u_report_catcher = u_callbacks#(u_report_object,u_report_catcher)::m_register_pair(); + endclass + + // Having this class (versus using #(u_object) is needed to hit the bug + class u_report_object extends u_object; + endclass + +endpackage + +module t; + + u_pkg::u_callback cb; + + initial begin + cb = new; + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule