diff --git a/Changes b/Changes index 9374ec69c..011d23228 100644 --- a/Changes +++ b/Changes @@ -72,6 +72,7 @@ Verilator 5.043 devel * Fix `--prof-exec` using `--lib-create`. [Geza Lore] * Fix fork scheduling semantics (#6730). [Artur Bieniek, Antmicro Ltd.] * Fix internal fault when cross-class calling with DPI (#6735) (#6742). [Matthew Ballance] +* Fix class-in-class extends with parameters (#6773). Verilator 5.042 2025-11-02 diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index aafc18630..257807fd8 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -4580,6 +4580,15 @@ class LinkDotResolveVisitor final : public VNVisitor { nodep->taskp(randFuncp); m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), randFuncp, m_modp); } + if (m_insideClassExtParam) { + // The reference may point to a method declared in a super class, which is proved + // by a parameter. In such a case, it can't be linked at the first stage. + // Must not do any linking, because e.g. might find an extends of an upper class + // because the current class (under parent) isn't yet importing it's extended class + // symbols + return; + } + VSymEnt* const foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot, first); AstNodeFTask* const taskp @@ -4593,10 +4602,6 @@ class LinkDotResolveVisitor final : public VNVisitor { nodep->taskp(taskp); nodep->classOrPackagep(foundp->classOrPackagep()); UINFO(7, indent() << "Resolved " << nodep); // Also prints taskp - } else if (m_insideClassExtParam) { - // The reference may point to a method declared in a super class, which is proved - // by a parameter. In such a case, it can't be linked at the first stage. - return; } else { // Note ParseRef has similar error handling/message output UINFO(7, indent() << " ErrFtask curSymp=se" << cvtToHex(m_curSymp) diff --git a/test_regress/t/t_class_class_extends.py b/test_regress/t/t_class_class_extends.py new file mode 100755 index 000000000..bd059b0f2 --- /dev/null +++ b/test_regress/t/t_class_class_extends.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(verilator_flags2=['--binary']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_class_class_extends.v b/test_regress/t/t_class_class_extends.v new file mode 100644 index 000000000..4baae7ce4 --- /dev/null +++ b/test_regress/t/t_class_class_extends.v @@ -0,0 +1,113 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +package uvm_pkg; + typedef class uvm_root; + + virtual class uvm_coreservice_t; + pure virtual function uvm_root get_root(); + static function uvm_coreservice_t get(); + return null; + endfunction + endclass + + class uvm_default_coreservice_t extends uvm_coreservice_t; + virtual function uvm_root get_root(); + return uvm_root::m_uvm_get_root(); + endfunction + endclass + + virtual class uvm_object; + endclass + + class uvm_report_object extends uvm_object; + // Note there's no 'virtual' on this function + function uvm_report_object uvm_get_report_object(); // Wrong target + return null; + endfunction + endclass + + class uvm_root extends uvm_report_object; + static function uvm_root m_uvm_get_root(); + uvm_root top; + top = new(); + return null; + endfunction + endclass + + virtual class uvm_process_guard_base extends uvm_object; + pure virtual function void do_trigger(); + // Removing m_process member works around the issue + protected process m_target_process; + function new(); + m_target_process = process::self(); + endfunction + function void m_process_guard(uvm_process_guard_base guard); + // Removing fork/join works around the issue + fork + begin + if (guard.m_target_process != null) begin + guard.do_trigger(); + end + end + join_none + endfunction + endclass + + class uvm_process_guard #( + type T = int + ) extends uvm_process_guard_base; + protected T m_context; + function void do_trigger(); + m_context.process_guard_triggered(this); + endfunction + endclass + + class uvm_sequence_item; + virtual function uvm_report_object uvm_get_report_object(); // Correct target + uvm_coreservice_t cs = uvm_coreservice_t::get(); + return cs.get_root(); + endfunction + endclass + + virtual class uvm_sequence_base extends uvm_sequence_item; + typedef uvm_process_guard#(uvm_sequence_base) m_guard_t; + function void process_guard_triggered(m_guard_t guard); + uvm_pkg::uvm_report_object _local_report_object_arg_; + _local_report_object_arg_ = uvm_get_report_object(); + // ^ calls uvm_sequence_base->virtual uvm_sequence_item::uvm_get_report_object() + endfunction + endclass + + virtual class uvm_sequence #( + type RSP = int + ) extends uvm_sequence_base; + endclass + +endpackage + +module t; + + import uvm_pkg::*; + + class Cls extends uvm_report_object; + + class transaction_sequence extends uvm_sequence #(uvm_object); + virtual task body(); + uvm_pkg::uvm_report_object _local_report_object_arg_; + _local_report_object_arg_ = uvm_get_report_object(); + // ^- calls transaction_sequence->uvm_sequence + // ->uvm_sequence_base->virtual uvm_sequence_item::uvm_get_report_object(); + endtask + endclass + + endclass + + initial begin + Cls c = new(); + $finish; + end +endmodule