From d658517715847b8bdbebf94bc39dbdb6fa1c9fd0 Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Wed, 25 Feb 2026 14:47:13 -0500 Subject: [PATCH] Change type definition error to show type chain with source context (#7151) --- src/V3AstNodes.cpp | 37 +++++++++++++++++++++- test_regress/t/t_recursive_typedef_bad.out | 30 ++++++++++++++++++ test_regress/t/t_recursive_typedef_bad.py | 16 ++++++++++ test_regress/t/t_recursive_typedef_bad.v | 22 +++++++++++++ test_regress/t/t_type_param_circ_bad.out | 6 +++- 5 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 test_regress/t/t_recursive_typedef_bad.out create mode 100755 test_regress/t/t_recursive_typedef_bad.py create mode 100644 test_regress/t/t_recursive_typedef_bad.v diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 6d98d1ab6..5c14d9b47 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -941,12 +941,21 @@ AstVar* AstVar::scVarRecurse(AstNode* nodep) { const AstNodeDType* AstNodeDType::skipRefIterp(bool skipConst, bool skipEnum, bool assertOn) const VL_MT_STABLE { static constexpr int MAX_TYPEDEF_DEPTH = 1000; + static constexpr int MAX_CHAIN_DISPLAY = 10; const AstNodeDType* nodep = this; + std::unordered_set visited; + std::vector chain; + bool isCycle = false; for (int depth = 0; depth < MAX_TYPEDEF_DEPTH; ++depth) { if (VN_IS(nodep, MemberDType) || VN_IS(nodep, ParamTypeDType) || VN_IS(nodep, RefDType) // || VN_IS(nodep, RequireDType) // || (VN_IS(nodep, ConstDType) && skipConst) // || (VN_IS(nodep, EnumDType) && skipEnum)) { + if (!visited.emplace(nodep).second) { + isCycle = true; + break; + } + if (chain.size() < static_cast(MAX_CHAIN_DISPLAY)) chain.push_back(nodep); if (const AstNodeDType* subp = nodep->subDTypep()) { nodep = subp; continue; @@ -957,7 +966,33 @@ const AstNodeDType* AstNodeDType::skipRefIterp(bool skipConst, bool skipEnum, } return nodep; } - nodep->v3error("Recursive type definition, or over " << MAX_TYPEDEF_DEPTH << " types deep"); + // Build user-facing error with type chain + V3Error::v3errorPrep(V3ErrorCode::EC_ERROR); + { + std::ostringstream& os = V3Error::v3errorStr(); + if (isCycle) { + os << "Recursive type definition"; + } else { + os << "Type definition over " << MAX_TYPEDEF_DEPTH << " types deep"; + } + bool first = true; + for (const AstNodeDType* chainp : chain) { + // Skip internal scaffolding nodes (e.g. REQUIREDTYPE) with no user-visible name + if (chainp->name().empty()) continue; + os << '\n' + << chainp->fileline()->warnOther() << "... Type chain: " << chainp->prettyTypeName() + << '\n' + << (first ? chainp->fileline()->warnContextPrimary() + : chainp->fileline()->warnContextSecondary()); + first = false; + } + if (visited.size() > static_cast(MAX_CHAIN_DISPLAY)) { + os << '\n' + << this->fileline()->warnMore() << "... and " + << (visited.size() - MAX_CHAIN_DISPLAY) << " more"; + } + } + this->v3errorEnd(V3Error::v3errorStr()); return nullptr; } diff --git a/test_regress/t/t_recursive_typedef_bad.out b/test_regress/t/t_recursive_typedef_bad.out new file mode 100644 index 000000000..f265a7f4e --- /dev/null +++ b/test_regress/t/t_recursive_typedef_bad.out @@ -0,0 +1,30 @@ +%Error: t/t_recursive_typedef_bad.v:11:30: Recursive type definition + : ... note: In instance 't.u_circ' + t/t_recursive_typedef_bad.v:11:30: ... Type chain: PARAMTYPEDTYPE 'A' + 11 | module circ #(parameter type A = B, parameter type B = A) + | ^ + t/t_recursive_typedef_bad.v:11:34: ... Type chain: REFDTYPE 'B' + 11 | module circ #(parameter type A = B, parameter type B = A) + | ^ + t/t_recursive_typedef_bad.v:11:52: ... Type chain: PARAMTYPEDTYPE 'B' + 11 | module circ #(parameter type A = B, parameter type B = A) + | ^ + t/t_recursive_typedef_bad.v:11:56: ... Type chain: REFDTYPE 'A' + 11 | module circ #(parameter type A = B, parameter type B = A) + | ^ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_recursive_typedef_bad.v:11:52: Recursive type definition + : ... note: In instance 't.u_circ' + t/t_recursive_typedef_bad.v:11:52: ... Type chain: PARAMTYPEDTYPE 'B' + 11 | module circ #(parameter type A = B, parameter type B = A) + | ^ + t/t_recursive_typedef_bad.v:11:56: ... Type chain: REFDTYPE 'A' + 11 | module circ #(parameter type A = B, parameter type B = A) + | ^ + t/t_recursive_typedef_bad.v:11:30: ... Type chain: PARAMTYPEDTYPE 'A' + 11 | module circ #(parameter type A = B, parameter type B = A) + | ^ + t/t_recursive_typedef_bad.v:11:34: ... Type chain: REFDTYPE 'B' + 11 | module circ #(parameter type A = B, parameter type B = A) + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_recursive_typedef_bad.py b/test_regress/t/t_recursive_typedef_bad.py new file mode 100755 index 000000000..3b2f23460 --- /dev/null +++ b/test_regress/t/t_recursive_typedef_bad.py @@ -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: 2025 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('linter') + +test.lint(fails=test.vlt_all, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_recursive_typedef_bad.v b/test_regress/t/t_recursive_typedef_bad.v new file mode 100644 index 000000000..80e4c8ee3 --- /dev/null +++ b/test_regress/t/t_recursive_typedef_bad.v @@ -0,0 +1,22 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2025 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 +// +// Recursive type definition via circular parameter type defaults +// should produce a clear error with type chain display. + +// Circular: A defaults to B, B defaults to A +module circ #(parameter type A = B, parameter type B = A) + (input A ai, output B bo); + assign bo = ai; +endmodule + +module t(); + logic [7:0] x, y; + circ u_circ(.ai(x), .bo(y)); + initial begin + $finish; + end +endmodule diff --git a/test_regress/t/t_type_param_circ_bad.out b/test_regress/t/t_type_param_circ_bad.out index fdc98f7cd..58dc74892 100644 --- a/test_regress/t/t_type_param_circ_bad.out +++ b/test_regress/t/t_type_param_circ_bad.out @@ -1,6 +1,10 @@ -%Error: t/t_type_param_circ_bad.v:14:22: Recursive type definition, or over 1000 types deep +%Error: t/t_type_param_circ_bad.v:14:22: Recursive type definition : ... note: In instance 't' + t/t_type_param_circ_bad.v:14:22: ... Type chain: PARAMTYPEDTYPE 'SZ' 14 | # (parameter type SZ = SZ) | ^~ + t/t_type_param_circ_bad.v:14:27: ... Type chain: REFDTYPE 'SZ' + 14 | # (parameter type SZ = SZ) + | ^~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. %Error: Exiting due to