fix typedefs

This commit is contained in:
Edmund Lam 2026-06-09 13:03:00 -04:00
parent e0c55cc296
commit f18aaeea83
5 changed files with 192 additions and 3 deletions

View File

@ -1288,7 +1288,19 @@ class ParamProcessor final {
// Param/lparam member: substitute its constant value so the caller's constify can
// succeed.
if (varp->isParam() && varp->valuep()) {
if (!VN_IS(varp->valuep(), Const)) V3Const::constifyParamsEdit(varp);
if (!VN_IS(varp->valuep(), Const)) {
// If the value contains nested class::member Dots (e.g. this
// member's expression references another paramed class), resolve
// those first so constify can fold the whole chain.
std::vector<AstDot*> nestedDots;
varp->valuep()->foreach([&](AstDot* dp) {
if (VN_IS(dp->lhsp(), ClassOrPackageRef)) nestedDots.push_back(dp);
});
for (auto it = nestedDots.rbegin(); it != nestedDots.rend(); ++it) {
resolveDotToTypedef(*it);
}
V3Const::constifyParamsEdit(varp);
}
if (AstConst* const constp = VN_CAST(varp->valuep(), Const)) {
dotp->replaceWith(constp->cloneTree(false));
VL_DO_DANGLING(dotp->deleteTree(), dotp);
@ -2182,13 +2194,21 @@ public:
// VarRefs to all leaf dependencies. Then resolve these to typedefs.
void resolveDeferredDotsReachableFrom(AstNode* rootp, AstNodeModule* modp) {
std::vector<AstDot*> dotps;
std::vector<AstTypedef*> tdefps;
const auto& deferredVarps = v3Global.rootp()->deferredParamVarps();
std::set<AstVar*> reachedDeferred;
std::set<const AstTypedef*> reachedTypedefs;
auto collectDots = [&](AstNode* p) {
p->foreach([&](AstDot* dotp) {
if (VN_IS(dotp->lhsp(), ClassOrPackageRef)) dotps.push_back(dotp);
});
};
auto collectTypedefs = [&](AstNode* p) {
p->foreach([&](const AstRefDType* refp) {
AstTypedef* const tdefp = refp->typedefp();
if (tdefp && reachedTypedefs.insert(tdefp).second) tdefps.push_back(tdefp);
});
};
std::vector<AstVar*> workVarps;
auto enqueueRefs = [&](AstNode* p) {
p->foreach([&](const AstVarRef* refp) {
@ -2200,18 +2220,25 @@ public:
});
};
collectDots(rootp);
collectTypedefs(rootp);
enqueueRefs(rootp);
// Handle deferred VarRefs recursively until no more, accumulating Dots along the way.
// Handle deferred VarRefs recursively until no more, accumulating Dots and
// typedef references along the way.
while (!workVarps.empty()) {
AstVar* const varp = workVarps.back();
workVarps.pop_back();
if (!varp->valuep()) continue;
collectDots(varp->valuep());
collectTypedefs(varp->valuep());
enqueueRefs(varp->valuep());
}
if (dotps.empty()) return;
if (dotps.empty() && tdefps.empty()) return;
VL_RESTORER(m_modp);
m_modp = modp;
// Resolve unresolved class-scoped RefDTypes buried in any typedef reachable from
// the pin/expr tree (e.g. `$bits(wrap_t)` where `wrap_t`'s struct members
// reference `CFG::data_t` from a parameterized-class typedef alias).
for (AstTypedef* const tdefp : tdefps) resolveParamClassRefDType(tdefp->subDTypep());
// Reverse-iterate so inner Dots resolve before outer.
for (auto it = dotps.rbegin(); it != dotps.rend(); ++it) resolveDotToTypedef(*it);
}

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,72 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// Multi-level class hierarchies where one parameterized class's
// localparam value references another parameterized class via its
// own class-scope Dot (e.g. B::width = inner_a::v where inner_a is
// a typedef of A#(...)). resolveDotToTypedef must recursively resolve
// nested class::member Dots in a member's value before constifying,
// otherwise cell-pin deparam fails for the outer Dot.
//
// 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 Sub #(
parameter int WIDTH
) ();
endmodule
module t;
// Two-level: B's lparam value contains a class::member Dot of its own
virtual class A #(
parameter int x
);
localparam int v = x * 2;
endclass
virtual class B #(
parameter int y
);
typedef A#(y + 1) inner_a;
localparam int width = inner_a::v; // nested Dot inside B's lparam
endclass
// Three-level: C wraps B wraps A
virtual class C #(
parameter int z
);
typedef B#(z * 3) inner_b;
localparam int total = inner_b::width; // double-nested Dot
endclass
typedef B#(5) BInst;
typedef C#(2) CInst;
// (1) Standalone localparam — resolves via deferred-lparam path
localparam int two_level = BInst::width; // = A#(6)::v = 12
localparam int three_level = CInst::total; // = B#(6)::width = A#(7)::v = 14
// (2) Cell pin uses the nested-class value directly — exercises the
// recursive Dot resolution inside resolveDotToTypedef
Sub #(BInst::width) m_pin_direct ();
Sub #(CInst::total) m_pin_deep ();
Sub #(BInst::width + 1) m_pin_expr ();
// (3) Cell pin chains through a deferred lparam whose value is
// itself a nested-class Dot
localparam int chain = BInst::width;
Sub #(chain) m_pin_chain ();
initial begin
`checkh(two_level, 32'd12);
`checkh(three_level, 32'd14);
`checkh(chain, 32'd12);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

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,54 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// A struct typedef whose member references a class-scope-resolved typedef
// from a parameterized-class typedef alias (e.g. CFG::data_t). The struct
// is consumed by `$bits(...)` on a cell parameter pin, which forces V3Param
// to constify the pin during cell deparameterization. V3Param must follow
// typedef references in the pin tree and resolve the unresolved struct-member
// RefDTypes that target the parameterized class, otherwise V3Width hits
// "REFDTYPE 'data_t' not linked to type" (V3AstNodes.cpp).
//
// 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
package pkg;
virtual class C #(parameter int W = 1);
typedef logic [W-1:0] data_t;
endclass
endpackage
module Sink #(parameter int BITS = 1) ();
logic [BITS-1:0] data;
endmodule
module Sub #(parameter int W = 8) ();
typedef pkg::C#(W) CFG;
// Struct typedef whose member is a class-scope-resolved typedef from
// a parameterized-class typedef alias. Without the V3Param fix, the
// member's RefDType stays UNLINKED and the cell pin's $bits trips an
// internal error.
typedef struct packed {
CFG::data_t payload;
logic v;
} wrap_t;
Sink #(.BITS($bits(wrap_t))) u_sink ();
endmodule
module t;
Sub #(.W(8)) u ();
initial begin
// $bits(wrap_t) = 8 (payload) + 1 (v) = 9
`checkh($bits(u.u_sink.data), 32'd9);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule