diff --git a/gcc/d/d-codegen.cc b/gcc/d/d-codegen.cc index 95dc8b6327ef99d46c77c359459c7e4e31b21498..dc528164aaffd6e001eb2268a7c252a3193cb61c 100644 --- a/gcc/d/d-codegen.cc +++ b/gcc/d/d-codegen.cc @@ -2270,10 +2270,17 @@ d_build_call (TypeFunction *tf, tree callable, tree object, Type *t = arg->type->toBasetype (); StructDeclaration *sd = t->baseElemOf ()->isTypeStruct ()->sym; - /* Nested structs also have ADDRESSABLE set, but if the type has - neither a copy constructor nor a destructor available, then we - need to take care of copying its value before passing it. */ - if (arg->op == EXP::structLiteral || (!sd->postblit && !sd->dtor)) + /* Need to take care of copying its value before passing the + argument in the following scenarios: + - The argument is a literal expression; a CONSTRUCTOR can't + have its address taken. + - The type has neither a copy constructor nor a destructor + available; nested structs also have ADDRESSABLE set. + - The ABI of the function expects the callee to destroy its + arguments; when the caller is handles destruction, then `targ' + has already been made into a temporary. */ + if (arg->op == EXP::structLiteral || (!sd->postblit && !sd->dtor) + || target.isCalleeDestroyingArgs (tf)) targ = force_target_expr (targ); targ = convert (build_reference_type (TREE_TYPE (targ)), diff --git a/gcc/d/decl.cc b/gcc/d/decl.cc index d2e84d3ba619504f769d8765a9fe746b19579615..827495b3e3036e39cb0572a576087384bb8d17d6 100644 --- a/gcc/d/decl.cc +++ b/gcc/d/decl.cc @@ -863,10 +863,28 @@ public: /* Maybe put variable on list of things needing destruction. */ if (d->needsScopeDtor ()) { + /* Rewrite: `decl = exp' => TARGET_EXPR(decl, exp, dtor). */ vec_safe_push (d_function_chain->vars_in_scope, decl); + /* Force a TARGET_EXPR to add the corresponding cleanup. */ - exp = force_target_expr (compound_expr (exp, decl)); - TARGET_EXPR_CLEANUP (exp) = build_expr (d->edtor); + if (TREE_CODE (exp) != TARGET_EXPR) + { + if (VOID_TYPE_P (TREE_TYPE (exp))) + exp = compound_expr (exp, decl); + + exp = force_target_expr (exp); + } + + TARGET_EXPR_CLEANUP (exp) + = compound_expr (TARGET_EXPR_CLEANUP (exp), + build_expr (d->edtor)); + + /* The decl is really an alias for the TARGET_EXPR slot. */ + SET_DECL_VALUE_EXPR (decl, TARGET_EXPR_SLOT (exp)); + DECL_HAS_VALUE_EXPR_P (decl) = 1; + /* This tells the gimplifier not to emit a clobber for the decl + as its lifetime ends when the slot gets cleaned up. */ + TREE_ADDRESSABLE (decl) = 0; } add_stmt (exp); diff --git a/gcc/testsuite/gdc.dg/torture/pr113758.d b/gcc/testsuite/gdc.dg/torture/pr113758.d new file mode 100644 index 0000000000000000000000000000000000000000..dc53883a8dea5fb572f8704fe78a4b4a3d334822 --- /dev/null +++ b/gcc/testsuite/gdc.dg/torture/pr113758.d @@ -0,0 +1,19 @@ +// { dg-do run } +// { dg-skip-if "needs gcc/config.d" { ! d_runtime } } +struct S113758 +{ + int field; + ~this() { field = 0; } +} + +void main() +{ + auto var = S113758(1); + f113758d(var); + assert(var.field == 1); + f113758cxx(var); + assert(var.field == 1); +} + +extern (D) void f113758d(S113758 arg) { } +extern (C++) void f113758cxx(S113758 arg) { }