diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index a31d982fb..310ef6279 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -318,6 +318,7 @@ public: void markConstrainedRand(bool flag) { m_constrainedRand = flag; } bool emitToString() const { return m_emitToString; } void setEmitToString() { m_emitToString = true; } + bool isAggregateType() const override { return !packed(); } }; // === Concrete node types ===================================================== diff --git a/src/V3Width.cpp b/src/V3Width.cpp index cf8466a33..5879863f0 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -8568,12 +8568,12 @@ class WidthVisitor final : public VNVisitor { } } - // LRM 6.22.2 Equivalent types + // IEEE 1800-2023 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(); + lhs = lhs->skipRefToEnump(); + rhs = rhs->skipRefToEnump(); 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)) { @@ -8588,7 +8588,7 @@ class WidthVisitor final : public VNVisitor { 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)) { + if (lhsIsUnpackArray && rhsIsUnpackArray) { const AstUnpackArrayDType* const lhsp = VN_CAST(lhs, UnpackArrayDType); const AstUnpackArrayDType* const rhsp = VN_CAST(rhs, UnpackArrayDType); const int lsz = lhsp->elementsConst(); @@ -8643,7 +8643,13 @@ class WidthVisitor final : public VNVisitor { return true; } - return true; + // b) An anonymous enum, unpacked struct, or unpacked union type is equivalent to itself + // among data objects declared within the same declaration statement and no other data + // types. + + // DTypes compared above, if any remained then not equivalent. + if (VN_IS(lhs, EnumDType) || VN_IS(rhs, EnumDType)) return false; + return !(VN_IS(lhs, NodeUOrStructDType) || VN_IS(rhs, NodeUOrStructDType)); } static bool isAggregateType(const AstNode* nodep) { diff --git a/test_regress/t/t_lint_dtype_compare.v b/test_regress/t/t_lint_dtype_compare.v index ea54c8705..34d6affa5 100644 --- a/test_regress/t/t_lint_dtype_compare.v +++ b/test_regress/t/t_lint_dtype_compare.v @@ -47,30 +47,61 @@ module t; automatic myval_t aa_typedef1[mykey_t]; automatic int aa_typedef2[string]; + // Aggregate types + automatic struct {int val;} s1 = '{0}, s2 = '{0}; + automatic + union { + int val; + int other; + } + un1, un2; + automatic + enum { + ENUM_A, + ENUM_B, + ENUM_C + } + e1 = ENUM_A, e2 = ENUM_A; + + // Packed structs with different definitions are equivalent by packed width + automatic struct packed {bit [3:0] val;} ps1 = 0; + automatic struct packed {bit [3:0] other;} ps2 = 0; + // Typedef scalar automatic bit signed [31:0] b1 = 1; automatic int i1 = 1; - d1[0] = 5; d1[1] = 6; - d2[0] = 5; d2[1] = 6; + d1[0] = 5; + d1[1] = 6; + d2[0] = 5; + d2[1] = 6; - aa1["a"] = 1; aa2["a"] = 1; - aa1["b"] = 2; aa2["b"] = 2; + 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"); + un1.val = 1; + un2.val = 1; + + 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"); + + if (s1 != s2) $fatal(0, "Unpacked struct comparison failed"); + if (un1 != un2) $fatal(0, "Unpacked union comparison failed"); + if (e1 != e2) $fatal(0, "Enum comparison failed"); + if (ps1 != ps2) $fatal(0, "Packed struct comparison failed"); $display("*-* All Finished *-*"); $finish; diff --git a/test_regress/t/t_lint_dtype_compare_bad.out b/test_regress/t/t_lint_dtype_compare_bad.out index a3818027c..6afa60c02 100644 --- a/test_regress/t/t_lint_dtype_compare_bad.out +++ b/test_regress/t/t_lint_dtype_compare_bad.out @@ -1,68 +1,116 @@ -%Error: t/t_lint_dtype_compare_bad.v:52:19: Comparison requires matching data types +%Error: t/t_lint_dtype_compare_bad.v:91:11: Comparison requires matching data types : ... note: In instance 't' - : ... Left-hand data type: 'int$[$]' + : ... Left-hand data type: 'struct $unit::X_t' : ... Right-hand data type: 'bit[31:0]' - 52 | if (queue_var == 1) begin end - | ^~ + 91 | if (x == 2) begin + | ^~ ... 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 +%Error: t/t_lint_dtype_compare_bad.v:93:11: Comparison requires matching data types : ... note: In instance 't' : ... Left-hand data type: 'bit[31:0]' - : ... Right-hand data type: 'int$[$]' - 55 | if (1 == queue_var) begin end + : ... Right-hand data type: 'struct $unit::X_t' + 93 | if (2 == x) begin | ^~ -%Error: t/t_lint_dtype_compare_bad.v:58:12: Comparison requires matching data types +%Error: t/t_lint_dtype_compare_bad.v:97:11: 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 + : ... Left-hand data type: 'union $unit::U_t' + : ... Right-hand data type: 'bit[31:0]' + 97 | if (u == 2) begin + | ^~ +%Error: t/t_lint_dtype_compare_bad.v:99:11: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'bit[31:0]' + : ... Right-hand data type: 'union $unit::U_t' + 99 | if (2 == u) begin + | ^~ +%Error: t/t_lint_dtype_compare_bad.v:103:11: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'enum $unit::E_t' + : ... Right-hand data type: 'struct $unit::X_t' + 103 | if (e == x) begin + | ^~ +%Error: t/t_lint_dtype_compare_bad.v:105:11: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'struct $unit::X_t' + : ... Right-hand data type: 'enum $unit::E_t' + 105 | if (x == e) begin + | ^~ +%Error: t/t_lint_dtype_compare_bad.v:109:16: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'struct t.__typeimpmod1' + : ... Right-hand data type: 'struct t.__typeimpmod2' + 109 | if (bad_s1 == bad_s2) begin + | ^~ +%Error: t/t_lint_dtype_compare_bad.v:113:16: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'union t.__typeimpmod3' + : ... Right-hand data type: 'union t.__typeimpmod4' + 113 | if (bad_u1 == bad_u2) begin + | ^~ +%Error: t/t_lint_dtype_compare_bad.v:121:19: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'int$[$]' + : ... Right-hand data type: 'bit[31:0]' + 121 | if (queue_var == 1) begin + | ^~ +%Error: t/t_lint_dtype_compare_bad.v:125:11: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'bit[31:0]' + : ... Right-hand data type: 'int$[$]' + 125 | if (1 == queue_var) begin + | ^~ +%Error: t/t_lint_dtype_compare_bad.v:129:12: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'int$[$]' + : ... Right-hand data type: 'bit$[$]' + 129 | if (q1 == q2) begin | ^~ -%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:133:12: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'int$[]' + : ... Right-hand data type: 'bit$[]' + 133 | if (d1 == d2) begin | ^~ -%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:137: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]' + 137 | if (u1 == u2) begin | ^~ -%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:141:12: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'int$[0:1]' + : ... Right-hand data type: 'int$[0:2]' + 141 | if (a1 == a2) begin | ^~ -%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:145:13: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'int$[string]' + : ... Right-hand data type: 'int$[int]' + 145 | if (aa1 == aa2) begin | ^~ -%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:149:13: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'int$[string]' + : ... Right-hand data type: 'logic[3:0]$[string]' + 149 | if (aa3 == aa4) begin | ^~ -%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:153:14: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'int$[0:1]' + : ... Right-hand data type: 'bit$[0:1]' + 153 | if (bad1 == bad2) begin | ^~ -%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:157:14: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'int$[string]' + : ... Right-hand data type: 'logic[31:0]$[string]' + 157 | if (val1 == val2) begin | ^~ -%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: t/t_lint_dtype_compare_bad.v:161:13: Comparison requires matching data types + : ... note: In instance 't' + : ... Left-hand data type: 'int$[string]' + : ... Right-hand data type: 'int$[int]' + 161 | if (aa5 == aa6) begin | ^~ %Error: Exiting due to diff --git a/test_regress/t/t_lint_dtype_compare_bad.v b/test_regress/t/t_lint_dtype_compare_bad.v index 13be87d02..e2afd13c5 100644 --- a/test_regress/t/t_lint_dtype_compare_bad.v +++ b/test_regress/t/t_lint_dtype_compare_bad.v @@ -10,11 +10,21 @@ // SPDX-FileCopyrightText: 2025 Shou-Li Hsu // SPDX-License-Identifier: CC0-1.0 +typedef struct {int val;} X_t; +typedef union { + int val; + int other; +} U_t; +typedef enum { + A, + B, + C +} E_t; module t; - typedef int myint_t; - typedef bit mybit_t; + typedef int myint_t; + typedef bit mybit_t; typedef string mystr_t; - typedef int myval_t; + typedef int myval_t; typedef logic [31:0] mylogic_t; initial begin @@ -40,45 +50,115 @@ module t; automatic myint_t bad1[2] = '{1, 2}; automatic mybit_t bad2[2] = '{1, 0}; - automatic myval_t val1[mystr_t] = '{"foo": 123}; + automatic myval_t val1[mystr_t] = '{"foo": 123}; automatic mylogic_t val2[string] = '{"foo": 32'h12345678}; automatic myint_t aa5[string]; automatic myint_t aa6[int]; + automatic X_t x = {2'h2}; + automatic E_t e; + automatic U_t u = {2'h2}; + automatic struct {int val;} bad_s1; + automatic struct {int val;} bad_s2; + automatic + union { + int val; + int other; + } + bad_u1; + automatic + union { + int val; + int other; + } + bad_u2; + automatic + enum { + BAD_ENUM_A, + BAD_ENUM_B + } + bad_e1; + automatic + enum { + BAD_ENUM_C, + BAD_ENUM_D + } + bad_e2; aa5["a"] = 1; - aa6[1] = 1; + aa6[1] = 1; + + // unpacked struct vs scalar + if (x == 2) begin + end + if (2 == x) begin + end + + // unpacked union vs scalar + if (u == 2) begin + end + if (2 == u) begin + end + + // enum vs unpacked struct + if (e == x) begin + end + if (x == e) begin + end + + // Distinct anonymous unpacked structs + if (bad_s1 == bad_s2) begin + end + + // Distinct anonymous unpacked unions + if (bad_u1 == bad_u2) begin + end + + // Distinct anonymous enums + if (bad_e1 == bad_e2) begin + end // queue vs scalar - if (queue_var == 1) begin end + if (queue_var == 1) begin + end // scalar vs queue - if (1 == queue_var) begin end + if (1 == queue_var) begin + end // queue with diff type - if (q1 == q2) begin end + if (q1 == q2) begin + end // dyn array with diff type - if (d1 == d2) begin end + if (d1 == d2) begin + end // unpacked diff dim - if (u1 == u2) begin end + if (u1 == u2) begin + end // unpacked diff size - if (a1 == a2) begin end + if (a1 == a2) begin + end // assoc array diff key type - if (aa1 == aa2) begin end + if (aa1 == aa2) begin + end // assoc array diff value type - if (aa3 == aa4) begin end + if (aa3 == aa4) begin + end // typedef mismatch in unpacked array - if (bad1 == bad2) begin end + if (bad1 == bad2) begin + end // typedef mismatch in assoc array value - if (val1 == val2) begin end + if (val1 == val2) begin + end // typedef mismatch in assoc array key - if (aa5 == aa6) begin end + if (aa5 == aa6) begin + end end endmodule