Fix extern function that returns parametrized class (#4924).

This commit is contained in:
Wilson Snyder 2025-12-06 16:16:06 -05:00
parent 5278f42025
commit 040484cc3f
5 changed files with 102 additions and 13 deletions

View File

@ -41,6 +41,7 @@ Verilator 5.043 devel
* Optimize $past delayed variable reuse (#6689). [Geza Lore, Fractile Ltd.]
* Optimize combinational loops through sign extension (#6724). [Geza Lore]
* Optimize trace initialization code size (#6749). [Geza Lore]
* Fix extern function that returns parametrized class (#4924).
* Fix expression short circuiting (#6483). [Todd Strader]
* Fix expression coverage of system calls (#6592). [Todd Strader]
* Fix `--timing` with `--x-initial-edge` (#6603) (#6631). [Krzysztof Bieganski, Antmicro Ltd.]

View File

@ -261,6 +261,16 @@
VL_DANGLING(var); \
} while (false)
/// As with VL_DO_DANGLING, but two variables dangle.
#define VL_DO_DANGLING2(stmt, var, var2) \
do { \
do { \
stmt; \
} while (false); \
VL_DANGLING(var); \
VL_DANGLING(var2); \
} while (false)
/// Perform an e.g. delete, then set variable to nullptr as a requirement
#define VL_DO_CLEAR(stmt, stmt2) \
do { \

View File

@ -966,18 +966,17 @@ class LinkDotFindVisitor final : public VNVisitor {
void moveExternFuncDecl(AstNodeFTask* nodep, AstClass* classp) {
// Move an 'extern function|task' to inside a class or, from an outer to inner-class
if (!nodep->isExternDef()) {
// Move it to proper spot under the target class
nodep->unlinkFrBack();
classp->addStmtsp(nodep);
nodep->isExternDef(true); // So we check there's a matching extern
nodep->classOrPackagep()->unlinkFrBack()->deleteTree();
// Any "Type::" reference in the function's IO are really "MovedToClass::" references
if (nodep->fvarp()) moveExternFuncDeclRefs(nodep->fvarp(), classp);
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
if (AstVar* const portp = VN_CAST(stmtp, Var)) {
if (portp->isIO()) moveExternFuncDeclRefs(portp, classp);
}
if (nodep->isExternDef()) return;
// Move it to proper spot under the target class
nodep->unlinkFrBack();
classp->addStmtsp(nodep);
nodep->isExternDef(true); // So we check there's a matching extern
nodep->classOrPackagep()->unlinkFrBack()->deleteTree();
// Any "Type::" reference in the function's IO are really "MovedToClass::" references
if (nodep->fvarp()) moveExternFuncDeclRefs(nodep->fvarp(), classp);
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
if (AstVar* const portp = VN_CAST(stmtp, Var)) {
if (portp->isIO()) moveExternFuncDeclRefs(portp, classp);
}
}
}
@ -985,7 +984,14 @@ class LinkDotFindVisitor final : public VNVisitor {
nodep->foreach([this, classp](AstClassOrPackageRef* refp) { //
if (refp->name() == classp->name() && !refp->paramsp()) {
UINFO(9, "Cleaning up external function type for class " << refp);
pushDeletep(refp->unlinkFrBack());
AstDot* const dotp = VN_CAST(refp->backp(), Dot);
if (dotp && dotp->lhsp() == refp) {
// If had DOT(CLASSREF this, ...) the DOT can go away
dotp->replaceWith(dotp->rhsp()->unlinkFrBack());
VL_DO_DANGLING2(pushDeletep(dotp), dotp, refp);
} else {
VL_DO_DANGLING(pushDeletep(refp->unlinkFrBack()), refp);
}
}
});
}

View File

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

View File

@ -0,0 +1,54 @@
// 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
class One #(
type VALUE_T = int unsigned
);
typedef One#(VALUE_T) self_t;
VALUE_T value;
extern static function self_t create(VALUE_T value);
endclass
class Two #(
type VALUE_T = int unsigned
);
VALUE_T value;
extern static function Two#(VALUE_T) create(VALUE_T value);
`ifdef NEVER
// works ok if put function directly here
static function Two#(VALUE_T) create(VALUE_T value);
Two #(VALUE_T) obj = new();
obj.value = value;
return obj;
endfunction
`endif
endclass
function One::self_t One::create(VALUE_T value);
One #(VALUE_T) obj = new();
obj.value = value;
return obj;
endfunction
function Two#(Two::VALUE_T) Two::create(VALUE_T value);
Two #(VALUE_T) obj = new();
obj.value = value;
return obj;
endfunction
module t;
initial begin
One #(string) one;
Two #(string) two;
one = One#(string)::create("one");
two = Two#(string)::create("two");
if (one.value !== "one") $stop;
if (two.value !== "two") $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule