From 2baca68f86d42457403c7ec1c1940a4e81598274 Mon Sep 17 00:00:00 2001 From: Tom Jackson Date: Wed, 24 Jun 2026 00:43:31 +0000 Subject: [PATCH] Fix class/var named identically to an enclosing-scope type (#7827) (#7828) Fixes #7827. --- docs/CONTRIBUTORS | 1 + src/V3LinkDot.cpp | 10 +++- test_regress/t/t_class_member_shadow_type.py | 18 +++++++ test_regress/t/t_class_member_shadow_type.v | 53 ++++++++++++++++++++ 4 files changed, 80 insertions(+), 2 deletions(-) create mode 100755 test_regress/t/t_class_member_shadow_type.py create mode 100644 test_regress/t/t_class_member_shadow_type.v diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index a22db1f07..44f7ecb2c 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -279,6 +279,7 @@ Tobias Jensen Tobias Rosenkranz Tobias Wölfel Todd Strader +Tom Jackson Tom Manner Tomasz Gorochowik Topa Topino diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index a6b038396..7dd5fe3ea 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -3562,12 +3562,18 @@ class LinkDotResolveVisitor final : public VNVisitor { } static VSymEnt* findIdFallbackSkipMemberDType(VSymEnt* lookp, const string& name) { + VSymEnt* shadowEntp = nullptr; // Shadowing variable: not a type, kept for error report while (lookp) { VSymEnt* const foundp = lookp->findIdFlat(name); - if (foundp && !VN_IS(foundp->nodep(), MemberDType)) return foundp; + if (foundp && !VN_IS(foundp->nodep(), MemberDType)) { + // A variable is not a type candidate (IEEE 1800-2023 6.18); skip it so an + // enclosing type is found, but keep it to preserve the "found: VAR" error. + if (!VN_IS(foundp->nodep(), Var)) return foundp; + if (!shadowEntp) shadowEntp = foundp; + } lookp = lookp->fallbackp(); } - return nullptr; + return shadowEntp; } void symIterateChildren(AstNode* nodep, VSymEnt* symp) { diff --git a/test_regress/t/t_class_member_shadow_type.py b/test_regress/t/t_class_member_shadow_type.py new file mode 100755 index 000000000..8a938befd --- /dev/null +++ b/test_regress/t/t_class_member_shadow_type.py @@ -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() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_class_member_shadow_type.v b/test_regress/t/t_class_member_shadow_type.v new file mode 100644 index 000000000..e0d11a0c7 --- /dev/null +++ b/test_regress/t/t_class_member_shadow_type.v @@ -0,0 +1,53 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// A data member/variable named identically to a type from an enclosing scope +// must resolve its type specifier against the enclosing-scope type, not against +// the just-declared variable (IEEE 1800-2023 6.18). This is the UVM RAL / +// peakrdl-regblock pattern: 'rand ;'. +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Tom Jackson +// 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=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +// verilog_format: on + +typedef logic [7:0] my_t; // type at $unit scope + +class C; + my_t my_t; // member named the same as its (outer-scope) type +endclass + +class D; + my_t a; // second use of the type name after the shadowing variable... + my_t my_t; // ... is also legal; both resolve to the $unit typedef +endclass + +// The exact RAL shape: class member named after its class type +class my_blk; + int x; +endclass + +class parent; + rand my_blk my_blk; +endclass + +module t; + my_t my_t; // also legal at module scope + + initial begin + static C c = new; + static parent p = new; + c.my_t = 8'hAB; + p.my_blk = new; + p.my_blk.x = 5; + my_t = 8'h12; + `checkh(c.my_t, 8'hAB); + `checkh(p.my_blk.x, 5); + `checkh(my_t, 8'h12); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule