diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index 4a3f451..2090b2d 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -1711,6 +1711,12 @@ novirtual class AssignGenMixin: # 生命期已是 FRAME(propagate_life 同一谓词),这里补上**出口释放**这一半。 owned = self.Gen.CallGenCtor._new_is_nll_demotable(trans, rhs_node, target_name) if owned != 0: + # `new` yields T*: a compiler-owned slife binding must be a pointer + # variable. A value-typed `T` target would make the scope-exit free + # load the struct value and bitcast it to a pointer (invalid IR). + if target_slot.Ty is None or target_slot.Ty.Pointee == 0 or self.Gen.ExprGenBase.is_ptr_type(target_slot.Ty.Pointee) == 0: + self.Gen.TypeGenErr.fatal_error(rhs_node, "`new ...` returns T* and must be stored in a pointer variable (T*); a value-typed T target cannot own the heap object", "TypeError") + return 0 sh: mlirlite.Value* = self.Gen.VarGen._SlifeEnsureOwned(pool, builder, target_slot) if sh is None: return 0 self._SlifeDropOwned(pool, builder, mod, trans, target_slot) diff --git a/App/lib/core/Gen/Call/CallGenMixin.vp b/App/lib/core/Gen/Call/CallGenMixin.vp index d261727..f7a2827 100644 --- a/App/lib/core/Gen/Call/CallGenMixin.vp +++ b/App/lib/core/Gen/Call/CallGenMixin.vp @@ -601,6 +601,12 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase): if gen_has_defaults == 1: # Has defaults: use the constructor path to apply them. return self.Gen.CallGenCtor._translate_struct_ctor(pool, builder, mod, struct_ty, func_name, cargs, sizet(0), trans, cl.IsVolatile) + # `new ... T()` already allocated storage: construct there and return its + # pointer. Returning ConstZero would discard that storage and make the + # caller materialize a stack temporary instead (leaking the heap block and + # freeing a frame address at scope exit). + if self.Gen.CallGenCtor.CurCtorStorage() is not None: + return self.Gen.CallGenCtor._translate_struct_ctor(pool, builder, mod, struct_ty, func_name, cargs, sizet(0), trans, cl.IsVolatile) # No defaults: return zeroinitializer. ret: mlirlite.Value* = mlirlite.ConstZero(pool, struct_ty) return ret