The testcase in 57728 demonstrates that we have been treating functions explicitly defaulted in the class body inconsistently with explicit instantiation: an extern instantiation causes them not to be generated, but a normal explicit instantiation doesn't cause them to be emitted, leading to link errors.
After discussing this issue with other vendors (who had the same bug), we have decided to treat these functions like implicitly declared members, so explicit instantiation consistently doesn't affect them. The second patch addresses a FIXME I noticed while looking at this: there's no reason for us to be calling a trivial default constructor in the first place. Tested x86_64-pc-linux-gnu, applying to trunk.
commit ec984d2d6e9258cfbf35d5198f4c4fc97fedf84e Author: Jason Merrill <ja...@redhat.com> Date: Mon Aug 15 15:18:22 2016 -0400 PR c++/57728 - explicit instantiation and defaulted functions * pt.c (do_type_instantiation): Don't mess with non-user-provided member functions. diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 3ad1b89..20689e4 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -5186,8 +5186,7 @@ in_class_defaulted_default_constructor (tree t) } /* Returns true iff FN is a user-provided function, i.e. user-declared - and not defaulted at its first declaration; or explicit, private, - protected, or non-const. */ + and not defaulted at its first declaration. */ bool user_provided_p (tree fn) diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 5d4f5ef..b0f0664 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -21479,7 +21479,8 @@ do_type_instantiation (tree t, tree storage, tsubst_flags_t complain) if (! static_p) for (tmp = TYPE_METHODS (t); tmp; tmp = DECL_CHAIN (tmp)) if (TREE_CODE (tmp) == FUNCTION_DECL - && DECL_TEMPLATE_INSTANTIATION (tmp)) + && DECL_TEMPLATE_INSTANTIATION (tmp) + && user_provided_p (tmp)) instantiate_class_member (tmp, extern_p); for (tmp = TYPE_FIELDS (t); tmp; tmp = DECL_CHAIN (tmp)) diff --git a/gcc/testsuite/g++.dg/cpp0x/explicit11.C b/gcc/testsuite/g++.dg/cpp0x/explicit11.C new file mode 100644 index 0000000..06d607f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/explicit11.C @@ -0,0 +1,19 @@ +// Test that we treat defaulted-in-class members like implicitly declared +// members for explicit instantiation. + +// { dg-do compile { target c++11 } } + +template<typename T> +struct A +{ + T x; + A() = default; + A(const A &other) = default; + A& operator=(const A&) = default; +}; + +template class A<int>; + +// { dg-final { scan-assembler-not "_ZN1AIiEC1Ev" } } +// { dg-final { scan-assembler-not "_ZN1AIiEC1ERKS0_" } } +// { dg-final { scan-assembler-not "_ZN1AIiEaSERKS0_" } } diff --git a/gcc/testsuite/g++.dg/cpp0x/explicit12.C b/gcc/testsuite/g++.dg/cpp0x/explicit12.C new file mode 100644 index 0000000..5c14c01 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/explicit12.C @@ -0,0 +1,17 @@ +// PR c++/57728 +// { dg-do link { target c++11 } } + +template<typename T> +struct A +{ + T x; + A() = default; + A(const A &other) = delete; +}; + +extern template class A<int>; + +int main() +{ + A<int> a; +}
commit 4d2423235cc1f701fa23a66b82739b166d4df6a1 Author: Jason Merrill <ja...@redhat.com> Date: Mon Aug 15 15:25:34 2016 -0400 Avoid calling a trivial default constructor. * class.c (default_ctor_p): New. (in_class_defaulted_default_constructor): Use it. (type_has_non_user_provided_default_constructor): Use it. * call.c (build_over_call): Handle trivial default constructor. * cp-tree.h: Declare default_ctor_p. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 05f0431..024519d 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -7798,11 +7798,19 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) return val; } - else if (DECL_DESTRUCTOR_P (fn) - && trivial_fn_p (fn) - && !DECL_DELETED_FN (fn)) - return fold_convert (void_type_node, argarray[0]); - /* FIXME handle trivial default constructor, too. */ + else if (!DECL_DELETED_FN (fn) + && trivial_fn_p (fn)) + { + if (DECL_DESTRUCTOR_P (fn)) + return fold_convert (void_type_node, argarray[0]); + else if (default_ctor_p (fn)) + { + if (is_dummy_object (argarray[0])) + return force_target_expr (DECL_CONTEXT (fn), void_node, complain); + else + return cp_build_indirect_ref (argarray[0], RO_NULL, complain); + } + } /* For calls to a multi-versioned function, overload resolution returns the function with the highest target priority, that is, diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 20689e4..a4f3c6b 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -5133,8 +5133,17 @@ set_method_tm_attributes (tree t) } } -/* Returns true iff class T has a user-defined constructor other than - the default constructor. */ +/* Returns true if FN is a default constructor. */ + +bool +default_ctor_p (tree fn) +{ + return (DECL_CONSTRUCTOR_P (fn) + && sufficient_parms_p (FUNCTION_FIRST_USER_PARMTYPE (fn))); +} + +/* Returns true iff class T has a user-defined constructor that can be called + with more than zero arguments. */ bool type_has_user_nondefault_constructor (tree t) @@ -5163,23 +5172,16 @@ type_has_user_nondefault_constructor (tree t) tree in_class_defaulted_default_constructor (tree t) { - tree fns, args; - if (!TYPE_HAS_USER_CONSTRUCTOR (t)) return NULL_TREE; - for (fns = CLASSTYPE_CONSTRUCTORS (t); fns; fns = OVL_NEXT (fns)) + for (tree fns = CLASSTYPE_CONSTRUCTORS (t); fns; fns = OVL_NEXT (fns)) { tree fn = OVL_CURRENT (fns); - if (DECL_DEFAULTED_IN_CLASS_P (fn)) - { - args = FUNCTION_FIRST_USER_PARMTYPE (fn); - while (args && TREE_PURPOSE (args)) - args = TREE_CHAIN (args); - if (!args || args == void_list_node) - return fn; - } + if (DECL_DEFAULTED_IN_CLASS_P (fn) + && default_ctor_p (fn)) + return fn; } return NULL_TREE; @@ -5268,8 +5270,8 @@ type_has_non_user_provided_default_constructor (tree t) { tree fn = OVL_CURRENT (fns); if (TREE_CODE (fn) == FUNCTION_DECL - && !user_provided_p (fn) - && sufficient_parms_p (FUNCTION_FIRST_USER_PARMTYPE (fn))) + && default_ctor_p (fn) + && !user_provided_p (fn)) return true; } diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 8a32f17..72a128d 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -5666,6 +5666,7 @@ extern void determine_key_method (tree); extern void check_for_override (tree, tree); extern void push_class_stack (void); extern void pop_class_stack (void); +extern bool default_ctor_p (tree); extern bool type_has_user_nondefault_constructor (tree); extern tree in_class_defaulted_default_constructor (tree); extern bool user_provided_p (tree); diff --git a/gcc/testsuite/g++.dg/cpp0x/explicit12.C b/gcc/testsuite/g++.dg/cpp0x/explicit12.C index 5c14c01..912d507 100644 --- a/gcc/testsuite/g++.dg/cpp0x/explicit12.C +++ b/gcc/testsuite/g++.dg/cpp0x/explicit12.C @@ -15,3 +15,5 @@ int main() { A<int> a; } + +// { dg-final { scan-assembler-not "_ZN1AIiEC1Ev" } }