Improve assignment-compatibility type check (#2843) (#5666) (#7052)

This commit is contained in:
Pawel Kojma 2026-02-28 15:55:06 +01:00 committed by GitHub
parent 139bdc1ae3
commit face700f29
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 287 additions and 12 deletions

View File

@ -129,6 +129,7 @@ public:
// Ideally an IEEE $typename
virtual string prettyDTypeName(bool) const { return prettyTypeName(); }
string prettyDTypeNameQ() const { return "'" + prettyDTypeName(false) + "'"; }
string stateDTypeName() const { return this->isFourstate() ? "(4-state)" : "(2-state)"; }
//
// Changing the width may confuse the data type resolution, so must clear
// TypeTable cache after use.

View File

@ -5797,13 +5797,12 @@ class WidthVisitor final : public VNVisitor {
// IEEE 1800-2023 7.6: For unpacked arrays to be assignment compatible,
// the element types shall be equivalent (IEEE 1800-2023 6.22.2).
// Check specifically for 2-state vs 4-state mismatch for unpacked array
// to unpacked array assignments, as this is a common IEEE compliance issue.
// Note: Streaming operators and string literals have implicit conversion rules.
if (nodep->rhsp()->dtypep()) { // May be null on earlier errors
const AstNodeDType* const lhsDtp = lhsDTypep->skipRefp();
const AstNodeDType* const rhsDtp = nodep->rhsp()->dtypep()->skipRefp();
// Only check unpacked array to unpacked array assignments
// Only check if number of states match for unpacked array to unpacked array
// assignments
const bool lhsIsUnpackArray
= VN_IS(lhsDtp, UnpackArrayDType) || VN_IS(lhsDtp, DynArrayDType)
|| VN_IS(lhsDtp, QueueDType) || VN_IS(lhsDtp, AssocArrayDType);
@ -5812,15 +5811,17 @@ class WidthVisitor final : public VNVisitor {
|| VN_IS(rhsDtp, QueueDType) || VN_IS(rhsDtp, AssocArrayDType);
if (lhsIsUnpackArray && rhsIsUnpackArray) {
if (lhsDtp->isFourstate() != rhsDtp->isFourstate()) {
nodep->v3error(
"Assignment between 2-state and 4-state types requires "
"equivalent element types (IEEE 1800-2023 6.22.2, 7.6)\n"
<< nodep->warnMore() << "... LHS type: " << lhsDtp->prettyDTypeNameQ()
<< (lhsDtp->isFourstate() ? " (4-state)" : " (2-state)") << "\n"
<< nodep->warnMore() << "... RHS type: " << rhsDtp->prettyDTypeNameQ()
<< (rhsDtp->isFourstate() ? " (4-state)" : " (2-state)"));
nodep->v3error("Assignment between 2-state and 4-state types requires "
"equivalent element types (IEEE 1800-2023 6.22.2, 7.6)\n"
<< nodep->warnMore()
<< "... Left-hand type: " << lhsDtp->prettyDTypeNameQ()
<< lhsDtp->stateDTypeName() << "\n"
<< nodep->warnMore() << "... Right-hand type: "
<< rhsDtp->prettyDTypeNameQ() << rhsDtp->stateDTypeName());
}
}
checkUnpackedArrayAssignmentCompatible<AstNodeVarRef, AstNodeVarRef>(
nodep, VN_CAST(nodep->lhsp(), NodeVarRef), VN_CAST(nodep->rhsp(), NodeVarRef));
}
iterateCheckAssign(nodep, "Assign RHS", nodep->rhsp(), FINAL, lhsDTypep);
@ -6433,6 +6434,11 @@ class WidthVisitor final : public VNVisitor {
<< (exprArrayp ? "" : " not") << " an array. (IEEE 1800-2023 7.6)");
UINFO(1, " Related lo: " << modDTypep);
UINFO(1, " Related hi: " << conDTypep);
} else {
checkUnpackedArrayAssignmentCompatible<AstVar, AstNodeVarRef>(
nodep, nodep->modVarp(), VN_CAST(nodep->exprp(), NodeVarRef));
UINFO(1, " Related lo: " << modDTypep);
UINFO(1, " Related hi: " << conDTypep);
}
iterateCheckAssign(nodep, "pin connection", nodep->exprp(), FINAL, subDTypep);
}
@ -8055,6 +8061,89 @@ class WidthVisitor final : public VNVisitor {
}
return false;
}
// Checks whether two types are assignment-compatible according to IEEE 1800-2023 7.6
// Currently, this function only supports variables, which are of following types:
// - Fixed-size unpacked array
// - Dynamic unpacked array
// - Associative array
template <typename T, typename N>
void checkUnpackedArrayAssignmentCompatible(const AstNode* nodep, const T* const lhsRefp,
const N* const rhsRefp) {
static_assert(
(std::is_same<T, AstVar>::value || std::is_same<T, AstNodeVarRef>::value)
&& (std::is_same<N, AstVar>::value || std::is_same<N, AstNodeVarRef>::value),
"Unsupported types provided.");
if (!lhsRefp || !rhsRefp) return;
string lhsName, rhsName;
if (VN_IS(nodep, Pin)) {
lhsName = std::string{"Pin"};
rhsName = std::string{"Expression"};
} else {
lhsName = std::string{"Left-hand"};
rhsName = std::string{"Right-hand"};
}
const AstNodeDType* const lhsDtp = lhsRefp->dtypep()->skipRefp();
const AstNodeDType* const rhsDtp = rhsRefp->dtypep()->skipRefp();
const bool isLhsAggregate = lhsDtp->isAggregateType();
const bool isRhsAggregate = rhsDtp->isAggregateType();
if (!isLhsAggregate && !isRhsAggregate) return;
if (isLhsAggregate ^ isRhsAggregate) {
nodep->v3error(
"Illegal assignment: types are not assignment compatible (IEEE 1800-2023 7.6)\n"
<< nodep->warnMore() << "... " << lhsName << " data type: "
<< lhsDtp->prettyDTypeNameQ() << " " << lhsDtp->stateDTypeName() << "\n"
<< nodep->warnMore() << "... " << rhsName << " data type: "
<< rhsDtp->prettyDTypeNameQ() << " " << rhsDtp->stateDTypeName() << "\n");
return;
} else if (VN_IS(lhsDtp, QueueDType) && VN_IS(rhsDtp, EmptyQueueDType)) {
return;
}
std::pair<uint32_t, uint32_t> lhsDim = lhsDtp->dimensions(false),
rhsDim = rhsDtp->dimensions(false);
// Check if unpacked array dimensions are matching
if (lhsDim.second != rhsDim.second) {
nodep->v3error("Illegal assignment: Unmatched number of unpacked dimensions "
<< "(" << lhsDim.second << " vs " << rhsDim.second << ")");
return;
}
const AstNodeDType* lhsDtpIterp = lhsDtp;
const AstNodeDType* rhsDtpIterp = rhsDtp;
// Sizes of fixed-size arrays should be the same
// Dynamic-sized arrays are always assignable
for (uint32_t dim = 0; dim < rhsDim.second; dim++) {
if (const AstNodeArrayDType* rhsArrayp = VN_CAST(rhsDtpIterp, NodeArrayDType)) {
if (const AstNodeArrayDType* lhsArrayp = VN_CAST(lhsDtpIterp, NodeArrayDType)) {
if (lhsArrayp->elementsConst() != rhsArrayp->elementsConst()) {
nodep->v3error("Illegal assignment: Unmatched array sizes in dimension "
<< dim << " " << "(" << lhsArrayp->elementsConst() << " vs "
<< rhsArrayp->elementsConst() << ")");
return;
}
}
}
// Associative arrays are compatible only with each other
if (VN_IS(lhsDtpIterp, AssocArrayDType) ^ VN_IS(rhsDtpIterp, AssocArrayDType)) {
nodep->v3error("Illegal assignment: Associative arrays are assignment compatible "
"only with associative arrays (IEEE 1800-2023 7.6)");
return;
}
lhsDtpIterp = lhsDtpIterp->subDTypep();
rhsDtpIterp = rhsDtpIterp->subDTypep();
}
// Element types of source and target shall be equivalent
if (!isEquivalentDType(lhsDtpIterp, rhsDtpIterp)) {
nodep->v3error("Illegal assignment: Array element types are not equivalent (IEEE "
"1800-2023 6.22.2)\n"
<< nodep->warnMore() << "... " << lhsName << " data type: "
<< lhsDtp->prettyDTypeNameQ() << " " << lhsDtp->stateDTypeName() << "\n"
<< nodep->warnMore() << "... " << rhsName
<< " data type: " << rhsDtp->prettyDTypeNameQ() << " "
<< rhsDtp->stateDTypeName() << "\n");
}
}
void checkClassAssign(const AstNode* nodep, const char* side, AstNode* rhsp,
AstNodeDType* const lhsDTypep) {
UASSERT_OBJ(rhsp->dtypep(), rhsp, "Node has no type");

View File

@ -0,0 +1,64 @@
%Error: t/t_assignment_compatibility_bad.v:25:24: Illegal assignment: Unmatched array sizes in dimension 0 (3 vs 2)
: ... note: In instance 't'
25 | logic unpackedF[3] = unpackedA;
| ^~~~~~~~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: t/t_assignment_compatibility_bad.v:26:31: Assignment between 2-state and 4-state types requires equivalent element types (IEEE 1800-2023 6.22.2, 7.6)
: ... note: In instance 't'
: ... Left-hand type: 'bit$[0:1]'(2-state)
: ... Right-hand type: 'logic$[0:1]'(4-state)
26 | bit unpackedG[2] = unpackedB[0:1];
| ^
%Error: t/t_assignment_compatibility_bad.v:28:20: Illegal assignment: Unmatched array sizes in dimension 0 (3 vs 2)
: ... note: In instance 't'
28 | assign unpackedB = unpackedA;
| ^
%Error: t/t_assignment_compatibility_bad.v:29:20: Illegal assignment: Unmatched number of unpacked dimensions (1 vs 2)
: ... note: In instance 't'
29 | assign unpackedB = unpackedC;
| ^
%Error: t/t_assignment_compatibility_bad.v:30:20: Illegal assignment: Unmatched array sizes in dimension 0 (4 vs 3)
: ... note: In instance 't'
30 | assign unpackedD = unpackedC;
| ^
%Error: t/t_assignment_compatibility_bad.v:31:20: Assignment between 2-state and 4-state types requires equivalent element types (IEEE 1800-2023 6.22.2, 7.6)
: ... note: In instance 't'
: ... Left-hand type: 'struct{}$unit::struct_t$[0:3][0:1]'(2-state)
: ... Right-hand type: 'logic$[0:3][0:1]'(4-state)
31 | assign unpackedE = unpackedD;
| ^
%Error: t/t_assignment_compatibility_bad.v:31:20: Illegal assignment: Array element types are not equivalent (IEEE 1800-2023 6.22.2)
: ... note: In instance 't'
: ... Left-hand data type: 'struct{}$unit::struct_t$[0:3][0:1]' (2-state)
: ... Right-hand data type: 'logic$[0:3][0:1]' (4-state)
31 | assign unpackedE = unpackedD;
| ^
%Warning-WIDTHEXPAND: t/t_assignment_compatibility_bad.v:31:20: Operator ASSIGNW expects 64 bits on the Assign RHS, but Assign RHS's VARREF 'unpackedD' generates 1 bits.
: ... note: In instance 't'
31 | assign unpackedE = unpackedD;
| ^
... For warning description see https://verilator.org/warn/WIDTHEXPAND?v=latest
... Use "/* verilator lint_off WIDTHEXPAND */" and lint_on around source to disable this message.
%Error: t/t_assignment_compatibility_bad.v:32:23: Illegal assignment: types are not assignment compatible (IEEE 1800-2023 7.6)
: ... note: In instance 't'
: ... Left-hand data type: 'logic' (4-state)
: ... Right-hand data type: 'logic$[0:1]' (4-state)
32 | assign nonAggregate = unpackedA;
| ^
%Error: t/t_assignment_compatibility_bad.v:33:20: Assignment between 2-state and 4-state types requires equivalent element types (IEEE 1800-2023 6.22.2, 7.6)
: ... note: In instance 't'
: ... Left-hand type: 'logic$[0:1]'(4-state)
: ... Right-hand type: 'logic$[string]'(2-state)
33 | assign unpackedA = assocArrayA;
| ^
%Error: t/t_assignment_compatibility_bad.v:33:20: Illegal assignment: Associative arrays are assignment compatible only with associative arrays (IEEE 1800-2023 7.6)
: ... note: In instance 't'
33 | assign unpackedA = assocArrayA;
| ^
%Error: t/t_assignment_compatibility_bad.v:34:17: Illegal assignment: Array element types are not equivalent (IEEE 1800-2023 6.22.2)
: ... note: In instance 't'
: ... Left-hand data type: 'logic$[$]' (2-state)
: ... Right-hand data type: 'bit$[$]' (2-state)
34 | assign queueA = queueB;
| ^
%Error: Exiting due to

View File

@ -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('vlt')
test.lint(fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,35 @@
// DESCRIPTION: Verilator: Verilog Test module for SystemVerilog
//
// Assignment compatibility test.
//
// This file ONLY is placed under the Creative Commons Public Domain
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
typedef struct packed {
int a;
int b;
} struct_t;
module t;
logic unpackedA[2];
logic unpackedB[3];
logic unpackedC[3][2];
logic unpackedD[4][2];
struct_t unpackedE[4][2];
logic nonAggregate;
logic assocArrayA[string];
logic queueA[$];
bit queueB[$];
logic unpackedF[3] = unpackedA;
bit unpackedG[2] = unpackedB[0:1];
assign unpackedB = unpackedA;
assign unpackedB = unpackedC;
assign unpackedD = unpackedC;
assign unpackedE = unpackedD;
assign nonAggregate = unpackedA;
assign unpackedA = assocArrayA;
assign queueA = queueB;
endmodule

View File

@ -0,0 +1,12 @@
%Error: t/t_assignment_pin_bad.v:29:19: Illegal input port connection 'b', mismatch between port which is not an array, and expression which is an array. (IEEE 1800-2023 7.6)
: ... note: In instance 't'
29 | test1 i_test1 (.b);
| ^
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: t/t_assignment_pin_bad.v:30:19: Illegal assignment: Array element types are not equivalent (IEEE 1800-2023 6.22.2)
: ... note: In instance 't'
: ... Pin data type: 'bit$[0:1]' (2-state)
: ... Expression data type: 'logic$[0:1]' (4-state)
30 | test2 i_test2 (.b);
| ^
%Error: Exiting due to

View File

@ -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: 2024 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.lint(fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,32 @@
// DESCRIPTION: Verilator: Verilog Test module for SystemVerilog
//
// This file ONLY is placed under the Creative Commons Public Domain
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
module test1 (
input logic b
);
logic do_something;
assign do_something = b;
endmodule
module test2 (
input bit b[2]
);
bit do_something[2];
assign do_something = b;
endmodule
module t (
input logic a[2] // unpacked array
);
logic b[2];
assign b = a;
test1 i_test1 (.b);
test2 i_test2 (.b);
endmodule

View File

@ -1,8 +1,14 @@
%Error: t/t_fourstate_assign_bad.v:23:16: Assignment between 2-state and 4-state types requires equivalent element types (IEEE 1800-2023 6.22.2, 7.6)
: ... note: In instance 't'
: ... LHS type: 'bit[7:0]$[3:0]' (2-state)
: ... RHS type: 'logic[7:0]$[3:0]' (4-state)
: ... Left-hand type: 'bit[7:0]$[3:0]'(2-state)
: ... Right-hand type: 'logic[7:0]$[3:0]'(4-state)
23 | arr_2state = arr_4state;
| ^
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: t/t_fourstate_assign_bad.v:23:16: Illegal assignment: Array element types are not equivalent (IEEE 1800-2023 6.22.2)
: ... note: In instance 't'
: ... Left-hand data type: 'bit[7:0]$[3:0]' (2-state)
: ... Right-hand data type: 'logic[7:0]$[3:0]' (4-state)
23 | arr_2state = arr_4state;
| ^
%Error: Exiting due to

View File

@ -23,4 +23,8 @@
: ... note: In instance 't'
51 | active_command4[7:0] <= command_A4[8:0];
| ^
%Error: t/t_mem_slice_bad.v:56:28: Illegal assignment: Unmatched array sizes in dimension 0 (9 vs 8)
: ... note: In instance 't'
56 | active_command5[8:0] = command_A5[7:0];
| ^
%Error: Exiting due to