Fix for access to parameters via class::localparam (#7609) (#7671)

This commit is contained in:
em2machine 2026-05-28 17:40:18 -04:00 committed by GitHub
parent 77f0883b06
commit 3d126b77cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 90 additions and 7 deletions

View File

@ -1285,7 +1285,7 @@ class AstNetlist final : public AstNode {
bool m_timescaleSpecified = false; // Input HDL specified timescale
uint32_t m_nTraceCodes = 0; // Number of trace codes used by design
// V3Param-deferred params awaiting V3LinkDot::linkDotParamed scope-resolution.
std::vector<AstVar*> m_deferredParamVarps;
std::set<AstVar*> m_deferredParamVarps;
// Sparse metadata for constants produced from named parameters/localparams. Keep this off
// AstConst itself, as AstConst is a very common node and only a small fraction carry this
// name.
@ -1295,8 +1295,8 @@ public:
AstNetlist();
ASTGEN_MEMBERS_AstNetlist;
const char* broken() const override;
void pushDeferredParamVarp(AstVar* varp) { m_deferredParamVarps.push_back(varp); }
const std::vector<AstVar*>& deferredParamVarps() const { return m_deferredParamVarps; }
void pushDeferredParamVarp(AstVar* varp) { m_deferredParamVarps.insert(varp); }
const std::set<AstVar*>& deferredParamVarps() const { return m_deferredParamVarps; }
void clearDeferredParamVarps() { m_deferredParamVarps.clear(); }
void deleteContents();
void cloneRelink() override { V3ERROR_NA; } // Not cloneable

View File

@ -2924,10 +2924,22 @@ class ParamVisitor final : public VNVisitor {
}
});
if (hasUnresolvedLparamXRef) return;
// Defer if value contains a class::member Dot. By V3Param time the only
// surviving such Dots are paramed-class refs awaiting linkDotParamed.
if (nodep->valuep()->exists(
[](AstDot* dotp) { return VN_IS(dotp->lhsp(), ClassOrPackageRef); })) {
// Defer if value has a class::member Dot, or references a deferred lparam
const bool hasDot = nodep->valuep()->exists(
[](AstDot* dotp) { return VN_IS(dotp->lhsp(), ClassOrPackageRef); });
bool refsDeferred = false;
if (!hasDot) {
const auto& deferredVarps = v3Global.rootp()->deferredParamVarps();
nodep->valuep()->foreach([&](const AstVarRef* refp) {
if (refsDeferred) return;
AstVar* const refVarp = refp->varp();
if (refVarp && refVarp->varType() == VVarType::LPARAM
&& deferredVarps.count(refVarp)) {
refsDeferred = true;
}
});
}
if (hasDot || refsDeferred) {
v3Global.rootp()->pushDeferredParamVarp(nodep);
return;
}

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# 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-FileCopyrightText: 2026 Wilson Snyder
# 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()

View File

@ -0,0 +1,53 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// localparams that chain through a class-scope-resolved localparam
// (typedef alias of a parameterized class, e.g. inst::b). V3LinkDot defers
// the inst::b Dot until post-V3Param; V3Param must also defer any
// localparam whose value transitively depends on a deferred one, including
// same-module VarRef chains (b -> c -> d) and not just cross-module
// VarXRefs.
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
// verilog_format: on
module t;
virtual class C #(parameter int a);
localparam int b = a;
endclass
typedef C#(0) inst0;
typedef C#(42) inst42;
// Direct: inst::b Dot in the value
localparam int b0 = inst0::b;
localparam int b42 = inst42::b;
// One-step chain: refers to a deferred lparam
localparam int c0 = b0;
localparam int c42 = b42;
// Multi-step chain: d -> c -> b -> inst::b
localparam int d0 = c0;
localparam int d42 = c42;
// Expression referencing two deferred lparams
localparam int sum = b0 + b42;
initial begin
`checkh(b0, 32'd0);
`checkh(b42, 32'd42);
`checkh(c0, 32'd0);
`checkh(c42, 32'd42);
`checkh(d0, 32'd0);
`checkh(d42, 32'd42);
`checkh(sum, 32'd42);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule