From 25cb31c38bb1cde86f8ab378a6bebd42abf8e103 Mon Sep 17 00:00:00 2001 From: Shou-Li Hsu Date: Tue, 20 May 2025 06:10:22 -0700 Subject: [PATCH] Add aggregate type error checks (#5570) (#5950) --- docs/CONTRIBUTORS | 1 + src/V3AstNodeDType.h | 5 + src/V3Slice.cpp | 95 ++++++++--------- src/V3Width.cpp | 108 +++++++++++++++++++- test_regress/t/t_fuzz_eqne_bad.out | 7 +- test_regress/t/t_lint_dtype_compare.py | 18 ++++ test_regress/t/t_lint_dtype_compare.v | 78 ++++++++++++++ test_regress/t/t_lint_dtype_compare_bad.out | 68 ++++++++++++ test_regress/t/t_lint_dtype_compare_bad.py | 16 +++ test_regress/t/t_lint_dtype_compare_bad.v | 84 +++++++++++++++ test_regress/t/t_queue_compare.v | 2 +- test_regress/t/t_stream_unpack_lhs.out | 18 +++- 12 files changed, 440 insertions(+), 60 deletions(-) create mode 100755 test_regress/t/t_lint_dtype_compare.py create mode 100644 test_regress/t/t_lint_dtype_compare.v create mode 100644 test_regress/t/t_lint_dtype_compare_bad.out create mode 100755 test_regress/t/t_lint_dtype_compare_bad.py create mode 100644 test_regress/t/t_lint_dtype_compare_bad.v diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 0a16f5c7a..1ea98ae05 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -205,6 +205,7 @@ Sean Cross Sebastien Van Cauwenberghe Sergi Granell Seth Pellegrino +Shou-Li Hsu Srinivasan Venkataramanan Stefan Wallentowitz Stephen Henry diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index 3b4aa1f84..8542bfa1f 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -124,6 +124,7 @@ public: // Iff has a non-null subDTypep(), as generic node function virtual AstNodeDType* subDTypep() const VL_MT_STABLE { return nullptr; } virtual AstNodeDType* subDType2p() const VL_MT_STABLE { return nullptr; } + virtual bool isAggregateType() const { return false; } virtual bool isFourstate() const; // Ideally an IEEE $typename virtual string prettyDTypeName(bool) const { return prettyTypeName(); } @@ -366,6 +367,7 @@ public: AstNodeDType* subDType2p() const override VL_MT_STABLE { return m_keyDTypep ? m_keyDTypep : keyChildDTypep(); } + bool isAggregateType() const override { return true; } void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } AstNodeDType* virtRefDTypep() const override { return m_refDTypep; } void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); } @@ -742,6 +744,7 @@ public: AstNodeDType* subDTypep() const override VL_MT_STABLE { return m_refDTypep ? m_refDTypep : childDTypep(); } + bool isAggregateType() const override { return true; } void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } AstNodeDType* virtRefDTypep() const override { return m_refDTypep; } void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); } @@ -1108,6 +1111,7 @@ public: AstNodeDType* subDTypep() const override VL_MT_STABLE { return m_refDTypep ? m_refDTypep : childDTypep(); } + bool isAggregateType() const override { return true; } void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } inline int boundConst() const VL_MT_STABLE; AstNodeDType* virtRefDTypep() const override { return m_refDTypep; } @@ -1404,6 +1408,7 @@ public: const AstUnpackArrayDType* const sp = VN_DBG_AS(samep, UnpackArrayDType); return m_isCompound == sp->m_isCompound; } + bool isAggregateType() const override { return true; } // Outer dimension comes first. The first element is this node. std::vector unpackDimensions(); void isCompound(bool flag) { m_isCompound = flag; } diff --git a/src/V3Slice.cpp b/src/V3Slice.cpp index 27f123791..5744f5ed8 100644 --- a/src/V3Slice.cpp +++ b/src/V3Slice.cpp @@ -319,69 +319,58 @@ class SliceVisitor final : public VNVisitor { void expandBiOp(AstNodeBiop* nodep) { if (nodep->user1SetOnce()) return; // Process once - // If it's an unpacked array, blow it up into comparing each element - AstNodeDType* const fromDtp = nodep->lhsp()->dtypep()->skipRefp(); UINFO(9, " Bi-Eq/Neq expansion " << nodep << endl); + + // Only expand if lhs is an unpacked array (we assume type checks already passed) + const AstNodeDType* const fromDtp = nodep->lhsp()->dtypep()->skipRefp(); if (const AstUnpackArrayDType* const adtypep = VN_CAST(fromDtp, UnpackArrayDType)) { AstNodeBiop* logp = nullptr; - if (!VN_IS(nodep->lhsp()->dtypep()->skipRefp(), NodeArrayDType)) { - nodep->lhsp()->v3error( - "Slice operator " - << nodep->lhsp()->prettyTypeName() - << " on non-slicable (e.g. non-vector) left-hand-side operand"); - } else if (!VN_IS(nodep->rhsp()->dtypep()->skipRefp(), NodeArrayDType)) { - nodep->rhsp()->v3error( - "Slice operator " - << nodep->rhsp()->prettyTypeName() - << " on non-slicable (e.g. non-vector) right-hand-side operand"); - } else { - const int elements = adtypep->rangep()->elementsConst(); - for (int elemIdx = 0; elemIdx < elements; ++elemIdx) { - // EQ(a,b) -> LOGAND(EQ(ARRAYSEL(a,0), ARRAYSEL(b,0)), ...[1]) - // Original node is replaced, so it is safe to copy it one time even if it is - // impure. - AstNodeBiop* const clonep - = VN_AS(nodep->cloneType( - cloneAndSel(nodep->lhsp(), elements, elemIdx, elemIdx != 0), - cloneAndSel(nodep->rhsp(), elements, elemIdx, elemIdx != 0)), - NodeBiop); - if (elemIdx == 0) { - nodep->foreach([this](AstExprStmt* const exprp) { - // Result expression is always evaluated to the same value, so the - // statements can be removed once they were included in the expression - // created for the 1st element. - AstNodeExpr* const resultp = exprp->resultp()->unlinkFrBack(); - exprp->replaceWith(resultp); - VL_DO_DANGLING(pushDeletep(exprp), exprp); - }); - } - if (!logp) { - logp = clonep; - } else { - switch (nodep->type()) { - case VNType::atEq: // FALLTHRU - case VNType::atEqCase: - logp = new AstLogAnd{nodep->fileline(), logp, clonep}; - break; - case VNType::atNeq: // FALLTHRU - case VNType::atNeqCase: - logp = new AstLogOr{nodep->fileline(), logp, clonep}; - break; - default: - nodep->v3fatalSrc("Unknown node type processing array slice"); - break; - } + const int elements = adtypep->rangep()->elementsConst(); + for (int elemIdx = 0; elemIdx < elements; ++elemIdx) { + // EQ(a,b) -> LOGAND(EQ(ARRAYSEL(a,0), ARRAYSEL(b,0)), ...[1]) + // Original node is replaced, so it is safe to copy it one time even if it is + // impure. + AstNodeBiop* const clonep = VN_AS( + nodep->cloneType(cloneAndSel(nodep->lhsp(), elements, elemIdx, elemIdx != 0), + cloneAndSel(nodep->rhsp(), elements, elemIdx, elemIdx != 0)), + NodeBiop); + if (elemIdx == 0) { + nodep->foreach([this](AstExprStmt* const exprp) { + // Result expression is always evaluated to the same value, so the + // statements can be removed once they were included in the expression + // created for the 1st element. + AstNodeExpr* const resultp = exprp->resultp()->unlinkFrBack(); + exprp->replaceWith(resultp); + VL_DO_DANGLING(pushDeletep(exprp), exprp); + }); + } + + if (!logp) { + logp = clonep; + } else { + switch (nodep->type()) { + case VNType::atEq: // FALLTHRU + case VNType::atEqCase: + logp = new AstLogAnd{nodep->fileline(), logp, clonep}; + break; + case VNType::atNeq: // FALLTHRU + case VNType::atNeqCase: + logp = new AstLogOr{nodep->fileline(), logp, clonep}; + break; + default: nodep->v3fatalSrc("Unknown node type processing array slice"); break; } } - UASSERT_OBJ(logp, nodep, "Unpacked array with empty indices range"); - nodep->replaceWith(logp); - VL_DO_DANGLING(pushDeletep(nodep), nodep); - nodep = logp; } + UASSERT_OBJ(logp, nodep, "Unpacked array with empty indices range"); + nodep->replaceWith(logp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + nodep = logp; } + iterateChildren(nodep); } + void visit(AstEq* nodep) override { expandBiOp(nodep); } void visit(AstNeq* nodep) override { expandBiOp(nodep); } void visit(AstEqCase* nodep) override { expandBiOp(nodep); } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 2d80c44f1..328f2e0da 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -6551,6 +6551,93 @@ class WidthVisitor final : public VNVisitor { } } + // LRM 6.22.2 Equivalent types + bool isEquivalentDType(const AstNodeDType* lhs, const AstNodeDType* rhs) { + // a) If two types match, they are equivalent. + if (!lhs || !rhs) return false; + lhs = lhs->skipRefp(); + rhs = rhs->skipRefp(); + if (lhs == rhs) return true; + // If both are basic types, check if they are the same type + if (VN_IS(lhs, BasicDType) && VN_IS(rhs, BasicDType)) { + const auto* lb = VN_CAST(lhs, BasicDType); + const auto* rb = VN_CAST(rhs, BasicDType); + if (lb->isString() != rb->isString()) return false; + } + + // d) Unpacked fixed-size array types are equivalent if they have equivalent element types + // and equal size; the actual range bounds may differ. Note that the element type of a + // multidimensional array is itself an array type. + const bool lhsIsUnpackArray = VN_IS(lhs, UnpackArrayDType); + const bool rhsIsUnpackArray = VN_IS(rhs, UnpackArrayDType); + if (lhsIsUnpackArray || rhsIsUnpackArray) { + if (VN_IS(lhs, UnpackArrayDType) && VN_IS(rhs, UnpackArrayDType)) { + const AstUnpackArrayDType* const lhsp = VN_CAST(lhs, UnpackArrayDType); + const AstUnpackArrayDType* const rhsp = VN_CAST(rhs, UnpackArrayDType); + const int lsz = lhsp->elementsConst(); + const int rsz = rhsp->elementsConst(); + if (lsz >= 0 && rsz >= 0 && lsz != rsz) return false; + return isEquivalentDType(lhsp->subDTypep(), rhsp->subDTypep()); + } + return false; + } + + // e) Dynamic array, associative array, and queue types are equivalent if they are the same + // kind of array (dynamic, associative, or queue), have equivalent index types (for + // associative arrays), and have equivalent element types. + const bool lhsIsDynArray = VN_IS(lhs, DynArrayDType); + const bool rhsIsDynArray = VN_IS(rhs, DynArrayDType); + const bool lhsIsQueue = VN_IS(lhs, QueueDType); + const bool rhsIsQueue = VN_IS(rhs, QueueDType); + const bool lhsIsAssocArray = VN_IS(lhs, AssocArrayDType); + const bool rhsIsAssocArray = VN_IS(rhs, AssocArrayDType); + + if (lhsIsDynArray || rhsIsDynArray || lhsIsQueue || rhsIsQueue || lhsIsAssocArray + || rhsIsAssocArray) { + if (const AstDynArrayDType* const lhsp = VN_CAST(lhs, DynArrayDType)) { + if (const AstDynArrayDType* const rhsp = VN_CAST(rhs, DynArrayDType)) { + return isEquivalentDType(lhsp->subDTypep(), rhsp->subDTypep()); + } + } + + if (const AstQueueDType* const lhsp = VN_CAST(lhs, QueueDType)) { + if (const AstQueueDType* const rhsp = VN_CAST(rhs, QueueDType)) { + return isEquivalentDType(lhsp->subDTypep(), rhsp->subDTypep()); + } + } + + if (const AstAssocArrayDType* const lhsp = VN_CAST(lhs, AssocArrayDType)) { + if (const AstAssocArrayDType* const rhsp = VN_CAST(rhs, AssocArrayDType)) { + return isEquivalentDType(lhsp->subDTypep(), rhsp->subDTypep()) + && isEquivalentDType(lhsp->keyDTypep(), rhsp->keyDTypep()); + } + } + + return false; + } + + // c) Packed arrays, packed structures, packed unions, and built-in integral + // types are equivalent if they contain the same number of total bits, are either all + // 2-state or all 4-state, and are either all signed or all unsigned. + if (lhs->isIntegralOrPacked() && rhs->isIntegralOrPacked()) { + if (lhs->width() != rhs->width()) return false; + if (lhs->isFourstate() != rhs->isFourstate()) return false; + if (lhs->isSigned() != rhs->isSigned()) return false; + return true; + } + + return true; + } + + static bool isAggregateType(const AstNode* nodep) { + if (!nodep) return false; + const AstNodeDType* dtypep = nodep->dtypep(); + if (!dtypep) return false; + dtypep = dtypep->skipRefp(); + if (!dtypep) return false; + return dtypep->isAggregateType(); + } + void visit_cmp_eq_gt(AstNodeBiop* nodep, bool realok) { // CALLER: AstEq, AstGt, ..., AstLtS // Real allowed if and only if real_lhs set @@ -6567,7 +6654,26 @@ class WidthVisitor final : public VNVisitor { if (m_vup->prelim()) { userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p()); - if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) { + + const bool isAggrLhs = isAggregateType(nodep->lhsp()); + const bool isAggrRhs = isAggregateType(nodep->rhsp()); + + if ((isAggrLhs || isAggrRhs) && nodep->lhsp() && nodep->rhsp()) { + const AstNodeDType* const lhsDType = nodep->lhsp()->dtypep(); + const AstNodeDType* const rhsDType = nodep->rhsp()->dtypep(); + + if (lhsDType && rhsDType && !isEquivalentDType(lhsDType, rhsDType)) { + nodep->v3error("Comparison requires matching data types\n" + << nodep->warnMore() << "... Left-hand data type: " + << lhsDType->prettyDTypeNameQ() << "\n" + << nodep->warnMore() << "... Right-hand data type: " + << rhsDType->prettyDTypeNameQ()); + AstNode* const newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}}; + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + return; + } + } else if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) { if (!realok) { nodep->v3error("Real is illegal operand to ?== operator"); AstNode* const newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}}; diff --git a/test_regress/t/t_fuzz_eqne_bad.out b/test_regress/t/t_fuzz_eqne_bad.out index 301b13d55..72d8f23be 100644 --- a/test_regress/t/t_fuzz_eqne_bad.out +++ b/test_regress/t/t_fuzz_eqne_bad.out @@ -1,5 +1,8 @@ -%Error: t/t_fuzz_eqne_bad.v:12:23: Slice operator VARREF 't.b' on non-slicable (e.g. non-vector) right-hand-side operand +%Error: t/t_fuzz_eqne_bad.v:12:19: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'logic$[0:-1]' + : ... Right-hand data type: 'logic' 12 | initial c = (a != &b); - | ^ + | ^~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. %Error: Exiting due to diff --git a/test_regress/t/t_lint_dtype_compare.py b/test_regress/t/t_lint_dtype_compare.py new file mode 100755 index 000000000..d4f986441 --- /dev/null +++ b/test_regress/t/t_lint_dtype_compare.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 by Wilson Snyder. 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-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_lint_dtype_compare.v b/test_regress/t/t_lint_dtype_compare.v new file mode 100644 index 000000000..0962af834 --- /dev/null +++ b/test_regress/t/t_lint_dtype_compare.v @@ -0,0 +1,78 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t; + // Typedefs + typedef int myint_t; + typedef int myint2_t; + typedef int myq_t[$]; + typedef int myval_t; + typedef string mykey_t; + + initial begin + // Scalar + int a = 1, b = 1; + + // Unpacked array + int u1[2] = '{1, 2}; + int u2[2] = '{1, 2}; + + int m1[2][2] = '{{1, 2}, {3, 4}}; + int m2[2][2] = '{{1, 2}, {3, 4}}; + + // Dynamic array + int d1[] = new[2]; + int d2[] = new[2]; + + // Queue + int q1[$] = '{10, 20}; + int q2[$] = '{10, 20}; + + // Associative array + int aa1[string]; + int aa2[string]; + + // Typedef array + myint_t t1[2] = '{1, 2}; + myint2_t t2[2] = '{1, 2}; + + // Typedef queue + myq_t tq1 = '{1, 2}; + int tq2[$] = '{1, 2}; + + // Typedef associative array + myval_t aa_typedef1[mykey_t]; + int aa_typedef2[string]; + + // Typedef scalar + bit signed [31:0] b1 = 1; + int i1 = 1; + + d1[0] = 5; d1[1] = 6; + d2[0] = 5; d2[1] = 6; + + aa1["a"] = 1; aa2["a"] = 1; + aa1["b"] = 2; aa2["b"] = 2; + + aa_typedef1["foo"] = 123; + aa_typedef2["foo"] = 123; + + if (a != b) $fatal(0, "Scalar comparison failed"); + if (u1 != u2) $fatal(0, "Unpacked 1D array comparison failed"); + if (m1 != m2) $fatal(0, "Unpacked multi-dimensional array comparison failed"); + if (d1 != d2) $fatal(0, "Dynamic array comparison failed"); + if (q1 != q2) $fatal(0, "Queue comparison failed"); + if (aa1 != aa2) $fatal(0, "Associative array comparison failed"); + if (t1 != t2) $fatal(0, "Typedef unpacked array comparison failed"); + if (tq1 != tq2) $fatal(0, "Typedef queue comparison failed"); + if (aa_typedef1 != aa_typedef2) + $fatal(0, "Typedef associative array comparison failed"); + if (b1 != i1) $fatal(0, "bit[31:0] vs int comparison failed"); + + $display("*-* All Finished *-*"); + $finish; + end +endmodule diff --git a/test_regress/t/t_lint_dtype_compare_bad.out b/test_regress/t/t_lint_dtype_compare_bad.out new file mode 100644 index 000000000..6ce4166dc --- /dev/null +++ b/test_regress/t/t_lint_dtype_compare_bad.out @@ -0,0 +1,68 @@ +%Error: t/t_lint_dtype_compare_bad.v:52:19: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'int$[$]' + : ... Right-hand data type: 'logic[31:0]' + 52 | if (queue_var == 1) begin end + | ^~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_lint_dtype_compare_bad.v:55:11: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'logic[31:0]' + : ... Right-hand data type: 'int$[$]' + 55 | if (1 == queue_var) begin end + | ^~ +%Error: t/t_lint_dtype_compare_bad.v:58:12: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'int$[$]' + : ... Right-hand data type: 'bit$[$]' + 58 | if (q1 == q2) begin end + | ^~ +%Error: t/t_lint_dtype_compare_bad.v:61:12: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'int$[]' + : ... Right-hand data type: 'bit$[]' + 61 | if (d1 == d2) begin end + | ^~ +%Error: t/t_lint_dtype_compare_bad.v:64:12: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'int$[0:1]' + : ... Right-hand data type: 'int$[0:1][0:0]' + 64 | if (u1 == u2) begin end + | ^~ +%Error: t/t_lint_dtype_compare_bad.v:67:12: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'int$[0:1]' + : ... Right-hand data type: 'int$[0:2]' + 67 | if (a1 == a2) begin end + | ^~ +%Error: t/t_lint_dtype_compare_bad.v:70:13: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'int$[string]' + : ... Right-hand data type: 'int$[int]' + 70 | if (aa1 == aa2) begin end + | ^~ +%Error: t/t_lint_dtype_compare_bad.v:73:13: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'int$[string]' + : ... Right-hand data type: 'logic[3:0]$[string]' + 73 | if (aa3 == aa4) begin end + | ^~ +%Error: t/t_lint_dtype_compare_bad.v:76:14: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'int$[0:1]' + : ... Right-hand data type: 'bit$[0:1]' + 76 | if (bad1 == bad2) begin end + | ^~ +%Error: t/t_lint_dtype_compare_bad.v:79:14: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'int$[string]' + : ... Right-hand data type: 'logic[31:0]$[string]' + 79 | if (val1 == val2) begin end + | ^~ +%Error: t/t_lint_dtype_compare_bad.v:82:13: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'int$[string]' + : ... Right-hand data type: 'int$[int]' + 82 | if (aa5 == aa6) begin end + | ^~ +%Error: Exiting due to diff --git a/test_regress/t/t_lint_dtype_compare_bad.py b/test_regress/t/t_lint_dtype_compare_bad.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_lint_dtype_compare_bad.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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() diff --git a/test_regress/t/t_lint_dtype_compare_bad.v b/test_regress/t/t_lint_dtype_compare_bad.v new file mode 100644 index 000000000..3079ba052 --- /dev/null +++ b/test_regress/t/t_lint_dtype_compare_bad.v @@ -0,0 +1,84 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +// DESCRIPTION: Verilator: Invalid aggregate dtype comparisons +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Shou-Li Hsu. +// SPDX-License-Identifier: CC0-1.0 + +module t; + typedef int myint_t; + typedef bit mybit_t; + typedef string mystr_t; + typedef int myval_t; + typedef logic [31:0] mylogic_t; + + initial begin + int queue_var[$] = '{1, 2, 3}; + int q1[$] = '{1, 2}; + bit q2[$] = '{1'b1, 1'b0}; + + int d1[] = new[2]; + bit d2[] = new[2]; + + int u1[2] = '{1, 2}; + int u2[2][1] = '{{1}, {2}}; + + int a1[2] = '{1, 2}; + int a2[3] = '{1, 2, 3}; + + int aa1[string]; + int aa2[int]; + + int aa3[string]; + logic [3:0] aa4[string]; + + myint_t bad1[2] = '{1, 2}; + mybit_t bad2[2] = '{1, 0}; + + myval_t val1[mystr_t] = '{"foo": 123}; + mylogic_t val2[string] = '{"foo": 32'h12345678}; + + myint_t aa5[string]; + myint_t aa6[int]; + aa5["a"] = 1; + aa6[1] = 1; + + // queue vs scalar + if (queue_var == 1) begin end + + // scalar vs queue + if (1 == queue_var) begin end + + // queue with diff type + if (q1 == q2) begin end + + // dyn array with diff type + if (d1 == d2) begin end + + // unpacked diff dim + if (u1 == u2) begin end + + // unpacked diff size + if (a1 == a2) begin end + + // assoc array diff key type + if (aa1 == aa2) begin end + + // assoc array diff value type + if (aa3 == aa4) begin end + + // typedef mismatch in unpacked array + if (bad1 == bad2) begin end + + // typedef mismatch in assoc array value + if (val1 == val2) begin end + + // typedef mismatch in assoc array key + if (aa5 == aa6) begin end + end +endmodule diff --git a/test_regress/t/t_queue_compare.v b/test_regress/t/t_queue_compare.v index 2ae40e991..991a3659a 100644 --- a/test_regress/t/t_queue_compare.v +++ b/test_regress/t/t_queue_compare.v @@ -18,7 +18,7 @@ module t; initial begin begin // integers int q1[$]; - bit[31:0] q2[$]; + bit signed [31:0] q2[$]; q1.push_back(1); q2.push_back(1); q1.push_back(-2); diff --git a/test_regress/t/t_stream_unpack_lhs.out b/test_regress/t/t_stream_unpack_lhs.out index b7c7ae776..08b85723d 100644 --- a/test_regress/t/t_stream_unpack_lhs.out +++ b/test_regress/t/t_stream_unpack_lhs.out @@ -3,8 +3,20 @@ 113 | if (unpacked_siz_dout != '{8'h01, 8'h23, 8'h45, 8'h67}) $stop; | ^~ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Internal Error: t/t_stream_unpack_lhs.v:113:35: ../V3Width.cpp:#: Node has no type +%Error-UNSUPPORTED: t/t_stream_unpack_lhs.v:114:38: Unsupported/Illegal: Assignment pattern member not underneath a supported construct: NEQ + : ... note: In instance 't' + 114 | if (unpacked_asc_dout != '{8'h01, 8'h23, 8'h45, 8'h67}) $stop; + | ^~ +%Error-UNSUPPORTED: t/t_stream_unpack_lhs.v:115:38: Unsupported/Illegal: Assignment pattern member not underneath a supported construct: NEQ + : ... note: In instance 't' + 115 | if (unpacked_des_dout != '{8'h76, 8'h54, 8'h32, 8'h10}) $stop; + | ^~ +%Error-UNSUPPORTED: t/t_stream_unpack_lhs.v:117:36: Unsupported/Illegal: Assignment pattern member not underneath a supported construct: NEQ + : ... note: In instance 't' + 117 | if (packed_siz_dout != '{8'h01, 8'h23, 8'h45, 8'h67}) $stop; + | ^~ +%Error: Internal Error: t/t_stream_unpack_lhs.v:117:33: ../V3Width.cpp:#: Node has no type : ... note: In instance 't' - 113 | if (unpacked_siz_dout != '{8'h01, 8'h23, 8'h45, 8'h67}) $stop; - | ^~ + 117 | if (packed_siz_dout != '{8'h01, 8'h23, 8'h45, 8'h67}) $stop; + | ^~ ... This fatal error may be caused by the earlier error(s); resolve those first.