diff options
| -rw-r--r-- | lib/CodeGen/ConstructorGen.cc | 40 | ||||
| -rw-r--r-- | tests/i/string-aggregate-assignment/case.json | 1 | ||||
| -rw-r--r-- | tests/i/string-aggregate-assignment/expected.txt | 2 | ||||
| -rw-r--r-- | tests/i/string-aggregate-assignment/notes.md | 3 | ||||
| -rw-r--r-- | tests/i/string-aggregate-assignment/program.cps | 28 |
5 files changed, 72 insertions, 2 deletions
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<size_t> 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<Value *(const TypeInfo *, Value *)> 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<bool(const TypeInfo *, Value *)> 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; diff --git a/tests/i/string-aggregate-assignment/case.json b/tests/i/string-aggregate-assignment/case.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tests/i/string-aggregate-assignment/case.json @@ -0,0 +1 @@ +{} diff --git a/tests/i/string-aggregate-assignment/expected.txt b/tests/i/string-aggregate-assignment/expected.txt new file mode 100644 index 0000000..b5956eb --- /dev/null +++ b/tests/i/string-aggregate-assignment/expected.txt @@ -0,0 +1,2 @@ +left:changed:0:0 +2:0 diff --git a/tests/i/string-aggregate-assignment/notes.md b/tests/i/string-aggregate-assignment/notes.md new file mode 100644 index 0000000..592272d --- /dev/null +++ b/tests/i/string-aggregate-assignment/notes.md @@ -0,0 +1,3 @@ +# Aggregate assignment with STRING fields + +Human review: pending. Default class assignment copies STRING fields independently, including self-assignment, without invoking the enclosing class's copy constructor or destructor. The two declared boxes are destroyed only when Check returns. diff --git a/tests/i/string-aggregate-assignment/program.cps b/tests/i/string-aggregate-assignment/program.cps new file mode 100644 index 0000000..9659437 --- /dev/null +++ b/tests/i/string-aggregate-assignment/program.cps @@ -0,0 +1,28 @@ +GLOBAL + DECLARE Destroyed : INTEGER + DECLARE Copies : INTEGER +ENDGLOBAL +CLASS Box + Text : STRING + OVERLOAD PROCEDURE NEW() + ENDPROCEDURE + OVERLOAD PROCEDURE NEW(BYREF Other : Box) + Text <- Other.Text + Copies <- Copies + 1 + ENDPROCEDURE + PROCEDURE DELETE() + Destroyed <- Destroyed + 1 + ENDPROCEDURE +ENDCLASS +PROCEDURE Check() + DECLARE A : Box + DECLARE B : Box + A.Text <- "left" + B.Text <- "right" + B <- A + B <- B + B.Text <- "changed" + OUTPUT A.Text, ":", B.Text, ":", Destroyed, ":", Copies +ENDPROCEDURE +CALL Check() +OUTPUT Destroyed, ":", Copies |
