This commit is contained in:
parent
78ee787bb1
commit
79e1f33173
|
|
@ -1885,6 +1885,8 @@ public:
|
||||||
VlClass() {}
|
VlClass() {}
|
||||||
VlClass(const VlClass& copied) {}
|
VlClass(const VlClass& copied) {}
|
||||||
~VlClass() override = default;
|
~VlClass() override = default;
|
||||||
|
// Polymorphic shallow clone. Overridden in each generated concrete class.
|
||||||
|
virtual VlClass* clone() const { return nullptr; }
|
||||||
// METHODS
|
// METHODS
|
||||||
virtual const char* typeName() const { return "VlClass"; }
|
virtual const char* typeName() const { return "VlClass"; }
|
||||||
virtual std::string to_string() const { return ""; }
|
virtual std::string to_string() const { return ""; }
|
||||||
|
|
@ -2008,6 +2010,15 @@ public:
|
||||||
VlClassRef<T_OtherClass> dynamicCast() const {
|
VlClassRef<T_OtherClass> dynamicCast() const {
|
||||||
return VlClassRef<T_OtherClass>{dynamic_cast<T_OtherClass*>(m_objp)};
|
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
|
// Dereference operators
|
||||||
T_Class& operator*() const { return *m_objp; }
|
T_Class& operator*() const { return *m_objp; }
|
||||||
T_Class* operator->() const { return m_objp; }
|
T_Class* operator->() const { return m_objp; }
|
||||||
|
|
|
||||||
|
|
@ -1536,10 +1536,22 @@ public:
|
||||||
puts(")");
|
puts(")");
|
||||||
}
|
}
|
||||||
void visit(AstNewCopy* nodep) override {
|
void visit(AstNewCopy* nodep) override {
|
||||||
putns(nodep, "VL_NEW(" + EmitCUtil::prefixNameProtect(nodep->dtypep()));
|
// Polymorphic shallow clone: preserves runtime type via virtual clone()
|
||||||
puts(", *"); // i.e. make into a reference
|
// VL_NULL_CHECK enforces null check per IEEE 1800-2017 8.7
|
||||||
iterateAndNextConstNull(nodep->rhsp());
|
putns(nodep, "VL_NULL_CHECK(");
|
||||||
puts(")");
|
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 {
|
void visit(AstSel* nodep) override {
|
||||||
// Note ASSIGN checks for this on a LHS
|
// 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("__Vserialize") + "(VerilatedSerialize& os);\n");
|
||||||
puts("void " + protect("__Vdeserialize") + "(VerilatedDeserialize& 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) {
|
void emitEnums(const AstNodeModule* modp) {
|
||||||
bool first = true;
|
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