On 9/22/26 3:15 PM, Jakub Jelinek wrote:
On Tue, Sep 22, 2026 at 02:27:14PM -0400, Jason Merrill wrote:
This is still doing [temp.deduct.type] deduction rather than
[temp.deduct.call] as specified.
That is, we should fill args with the actual args rather than their types,
use DEDUCE_CALL, and not pass a return type.
I've tried incremental
--- a/gcc/cp/call.cc 2026-09-21 18:52:59.556721249 +0200
+++ b/gcc/cp/call.cc 2026-09-22 20:44:53.942872714 +0200
@@ -8151,16 +8151,15 @@
tree targs = make_tree_vec (DECL_NTPARMS (elt));
if (args == NULL)
{
- nargs = call_expr_nargs (placement) + 1;
+ nargs = call_expr_nargs (placement);
args = XALLOCAVEC (tree, nargs);
- args[0] = TREE_TYPE (addr);
- for (unsigned i = 1; i < nargs - 1; ++i)
- args[i] = TREE_TYPE (CALL_EXPR_ARG (placement, i));
- args[nargs - 1] = void_type_node;
+ args[0] = addr;
+ for (unsigned i = 1; i < nargs; ++i)
+ args[i] = CALL_EXPR_ARG (placement, i);
}
elt = fn_type_unification (elt, NULL_TREE, targs, args,
- nargs, void_type_node,
- DEDUCE_EXACT, LOOKUP_NORMAL,
+ nargs, NULL_TREE,
+ DEDUCE_CALL, LOOKUP_NORMAL,
NULL, false, false);
if (elt == error_mark_node)
/* Instantiation failed. */
but that doesn't work.
E.g. on the reduced
namespace std { using size_t = decltype (sizeof 0); }
template <int I>
struct B {};
B <0> b;
struct T { T () = default; T (int) { throw 1; } };
union U { unsigned char c[sizeof (T)]; T t; } u;
template <int I>
void *operator new (std::size_t, B <I> &) // #3
{
return &u.c[0];
}
template <int I>
void operator delete (void *, B <I> &) // #4
{ __builtin_abort (); }
void operator delete (void *, B <0> &) // #5
{ __builtin_abort (); }
int main () {
try
{
T *p = new (b) T (1); // OK, uses #3. No deallocation function is
selected (two candidates remain).
throw 2L;
}
catch (int)
{
}
}
During build_operator_new_call -> perform_overload_resolution ->
add_candidates -> add_template_candidate -> add_template_candidate_real
fn_type_unification is called also with DEDUCE_CALL and works, but
in that case args[0] is 1 and args[1] is VIEW_CONVERT_EXPR<struct B>(b)
but what placement contains is not VIEW_CONVERT_EXPR<struct B>(b)
but (struct B &) &b instead.
So, we'd in that case need placement to be the vec<tree, va_gc> **placement
argument of build_new_1 rather than the CALL_EXPR which
build_new_1 passes in:
3898 /* The Standard is unclear here, but the right thing to do
3899 is to use the same method for finding deallocation
3900 functions that we use for finding allocation functions. */
3901 cleanup = (build_op_delete_call
3902 (dcode,
3903 alloc_node,
3904 size,
3905 globally_qualified_p,
3906 placement_allocation_fn_p ? alloc_call : NULL_TREE,
3907 alloc_fn,
3908 complain));
Though, I'd be afraid we could then evaluate side-effects in the placement
arguments multiple times.
Or pass both placement and alloc_call and use one in fn_type_unification
and the other in the actual call args?
This last sounds right.
Jason