From be964b4df4db9f42e0b7d615c1cbfa6cf268e646 Mon Sep 17 00:00:00 2001 From: _ov4 Date: Tue, 20 Oct 2026 12:01:45 +0000 Subject: fix(strings): preserve enclosing class lifetimes during assignment Copy owned string fields without reconstructing or destroying their containing class, and retain rvalue ownership transfers. Co-Authored-By: Claude Opus 5.5 --- lib/CodeGen/ConstructorGen.cc | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/CodeGen/ConstructorGen.cc b/lib/CodeGen/ConstructorGen.cc index ec97fca..dfdfbb5 100644 --- a/lib/CodeGen/ConstructorGen.cc +++ b/lib/CodeGen/ConstructorGen.cc @@ -179,10 +179,46 @@ void CodeGen::emitAssignment(const LValueInfo &Target, ExprAST *Expr, const Symb } } if (Info && containsString(Info)) { + Value *Val = emitCoercedExpr(Expr, Info); + if (!Val) return; std::vector Transfers; - Value *Val = emitOwnedValue(Expr, Info, Transfers); + bool CopySource = isObjectLValue(Expr); + if (!CopySource) { + materializeOperatorReceiver(Val, Info); + if (typeNeedsDestruction(Info->Name)) Transfers.push_back(StackObjects.size() - 1); + } + // Default aggregate assignment replaces fields without reconstructing + // the enclosing class or calling its DELETE method. + std::function Clone = + [&](const TypeInfo *Type, Value *Value) -> llvm::Value * { + if (Type->isString()) { + auto *Source = CreateEntryBlockAlloca(Builder->GetInsertBlock()->getParent(), + Type->LLVMType, "assignment_source"); + Builder->CreateStore(Value, Source); + return copyClassValue(Type, Source, Transfers); + } + for (const auto &[Name, Field] : Type->Record.Fields) { + const auto *FieldType = resolveType(Field.TypeName); + if (!containsString(FieldType)) continue; + auto *Copy = Clone(FieldType, Builder->CreateExtractValue(Value, {Field.Index})); + if (!Copy) return nullptr; + Value = Builder->CreateInsertValue(Value, Copy, {Field.Index}); + } + return Value; + }; + if (CopySource) Val = Clone(Info, Val); if (!Val) return; - if (!emitDestroy(Info->Name, Target.Addr, DiagnosticLine)) return; + std::function Release = + [&](const TypeInfo *Type, Value *Address) { + if (Type->isString()) return emitDestroy(Type->Name, Address, DiagnosticLine); + for (const auto &[Name, Field] : Type->Record.Fields) { + const auto *FieldType = resolveType(Field.TypeName); + if (containsString(FieldType) && !Release(FieldType, + Builder->CreateStructGEP(Type->LLVMType, Address, Field.Index))) return false; + } + return true; + }; + if (!Release(Info, Target.Addr)) return; Builder->CreateStore(Val, Target.Addr); transferObjects(Transfers); return; -- cgit v1.3.1-4-g156e