more testing
This commit is contained in:
parent
bb2ea625cf
commit
dd1eb2dfe7
|
|
@ -1311,26 +1311,47 @@ class ParamProcessor final {
|
||||||
AstNodeDType* curDTypep = varp->dtypep();
|
AstNodeDType* curDTypep = varp->dtypep();
|
||||||
AstNode* topp = dotp;
|
AstNode* topp = dotp;
|
||||||
AstDot* outerDotp = VN_CAST(dotp->backp(), Dot);
|
AstDot* outerDotp = VN_CAST(dotp->backp(), Dot);
|
||||||
// On any mismatch (non-ParseRef rhs, non-packed-struct dtype, or
|
const auto errorRecover = [&](AstDot* const badp, const string& msg) {
|
||||||
// unknown field name) leave the outer Dots intact so V3Width can
|
badp->v3error(msg);
|
||||||
// emit the proper user-facing diagnostic.
|
badp->replaceWith(new AstConst{badp->fileline(), AstConst::Signed32{}, 0});
|
||||||
while (outerDotp && outerDotp->lhsp() == topp) {
|
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);
|
const AstParseRef* const fieldRefp = VN_CAST(outerDotp->rhsp(), ParseRef);
|
||||||
if (!fieldRefp) break;
|
if (!fieldRefp) {
|
||||||
AstNodeDType* const skippedp = curDTypep ? curDTypep->skipRefp() : nullptr;
|
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
|
AstNodeUOrStructDType* const structp
|
||||||
= VN_CAST(skippedp, NodeUOrStructDType);
|
= 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(
|
AstMemberDType* const foundMemp = VN_CAST(
|
||||||
m_memberMap.findMember(structp, fieldRefp->name()), MemberDType);
|
m_memberMap.findMember(structp, fieldRefp->name()), MemberDType);
|
||||||
UASSERT_OBJ(foundMemp, outerDotp, "member not found in struct/union");
|
if (!foundMemp) {
|
||||||
if (!foundMemp->subDTypep()) break;
|
errorRecover(outerDotp, "Member " + fieldRefp->prettyNameQ()
|
||||||
|
+ " not found in structure");
|
||||||
|
errored = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
UASSERT_OBJ(foundMemp->subDTypep(), outerDotp,
|
||||||
|
"member has null subDTypep");
|
||||||
totalLsb += foundMemp->lsb();
|
totalLsb += foundMemp->lsb();
|
||||||
sliceWidth = foundMemp->width();
|
sliceWidth = foundMemp->width();
|
||||||
curDTypep = foundMemp->subDTypep();
|
curDTypep = foundMemp->subDTypep();
|
||||||
topp = outerDotp;
|
topp = outerDotp;
|
||||||
outerDotp = VN_CAST(outerDotp->backp(), Dot);
|
outerDotp = VN_CAST(outerDotp->backp(), Dot);
|
||||||
}
|
}
|
||||||
|
if (errored) return;
|
||||||
if (topp == dotp) {
|
if (topp == dotp) {
|
||||||
dotp->replaceWith(constp->cloneTree(false));
|
dotp->replaceWith(constp->cloneTree(false));
|
||||||
VL_DO_DANGLING(dotp->deleteTree(), dotp);
|
VL_DO_DANGLING(dotp->deleteTree(), dotp);
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,12 @@ class C #(parameter int W = 1);
|
||||||
tag: W[7:0] + 8'd2};
|
tag: W[7:0] + 8'd2};
|
||||||
endclass
|
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) ();
|
module Sub #(parameter int WIDTH = 0) ();
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
|
|
@ -47,6 +53,9 @@ module Mid #(parameter int W = 8) ();
|
||||||
// (2) nested struct field access -> two loop iterations, lsb accumulation
|
// (2) nested struct field access -> two loop iterations, lsb accumulation
|
||||||
Sub #(int'(CFG::cfg.jt.cam_type)) u_cam ();
|
Sub #(int'(CFG::cfg.jt.cam_type)) u_cam ();
|
||||||
Sub #(int'(CFG::cfg.jt.depth)) u_depth ();
|
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
|
endmodule
|
||||||
|
|
||||||
module t;
|
module t;
|
||||||
|
|
@ -59,6 +68,8 @@ module t;
|
||||||
`checkh(u.u_cam.WIDTH, 32'd8);
|
`checkh(u.u_cam.WIDTH, 32'd8);
|
||||||
// cfg.jt.depth = W + 1 = 9 (nested, lsb 8)
|
// cfg.jt.depth = W + 1 = 9 (nested, lsb 8)
|
||||||
`checkh(u.u_depth.WIDTH, 32'd9);
|
`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");
|
$write("*-* All Finished *-*\n");
|
||||||
$finish;
|
$finish;
|
||||||
end
|
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