]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: processing_template_decl vs template depth [PR103408]
authorPatrick Palka <ppalka@redhat.com>
Tue, 14 Dec 2021 13:15:52 +0000 (08:15 -0500)
committerPatrick Palka <ppalka@redhat.com>
Tue, 14 Dec 2021 13:15:52 +0000 (08:15 -0500)
We use processing_template_decl in two slightly different ways: as a
flag to signal that we're dealing with templated trees, and as a measure
of the current syntactic template nesting depth.  This overloaded
meaning of p_t_d is conceptually confusing and leads to bugs that we end
up working around in an ad-hoc fashion.

This patch replaces all uses of processing_template_decl that care about
its magnitude to instead look at the depth of current_template_parms
via a new macro current_template_depth.  This allows us to eliminate 3
workarounds in the concepts code: two about non-templated
requires-expressions (in constraint.cc) and one about lambdas inside
constraints (in cp_parser_requires_clause_expression etc).  This also
fixes the testcase in PR103408 about auto(x) used inside a non-templated
requires-expression.

The replacement was mostly mechanical, aside from two issues:

  * In synthesize_implicit_template_parm, when introducing a new template
    parameter list for an abbreviated function template, we need to add
    the new level of current_template_parms sooner, before calling
    process_template_parm, since this latter function now looks at
    current_template_depth to determine the level of the new parameter.

  * In instantiate_class_template_1 after substituting a template
    friend declaration, we currently increment processing_template_decl
    around the call to make_friend_class so that the friend_depth
    computation within this subroutine yields a nonzero value.  We could
    just replace this with an equivalent manipulation of
    current_template_depth, but this patch instead rewrites the
    friend_depth calculation within make_friend_class to not depend on
    p_t_d / c_t_d at all when called from instantiate_class_template_1.

PR c++/103408

gcc/cp/ChangeLog:

* constraint.cc (type_deducible_p): Remove workaround for
non-templated requires-expressions.
(normalize_placeholder_type_constraints): Likewise.
* cp-tree.h (current_template_depth): Define.
(PROCESSING_REAL_TEMPLATE_DECL): Inspect current_template_depth
instead of the magnitude of processing_template_decl.
* decl.c (start_decl): Likewise.
(grokfndecl): Likewise.
(grokvardecl): Likewise.
(grokdeclarator): Likewise.
* friend.c (make_friend_class): Likewise.  Calculate
friend_depth differently when called at instantiation time
instead of parse time.
(do_friend): Likewise.
* parser.c (cp_parser_requires_clause_expression): Remove
workaround for lambdas inside constraints.
(cp_parser_constraint_expression): Likewise.
(cp_parser_requires_expression): Likewise.
(synthesize_implicit_template_parm): Add to current_template_parms
before calling process_template_parm.
* pt.c (inline_needs_template_parms): Inspect
current_template_depth instead of the magnitude of
processing_template_decl.
(push_inline_template_parms_recursive): Likewise.
(maybe_begin_member_template_processing): Likewise.
(begin_template_parm_list): Likewise.
(process_template_parm): Likewise.
(end_template_parm_list): Likewise.
(push_template_decl): Likewise.
(add_inherited_template_parms): Likewise.
(instantiate_class_template_1): Don't adjust
processing_template_decl around the call to make_friend_class.
adjust_processing_template_decl to adjust_template_depth.  Set
current_template_parms instead of processing_template_decl when
adjust_template_depth.
(make_auto_1): Inspect current_template_depth instead of the
magnitude of processing_template_decl.
(splice_late_return_type): Likewise.
* semantics.c (fixup_template_type): Likewise.

gcc/testsuite/ChangeLog:

* g++.dg/concepts/diagnostic18.C: Expect a "constraints on a
non-templated function" error.
* g++.dg/cpp23/auto-fncast11.C: New test.

gcc/cp/constraint.cc
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/friend.c
gcc/cp/parser.c
gcc/cp/pt.c
gcc/cp/semantics.c
gcc/testsuite/g++.dg/concepts/diagnostic18.C
gcc/testsuite/g++.dg/cpp23/auto-fncast11.C [new file with mode: 0644]

