Instantiate default arguments/member initializers once.
authorJason Merrill <jason@redhat.com>
Tue, 29 Aug 2017 19:40:08 +0000 (15:40 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Tue, 29 Aug 2017 19:40:08 +0000 (15:40 -0400)
* 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
gcc/cp/call.c
gcc/cp/cp-tree.h
gcc/cp/init.c
gcc/cp/parser.c
gcc/cp/pt.c
gcc/testsuite/g++.dg/cpp0x/lambda/lambda-62155.C
gcc/testsuite/g++.dg/cpp1z/direct-enum-init1.C

index fc244a1..308e13a 100644 (file)
@@ -1,5 +1,15 @@
 2017-08-29  Jason Merrill  <jason@redhat.com>
 
+       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.
index 6405be2..cfedd30 100644 (file)
@@ -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:
 
index 7d601ab..17e7aad 100644 (file)
@@ -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,
index 83e685c..56a5df8 100644 (file)
@@ -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));
index 9b7c2c0..d66f146 100644 (file)
@@ -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;
     }
index 564ffb0..eec89db 100644 (file)
@@ -12000,11 +12000,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;
@@ -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<tree,tree>::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
index 76eb2da..3d1c113 100644 (file)
@@ -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" }
index b473ed5..ee39ab4 100644 (file)
@@ -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 "" }
 }