This commit is contained in:
parent
78ee787bb1
commit
79e1f33173
|
|
@ -1885,6 +1885,8 @@ public:
|
|||
VlClass() {}
|
||||
VlClass(const VlClass& copied) {}
|
||||
~VlClass() override = default;
|
||||
// Polymorphic shallow clone. Overridden in each generated concrete class.
|
||||
virtual VlClass* clone() const { return nullptr; }
|
||||
// METHODS
|
||||
virtual const char* typeName() const { return "VlClass"; }
|
||||
virtual std::string to_string() const { return ""; }
|
||||
|
|
@ -2008,6 +2010,15 @@ public:
|
|||
VlClassRef<T_OtherClass> dynamicCast() const {
|
||||
return VlClassRef<T_OtherClass>{dynamic_cast<T_OtherClass*>(m_objp)};
|
||||
}
|
||||
// Polymorphic shallow clone (IEEE 1800-2017 8.7: new <handle> preserves runtime type)
|
||||
VlClassRef clone(VlDeleter& deleter) const {
|
||||
VlClass* clonedp = m_objp->clone();
|
||||
if (VL_UNLIKELY(!clonedp)) return {};
|
||||
clonedp->m_deleterp = &deleter;
|
||||
VlClassRef result;
|
||||
result.m_objp = dynamic_cast<T_Class*>(clonedp);
|
||||
return result;
|
||||
}
|
||||
// Dereference operators
|
||||
T_Class& operator*() const { return *m_objp; }
|
||||
T_Class* operator->() const { return m_objp; }
|
||||
|
|
|
|||
|
|
@ -1536,10 +1536,22 @@ public:
|
|||
puts(")");
|
||||
}
|
||||
void visit(AstNewCopy* nodep) override {
|
||||
putns(nodep, "VL_NEW(" + EmitCUtil::prefixNameProtect(nodep->dtypep()));
|
||||
puts(", *"); // i.e. make into a reference
|
||||
iterateAndNextConstNull(nodep->rhsp());
|
||||
puts(")");
|
||||
// Polymorphic shallow clone: preserves runtime type via virtual clone()
|
||||
// VL_NULL_CHECK enforces null check per IEEE 1800-2017 8.7
|
||||
putns(nodep, "VL_NULL_CHECK(");
|
||||
if (VN_IS(nodep->rhsp(), Const) && VN_AS(nodep->rhsp(), Const)->isNull()) {
|
||||
// V3Const folded rhs to null: emit a typed empty ref so VL_NULL_CHECK fires
|
||||
const AstClassRefDType* const refDTypep
|
||||
= VN_CAST(nodep->dtypep()->skipRefp(), ClassRefDType);
|
||||
puts(refDTypep->cType("", false, false) + "{}");
|
||||
} else {
|
||||
iterateAndNextConstNull(nodep->rhsp());
|
||||
}
|
||||
puts(", ");
|
||||
putsQuoted(protect(nodep->fileline()->filename()));
|
||||
puts(", ");
|
||||
puts(cvtToStr(nodep->fileline()->lineno()));
|
||||
puts(").clone(vlSymsp->__Vm_deleter)");
|
||||
}
|
||||
void visit(AstSel* nodep) override {
|
||||
// Note ASSIGN checks for this on a LHS
|
||||
|
|
|
|||
|
|
@ -213,6 +213,15 @@ class EmitCHeader final : public EmitCConstInit {
|
|||
puts("void " + protect("__Vserialize") + "(VerilatedSerialize& os);\n");
|
||||
puts("void " + protect("__Vdeserialize") + "(VerilatedDeserialize& os);\n");
|
||||
}
|
||||
|
||||
// Polymorphic clone for concrete (non-abstract, non-interface) classes
|
||||
if (const AstClass* const classp = VN_CAST(modp, Class)) {
|
||||
if (!classp->isInterfaceClass() && !classp->isVirtual()) {
|
||||
decorateFirst(first, section);
|
||||
putns(classp, "VlClass* clone() const { return new "
|
||||
+ EmitCUtil::prefixNameProtect(classp) + "(*this); }\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
void emitEnums(const AstNodeModule* modp) {
|
||||
bool first = true;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
%Error: t/t_class_new_copy_null_bad.v:16: Null pointer dereferenced
|
||||
Aborting...
|
||||
|
|
@ -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(fails=True, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2025 PlanV GmbH
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
class Base;
|
||||
int value;
|
||||
endclass
|
||||
|
||||
module t;
|
||||
Base b;
|
||||
Base a;
|
||||
initial begin
|
||||
b = null;
|
||||
a = new b; // BAD: null handle dereference (IEEE 8.7)
|
||||
if (a != null) $write("unexpected clone\n");
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -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()
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 PlanV GmbH
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test that `new <handle>` (shallow copy) preserves the runtime type
|
||||
// of the source object, per IEEE 1800-2017 8.7.
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checks(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
`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
|
||||
|
||||
class Base;
|
||||
int value;
|
||||
function new();
|
||||
value = 10;
|
||||
endfunction
|
||||
virtual function string get_type();
|
||||
return "BASE";
|
||||
endfunction
|
||||
virtual function int get_id();
|
||||
return 1;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class Derived extends Base;
|
||||
int extra;
|
||||
function new();
|
||||
super.new();
|
||||
value = 20;
|
||||
extra = 99;
|
||||
endfunction
|
||||
virtual function string get_type();
|
||||
return "DERIVED";
|
||||
endfunction
|
||||
virtual function int get_id();
|
||||
return 2;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class GrandChild extends Derived;
|
||||
function new();
|
||||
super.new();
|
||||
value = 30;
|
||||
extra = 88;
|
||||
endfunction
|
||||
virtual function string get_type();
|
||||
return "GRANDCHILD";
|
||||
endfunction
|
||||
virtual function int get_id();
|
||||
return 3;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t;
|
||||
initial begin
|
||||
Base b;
|
||||
Derived d;
|
||||
Base copy;
|
||||
|
||||
// Test 1: Copy via base handle pointing to Derived
|
||||
d = new();
|
||||
b = d;
|
||||
copy = new b;
|
||||
`checks(copy.get_type(), "DERIVED");
|
||||
`checkd(copy.get_id(), 2);
|
||||
`checkd(copy.value, 20);
|
||||
|
||||
// Test 2: Verify it's a true copy (not alias)
|
||||
copy.value = 999;
|
||||
`checkd(d.value, 20);
|
||||
|
||||
// Test 3: Copy via base handle pointing to GrandChild
|
||||
begin
|
||||
GrandChild gc;
|
||||
gc = new();
|
||||
b = gc;
|
||||
copy = new b;
|
||||
`checks(copy.get_type(), "GRANDCHILD");
|
||||
`checkd(copy.get_id(), 3);
|
||||
`checkd(copy.value, 30);
|
||||
end
|
||||
|
||||
// Test 4: Copy of base-type object (no polymorphism, still works)
|
||||
begin
|
||||
Base b2;
|
||||
Base copy2;
|
||||
b2 = new();
|
||||
copy2 = new b2;
|
||||
`checks(copy2.get_type(), "BASE");
|
||||
`checkd(copy2.get_id(), 1);
|
||||
`checkd(copy2.value, 10);
|
||||
end
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Loading…
Reference in New Issue