From feb1f64be8e4fdae962d488ddcc3895c14c6582b Mon Sep 17 00:00:00 2001 From: Kristof Marien Date: Tue, 29 Sep 2026 13:53:54 -0700 Subject: [PATCH] Fix extern class method rejects matching virtual interface argument type #8525 (#8526) --- src/V3AstNodeDType.h | 9 +- .../t/t_class_extern_vif_mismatch_bad.out | 65 ++++++++++ .../t/t_class_extern_vif_mismatch_bad.py | 16 +++ .../t/t_class_extern_vif_mismatch_bad.v | 52 ++++++++ .../t/t_class_extern_virtual_interface.py | 16 +++ .../t/t_class_extern_virtual_interface.v | 119 ++++++++++++++++++ 6 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 test_regress/t/t_class_extern_vif_mismatch_bad.out create mode 100755 test_regress/t/t_class_extern_vif_mismatch_bad.py create mode 100644 test_regress/t/t_class_extern_vif_mismatch_bad.v create mode 100755 test_regress/t/t_class_extern_virtual_interface.py create mode 100644 test_regress/t/t_class_extern_virtual_interface.v diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index 9cbe850ea..364da3379 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -1010,7 +1010,14 @@ public: void dumpJson(std::ostream& str = std::cout) const override; void dumpSmall(std::ostream& str) const override; AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; } - bool similarDTypeNode(const AstNodeDType* samep) const override { return this == samep; } + bool similarDTypeNode(const AstNodeDType* samep) const override { + // Each occurrence of a virtual interface type parses to its own node, + // so pointer identity is insufficient; compare the referenced + // interface instead, same equivalence as AstNode::computeCastable uses. + const AstIfaceRefDType* const asamep = VN_DBG_AS(samep, IfaceRefDType); + return ifaceViaCellp() && ifaceViaCellp() == asamep->ifaceViaCellp() + && modportName() == asamep->modportName() && isVirtual() == asamep->isVirtual(); + } int widthAlignBytes() const override { return 0; } int widthTotalBytes() const override { return 0; } bool isVirtual() const { return m_virtual; } diff --git a/test_regress/t/t_class_extern_vif_mismatch_bad.out b/test_regress/t/t_class_extern_vif_mismatch_bad.out new file mode 100644 index 000000000..48cbb4196 --- /dev/null +++ b/test_regress/t/t_class_extern_vif_mismatch_bad.out @@ -0,0 +1,65 @@ +%Error-PROTOTYPEMIS: t/t_class_extern_vif_mismatch_bad.v:21:50: In prototype for 'drive_modport_bad', argument 'bus' data-type does not match out-of-block declaration's data-type (IEEE 1800-2023 8.24) + : ... note: In instance 't' + : ... Prototype data type: 'IFACEREFDTYPE' + : ... Declaration data type: 'IFACEREFDTYPE' + 21 | extern function void drive_modport_bad(virtual Bus.Source bus); + | ^~~ + t/t_class_extern_vif_mismatch_bad.v:30:49: ... Location of out-of-block declaration + 30 | function void Driver::drive_modport_bad(virtual Bus.Sink bus); + | ^~~ + ... For error description see https://verilator.org/warn/PROTOTYPEMIS?v=latest +%Error-PROTOTYPEMIS: t/t_class_extern_vif_mismatch_bad.v:22:48: In prototype for 'drive_iface_bad', argument 'bus' data-type does not match out-of-block declaration's data-type (IEEE 1800-2023 8.24) + : ... note: In instance 't' + : ... Prototype data type: 'IFACEREFDTYPE' + : ... Declaration data type: 'IFACEREFDTYPE' + 22 | extern function void drive_iface_bad(virtual Bus bus); + | ^~~ + t/t_class_extern_vif_mismatch_bad.v:33:47: ... Location of out-of-block declaration + 33 | function void Driver::drive_iface_bad(virtual OtherBus bus); + | ^~~~~~~~ +%Error-PROTOTYPEMIS: t/t_class_extern_vif_mismatch_bad.v:23:48: In prototype for 'drive_param_bad', argument 'bus' data-type does not match out-of-block declaration's data-type (IEEE 1800-2023 8.24) + : ... note: In instance 't' + : ... Prototype data type: 'IFACEREFDTYPE' + : ... Declaration data type: 'IFACEREFDTYPE' + 23 | extern function void drive_param_bad(virtual Bus #(7) bus); + | ^~~ + t/t_class_extern_vif_mismatch_bad.v:36:47: ... Location of out-of-block declaration + 36 | function void Driver::drive_param_bad(virtual Bus #(8) bus); + | ^~~ +%Error-PROTOTYPEMIS: t/t_class_extern_vif_mismatch_bad.v:24:64: In prototype for 'drive_queue_bad', argument 'buses' data-type does not match out-of-block declaration's data-type (IEEE 1800-2023 8.24) + : ... note: In instance 't' + : ... Prototype data type: 'IFACEREFDTYPE$[$]' + : ... Declaration data type: 'IFACEREFDTYPE$[$]' + 24 | extern function void drive_queue_bad(virtual Bus.Source buses[$]); + | ^ + t/t_class_extern_vif_mismatch_bad.v:39:61: ... Location of out-of-block declaration + 39 | function void Driver::drive_queue_bad(virtual Bus.Sink buses[$]); + | ^ +%Error-PROTOTYPEMIS: t/t_class_extern_vif_mismatch_bad.v:25:55: In prototype for 'drive_dyn_bad', argument 'buses' data-type does not match out-of-block declaration's data-type (IEEE 1800-2023 8.24) + : ... note: In instance 't' + : ... Prototype data type: 'IFACEREFDTYPE$[]' + : ... Declaration data type: 'IFACEREFDTYPE$[]' + 25 | extern function void drive_dyn_bad(virtual Bus buses[]); + | ^ + t/t_class_extern_vif_mismatch_bad.v:42:59: ... Location of out-of-block declaration + 42 | function void Driver::drive_dyn_bad(virtual OtherBus buses[]); + | ^ +%Error-PROTOTYPEMIS: t/t_class_extern_vif_mismatch_bad.v:26:69: In prototype for 'drive_assoc_bad', argument 'buses' data-type does not match out-of-block declaration's data-type (IEEE 1800-2023 8.24) + : ... note: In instance 't' + : ... Prototype data type: 'IFACEREFDTYPE$[int]' + : ... Declaration data type: 'IFACEREFDTYPE$[int]' + 26 | extern function void drive_assoc_bad(virtual Bus #(7).Source buses[int]); + | ^ + t/t_class_extern_vif_mismatch_bad.v:45:68: ... Location of out-of-block declaration + 45 | function void Driver::drive_assoc_bad(virtual Bus #(8).Source buses[int]); + | ^ +%Error-PROTOTYPEMIS: t/t_class_extern_vif_mismatch_bad.v:27:27: In prototype for 'get_bus_bad', return data type does not match out-of-block declaration data-type (IEEE 1800-2023 8.24) + : ... note: In instance 't' + : ... Prototype data type: 'IFACEREFDTYPE' + : ... Declaration data type: 'IFACEREFDTYPE' + 27 | extern function virtual Bus.Source get_bus_bad(); + | ^~~ + t/t_class_extern_vif_mismatch_bad.v:48:18: ... Location of out-of-block declaration + 48 | function virtual Bus.Sink Driver::get_bus_bad(); + | ^~~ +%Error: Exiting due to diff --git a/test_regress/t/t_class_extern_vif_mismatch_bad.py b/test_regress/t/t_class_extern_vif_mismatch_bad.py new file mode 100755 index 000000000..38cf36b43 --- /dev/null +++ b/test_regress/t/t_class_extern_vif_mismatch_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: 2026 Wilson Snyder +# 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_class_extern_vif_mismatch_bad.v b/test_regress/t/t_class_extern_vif_mismatch_bad.v new file mode 100644 index 000000000..73ec319df --- /dev/null +++ b/test_regress/t/t_class_extern_vif_mismatch_bad.v @@ -0,0 +1,52 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +interface Bus #( + parameter int WIDTH = 1 +); + logic [WIDTH-1:0] value; + + modport Source(output value); + modport Sink(input value); +endinterface + +interface OtherBus; + logic value; +endinterface + +class Driver; + extern function void drive_modport_bad(virtual Bus.Source bus); // <--- Error (modport) + extern function void drive_iface_bad(virtual Bus bus); // <--- Error (interface) + extern function void drive_param_bad(virtual Bus #(7) bus); // <--- Error (parameter) + extern function void drive_queue_bad(virtual Bus.Source buses[$]); // <--- Error (modport) + extern function void drive_dyn_bad(virtual Bus buses[]); // <--- Error (interface) + extern function void drive_assoc_bad(virtual Bus #(7).Source buses[int]); // <--- Error (parameter) + extern function virtual Bus.Source get_bus_bad(); // <--- Error (return modport) +endclass + +function void Driver::drive_modport_bad(virtual Bus.Sink bus); +endfunction + +function void Driver::drive_iface_bad(virtual OtherBus bus); +endfunction + +function void Driver::drive_param_bad(virtual Bus #(8) bus); +endfunction + +function void Driver::drive_queue_bad(virtual Bus.Sink buses[$]); +endfunction + +function void Driver::drive_dyn_bad(virtual OtherBus buses[]); +endfunction + +function void Driver::drive_assoc_bad(virtual Bus #(8).Source buses[int]); +endfunction + +function virtual Bus.Sink Driver::get_bus_bad(); +endfunction + +module t; +endmodule diff --git a/test_regress/t/t_class_extern_virtual_interface.py b/test_regress/t/t_class_extern_virtual_interface.py new file mode 100755 index 000000000..4aff8b3e0 --- /dev/null +++ b/test_regress/t/t_class_extern_virtual_interface.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: 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() diff --git a/test_regress/t/t_class_extern_virtual_interface.v b/test_regress/t/t_class_extern_virtual_interface.v new file mode 100644 index 000000000..f5a6c2e8c --- /dev/null +++ b/test_regress/t/t_class_extern_virtual_interface.v @@ -0,0 +1,119 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// verilog_format: off +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + +interface Bus #( + parameter int WIDTH = 1 +); + logic [WIDTH-1:0] value; + + modport Source(output value); +endinterface + +typedef virtual Bus #(7).Source SourceBus; + +class Driver; + extern function void drive_external(virtual Bus bus, logic value); + extern function void drive_parameter(virtual Bus #(7) bus, logic [6:0] value); + extern function void drive_modport(virtual Bus #(7).Source bus, logic [6:0] value); + extern function void drive_array(virtual Bus #(7).Source buses[2], logic [6:0] value); + extern function void drive_queue(virtual Bus #(7).Source buses[$], logic [6:0] value); + extern function void drive_dyn(virtual Bus #(7).Source buses[], logic [6:0] value); + extern function void drive_assoc(virtual Bus #(7).Source buses[int], logic [6:0] value); + extern function SourceBus get_bus(SourceBus bus); + + function void drive_inline(virtual Bus bus, logic value); + bus.value = value; + endfunction +endclass + +function void Driver::drive_external(virtual Bus bus, logic value); + bus.value = value; +endfunction + +function void Driver::drive_parameter(virtual Bus #(7) bus, logic [6:0] value); + bus.value = value; +endfunction + +function void Driver::drive_modport(virtual Bus #(7).Source bus, logic [6:0] value); + bus.value = value; +endfunction + +function void Driver::drive_array(virtual Bus #(7).Source buses[2], logic [6:0] value); + buses[1].value = value; +endfunction + +function void Driver::drive_queue(virtual Bus #(7).Source buses[$], logic [6:0] value); + buses[1].value = value; +endfunction + +function void Driver::drive_dyn(virtual Bus #(7).Source buses[], logic [6:0] value); + buses[1].value = value; +endfunction + +function void Driver::drive_assoc(virtual Bus #(7).Source buses[int], logic [6:0] value); + buses[7].value = value; +endfunction + +function SourceBus Driver::get_bus(SourceBus bus); + return bus; +endfunction + +module t; + Bus bus(); + Bus #(7) parameter_bus(); + Bus #(7) bus_array[2](); + Driver driver = new; + + initial begin + automatic SourceBus returned_bus; + automatic virtual Bus #(7).Source queue_buses[$]; + automatic virtual Bus #(7).Source dyn_buses[]; + automatic virtual Bus #(7).Source assoc_buses[int]; + + bus.value = 1'b0; + driver.drive_inline(bus, 1'b1); + `checkd(bus.value, 1) + + driver.drive_external(bus, 1'b0); + `checkd(bus.value, 0) + + driver.drive_parameter(parameter_bus, 7'd31); + `checkd(parameter_bus.value, 31) + + driver.drive_modport(parameter_bus.Source, 7'd47); + `checkd(parameter_bus.value, 47) + + driver.drive_array(bus_array, 7'd63); + `checkd(bus_array[1].value, 63) + + queue_buses.push_back(parameter_bus.Source); + queue_buses.push_back(bus_array[0].Source); + driver.drive_queue(queue_buses, 7'd15); + `checkd(bus_array[0].value, 15) + + dyn_buses = new[2]; + dyn_buses[0] = parameter_bus.Source; + dyn_buses[1] = bus_array[1].Source; + driver.drive_dyn(dyn_buses, 7'd23); + `checkd(bus_array[1].value, 23) + + assoc_buses[7] = parameter_bus.Source; + driver.drive_assoc(assoc_buses, 7'd39); + `checkd(parameter_bus.value, 39) + + returned_bus = driver.get_bus(parameter_bus.Source); + returned_bus.value = 7'd79; + `checkd(parameter_bus.value, 79) + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule