more testing

This commit is contained in:
UnsignedByte 2026-07-09 15:10:45 -04:00
parent bb2ea625cf
commit dd1eb2dfe7
5 changed files with 117 additions and 9 deletions

View File

@ -1311,26 +1311,47 @@ class ParamProcessor final {
AstNodeDType* curDTypep = varp->dtypep();
AstNode* topp = dotp;
AstDot* outerDotp = VN_CAST(dotp->backp(), Dot);
// On any mismatch (non-ParseRef rhs, non-packed-struct dtype, or
// unknown field name) leave the outer Dots intact so V3Width can
// emit the proper user-facing diagnostic.
while (outerDotp && outerDotp->lhsp() == topp) {
const auto errorRecover = [&](AstDot* const badp, const string& msg) {
badp->v3error(msg);
badp->replaceWith(new AstConst{badp->fileline(), AstConst::Signed32{}, 0});
VL_DO_DANGLING(badp->deleteTree(), badp);
};
bool errored = false;
while (outerDotp && outerDotp->lhsp() == topp) { // LCOV_EXCL_BR_LINE
const AstParseRef* const fieldRefp = VN_CAST(outerDotp->rhsp(), ParseRef);
if (!fieldRefp) break;
AstNodeDType* const skippedp = curDTypep ? curDTypep->skipRefp() : nullptr;
if (!fieldRefp) {
errorRecover(outerDotp, "Malformed dotted select in parameter value");
errored = true;
break;
}
UASSERT_OBJ(curDTypep, outerDotp,
"curDTypep null in struct field chain walk");
AstNodeDType* const skippedp = curDTypep->skipRefp();
AstNodeUOrStructDType* const structp
= VN_CAST(skippedp, NodeUOrStructDType);
if (!structp) break;
if (!structp) {
errorRecover(outerDotp,
"Dotted member select on non-struct parameter value");
errored = true;
break;
}
AstMemberDType* const foundMemp = VN_CAST(
m_memberMap.findMember(structp, fieldRefp->name()), MemberDType);
UASSERT_OBJ(foundMemp, outerDotp, "member not found in struct/union");
if (!foundMemp->subDTypep()) break;
if (!foundMemp) {
errorRecover(outerDotp, "Member " + fieldRefp->prettyNameQ()
+ " not found in structure");
errored = true;
break;
}
UASSERT_OBJ(foundMemp->subDTypep(), outerDotp,
"member has null subDTypep");
totalLsb += foundMemp->lsb();
sliceWidth = foundMemp->width();
curDTypep = foundMemp->subDTypep();
topp = outerDotp;
outerDotp = VN_CAST(outerDotp->backp(), Dot);
}
if (errored) return;
if (topp == dotp) {
dotp->replaceWith(constp->cloneTree(false));
VL_DO_DANGLING(dotp->deleteTree(), dotp);

View File

@ -35,6 +35,12 @@ class C #(parameter int W = 1);
tag: W[7:0] + 8'd2};
endclass
// Wrapper class containing the use of another class
class D #(parameter int W = 1);
typedef C#(W) CC;
localparam int q = int'(CC::cfg.tag) + 100;
endclass
module Sub #(parameter int WIDTH = 0) ();
endmodule
@ -47,6 +53,9 @@ module Mid #(parameter int W = 8) ();
// (2) nested struct field access -> two loop iterations, lsb accumulation
Sub #(int'(CFG::cfg.jt.cam_type)) u_cam ();
Sub #(int'(CFG::cfg.jt.depth)) u_depth ();
// (3) nested struct-field Dot buried in a wrapper class's lparam value
typedef D#(W) DD;
Sub #(int'(DD::q)) u_q ();
endmodule
module t;
@ -59,6 +68,8 @@ module t;
`checkh(u.u_cam.WIDTH, 32'd8);
// cfg.jt.depth = W + 1 = 9 (nested, lsb 8)
`checkh(u.u_depth.WIDTH, 32'd9);
// q = cfg.tag + 100 = 10 + 100 = 110
`checkh(u.u_q.WIDTH, 32'd110);
$write("*-* All Finished *-*\n");
$finish;
end

View File

@ -0,0 +1,14 @@
%Error: t/t_class_lparam_struct_field_chain_bad.v:37:25: Malformed dotted select in parameter value
: ... note: In instance 't.u'
37 | Sub #(int'(CFG::cfg.jt.cam_type[0])) u_fieldref ();
| ^
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: t/t_class_lparam_struct_field_chain_bad.v:39:26: Dotted member select on non-struct parameter value
: ... note: In instance 't.u'
39 | Sub #(int'(CFG::cfg.tag.bogus)) u_nonstruct ();
| ^
%Error: t/t_class_lparam_struct_field_chain_bad.v:41:25: Member 'nosuchfield' not found in structure
: ... note: In instance 't.u'
41 | Sub #(int'(CFG::cfg.jt.nosuchfield)) u_nomember ();
| ^
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/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('linter')
test.lint(fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,46 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// Negative test for the struct-localparam field-access folding in V3Param
// (resolveDotToTypedef). Malformed outer `.field` chains on a class
// localparam value must produce clean user-facing errors, NOT an internal
// error or a crash in the following width pass. Exercises the three
// mismatch guards in the field-chain walk:
// (1) outer Dot rhs is not a plain field identifier (bit-select)
// (2) `.field` applied to a non-struct value
// (3) field name not a member of the struct
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
typedef struct packed {
logic [7:0] cam_type;
logic [7:0] depth;
} inner_t;
typedef struct packed {
inner_t jt;
logic [7:0] tag;
} cfg_t;
class C #(parameter int W = 1);
localparam cfg_t cfg = '{jt: '{cam_type: W[7:0], depth: W[7:0] + 8'd1},
tag: W[7:0] + 8'd2};
endclass
module Sub #(parameter int WIDTH = 0) ();
endmodule
module Mid #(parameter int W = 8) ();
typedef C#(W) CFG;
// (1) bit-select after the field chain: outer Dot rhs is not a ParseRef
Sub #(int'(CFG::cfg.jt.cam_type[0])) u_fieldref ();
// (2) `.bogus` on the scalar field `tag`: dotting into a non-struct
Sub #(int'(CFG::cfg.tag.bogus)) u_nonstruct ();
// (3) `.nosuchfield` not a member of struct `jt`
Sub #(int'(CFG::cfg.jt.nosuchfield)) u_nomember ();
endmodule
module t;
Mid #(.W(8)) u ();
endmodule