On 11/19/19 1:44 AM, Marek Polacek wrote:
It also looks like you're using the LOOKUP flag to mean two different
things:
1) try to treat parenthesized args as an aggregate initializer
(build_new_method_call_1)
2) treat this CONSTRUCTOR as coming from parenthesized args
(store_init_value/digest_init)
Correct.
Why is the flag needed for #1? When do we not want to try to treat the args
as an aggregate initializer?
There are cases where treating the args as an aggregate initializer causes
spurious overload resolution successes, e.g.
void swap(int&, int&);
int& get();
struct pair {
void swap(pair&) noexcept(noexcept(swap(get(), get()))) { } // { dg-error "no
matching function for call" }
};
There are no viable candidates for pair::swap (# of args mismatch) but since
pair is an aggregate, build_new_method_call_1 would return a CONSTRUCTOR so
overload resolution would succeed. Another case had to do with SFINAE and
decltype where we didn't evaluate the arg, but succeeding in the
no-viable-function case caused the compiler to choose the wrong function.
Hmm, but then the parenthesized list is arguments for swap, not an
initializer for a single argument of swap. That would be using it for
copy-initialization, and we only want to treat parenthesized args as an
aggregate initializer in direct-initialization. Can we check for
direct-init (i.e. !LOOKUP_ONLYCONVERTING) instead?
- expr = get_target_expr_sfinae (digest_init (totype, expr, complain),
+ /* We want an implicit lookup, but when initializing an aggregate
+ from a parenthesized list, we must remember not to warn about
+ narrowing conversions. */
+ flags = LOOKUP_IMPLICIT;
+ if (CONSTRUCTOR_IS_PAREN_INIT (expr))
+ flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+ expr = get_target_expr_sfinae (digest_init_flags (totype, expr,
+ flags, complain),
I was thinking to set the flag inside digest_init, so callers don't need
to know about it.
@@ -6704,6 +6761,14 @@ check_initializer (tree decl, tree init, int flags,
vec<tree, va_gc> **cleanups)
}
init_code = NULL_TREE;
}
+ else if (BRACE_ENCLOSED_INITIALIZER_P (init_code))
+ {
+ /* In C++20, the call to build_aggr_init could have created
+ a CONSTRUCTOR to handle A(1, 2). */
+ gcc_assert (cxx_dialect >= cxx2a);
+ init = init_code;
+ flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+ }
I think you want to clear init_code after copying it into init.
+ init = build_aggr_init (decl, init, flags, tf_warning_or_error);
+ /* In C++20, a member initializer list can be initializing an
+ aggregate from a parenthesized list of values:
+
+ struct S {
+ A aggr;
+ S() : aggr(1, 2, 3) { }
+ };
+
+ Build up an INIT_EXPR like we do for aggr{1, 2, 3}, so that
+ build_data_member_initialization can grok it. */
+ if (cxx_dialect >= cxx2a)
+ {
+ tree t = init;
+ while (TREE_CODE (t) == EXPR_STMT
+ || TREE_CODE (t) == CONVERT_EXPR)
+ t = TREE_OPERAND (t, 0);
+ if (BRACE_ENCLOSED_INITIALIZER_P (t))
+ {
+ t = digest_init_flags (type, t,
+ (LOOKUP_IMPLICIT
+ | LOOKUP_AGGREGATE_PAREN_INIT),
+ tf_warning_or_error);
+ init = build2 (INIT_EXPR, type, decl, t);
+ }
+ }
All this should happen within the call to build_aggr_init.
expand_default_init already builds INIT_EXPR in various cases; it could
do so for this case, too.
+ if (BRACE_ENCLOSED_INITIALIZER_P (exp))
+ {
+ gcc_assert (cxx_dialect >= cxx2a);
+ return finish_compound_literal (type, exp, complain,
+ fcl_functional_paren);
+ }
How about handling this in build_cplus_new instead of places that also
call build_cplus_new?
It might also be better to call digest_init in build_new_method_call_1
and not involve finish_compound_literal at all, since the latter calls
reshape_init.
Jason