Hi,

This patch fixes https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82235
For the following test case

struct Foo {
    Foo() {}
    explicit Foo(const Foo& aOther) {}
};
struct Bar {
    Foo m[1];
};
void test() {
    Bar a;
    Bar b = a;
}

the compiler issues an error when the compiler generated copy constructor of class Bar calls the explicit copy constructor of class Foo. The fix is to implement ISO C++/17 16.3.1.4 (over.match.copy) correctly.

Bootstrapped and tested with 'make check' on x86_64-linux.

Mukesh

/gcc/cp
2017-11-14  Mukesh Kapoor   <mukesh.kap...@oracle.com>

        PR c++/82235
        * init.c (build_aggr_init): Allow explicit constructors
        for the following special case:
        ISO C++/17 16.3.1.4 (over.match.copy):
        When initializing a temporary to be bound to the first
        parameter of a constructor where the parameter is of type
        "reference to possibly cv-qualified T" and the constructor
        is called with a single argument in the context of
        direct-initialization of an object of type "cv2 T", explicit
        conversion functions are also considered.

        Added function ref_first_parm_of_constructor() to check for
        this case. In build_aggr_init(), don't set LOOKUP_ONLYCONVERTING
        in flags if ref_first_parm_of_constructor() returns true.

/testsuite
2017-11-14  Mukesh Kapoor   <mukesh.kap...@oracle.com>

        PR c++/82235
        * g++.dg/cpp0x/explicit13.C: New

Index: gcc/cp/init.c
===================================================================
--- gcc/cp/init.c       (revision 254412)
+++ gcc/cp/init.c       (working copy)
@@ -1615,6 +1615,26 @@ expand_member_init (tree name)
   return NULL_TREE;
 }
 
+/* Return true if current_function_decl is a constructor
+   and its first argument is a reference type and it is
+   a direct initialization. 
+   If FLAGS is 0 then it is a direct initialization, the (init) form.  */
+
+static inline bool
+ref_first_parm_of_constructor (int flags)
+{
+  bool res = false;
+  if (current_function_decl != NULL_TREE)
+    {
+      tree parmlist = FUNCTION_FIRST_USER_PARMTYPE (current_function_decl);
+      tree firstparm = TREE_VALUE (parmlist);
+      res = ((flags == 0) && DECL_CONSTRUCTOR_P (current_function_decl)
+           && (TREE_CODE (firstparm) == REFERENCE_TYPE));
+    }
+
+  return res;
+}
+
 /* This is like `expand_member_init', only it stores one aggregate
    value into another.
 
@@ -1728,10 +1748,20 @@ build_aggr_init (tree exp, tree init, int flags, t
       return stmt_expr;
     }
 
+   /* Don't set LOOKUP_ONLYCONVERTING for the following special case:
+   ISO C++/17 16.3.1.4 (over.match.copy):
+   When initializing a temporary to be bound to the first
+   parameter of a constructor where the parameter is of type
+   "reference to possibly cv-qualified T" and the constructor
+   is called with a single argument in the context of
+   direct-initialization of an object of type "cv2 T", explicit
+   conversion functions are also considered.  */
+
   if (init && init != void_type_node
       && TREE_CODE (init) != TREE_LIST
       && !(TREE_CODE (init) == TARGET_EXPR
           && TARGET_EXPR_DIRECT_INIT_P (init))
+      && !ref_first_parm_of_constructor (flags)
       && !DIRECT_LIST_INIT_P (init))
     flags |= LOOKUP_ONLYCONVERTING;
 
Index: gcc/testsuite/g++.dg/cpp0x/explicit13.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/explicit13.C     (revision 0)
+++ gcc/testsuite/g++.dg/cpp0x/explicit13.C     (working copy)
@@ -0,0 +1,17 @@
+// PR c++/82235
+// { dg-do compile { target c++11 } }
+
+struct Foo {
+  Foo() {}
+  explicit Foo(const Foo& aOther) {}
+};
+
+struct Bar {
+  Foo m[1];
+};
+
+void test()
+{
+  Bar a;
+  Bar b = a;
+}

Reply via email to