index 2896efdd7f2e8dd78f33a59eeb016474ab9aa7f6..566f4e38fac9a1e8664da1c6f0bd7799709fa343 100644 (file)
@@ -2016,14 +2016,6 @@ type_deducible_p (tree expr, tree type, tree placeholder, tree args,
      references are preserved in the result.  */
   expr = force_paren_expr_uneval (expr);
 
-  /* When args is NULL, we're evaluating a non-templated requires expression,
-     but even those are parsed under processing_template_decl == 1, and so the
-     placeholder 'auto' inside this return-type-requirement has level 2.  In
-     order to have all parms and arguments match up for satisfaction, we need
-     to pass an empty level of OUTER_TARGS in this case.  */
-  if (!args)
-    args = make_tree_vec (0);
-
   tree deduced_type = do_auto_deduction (type, expr, placeholder,
                                         info.complain, adc_requirement,
                                         /*outer_targs=*/args);
@@ -3064,14 +3056,6 @@ normalize_placeholder_type_constraints (tree t, bool diag)
      parameters for normalization.  */
   tree initial_parms = TREE_PURPOSE (ci);
 
-  if (!initial_parms && TEMPLATE_TYPE_LEVEL (t) == 2)
-    /* This is a return-type-requirement of a non-templated requires-expression,
-       which are parsed under processing_template_decl == 1 and empty
-       current_template_parms; hence the 'auto' has level 2 and initial_parms
-       is empty.  Fix up initial_parms to be consistent with the value of
-       processing_template_decl whence the 'auto' was created.  */
-    initial_parms = build_tree_list (size_int (1), make_tree_vec (0));
-
   /* The 'auto' itself is used as the first argument in its own constraints,
      and its level is one greater than its template depth.  So in order to
      capture all used template parameters, we need to add an extra level of
index e4330fb1f8bed69adce815944d5e310dba1a7d5f..7f32cf56383cf4131eee704122f0df86ef53f4c2 100644 (file)
@@ -1886,6 +1886,8 @@ extern GTY(()) struct saved_scope *scope_chain;
    stored in the TREE_VALUE.  */
 
 #define current_template_parms scope_chain->template_parms
+#define current_template_depth \
+  (current_template_parms ? TMPL_PARMS_DEPTH (current_template_parms) : 0)
 
 #define processing_template_decl scope_chain->x_processing_template_decl
 #define processing_specialization scope_chain->x_processing_specialization
@@ -5099,7 +5101,7 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
    full specialization.  */
 #define PROCESSING_REAL_TEMPLATE_DECL_P() \
   (!processing_template_parmlist \
-   && processing_template_decl > template_class_depth (current_scope ()))
+   && current_template_depth > template_class_depth (current_scope ()))
 
 /* Nonzero if this VAR_DECL or FUNCTION_DECL has already been
    instantiated, i.e. its definition has been generated from the
index 56f80775ca03e9e6648107c582c188eca331f9be..7c2048c6acb783a406bc2b9f91ac97ec5da6d6f0 100644 (file)
@@ -5567,7 +5567,7 @@ start_decl (const cp_declarator *declarator,
 
   if (TYPE_P (context) && COMPLETE_TYPE_P (complete_type (context)))
     {
-      bool this_tmpl = (processing_template_decl
+      bool this_tmpl = (current_template_depth
                        > template_class_depth (context));
       if (VAR_P (decl))
        {
@@ -9878,7 +9878,7 @@ grokfndecl (tree ctype,
       tree ctx = friendp ? current_class_type : ctype;
       bool block_local = TREE_CODE (current_scope ()) == FUNCTION_DECL;
       bool memtmpl = (!block_local
-                     && (processing_template_decl
+                     && (current_template_depth
                          > template_class_depth (ctx)));
       if (memtmpl)
        {
@@ -10300,7 +10300,7 @@ grokfndecl (tree ctype,
   if (ctype != NULL_TREE && check)
     {
       tree old_decl = check_classfn (ctype, decl,
-                                    (processing_template_decl
+                                    (current_template_depth
                                      > template_class_depth (ctype))
                                     ? current_template_parms
                                     : NULL_TREE);
@@ -10576,7 +10576,7 @@ grokvardecl (tree type,
         }
     }
   else if (flag_concepts
-          && processing_template_decl > template_class_depth (scope))
+          && current_template_depth > template_class_depth (scope))
     {
       tree reqs = TEMPLATE_PARMS_CONSTRAINTS (current_template_parms);
       tree ci = build_constraints (reqs, NULL_TREE);
@@ -13975,7 +13975,7 @@ grokdeclarator (const cp_declarator *declarator,
                  }
 
                /* Set the constraints on the declaration.  */
-               bool memtmpl = (processing_template_decl
+               bool memtmpl = (current_template_depth
                                > template_class_depth (current_class_type));
                if (memtmpl)
                  {
index 4f6288414d988ff3957329caea68a81af2637c69..e552659ab5777ad4cf73d16fa2d52b7088f0f353 100644 (file)
@@ -261,7 +261,20 @@ make_friend_class (tree type, tree friend_type, bool complain)
      The friend is a template friend iff FRIEND_DEPTH is nonzero.  */
 
   int class_template_depth = template_class_depth (type);
-  int friend_depth = processing_template_decl - class_template_depth;
+  int friend_depth = 0;
+  if (current_template_depth)
+    /* When processing a friend declaration at parse time, just compare the
+       current depth to that of the class template.  */
+    friend_depth = current_template_depth - class_template_depth;
+  else
+    {
+      /* Otherwise, we got here from instantiate_class_template.  Determine
+        the friend depth by looking at the template parameters used within
+        FRIEND_TYPE.  */
+      gcc_checking_assert (class_template_depth == 0);
+      while (uses_template_parms_level (friend_type, friend_depth + 1))
+       ++friend_depth;
+    }
 
   if (! MAYBE_CLASS_TYPE_P (friend_type)
       && TREE_CODE (friend_type) != TEMPLATE_TEMPLATE_PARM)
@@ -351,8 +364,13 @@ make_friend_class (tree type, tree friend_type, bool complain)
          tree name = TYPE_IDENTIFIER (friend_type);
          tree decl;
 
-         if (!uses_template_parms_level (ctype, class_template_depth
-                                                + friend_depth))
+         /* We need to distinguish a TYPENAME_TYPE for the non-template
+            class B in
+              template<class T> friend class A<T>::B;
+            vs for the class template B in
+              template<class T> template<class U> friend class A<T>::B;  */
+         if (current_template_depth
+             && !uses_template_parms_level (ctype, current_template_depth))
            template_member_p = true;
 
          if (class_template_depth)
@@ -517,7 +535,7 @@ do_friend (tree ctype, tree declarator, tree decl,
         3. TEMPLATE_MEMBER_P is true (for `W').  */
 
       int class_template_depth = template_class_depth (current_class_type);
-      int friend_depth = processing_template_decl - class_template_depth;
+      int friend_depth = current_template_depth - class_template_depth;
       /* We will figure this out later.  */
       bool template_member_p = false;
 
index 52225d46d4ee1e7082cab5f3a000be50fa1aa349..c2564e51e41682391a2150ef029a831ce7606501 100644 (file)
@@ -29779,14 +29779,9 @@ static tree
 cp_parser_requires_clause_expression (cp_parser *parser, bool lambda_p)
 {
   processing_constraint_expression_sentinel parsing_constraint;
-  temp_override<int> ovr (processing_template_decl);
-  if (!processing_template_decl)
-    /* Adjust processing_template_decl so that we always obtain template
-       trees here.  We don't do the usual ++processing_template_decl
-       because that would skew the template parameter depth of a lambda
-       within if we're already inside a template.  */
-    processing_template_decl = 1;
+  ++processing_template_decl;
   cp_expr expr = cp_parser_constraint_logical_or_expression (parser, lambda_p);
+  --processing_template_decl;
   if (check_for_bare_parameter_packs (expr))
     expr = error_mark_node;
   return expr;
@@ -29805,12 +29800,10 @@ static tree
 cp_parser_constraint_expression (cp_parser *parser)
 {
   processing_constraint_expression_sentinel parsing_constraint;
-  temp_override<int> ovr (processing_template_decl);
-  if (!processing_template_decl)
-    /* As in cp_parser_requires_clause_expression.  */
-    processing_template_decl = 1;
+  ++processing_template_decl;
   cp_expr expr = cp_parser_binary_expression (parser, false, true,
                                              PREC_NOT_OPERATOR, NULL);
+  --processing_template_decl;
   if (check_for_bare_parameter_packs (expr))
     expr = error_mark_node;
   expr.maybe_add_location_wrapper ();
@@ -29924,11 +29917,9 @@ cp_parser_requires_expression (cp_parser *parser)
       parms = NULL_TREE;
 
     /* Parse the requirement body. */
-    temp_override<int> ovr (processing_template_decl);
-    if (!processing_template_decl)
-      /* As in cp_parser_requires_clause_expression.  */
-      processing_template_decl = 1;
+    ++processing_template_decl;
     reqs = cp_parser_requirement_body (parser);
+    --processing_template_decl;
     if (reqs == error_mark_node)
       return error_mark_node;
   }
@@ -48091,6 +48082,10 @@ synthesize_implicit_template_parm  (cp_parser *parser, tree constr)
   gcc_assert(!proto || TREE_CODE (proto) == TYPE_DECL);
   synth_tmpl_parm = finish_template_type_parm (class_type_node, synth_id);
 
+  if (become_template)
+    current_template_parms = tree_cons (size_int (current_template_depth + 1),
+                                       NULL_TREE, current_template_parms);
+
   /* Attach the constraint to the parm before processing.  */
   tree node = build_tree_list (NULL_TREE, synth_tmpl_parm);
   TREE_TYPE (node) = constr;
@@ -48130,8 +48125,7 @@ synthesize_implicit_template_parm  (cp_parser *parser, tree constr)
 
       tree new_parms = make_tree_vec (1);
       TREE_VEC_ELT (new_parms, 0) = parser->implicit_template_parms;
-      current_template_parms = tree_cons (size_int (processing_template_decl),
-                                         new_parms, current_template_parms);
+      TREE_VALUE (current_template_parms) = new_parms;
     }
   else
     {
index cbdb4b566aae8db89ad1f5ede18b429acb008abc..42133a30c97f7a4dabc8a1583ad0908c4a052aaa 100644 (file)
@@ -448,7 +448,7 @@ inline_needs_template_parms (tree decl, bool nsdmi)
     return false;
 
   return (TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (most_general_template (decl)))
-         > (processing_template_decl + DECL_TEMPLATE_SPECIALIZATION (decl)));
+         > (current_template_depth + DECL_TEMPLATE_SPECIALIZATION (decl)));
 }
 
 /* Subroutine of maybe_begin_member_template_processing.
@@ -467,7 +467,7 @@ push_inline_template_parms_recursive (tree parmlist, int levels)
 
   ++processing_template_decl;
   current_template_parms
-    = tree_cons (size_int (processing_template_decl),
+    = tree_cons (size_int (current_template_depth + 1),
                 parms, current_template_parms);
   TEMPLATE_PARMS_FOR_INLINE (current_template_parms) = 1;
 
@@ -523,7 +523,7 @@ maybe_begin_member_template_processing (tree decl)
   if (inline_needs_template_parms (decl, nsdmi))
     {
       parms = DECL_TEMPLATE_PARMS (most_general_template (decl));
-      levels = TMPL_PARMS_DEPTH (parms) - processing_template_decl;
+      levels = TMPL_PARMS_DEPTH (parms) - current_template_depth;
 
       if (DECL_TEMPLATE_SPECIALIZATION (decl))
        {
@@ -716,7 +716,7 @@ begin_template_parm_list (void)
 
   /* Add a dummy parameter level while we process the parameter list.  */
   current_template_parms
-    = tree_cons (size_int (processing_template_decl),
+    = tree_cons (size_int (current_template_depth + 1),
                 make_tree_vec (0),
                 current_template_parms);
 }
@@ -4613,8 +4613,8 @@ process_template_parm (tree list, location_t parm_loc, tree parm,
       TREE_CONSTANT (decl) = 1;
       TREE_READONLY (decl) = 1;
       DECL_INITIAL (parm) = DECL_INITIAL (decl)
-       = build_template_parm_index (idx, processing_template_decl,
-                                    processing_template_decl,
+       = build_template_parm_index (idx, current_template_depth,
+                                    current_template_depth,
                                     decl, TREE_TYPE (parm));
 
       TEMPLATE_PARM_PARAMETER_PACK (DECL_INITIAL (parm))
@@ -4655,8 +4655,8 @@ process_template_parm (tree list, location_t parm_loc, tree parm,
       TYPE_STUB_DECL (t) = decl;
       parm = decl;
       TEMPLATE_TYPE_PARM_INDEX (t)
-       = build_template_parm_index (idx, processing_template_decl,
-                                    processing_template_decl,
+       = build_template_parm_index (idx, current_template_depth,
+                                    current_template_depth,
                                     decl, TREE_TYPE (parm));
       TEMPLATE_TYPE_PARAMETER_PACK (t) = is_parameter_pack;
       if (TREE_CODE (t) == TEMPLATE_TEMPLATE_PARM)
@@ -4705,7 +4705,7 @@ end_template_parm_list (tree parms)
   current_template_parms = TREE_CHAIN (current_template_parms);
 
   current_template_parms
-    = tree_cons (size_int (processing_template_decl),
+    = tree_cons (size_int (current_template_depth + 1),
                 saved_parmlist, current_template_parms);
 
   for (unsigned ix = 0; parms; ix++)
@@ -5747,7 +5747,7 @@ push_template_decl (tree decl, bool is_friend)
   /* See if this is a primary template.  */
   bool is_primary = false;
   if (is_friend && ctx
-      && uses_template_parms_level (ctx, processing_template_decl))
+      && uses_template_parms_level (ctx, current_template_depth))
     /* A friend template that specifies a class context, i.e.
          template <typename T> friend void A<T>::f();
        is not primary.  */
@@ -6157,7 +6157,7 @@ add_inherited_template_parms (tree fn, tree inherited)
     = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (inherited));
   inner_parms = copy_node (inner_parms);
   tree parms
-    = tree_cons (size_int (processing_template_decl + 1),
+    = tree_cons (size_int (current_template_depth + 1),
                 inner_parms, current_template_parms);
   tree tmpl = build_template_decl (fn, parms, /*member*/true);
   tree args = template_parms_to_args (parms);
@@ -12165,13 +12165,10 @@ instantiate_class_template_1 (tree type)
              /* Build new CLASSTYPE_FRIEND_CLASSES.  */
 
              tree friend_type = t;
-             bool adjust_processing_template_decl = false;
-
              if (TREE_CODE (friend_type) == TEMPLATE_DECL)
                {
                  /* template <class T> friend class C;  */
                  friend_type = tsubst_friend_class (friend_type, args);
-                 adjust_processing_template_decl = true;
                }
              else if (TREE_CODE (friend_type) == UNBOUND_CLASS_TEMPLATE)
                {
@@ -12180,7 +12177,6 @@ instantiate_class_template_1 (tree type)
                                        tf_warning_or_error, NULL_TREE);
                  if (TREE_CODE (friend_type) == TEMPLATE_DECL)
                    friend_type = TREE_TYPE (friend_type);
-                 adjust_processing_template_decl = true;
                }
              else if (TREE_CODE (friend_type) == TYPENAME_TYPE
                       || TREE_CODE (friend_type) == TEMPLATE_TYPE_PARM)
@@ -12199,8 +12195,6 @@ instantiate_class_template_1 (tree type)
                  ++processing_template_decl;
                  friend_type = tsubst (friend_type, args,
                                        tf_warning_or_error, NULL_TREE);
-                 if (dependent_type_p (friend_type))
-                   adjust_processing_template_decl = true;
                  --processing_template_decl;
                }
              else if (uses_template_parms (friend_type))
@@ -12218,19 +12212,8 @@ instantiate_class_template_1 (tree type)
 
                 We don't have to do anything in these cases.  */
 
-             if (adjust_processing_template_decl)
-               /* Trick make_friend_class into realizing that the friend
-                  we're adding is a template, not an ordinary class.  It's
-                  important that we use make_friend_class since it will
-                  perform some error-checking and output cross-reference
-                  information.  */
-               ++processing_template_decl;
-
              if (friend_type != error_mark_node)
                make_friend_class (type, friend_type, /*complain=*/false);
-
-             if (adjust_processing_template_decl)
-               --processing_template_decl;
            }
          else
            {
@@ -28416,7 +28399,7 @@ make_auto_1 (tree name, bool set_canonical)
   TYPE_NAME (au) = build_decl (input_location, TYPE_DECL, name, au);
   TYPE_STUB_DECL (au) = TYPE_NAME (au);
   TEMPLATE_TYPE_PARM_INDEX (au) = build_template_parm_index
-    (0, processing_template_decl + 1, processing_template_decl + 1,
+    (0, current_template_depth + 1, current_template_depth + 1,
      TYPE_NAME (au), NULL_TREE);
   if (set_canonical)
     TYPE_CANONICAL (au) = canonical_type_parameter (au);
@@ -30069,7 +30052,7 @@ splice_late_return_type (tree type, tree late_return_type)
     }
 
   if (tree auto_node = find_type_usage (type, is_auto))
-    if (TEMPLATE_TYPE_LEVEL (auto_node) <= processing_template_decl)
+    if (TEMPLATE_TYPE_LEVEL (auto_node) <= current_template_depth)
       {
        /* In an abbreviated function template we didn't know we were dealing
           with a function template when we saw the auto return type, so rebuild
index cdf63c15e21c75f37fdd3a32b01e713d5386beba..d8b20ff5b1bd67db57a09a3b9a1a756e83a4ff01 100644 (file)
@@ -3629,7 +3629,7 @@ fixup_template_type (tree type)
   // the scope we're trying to enter.
   tree parms = current_template_parms;
   int depth = template_class_depth (type);
-  for (int n = processing_template_decl; n > depth && parms; --n)
+  for (int n = current_template_depth; n > depth && parms; --n)
     parms = TREE_CHAIN (parms);
   if (!parms)
     return type;
index 79f371b8092a01f56d6aaa9bd0402b8603a42650..c13b0472a6454d0758f35de9ddb87f77956beb73 100644 (file)
@@ -1,7 +1,7 @@
 // PR c++/100055
 // { dg-do compile { target concepts } }
 
-void foo(auto&& arg) requires({}); // { dg-error "statement-expressions are not allowed|braced-groups" }
+void foo(auto&& arg) requires({}); // { dg-error "statement-expressions are not allowed|braced-groups|non-templated" }
 
 template <auto = 0> requires ([]{}()); // { dg-error "expected unqualified-id" }
-auto f() requires ([]{}());
+auto f() requires ([]{}()); // { dg-error "constraints on a non-templated" }
diff --git a/gcc/testsuite/g++.dg/cpp23/auto-fncast11.C b/gcc/testsuite/g++.dg/cpp23/auto-fncast11.C
new file mode 100644 (file)
index 0000000..669dda1
--- /dev/null
@@ -0,0 +1,19 @@
+// PR c++/103408
+// { dg-do compile { target c++23 } }
+
+static_assert(requires { auto(0); });
+static_assert(requires { auto{0}; });
+
+static_assert(requires { auto(auto(0)); });
+static_assert(requires { auto{auto{0}}; });
+
+static_assert(requires { auto(auto(auto(0))); });
+static_assert(requires { auto{auto{auto{0}}}; });
+
+static_assert(requires { requires auto(true); });
+static_assert(requires { requires auto(auto(true)); });
+
+static_assert(!requires { requires auto(false); });
+static_assert(!requires { requires auto(auto(false)); });
+
+auto f() requires (auto(false)); // { dg-error "constraints on non-templated" }