more testing
This commit is contained in:
parent
bb2ea625cf
commit
dd1eb2dfe7
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue