G++ has traditionally instantiated default arguments and default member initializers wherever they are needed, but the standard describes default arguments as separate definitions, and the old way is problematic for the new lambda instantiation model.
This patch charts a middle course: now we will remember instantiations of default arguments/DMI, but if the instantiation fails in SFINAE context we can still fail substitution rather than give a hard error. Tested x86_64-pc-linux-gnu, applying to trunk.
commit 68acac95b51e692c7ccd267aeebbc93e43e219d6 Author: Jason Merrill <ja...@redhat.com> Date: Tue Aug 15 14:14:44 2017 -0700 Instantiate default arguments/member initializers once. * init.c (get_nsdmi): Remember NSDMI instantiations. * parser.c (inject_this_parameter): Be more picky about current_class_ptr. * pt.c (tsubst_copy): Simplify 'this' handling. (tsubst_default_argument): Remember default argument instantiations. Take parameter number. (tsubst_default_arguments): Pass it. * call.c (convert_default_arg): Likewise. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 6405be2..cfedd30 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -7282,7 +7282,7 @@ convert_default_arg (tree type, tree arg, tree fn, int parmnum, push_defarg_context (fn); if (fn && DECL_TEMPLATE_INFO (fn)) - arg = tsubst_default_argument (fn, type, arg, complain); + arg = tsubst_default_argument (fn, parmnum, type, arg, complain); /* Due to: diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index f2e54a8..f0eafb3 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6465,7 +6465,7 @@ extern tree maybe_process_partial_specialization (tree); extern tree most_specialized_instantiation (tree); extern void print_candidates (tree); extern void instantiate_pending_templates (int); -extern tree tsubst_default_argument (tree, tree, tree, +extern tree tsubst_default_argument (tree, int, tree, tree, tsubst_flags_t); extern tree tsubst (tree, tree, tsubst_flags_t, tree); extern tree tsubst_copy_and_build (tree, tree, tsubst_flags_t, diff --git a/gcc/cp/init.c b/gcc/cp/init.c index 83e685c..56a5df8 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -535,6 +535,8 @@ perform_target_ctor (tree init) /* Return the non-static data initializer for FIELD_DECL MEMBER. */ +static GTY(()) hash_map<tree, tree> *nsdmi_inst; + tree get_nsdmi (tree member, bool in_ctor, tsubst_flags_t complain) { @@ -542,31 +544,36 @@ get_nsdmi (tree member, bool in_ctor, tsubst_flags_t complain) tree save_ccp = current_class_ptr; tree save_ccr = current_class_ref; - if (!in_ctor) - { - /* Use a PLACEHOLDER_EXPR when we don't have a 'this' parameter to - refer to; constexpr evaluation knows what to do with it. */ - current_class_ref = build0 (PLACEHOLDER_EXPR, DECL_CONTEXT (member)); - current_class_ptr = build_address (current_class_ref); - } - if (DECL_LANG_SPECIFIC (member) && DECL_TEMPLATE_INFO (member)) { init = DECL_INITIAL (DECL_TI_TEMPLATE (member)); + location_t expr_loc + = EXPR_LOC_OR_LOC (init, DECL_SOURCE_LOCATION (member)); + tree *slot; if (TREE_CODE (init) == DEFAULT_ARG) /* Unparsed. */; + else if (nsdmi_inst && (slot = nsdmi_inst->get (member))) + init = *slot; /* Check recursive instantiation. */ else if (DECL_INSTANTIATING_NSDMI_P (member)) { if (complain & tf_error) - error ("recursive instantiation of default member " - "initializer for %qD", member); + error_at (expr_loc, "recursive instantiation of default member " + "initializer for %qD", member); init = error_mark_node; } else { + int un = cp_unevaluated_operand; + cp_unevaluated_operand = 0; + + location_t sloc = input_location; + input_location = expr_loc; + DECL_INSTANTIATING_NSDMI_P (member) = 1; + inject_this_parameter (DECL_CONTEXT (member), TYPE_UNQUALIFIED); + /* Do deferred instantiation of the NSDMI. */ init = (tsubst_copy_and_build (init, DECL_TI_ARGS (member), @@ -575,6 +582,16 @@ get_nsdmi (tree member, bool in_ctor, tsubst_flags_t complain) init = digest_nsdmi_init (member, init, complain); DECL_INSTANTIATING_NSDMI_P (member) = 0; + + if (init != error_mark_node) + { + if (!nsdmi_inst) + nsdmi_inst = hash_map<tree,tree>::create_ggc (37); + nsdmi_inst->put (member, init); + } + + input_location = sloc; + cp_unevaluated_operand = un; } } else @@ -592,6 +609,19 @@ get_nsdmi (tree member, bool in_ctor, tsubst_flags_t complain) init = error_mark_node; } + if (in_ctor) + { + current_class_ptr = save_ccp; + current_class_ref = save_ccr; + } + else + { + /* Use a PLACEHOLDER_EXPR when we don't have a 'this' parameter to + refer to; constexpr evaluation knows what to do with it. */ + current_class_ref = build0 (PLACEHOLDER_EXPR, DECL_CONTEXT (member)); + current_class_ptr = build_address (current_class_ref); + } + /* Strip redundant TARGET_EXPR so we don't need to remap it, and so the aggregate init code below will see a CONSTRUCTOR. */ bool simple_target = (init && SIMPLE_TARGET_EXPR_P (init)); diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 9b7c2c0..d66f146 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -20715,7 +20715,9 @@ inject_this_parameter (tree ctype, cp_cv_quals quals) { /* We don't clear this between NSDMIs. Is it already what we want? */ tree type = TREE_TYPE (TREE_TYPE (current_class_ptr)); - if (same_type_ignoring_top_level_qualifiers_p (ctype, type) + if (DECL_P (current_class_ptr) + && DECL_CONTEXT (current_class_ptr) == NULL_TREE + && same_type_ignoring_top_level_qualifiers_p (ctype, type) && cp_type_quals (type) == quals) return; } diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index d5d6dd0..847cd68 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -12001,11 +12001,14 @@ tsubst_aggr_type (tree t, } } +static GTY(()) hash_map<tree, tree> *defarg_inst; + /* Substitute into the default argument ARG (a default argument for FN), which has the indicated TYPE. */ tree -tsubst_default_argument (tree fn, tree type, tree arg, tsubst_flags_t complain) +tsubst_default_argument (tree fn, int parmnum, tree type, tree arg, + tsubst_flags_t complain) { tree saved_class_ptr = NULL_TREE; tree saved_class_ref = NULL_TREE; @@ -12015,6 +12018,17 @@ tsubst_default_argument (tree fn, tree type, tree arg, tsubst_flags_t complain) if (TREE_CODE (arg) == DEFAULT_ARG) return arg; + tree parm = FUNCTION_FIRST_USER_PARM (fn); + parm = chain_index (parmnum, parm); + tree parmtype = TREE_TYPE (parm); + if (DECL_BY_REFERENCE (parm)) + parmtype = TREE_TYPE (parmtype); + gcc_assert (same_type_ignoring_top_level_qualifiers_p (type, parmtype)); + + tree *slot; + if (defarg_inst && (slot = defarg_inst->get (parm))) + return *slot; + /* This default argument came from a template. Instantiate the default argument here, not in tsubst. In the case of something like: @@ -12067,6 +12081,13 @@ tsubst_default_argument (tree fn, tree type, tree arg, tsubst_flags_t complain) pop_access_scope (fn); + if (arg != error_mark_node && !cp_unevaluated_operand) + { + if (!defarg_inst) + defarg_inst = hash_map<tree,tree>::create_ggc (37); + defarg_inst->put (parm, arg); + } + return arg; } @@ -12088,11 +12109,12 @@ tsubst_default_arguments (tree fn, tsubst_flags_t complain) if (DECL_CLONED_FUNCTION_P (fn)) return; + int i = 0; for (arg = TYPE_ARG_TYPES (TREE_TYPE (fn)); arg; - arg = TREE_CHAIN (arg)) + arg = TREE_CHAIN (arg), ++i) if (TREE_PURPOSE (arg)) - TREE_PURPOSE (arg) = tsubst_default_argument (fn, + TREE_PURPOSE (arg) = tsubst_default_argument (fn, i, TREE_VALUE (arg), TREE_PURPOSE (arg), complain); @@ -14500,13 +14522,8 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl) if (r == NULL_TREE) { - /* We get here for a use of 'this' in an NSDMI as part of a - constructor call or as part of an aggregate initialization. */ - if (DECL_NAME (t) == this_identifier - && ((current_function_decl - && DECL_CONSTRUCTOR_P (current_function_decl)) - || (current_class_ref - && TREE_CODE (current_class_ref) == PLACEHOLDER_EXPR))) + /* We get here for a use of 'this' in an NSDMI. */ + if (DECL_NAME (t) == this_identifier && current_class_ptr) return current_class_ptr; /* This can happen for a parameter name used later in a function diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-62155.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-62155.C index 76eb2da..3d1c113 100644 --- a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-62155.C +++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-62155.C @@ -1,8 +1,8 @@ // PR c++/62155 // { dg-do compile { target c++11 } } -template <typename T> struct S { // { dg-error "cannot convert" } - T i{[this] {}}; +template <typename T> struct S { + T i{[this] {}}; // { dg-error "cannot convert" } }; -S<int> s; // { dg-error "cannot convert" } +S<int> s; // { dg-message "required" } diff --git a/gcc/testsuite/g++.dg/cpp1z/direct-enum-init1.C b/gcc/testsuite/g++.dg/cpp1z/direct-enum-init1.C index b473ed5..ee39ab4 100644 --- a/gcc/testsuite/g++.dg/cpp1z/direct-enum-init1.C +++ b/gcc/testsuite/g++.dg/cpp1z/direct-enum-init1.C @@ -140,11 +140,11 @@ struct U2 template <int N> struct W2 { - A a { 5 }; // { dg-error "invalid conversion from 'int' to 'A'" "" { target *-*-* } .-2 } - B b { 6 }; // { dg-error "invalid conversion from 'int' to 'B'" "" { target c++14_down } .-3 } - C c { 3.0f }; // { dg-error "cannot convert \[^\n\r]* to 'C' in initialization" "" { target c++14_down } .-4 } - // { dg-error "narrowing conversion of '3.0e.0f' from 'float' to 'int' inside" "" { target c++1z } .-5 } - D d = { 7 }; // { dg-error "cannot convert \[^\n\r]* to 'D' in initialization" "" { target *-*-* } .-6 } + A a { 5 }; // { dg-error "invalid conversion from 'int' to 'A'" "" { target *-*-* } } + B b { 6 }; // { dg-error "invalid conversion from 'int' to 'B'" "" { target c++14_down } } + C c { 3.0f }; // { dg-error "cannot convert \[^\n\r]* to 'C' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of '3.0e.0f' from 'float' to 'int' inside" "" { target c++1z } .-1 } + D d = { 7 }; // { dg-error "cannot convert \[^\n\r]* to 'D' in initialization" "" { target *-*-* } } }; template <typename H, typename I, typename J, typename K, typename L, typename M> @@ -208,11 +208,11 @@ struct U3 template <typename H, typename I, typename J, typename K> struct W3 { - H a { 5 }; // { dg-error "invalid conversion from 'int' to 'A'" "" { target *-*-* } .-2 } - I b { 6 }; // { dg-error "invalid conversion from 'int' to 'B'" "" { target c++14_down } .-3 } - J c { 3.0f }; // { dg-error "cannot convert \[^\n\r]* to 'C' in initialization" "" { target c++14_down } .-4 } - // { dg-error "narrowing conversion of '3.0e.0f' from 'float' to 'int' inside" "" { target c++1z } .-5 } - K d = { 7 }; // { dg-error "cannot convert \[^\n\r]* to 'D' in initialization" "" { target *-*-* } .-6 } + H a { 5 }; // { dg-error "invalid conversion from 'int' to 'A'" "" { target *-*-* } } + I b { 6 }; // { dg-error "invalid conversion from 'int' to 'B'" "" { target c++14_down } } + J c { 3.0f }; // { dg-error "cannot convert \[^\n\r]* to 'C' in initialization" "" { target c++14_down } } + // { dg-error "narrowing conversion of '3.0e.0f' from 'float' to 'int' inside" "" { target c++1z } .-1 } + K d = { 7 }; // { dg-error "cannot convert \[^\n\r]* to 'D' in initialization" "" { target *-*-* } } }; void @@ -221,17 +221,9 @@ test () foo2<0> (); U2<0> u20; U2<1> u21 (5); - W2<0> w2; // { dg-error "invalid conversion from 'int' to 'A'" } - // { dg-error "invalid conversion from 'int' to 'B'" "" { target c++14_down } .-1 } - // { dg-error "cannot convert \[^\n\r]* to 'C' in initialization" "" { target c++14_down } .-2 } - // { dg-error "narrowing conversion of '3.0e.0f' from 'float' to 'int' inside" "" { target c++1z } .-3 } - // { dg-error "cannot convert \[^\n\r]* to 'D' in initialization" "" { target *-*-* } .-4 } + W2<0> w2; // { dg-message "" } foo3<A, B, C, D, E, V> (); U3<E> u30; U3<E> u31 (5); - W3<A, B, C, D> w3; // { dg-error "invalid conversion from 'int' to 'A'" } - // { dg-error "invalid conversion from 'int' to 'B'" "" { target c++14_down } .-1 } - // { dg-error "cannot convert \[^\n\r]* to 'C' in initialization" "" { target c++14_down } .-2 } - // { dg-error "narrowing conversion of '3.0e.0f' from 'float' to 'int' inside" "" { target c++1z } .-3 } - // { dg-error "cannot convert \[^\n\r]* to 'D' in initialization" "" { target *-*-* } .-4 } + W3<A, B, C, D> w3; // { dg-message "" } }