On 11/20/19 8:37 PM, Marek Polacek wrote:
On Tue, Nov 19, 2019 at 05:33:09PM -0500, Jason Merrill wrote:
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?

Unfortunately that doesn't work.  We call build_new_method_call from context
where LOOKUP_ONLYCONVERTING isn't set and so it would still break things.

How so? If we call it for a constructor, surely we can check that flag to distinguish between copy- and direct-initialization, or I'd expect wrong behavior wrt explicit.

+  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?

Is it really what we want?  We now have two spots where we need to handle
the case when build_special_member_call returns a BRACE_ENCLOSED_INITIALIZER_P
but build_cplus_new is called in many other spots where we don't expect to see
a CONSTRUCTOR.

I think build_cplus_new should be able to handle whatever build_special_member_call returns.

@@ -921,8 +921,20 @@ perform_member_init (tree member, tree init)
                inform (DECL_SOURCE_LOCATION (member),
                        "%q#D should be initialized", member );
            }
-         finish_expr_stmt (build_aggr_init (decl, init, flags,
-                                            tf_warning_or_error));
+         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) { }
+              };
+
+             In such case, build_aggr_init will build up an INIT_EXPR like
+             we do for aggr{1, 2, 3}, so that build_data_member_initialization
+             can grok it.  */
+         if (TREE_CODE (init) != INIT_EXPR)
+           finish_expr_stmt (init);

Why don't we want to finish_expr_stmt an INIT_EXPR?

@@ -1272,8 +1274,13 @@ digest_init_r (tree type, tree init, int nested, int 
flags,
                inform (loc, "remove %<{ }%> around initializer");
            }
          else if (flag_checking)
-           /* We should have fixed this in reshape_init.  */
-           gcc_unreachable ();
+           /* We should have fixed this in reshape_init.  Except that we
+              don't reshape parenthesized lists where brace elision is
+              not permitted.  */
+           {
+             gcc_assert (flags & LOOKUP_AGGREGATE_PAREN_INIT);
+             return error_mark_node;

We shouldn't get here for parenthesized lists, either, I thnk; we should have called the copy constructor rather than try aggregate initialization.

@@ -1319,12 +1326,19 @@ digest_init_r (tree type, tree init, int nested, int 
flags,
  tree
  digest_init (tree type, tree init, tsubst_flags_t complain)
  {
-  return digest_init_r (type, init, 0, LOOKUP_IMPLICIT, complain);
+  int flags = LOOKUP_IMPLICIT;
+  if (BRACE_ENCLOSED_INITIALIZER_P (init)
+      && CONSTRUCTOR_IS_PAREN_INIT (init))
+    flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+  return digest_init_r (type, init, 0, flags, complain);
  }
tree
  digest_init_flags (tree type, tree init, int flags, tsubst_flags_t complain)
  {
+  if (BRACE_ENCLOSED_INITIALIZER_P (init)
+      && CONSTRUCTOR_IS_PAREN_INIT (init))
+    flags |= LOOKUP_AGGREGATE_PAREN_INIT;

Maybe do this in digest_init_r? What happens if we have a braced aggregate initializer containing a parenthesized aggregate initializer, e.g.

{ aggr(1,2,3) }

Jason

Reply via email to