I tried State->dump() and it shows there is no default binding for a struct variable that copies another. See below. I certainly can (and should) use the field symbols for my work. I was curious about the internals of the analyzer engine. Thank you for the detailed explanation!
struct XY pos1 = next_pos(10, 20); // Binding: pos1 → conj_$3{struct XY, LC1, S45538, #1}
move_to_pos(pos1);
// evalCall, State->dump():
//
// Store (direct and default bindings), 0x4176b88 :
// (GlobalInternalSpaceRegion,0,default) : conj_$1{int, LC1, S45538, #1}
// (GlobalSystemSpaceRegion,0,default) : conj_$2{int, LC1, S45538, #1}
// (pos1,0,default) : conj_$3{struct XY, LC1, S45538, #1}
//
// Expressions by stack frame:
// #0 Calling main
// (LC1, S45573) move_to_pos : &code{move_to_pos}
// (LC1, S45581) pos1 : lazyCompoundVal{0x4176b88,pos1}
//
// Ranges are empty.
//
// StoreMgr.getBinding(St, loc::MemRegionVal(Arg0Reg)) → conj_$3{struct XY, LC1, S45538, #1}
struct XY pos2 = pos1; // Binding: pos2 → lazyCompoundVal{0x4176b88,pos1}
move_to_pos(pos2);
// evalCall, State->dump():
//
// Store (direct and default bindings), 0x4176b88 :
// (GlobalInternalSpaceRegion,0,default) : conj_$1{int, LC1, S45538, #1}
// (GlobalSystemSpaceRegion,0,default) : conj_$2{int, LC1, S45538, #1}
// (pos1,0,default) : conj_$3{struct XY, LC1, S45538, #1}
//
// Expressions by stack frame:
// #0 Calling main
// (LC1, S45618) move_to_pos : &code{move_to_pos}
// (LC1, S45626) pos2 : lazyCompoundVal{0x4182e58,pos2}
//
// Ranges are empty.
//
// StoreMgr.getBinding(St, loc::MemRegionVal(Arg0Reg)) → Undefined