aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/CodeGen/ConstructorGen.cc40
-rw-r--r--tests/i/string-aggregate-assignment/case.json1
-rw-r--r--tests/i/string-aggregate-assignment/expected.txt2
-rw-r--r--tests/i/string-aggregate-assignment/notes.md3
-rw-r--r--tests/i/string-aggregate-assignment/program.cps28
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