Support assignment patterns as comparison operands (#7269)

* Support assignment patterns as EQ/NEQ comparison operands

* Apply 'make format'

* update the .out file

* add both-sides case

---------

Co-authored-by: github action <action@example.com>
This commit is contained in:
Yilou Wang 2026-03-17 19:36:54 +01:00 committed by GitHub
parent ef5281ab73
commit 316fb02c60
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 205 additions and 24 deletions

View File

@ -7545,8 +7545,22 @@ class WidthVisitor final : public VNVisitor {
UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
assertAtExpr(nodep);
if (m_vup->prelim()) {
userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
// Assignment patterns cannot self-determine their type.
// Iterate the non-pattern operand first so its dtype is available,
// then propagate that dtype to the pattern operand (analogous to
// how assignments pass LHS dtype to RHS patterns).
const bool lhsIsPat = VN_IS(nodep->lhsp(), Pattern);
const bool rhsIsPat = VN_IS(nodep->rhsp(), Pattern);
if (lhsIsPat && !rhsIsPat) {
userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
userIterateAndNext(nodep->lhsp(), WidthVP{nodep->rhsp()->dtypep(), PRELIM}.p());
} else if (rhsIsPat && !lhsIsPat) {
userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
userIterateAndNext(nodep->rhsp(), WidthVP{nodep->lhsp()->dtypep(), PRELIM}.p());
} else {
userIterateAndNext(nodep->lhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
userIterateAndNext(nodep->rhsp(), WidthVP{CONTEXT_DET, PRELIM}.p());
}
const bool isAggrLhs = isAggregateType(nodep->lhsp());
const bool isAggrRhs = isAggregateType(nodep->rhsp());

View File

@ -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()

View File

@ -0,0 +1,166 @@
// DESCRIPTION: Verilator: Test assignment patterns as comparison operands
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 PlanV GmbH
// SPDX-License-Identifier: CC0-1.0
module t;
// Fixed-size array comparison
int arr4[4];
typedef int arr4_t[4];
// Queue comparison
int q[$];
int sub[$];
// Struct comparison
typedef struct packed {
logic [7:0] a;
logic [7:0] b;
} pair_t;
pair_t p1;
// Nested: array of structs
pair_t parr[2];
// Different element types
logic [31:0] wide_arr[3];
byte byte_arr[2];
initial begin
// -------------------------------------------------------
// 1. Fixed-size array: EQ with assignment pattern
// -------------------------------------------------------
arr4 = '{10, 20, 30, 40};
if (arr4 == '{10, 20, 30, 40}) begin
// expected
end else begin
$display("FAIL: arr4 == pattern");
$stop;
end
// 2. Fixed-size array: NEQ with assignment pattern
if (arr4 != '{10, 20, 30, 99}) begin
// expected
end else begin
$display("FAIL: arr4 != pattern");
$stop;
end
// 3. Pattern on LHS of comparison
if ('{10, 20, 30, 40} == arr4) begin
// expected
end else begin
$display("FAIL: pattern == arr4 (LHS)");
$stop;
end
// 4. Pattern on LHS of NEQ
if ('{10, 20, 30, 99} != arr4) begin
// expected
end else begin
$display("FAIL: pattern != arr4 (LHS NEQ)");
$stop;
end
// -------------------------------------------------------
// 5. Queue: EQ with assignment pattern
// -------------------------------------------------------
q = '{10, 20, 30, 40, 50};
sub = q[1:3];
if (sub == '{20, 30, 40}) begin
// expected
end else begin
$display("FAIL: queue slice == pattern");
$stop;
end
// 6. Queue: NEQ with assignment pattern
if (sub != '{20, 30, 99}) begin
// expected
end else begin
$display("FAIL: queue slice != pattern");
$stop;
end
// -------------------------------------------------------
// 7. Struct: EQ with assignment pattern
// -------------------------------------------------------
p1 = '{a: 8'hAA, b: 8'h55};
if (p1 == '{a: 8'hAA, b: 8'h55}) begin
// expected
end else begin
$display("FAIL: struct == pattern");
$stop;
end
// 8. Struct: NEQ with assignment pattern
if (p1 != '{a: 8'hAA, b: 8'hFF}) begin
// expected
end else begin
$display("FAIL: struct != pattern");
$stop;
end
// -------------------------------------------------------
// 9. Array of structs: EQ with nested pattern
// -------------------------------------------------------
parr[0] = '{a: 8'h01, b: 8'h02};
parr[1] = '{a: 8'h03, b: 8'h04};
if (parr == '{'{a: 8'h01, b: 8'h02}, '{a: 8'h03, b: 8'h04}}) begin
// expected
end else begin
$display("FAIL: array of structs == nested pattern");
$stop;
end
// -------------------------------------------------------
// 10. Different element widths
// -------------------------------------------------------
wide_arr = '{32'hDEAD_BEEF, 32'hCAFE_BABE, 32'h1234_5678};
if (wide_arr == '{32'hDEAD_BEEF, 32'hCAFE_BABE, 32'h1234_5678}) begin
// expected
end else begin
$display("FAIL: wide array == pattern");
$stop;
end
byte_arr = '{8'hAB, 8'hCD};
if (byte_arr == '{8'hAB, 8'hCD}) begin
// expected
end else begin
$display("FAIL: byte array == pattern");
$stop;
end
// -------------------------------------------------------
// 11. Negative case: pattern that should not match
// -------------------------------------------------------
if (arr4 == '{99, 99, 99, 99}) begin
$display("FAIL: arr4 should not match all-99 pattern");
$stop;
end
// -------------------------------------------------------
// 12. Both sides are typed assignment patterns
// -------------------------------------------------------
if (arr4_t'{10, 20, 30, 40} == arr4_t'{10, 20, 30, 40}) begin
// expected
end else begin
$display("FAIL: typed pattern == typed pattern");
$stop;
end
if (arr4_t'{10, 20, 30, 40} != arr4_t'{10, 20, 30, 99}) begin
// expected
end else begin
$display("FAIL: typed pattern != typed pattern");
$stop;
end
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -1,22 +1,5 @@
%Error-UNSUPPORTED: t/t_stream_unpack_lhs.v:112:34: Unsupported/Illegal: Assignment pattern member not underneath a supported construct: NEQ
: ... note: In instance 't'
112 | 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-UNSUPPORTED: t/t_stream_unpack_lhs.v:113:34: Unsupported/Illegal: Assignment pattern member not underneath a supported construct: NEQ
: ... note: In instance 't'
113 | if (unpacked_asc_dout != '{8'h01, 8'h23, 8'h45, 8'h67}) $stop;
| ^~
%Error-UNSUPPORTED: t/t_stream_unpack_lhs.v:114:34: Unsupported/Illegal: Assignment pattern member not underneath a supported construct: NEQ
: ... note: In instance 't'
114 | if (unpacked_des_dout != '{8'h76, 8'h54, 8'h32, 8'h10}) $stop;
| ^~
%Error-UNSUPPORTED: t/t_stream_unpack_lhs.v:116:32: Unsupported/Illegal: Assignment pattern member not underneath a supported construct: NEQ
: ... note: In instance 't'
116 | if (packed_siz_dout != '{8'h01, 8'h23, 8'h45, 8'h67}) $stop;
| ^~
%Error: Internal Error: t/t_stream_unpack_lhs.v:116:32: ../V3Width.cpp:#: Node has no type
: ... note: In instance 't'
116 | 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.
%Error: Internal Error: t/t_stream_unpack_lhs.v:58:85: ../V3Const.cpp:#: sWidth >= dWidth should have caused an error earlier
: ... note: In instance 't'
58 | {>>{concat5_dout4, concat5_dout3, concat5_dout2, concat5_dout1, concat5_dout0}} = concat_din;
| ^
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.