Difficulty: Medium · Puzzle 2 of 6 · Topic: Handle and Copy Puzzles
A sequence item stores a queue of class-handle cells. Why does `dst.cells[0].v` change when only `src` is modified after copy?
class cell extends uvm_object;
`uvm_object_utils(cell)
int v;
function new(string name="cell"); super.new(name); endfunction
endclass
class tr extends uvm_sequence_item;
`uvm_object_utils(tr)
cell cells[$];
function new(string name="tr");
super.new(name);
cells.push_back(cell::type_id::create("c0"));
cells.push_back(cell::type_id::create("c1"));
endfunction
function void do_copy(uvm_object rhs);
tr r;
if (!$cast(r, rhs)) return;
cells = r.cells;
endfunction
endclass
initial begin
tr src = tr::type_id::create("src");
tr dst = tr::type_id::create("dst");
src.cells[0].v = 5;
dst.copy(src);
src.cells[0].v = 99;
$display("src=%0d dst=%0d", src.cells[0].v, dst.cells[0].v);
endQueue assignment copies queue structure, but inspect the element type.
1) `cells` is a queue of class handles, not packed values.
2) `cells = r.cells` duplicates queue indices but each element still points to the same `cell` objects.
3) Mutating `src.cells[0].v` mutates the shared object referenced by both queues.
4) Deep-copy by resizing `cells` and cloning each element in a loop.Answer: Because queue assignment is shallow for class-handle elements; both queues share identical `cell` objects.
Checked by running the code in Verilator 5; the printed output supports the answer above. A hand-written model of the copy flow (not the UVM library): assigning a queue of handles shares the cells, a per-element new keeps them apart.
People treat class-handle queues like queues of ints and assume assignment implies value-copy.
How would you deep-copy while preserving element dynamic type if `cell` has subclasses? Model answer: loop over `r.cells`, and for each element do `$cast(c, r.cells[i].clone()); cells.push_back(c);` (after `cells.delete()`). `clone()` calls the element's own `create()`, so a subclass instance stays a subclass instance; `cell::type_id::create` + `copy` would always build a plain `cell`.
The destination changes because queue assignment copies the handles, not the cell objects they point to. So both queues hold the same cell objects, and mutating one through src shows up through dst. The trap is assuming that assigning a queue duplicates its contents like it does for ints. I would resize the destination queue and clone each element in a loop, then check that the handles differ.