From 12659e10c78200717fc82ed77892de5059fa44b5 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Tue, 29 Aug 2017 15:40:08 -0400 Subject: [PATCH] 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. From-SVN: r251422 --- gcc/cp/ChangeLog | 10 ++++ gcc/cp/call.c | 2 +- gcc/cp/cp-tree.h | 2 +- gcc/cp/init.c | 50 +++++++++++++++---- gcc/cp/parser.c | 4 +- gcc/cp/pt.c | 37 ++++++++++---- .../g++.dg/cpp0x/lambda/lambda-62155.C | 6 +-- .../g++.dg/cpp1z/direct-enum-init1.C | 32 +++++------- 8 files changed, 97 insertions(+), 46 deletions(-) diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index fc244a13803..308e13abba7 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,15 @@ 2017-08-29 Jason Merrill + 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. + Fix default argument conversion failure and SFINAE. * call.c (build_over_call): Check convert_default_arg result for error_mark_node. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 6405be2d480..cfedd30cea3 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 7d601abe56b..17e7aad08f6 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 83e685c0011..56a5df87cb3 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 *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::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 9b7c2c0dd6e..d66f1463efa 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 564ffb05f28..eec89dbce5b 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -12000,11 +12000,14 @@ tsubst_aggr_type (tree t, } } +static GTY(()) hash_map *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; @@ -12014,6 +12017,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: @@ -12066,6 +12080,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::create_ggc (37); + defarg_inst->put (parm, arg); + } + return arg; } @@ -12087,11 +12108,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); @@ -14499,13 +14521,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 76eb2da7836..3d1c113d832 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 struct S { // { dg-error "cannot convert" } - T i{[this] {}}; +template struct S { + T i{[this] {}}; // { dg-error "cannot convert" } }; -S s; // { dg-error "cannot convert" } +S 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 b473ed553c9..ee39ab4b169 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 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 @@ -208,11 +208,11 @@ struct U3 template 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 (); U3 u30; U3 u31 (5); - W3 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 w3; // { dg-message "" } } -- 2.30.2