public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 1/2] c++: requires-exprs and partial constraint subst [PR112769]
@ 2024-02-02 19:41 Patrick Palka
  2024-02-02 19:41 ` [PATCH 2/2] c++: remove lookup_template_class's entering_scope flag Patrick Palka
                   ` (2 more replies)
  0 siblings, 3 replies; 13+ messages in thread
From: Patrick Palka @ 2024-02-02 19:41 UTC (permalink / raw)
  To: gcc-patches; +Cc: jason, Patrick Palka

Bootstrapped and regtested on x86_64-pc-linux-gnu, does this
look OK for trunk?

-- >8 --

In r11-3261-gb28b621ac67bee we made tsubst_requires_expr never partially
substitute into a requires-expression so as to avoid checking its
requirements out of order during e.g. generic lambda regeneration.

Unfortunately we still do need to partially substitute into a
requires-expression in rare cases, in particular when it's used in
associated constraints that we are directly substituting for sake of
declaration matching or dguide constraint rewriting.  We can identify
this situation by checking processing_constraint_expression_p, so this
patch uses this predicate to control whether we defer substitution or
partially substitute.  The entering_scope=true change in tsubst_baselink
is needed to avoid ICEing from tsubst_baselink during name lookup when
rewriting std::ranges::ref_view's dguide constraints.

	PR c++/112769
	PR c++/110006

gcc/cp/ChangeLog:

	* constraint.cc (tsubst_simple_requirement): Return a
	substituted _REQ node when processing_template_decl.
	(tsubst_type_requirement): Likewise.
	(tsubst_compound_requirement): Likewise.
	(tsubst_nested_requirement): Likewise.
	(tsubst_requires_expr): Don't defer partial substitution
	when processing_constraint_expression_p is true, in which
	case return a substituted REQUIRES_EXPR.
	* pt.cc (tsubst_baselink): Use tsubst_aggr_type with
	entring_scope=true instead of tsubst to substitute
	qualifying_scope.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/class-deduction-alias18.C: New test.
	* g++.dg/cpp2a/concepts-friend16.C: New test.
---
 gcc/cp/constraint.cc                          | 38 +++++++++++++++++--
 gcc/cp/pt.cc                                  |  3 +-
 .../g++.dg/cpp2a/class-deduction-alias18.C    | 13 +++++++
 .../g++.dg/cpp2a/concepts-friend16.C          | 25 ++++++++++++
 4 files changed, 74 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/class-deduction-alias18.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-friend16.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index fef68cf7ab2..450ae548f9a 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -2028,6 +2028,8 @@ tsubst_simple_requirement (tree t, tree args, sat_info info)
   tree expr = tsubst_valid_expression_requirement (t0, args, info);
   if (expr == error_mark_node)
     return error_mark_node;
+  if (processing_template_decl)
+    return finish_simple_requirement (EXPR_LOCATION (t), expr);
   return boolean_true_node;
 }
 
@@ -2068,6 +2070,8 @@ tsubst_type_requirement (tree t, tree args, sat_info info)
   tree type = tsubst_type_requirement_1 (t0, args, info, EXPR_LOCATION (t));
   if (type == error_mark_node)
     return error_mark_node;
+  if (processing_template_decl)
+    return finish_type_requirement (EXPR_LOCATION (t), type);
   return boolean_true_node;
 }
 
@@ -2182,6 +2186,9 @@ tsubst_compound_requirement (tree t, tree args, sat_info info)
 	}
     }
 
+  if (processing_template_decl)
+    return finish_compound_requirement (EXPR_LOCATION (t),
+					expr, type, noexcept_p);
   return boolean_true_node;
 }
 
@@ -2190,6 +2197,15 @@ tsubst_compound_requirement (tree t, tree args, sat_info info)
 static tree
 tsubst_nested_requirement (tree t, tree args, sat_info info)
 {
+  if (processing_template_decl)
+    {
+      tree req = TREE_OPERAND (t, 0);
+      req = tsubst_constraint (req, args, info.complain, info.in_decl);
+      if (req == error_mark_node)
+	return error_mark_node;
+      return finish_nested_requirement (EXPR_LOCATION (t), req);
+    }
+
   sat_info quiet (tf_none, info.in_decl);
   tree result = constraint_satisfaction_value (t, args, quiet);
   if (result == boolean_true_node)
@@ -2330,18 +2346,25 @@ tsubst_requires_expr (tree t, tree args, sat_info info)
 
   args = add_extra_args (REQUIRES_EXPR_EXTRA_ARGS (t), args,
 			 info.complain, info.in_decl);
-  if (processing_template_decl)
+  if (processing_template_decl
+      && !processing_constraint_expression_p ())
     {
       /* We're partially instantiating a generic lambda.  Substituting into
 	 this requires-expression now may cause its requirements to get
 	 checked out of order, so instead just remember the template
-	 arguments and wait until we can substitute them all at once.  */
+	 arguments and wait until we can substitute them all at once.
+
+	 Except if this requires-expr is part of associated constraints
+	 that we're substituting into directly (for e.g. declaration
+	 matching or dguide constraint rewriting), in which case we need
+	 to partially substitute.  */
       t = copy_node (t);
       REQUIRES_EXPR_EXTRA_ARGS (t) = build_extra_args (t, args, info.complain);
       return t;
     }
 
-  if (tree parms = REQUIRES_EXPR_PARMS (t))
+  tree parms = REQUIRES_EXPR_PARMS (t);
+  if (parms)
     {
       parms = tsubst_constraint_variables (parms, args, info);
       if (parms == error_mark_node)
@@ -2349,10 +2372,13 @@ tsubst_requires_expr (tree t, tree args, sat_info info)
     }
 
   tree result = boolean_true_node;
+  if (processing_template_decl)
+    result = NULL_TREE;
   for (tree reqs = REQUIRES_EXPR_REQS (t); reqs; reqs = TREE_CHAIN (reqs))
     {
       tree req = TREE_VALUE (reqs);
-      if (tsubst_requirement (req, args, info) == error_mark_node)
+      req = tsubst_requirement (req, args, info);
+      if (req == error_mark_node)
 	{
 	  result = boolean_false_node;
 	  if (info.diagnose_unsatisfaction_p ())
@@ -2360,7 +2386,11 @@ tsubst_requires_expr (tree t, tree args, sat_info info)
 	  else
 	    break;
 	}
+      else if (processing_template_decl)
+	result = tree_cons (NULL_TREE, req, result);
     }
+  if (processing_template_decl && result != boolean_false_node)
+    result = finish_requires_expr (EXPR_LOCATION (t), parms, nreverse (result));
   return result;
 }
 
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 9d30a271713..17ecd4b9c5a 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -17065,7 +17065,8 @@ tsubst_baselink (tree baselink, tree object_type,
 {
   bool qualified_p = BASELINK_QUALIFIED_P (baselink);
   tree qualifying_scope = BINFO_TYPE (BASELINK_ACCESS_BINFO (baselink));
-  qualifying_scope = tsubst (qualifying_scope, args, complain, in_decl);
+  qualifying_scope = tsubst_aggr_type (qualifying_scope, args, complain, in_decl,
+				       /*entering_scope=*/true);
 
   tree optype = BASELINK_OPTYPE (baselink);
   optype = tsubst (optype, args, complain, in_decl);
diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias18.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias18.C
new file mode 100644
index 00000000000..0bb11bb944c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias18.C
@@ -0,0 +1,13 @@
+// PR c++/112769
+// { dg-do compile { target c++20 } }
+
+template<int I, typename T>
+struct type
+{
+    type(T) requires requires { T{0}; };
+};
+
+template <typename T>
+using alias = type<0, T>;
+
+alias foo{123};
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend16.C b/gcc/testsuite/g++.dg/cpp2a/concepts-friend16.C
new file mode 100644
index 00000000000..18974eeb172
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend16.C
@@ -0,0 +1,25 @@
+// PR c++/110006
+// { dg-do compile { target c++20 } }
+
+template<typename T>
+class s;
+
+template<typename T>
+void constraint(s<T> const&, int&);
+
+template<typename U, typename T2>
+U function(s<T2> const x)
+	requires requires (U& u) { constraint(x, u); };
+
+template<typename T>
+class s
+{
+	template<typename U, typename T2>
+	friend U function(s<T2> const x)
+		requires requires (U& u) { constraint(x, u); };
+};
+
+int f(s<int> q)
+{
+	return function<int>(q); // { dg-bogus "ambiguous" }
+}
-- 
2.43.0.493.gbc7ee2e5e1


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 2/2] c++: remove lookup_template_class's entering_scope flag
  2024-02-02 19:41 [PATCH 1/2] c++: requires-exprs and partial constraint subst [PR112769] Patrick Palka
@ 2024-02-02 19:41 ` Patrick Palka
  2024-05-01 19:41   ` Patrick Palka
  2024-02-02 19:55 ` [PATCH 1/2] c++: requires-exprs and partial constraint subst [PR112769] Jason Merrill
  2024-02-02 20:01 ` Jason Merrill
  2 siblings, 1 reply; 13+ messages in thread
From: Patrick Palka @ 2024-02-02 19:41 UTC (permalink / raw)
  To: gcc-patches; +Cc: jason, Patrick Palka

Bootstrapped and regtested on x86_64-pc-linux, does this look like
an improvement?  This is not a bugfix and barely related to the previous
patch, but the previous patch's new use of entering_scope=true motivated
me to submit this patch since it seems like a nice simplification.

-- >8 --

lookup_template_class's entering_scope flag controls whether to prefer
returning the primary template type A<T> instead of the corresponding
implicit instantiation A<T>.  When we want to set this flag as part of
substitution, we need to use tsubst_aggr_type which also takes the flag,
but this separate entry point to substitution turned out to be subtly
problematic because it doesn't reuse typedefs like tsubst does, which
r13-4729-gbe124477b38a71 fixed in a way that respects the flag after the
fact, by adjusting the entering_scope=false result of
lookup_template_class as if entering_scope=true was passed.

But if that's possible then it means lookup_template_class's the
entering_scope flag is not necessary after all -- we can just do the
after-the-fact adjustment everywhere that we currently pass
entering_scope=true to it and tsubst_aggr_type.

To that end, this patch replaces this flag with an adjustment function
adjust_type_for_entering_scope, to be used whereever we currently need
the entering_scope=true behavior.  This means we can also get rid of
tsubst_aggr_type, since the only reason we needed this entry point
was to be able to pass entering_scope=true to lookup_template_class.

gcc/cp/ChangeLog:

	* coroutines.cc (instantiate_coro_traits): Adjust call to
	lookup_template_class.
	(instantiate_coro_handle_for_promise_type): Likewise.
	* cp-tree.h (adjust_type_for_entering_scope): Declare.
	(lookup_template_class): Adjust declaration.
	* decl.cc (make_typename_type): Adjust call to
	lookup_template_class. Likewise.
	(get_tuple_size): Likewise.
	(get_tuple_element_type): Likewise.
	* pt.cc (adjust_type_for_entering_scope): Define.
	(lookup_template_class): Remove entering_scope parameter.
	Replace tsubst_aggr_type call with tsubst followed by
	adjust_type_for_entering_scope.
	(tsubst_aggr_type): Remove.
	(tsubst_aggr_type_1): Inline into tsubst.
	(tsubst_function_decl): Replace tsubst_aggr_type call
	with tsubst followed by adjust_type_for_entering_scope.
	(tsubst_template_decl): Likewise.
	(tsubst_decl): Likewise.
	(tsubst) <case RECORD_TYPE, UNION_TYPE, ENUMERAL_TYPE>:
	Inlined from tsubst_aggr_type_1.
	<case BOUND_TEMPLATE_TEMPLATE_PARM>: Adjust calls to
	lookup_template_class.
	<case TYPENAME_TYPE>: Replace tsubst_aggr_type call with
	tsubst tsubst_scope followed by adjust_type_for_entering_scope.
	<case UNBOUND_CLASS_TEMPLATE>: Replace tsubst_aggr_type
	call with tsubst followed by adjust_type_for_entering_scope.
	Increment processing_template_decl when substituting the
	context.
	(tsubst_baselink): Replace tsubst_aggr_type call with tsubst
	followed by adjust_type_for_entering_scope.
	(tsubst_expr) <case FIELD_DECL>: Replace tsubst_aggr_type
	call with tsubst followed by adjust_type_for_entering_scope.
	<case TEMPLATE_DECL>: Likewise.
	(instantiate_template): Likewise.
	(resolve_typename_type): Adjust lookup_template_class call
	and call adjust_type_for_entering_scope afterward.
	(listify): Adjust lookup_template_class call.
	(alias_ctad_tweaks): Likewise.
	* semantics.cc (finish_template_type): Adjust lookup_template_class
	call and maybe call adjust_type_for_entering_scope afterward.
---
 gcc/cp/coroutines.cc |   4 +-
 gcc/cp/cp-tree.h     |   3 +-
 gcc/cp/decl.cc       |   4 +-
 gcc/cp/pt.cc         | 212 +++++++++++++++++--------------------------
 gcc/cp/semantics.cc  |   4 +-
 5 files changed, 91 insertions(+), 136 deletions(-)

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index 3194c911e8c..8dab173d5cb 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -353,7 +353,7 @@ instantiate_coro_traits (tree fndecl, location_t kw)
   tree traits_class
     = lookup_template_class (coro_traits_templ, targ,
 			     /*in_decl=*/NULL_TREE, /*context=*/NULL_TREE,
-			     /*entering scope=*/false, tf_warning_or_error);
+			     tf_warning_or_error);
 
   if (traits_class == error_mark_node)
     {
@@ -400,7 +400,7 @@ instantiate_coro_handle_for_promise_type (location_t kw, tree promise_type)
     = lookup_template_class (coro_handle_identifier, targ,
 			     /* in_decl=*/NULL_TREE,
 			     /* context=*/std_node,
-			     /* entering scope=*/false, tf_warning_or_error);
+			     tf_warning_or_error);
 
   if (handle_type == error_mark_node)
     {
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 969c7239c97..4f73a7c84b6 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7506,8 +7506,9 @@ extern tree push_template_decl			(tree, bool is_friend = false);
 extern tree add_inherited_template_parms	(tree, tree);
 extern void template_parm_level_and_index	(tree, int*, int*);
 extern bool redeclare_class_template		(tree, tree, tree);
+extern tree adjust_type_for_entering_scope	(tree);
 extern tree lookup_template_class		(tree, tree, tree, tree,
-						 int, tsubst_flags_t);
+						 tsubst_flags_t);
 extern tree lookup_template_function		(tree, tree);
 extern tree lookup_template_variable		(tree, tree, tsubst_flags_t);
 extern bool uses_template_parms			(tree);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 3e41fd4fa31..8149aab5781 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -4569,7 +4569,6 @@ make_typename_type (tree context, tree name, enum tag_types tag_type,
     {
       t = lookup_template_class (t, TREE_OPERAND (fullname, 1),
 				 NULL_TREE, context,
-				 /*entering_scope=*/0,
 				 complain | tf_user);
       if (t == error_mark_node)
 	return error_mark_node;
@@ -9163,7 +9162,7 @@ get_tuple_size (tree type)
   tree inst = lookup_template_class (tuple_size_identifier, args,
 				     /*in_decl*/NULL_TREE,
 				     /*context*/std_node,
-				     /*entering_scope*/false, tf_none);
+				     tf_none);
   inst = complete_type (inst);
   if (inst == error_mark_node
       || !COMPLETE_TYPE_P (inst)
@@ -9192,7 +9191,6 @@ get_tuple_element_type (tree type, unsigned i)
   tree inst = lookup_template_class (tuple_element_identifier, args,
 				     /*in_decl*/NULL_TREE,
 				     /*context*/std_node,
-				     /*entering_scope*/false,
 				     tf_warning_or_error);
   return make_typename_type (inst, type_identifier,
 			     none_type, tf_warning_or_error);
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 17ecd4b9c5a..baf9b0e6fda 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -185,8 +185,6 @@ static int unify_pack_expansion (tree, tree, tree,
 static tree copy_template_args (tree);
 static tree tsubst_template_parms (tree, tree, tsubst_flags_t);
 static void tsubst_each_template_parm_constraints (tree, tree, tsubst_flags_t);
-static tree tsubst_aggr_type (tree, tree, tsubst_flags_t, tree, int);
-static tree tsubst_aggr_type_1 (tree, tree, tsubst_flags_t, tree, int);
 static tree tsubst_arg_types (tree, tree, tree, tsubst_flags_t, tree);
 static tree tsubst_function_type (tree, tree, tsubst_flags_t, tree);
 static bool check_specialization_scope (void);
@@ -9873,6 +9871,24 @@ maybe_get_template_decl_from_type_decl (tree decl)
     ? CLASSTYPE_TI_TEMPLATE (TREE_TYPE (decl)) : decl;
 }
 
+/* If TYPE is the generic implicit instantiation A<T>, return the primary
+   template type A<T> (which is suitable for entering into e.g. for name
+   lookup), otherwise return TYPE.  */
+
+tree
+adjust_type_for_entering_scope (tree type)
+{
+  if (CLASS_TYPE_P (type)
+      && dependent_type_p (type)
+      && TYPE_TEMPLATE_INFO (type)
+      /* We detect the generic implicit instantiation A<T> by inspecting
+	 TYPE_CANONICAL, which lookup_template_class sets to the primary
+	 template type A<T>.  */
+      && TYPE_CANONICAL (type) == TREE_TYPE (TYPE_TI_TEMPLATE (type)))
+    type = TYPE_CANONICAL (type);
+  return type;
+}
+
 /* Given an IDENTIFIER_NODE (or type TEMPLATE_DECL) and a chain of
    parameters, find the desired type.
 
@@ -9881,9 +9897,6 @@ maybe_get_template_decl_from_type_decl (tree decl)
    IN_DECL, if non-NULL, is the template declaration we are trying to
    instantiate.
 
-   If ENTERING_SCOPE is nonzero, we are about to enter the scope of
-   the class we are looking up.
-
    Issue error and warning messages under control of COMPLAIN.
 
    If the template class is really a local class in a template
@@ -9899,7 +9912,7 @@ maybe_get_template_decl_from_type_decl (tree decl)
 
 tree
 lookup_template_class (tree d1, tree arglist, tree in_decl, tree context,
-		       int entering_scope, tsubst_flags_t complain)
+		       tsubst_flags_t complain)
 {
   auto_timevar tv (TV_TEMPLATE_INST);
 
@@ -10085,9 +10098,10 @@ lookup_template_class (tree d1, tree arglist, tree in_decl, tree context,
 	   template <class T> class C { void f(C<T>); }
 
 	 the `C<T>' is just the same as `C'.  Outside of the
-	 class, however, such a reference is an instantiation.  */
-      if (entering_scope
-	  || !PRIMARY_TEMPLATE_P (gen_tmpl)
+	 class, however, such a reference is an instantiation.
+	 One can use adjust_type_for_entering_scope to make
+	 this adjustment as needed.  */
+      if (!PRIMARY_TEMPLATE_P (gen_tmpl)
 	  || currently_open_class (template_type))
 	{
 	  tree tinfo = TYPE_TEMPLATE_INFO (template_type);
@@ -10154,8 +10168,8 @@ lookup_template_class (tree d1, tree arglist, tree in_decl, tree context,
 	    context = DECL_CONTEXT (templ);
 	  else
 	    {
-	      context = tsubst_aggr_type (context, arglist,
-					  complain, in_decl, true);
+	      context = tsubst (context, arglist, complain, in_decl);
+	      context = adjust_type_for_entering_scope (context);
 	      /* Try completing the enclosing context if it's not already so.  */
 	      if (context != error_mark_node
 		  && !COMPLETE_TYPE_P (context))
@@ -14091,94 +14105,6 @@ tsubst_each_template_parm_constraints (tree parms, tree args,
   --processing_template_decl;
 }
 
-/* Substitute the ARGS into the indicated aggregate (or enumeration)
-   type T.  If T is not an aggregate or enumeration type, it is
-   handled as if by tsubst.  IN_DECL is as for tsubst.  If
-   ENTERING_SCOPE is nonzero, T is the context for a template which
-   we are presently tsubst'ing.  Return the substituted value.  */
-
-static tree
-tsubst_aggr_type (tree t,
-		  tree args,
-		  tsubst_flags_t complain,
-		  tree in_decl,
-		  int entering_scope)
-{
-  if (t == NULL_TREE)
-    return NULL_TREE;
-
-  /* Handle typedefs via tsubst so that they get consistently reused.  */
-  if (typedef_variant_p (t))
-    {
-      t = tsubst (t, args, complain, in_decl);
-      if (t == error_mark_node)
-	return error_mark_node;
-
-      /* The effect of entering_scope is that for a dependent specialization
-	 A<T>, lookup_template_class prefers to return A's primary template
-	 type instead of the implicit instantiation.  So when entering_scope,
-	 we mirror this behavior by inspecting TYPE_CANONICAL appropriately,
-	 taking advantage of the fact that lookup_template_class links the two
-	 types by setting TYPE_CANONICAL of the latter to the former.  */
-      if (entering_scope
-	  && CLASS_TYPE_P (t)
-	  && dependent_type_p (t)
-	  && TYPE_TEMPLATE_INFO (t)
-	  && TYPE_CANONICAL (t) == TREE_TYPE (TYPE_TI_TEMPLATE (t)))
-	t = TYPE_CANONICAL (t);
-
-      return t;
-    }
-
-  switch (TREE_CODE (t))
-    {
-      case RECORD_TYPE:
-      case ENUMERAL_TYPE:
-      case UNION_TYPE:
-	return tsubst_aggr_type_1 (t, args, complain, in_decl, entering_scope);
-
-      default:
-	return tsubst (t, args, complain, in_decl);
-    }
-}
-
-/* The part of tsubst_aggr_type that's shared with the RECORD_, UNION_
-   and ENUMERAL_TYPE cases of tsubst.  */
-
-static tree
-tsubst_aggr_type_1 (tree t,
-		    tree args,
-		    tsubst_flags_t complain,
-		    tree in_decl,
-		    int entering_scope)
-{
-  if (TYPE_TEMPLATE_INFO (t) && uses_template_parms (t))
-    {
-      complain &= ~tf_qualifying_scope;
-
-      /* Figure out what arguments are appropriate for the
-	 type we are trying to find.  For example, given:
-
-	   template <class T> struct S;
-	   template <class T, class U> void f(T, U) { S<U> su; }
-
-	 and supposing that we are instantiating f<int, double>,
-	 then our ARGS will be {int, double}, but, when looking up
-	 S we only want {double}.  */
-      tree argvec = tsubst_template_args (TYPE_TI_ARGS (t), args,
-					  complain, in_decl);
-      if (argvec == error_mark_node)
-	return error_mark_node;
-
-      tree r = lookup_template_class (t, argvec, in_decl, NULL_TREE,
-				      entering_scope, complain);
-      return cp_build_qualified_type (r, cp_type_quals (t), complain);
-    }
-  else
-    /* This is not a template type, so there's nothing to do.  */
-    return t;
-}
-
 /* Map from a FUNCTION_DECL to a vec of default argument instantiations,
    indexed in reverse order of the parameters.  */
 
@@ -14544,8 +14470,10 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
     lambda_fntype = static_fn_type (lambda_fntype);
 
   if (member && !closure)
-    ctx = tsubst_aggr_type (ctx, args,
-			    complain, t, /*entering_scope=*/1);
+    {
+      ctx = tsubst (ctx, args, complain, t);
+      ctx = adjust_type_for_entering_scope (ctx);
+    }
 
   tree type = (lambda_fntype ? lambda_fntype
 	       : tsubst (TREE_TYPE (t), args,
@@ -14938,8 +14866,10 @@ tsubst_template_decl (tree t, tree args, tsubst_flags_t complain,
 	  inner = TREE_TYPE (inner);
 	}
       if (class_p)
-	inner = tsubst_aggr_type (inner, args, complain,
-				  in_decl, /*entering*/1);
+	{
+	  inner = tsubst (inner, args, complain, in_decl);
+	  inner = adjust_type_for_entering_scope (inner);
+	}
       else
 	inner = tsubst_decl (inner, args, complain, /*use_spec_table=*/false);
     }
@@ -15443,9 +15373,8 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain,
 	    local_p = false;
 	    if (DECL_CLASS_SCOPE_P (t))
 	      {
-		ctx = tsubst_aggr_type (ctx, args,
-					complain,
-					in_decl, /*entering_scope=*/1);
+		ctx = tsubst (ctx, args, complain, in_decl);
+		ctx = adjust_type_for_entering_scope (ctx);
 		if (DECL_SELF_REFERENCE_P (t))
 		  /* The context and type of an injected-class-name are
 		     the same, so we don't need to substitute both.  */
@@ -16241,8 +16170,31 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
       /* Fall through.  */
     case UNION_TYPE:
     case ENUMERAL_TYPE:
-      return tsubst_aggr_type_1 (t, args, complain, in_decl,
-				 /*entering_scope=*/0);
+      if (TYPE_TEMPLATE_INFO (t) && uses_template_parms (t))
+	{
+	  complain &= ~tf_qualifying_scope;
+
+	  /* Figure out what arguments are appropriate for the
+	     type we are trying to find.  For example, given:
+
+	       template <class T> struct S;
+	       template <class T, class U> void f(T, U) { S<U> su; }
+
+	     and supposing that we are instantiating f<int, double>,
+	     then our ARGS will be {int, double}, but, when looking up
+	     S we only want {double}.  */
+	  tree argvec = tsubst_template_args (TYPE_TI_ARGS (t), args,
+					      complain, in_decl);
+	  if (argvec == error_mark_node)
+	    return error_mark_node;
+
+	  tree r = lookup_template_class (t, argvec, in_decl, NULL_TREE,
+					  complain);
+	  return cp_build_qualified_type (r, cp_type_quals (t), complain);
+	}
+      else
+	/* This is not a template type, so there's nothing to do.  */
+	return t;
 
     case ERROR_MARK:
     case IDENTIFIER_NODE:
@@ -16424,7 +16376,6 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 		r = lookup_template_class (arg,
 					   argvec, in_decl,
 					   DECL_CONTEXT (arg),
-					    /*entering_scope=*/0,
 					   complain);
 		return cp_build_qualified_type
 		  (r, cp_type_quals (t) | cp_type_quals (r), complain);
@@ -16516,7 +16467,7 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	      if (argvec == error_mark_node)
 		return error_mark_node;
 	      r = lookup_template_class (tmpl, argvec, in_decl, NULL_TREE,
-					 /*entering_scope=*/false, complain);
+					 complain);
 	      r = cp_build_qualified_type (r, cp_type_quals (t), complain);
 	      break;
 	    }
@@ -16771,9 +16722,10 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	    ctx = TREE_VEC_ELT (ctx, 0);
 	  }
 	else
-	  ctx = tsubst_aggr_type (ctx, args,
-				  complain | tf_qualifying_scope,
-				  in_decl, /*entering_scope=*/1);
+	  {
+	    ctx = tsubst_scope (ctx, args, complain, in_decl);
+	    ctx = adjust_type_for_entering_scope (ctx);
+	  }
 	if (ctx == error_mark_node)
 	  return error_mark_node;
 
@@ -16846,8 +16798,10 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 
     case UNBOUND_CLASS_TEMPLATE:
       {
-	tree ctx = tsubst_aggr_type (TYPE_CONTEXT (t), args, complain,
-				     in_decl, /*entering_scope=*/1);
+	++processing_template_decl;
+	tree ctx = tsubst (TYPE_CONTEXT (t), args, complain, in_decl);
+	ctx = adjust_type_for_entering_scope (ctx);
+	--processing_template_decl;
 	tree name = TYPE_IDENTIFIER (t);
 	tree parm_list = DECL_TEMPLATE_PARMS (TYPE_NAME (t));
 
@@ -17065,8 +17019,8 @@ tsubst_baselink (tree baselink, tree object_type,
 {
   bool qualified_p = BASELINK_QUALIFIED_P (baselink);
   tree qualifying_scope = BINFO_TYPE (BASELINK_ACCESS_BINFO (baselink));
-  qualifying_scope = tsubst_aggr_type (qualifying_scope, args, complain, in_decl,
-				       /*entering_scope=*/true);
+  qualifying_scope = tsubst (qualifying_scope, args, complain, in_decl);
+  qualifying_scope = adjust_type_for_entering_scope (qualifying_scope);
 
   tree optype = BASELINK_OPTYPE (baselink);
   optype = tsubst (optype, args, complain, in_decl);
@@ -21451,9 +21405,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 
 	   When we instantiate f<7>::S::g(), say, lookup_name is not
 	   clever enough to find f<7>::a.  */
-	enum_type
-	  = tsubst_aggr_type (DECL_CONTEXT (t), args, complain, in_decl,
-			      /*entering_scope=*/0);
+	enum_type = tsubst (DECL_CONTEXT (t), args, complain, in_decl);
 
 	for (v = TYPE_VALUES (enum_type);
 	     v != NULL_TREE;
@@ -21473,8 +21425,8 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	{
 	  tree ctx;
 
-	  ctx = tsubst_aggr_type (DECL_CONTEXT (t), args, complain, in_decl,
-				  /*entering_scope=*/1);
+	  ctx = tsubst (DECL_CONTEXT (t), args, complain, in_decl);
+	  ctx = adjust_type_for_entering_scope (ctx);
 	  if (ctx != DECL_CONTEXT (t))
 	    {
 	      tree r = lookup_field (ctx, DECL_NAME (t), 0, false);
@@ -21517,8 +21469,8 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	     TEMPLATE_DECL with `D<T>' as its DECL_CONTEXT.  Now we
 	     have to substitute this with one having context `D<int>'.  */
 
-	  tree context = tsubst_aggr_type (DECL_CONTEXT (t), args, complain,
-					   in_decl, /*entering_scope=*/true);
+	  tree context = tsubst (DECL_CONTEXT (t), args, complain, in_decl);
+	  context = adjust_type_for_entering_scope (context);
 	  RETURN (lookup_field (context, DECL_NAME(t), 0, false));
 	}
       else
@@ -22060,8 +22012,10 @@ instantiate_template (tree tmpl, tree orig_args, tsubst_flags_t complain)
 	   already non-dependent, then we might as well use it.  */
 	ctx = DECL_CONTEXT (tmpl);
       else
-	ctx = tsubst_aggr_type (DECL_CONTEXT (gen_tmpl), targ_ptr,
-				complain, gen_tmpl, true);
+	{
+	  ctx = tsubst (DECL_CONTEXT (gen_tmpl), targ_ptr, complain, gen_tmpl);
+	  ctx = adjust_type_for_entering_scope (ctx);
+	}
       push_nested_class (ctx);
     }
 
@@ -29214,8 +29168,8 @@ resolve_typename_type (tree type, bool only_current_p)
       tree args = TREE_OPERAND (fullname, 1);
       /* Instantiate the template.  */
       result = lookup_template_class (tmpl, args, NULL_TREE, NULL_TREE,
-				      /*entering_scope=*/true,
 				      tf_error | tf_user);
+      result = adjust_type_for_entering_scope (result);
       if (result == error_mark_node)
 	result = NULL_TREE;
     }
@@ -29443,7 +29397,7 @@ listify (tree arg)
   TREE_VEC_ELT (argvec, 0) = arg;
 
   return lookup_template_class (std_init_list, argvec, NULL_TREE,
-				NULL_TREE, 0, tf_warning_or_error);
+				NULL_TREE, tf_warning_or_error);
 }
 
 /* Replace auto in TYPE with std::initializer_list<auto>.  */
@@ -30389,7 +30343,7 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
 	    fprime = copy_decl (fprime);
 	  tree fntype = TREE_TYPE (fprime);
 	  ret = lookup_template_class (TPARMS_PRIMARY_TEMPLATE (atparms), targs,
-				       in_decl, NULL_TREE, false, complain);
+				       in_decl, NULL_TREE, complain);
 	  fntype = build_function_type (ret, TYPE_ARG_TYPES (fntype));
 	  TREE_TYPE (fprime) = fntype;
 	  if (TREE_CODE (fprime) == TEMPLATE_DECL)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 3299e270446..cea819db221 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -3946,8 +3946,10 @@ finish_template_type (tree name, tree args, int entering_scope)
   tree type;
 
   type = lookup_template_class (name, args,
-				NULL_TREE, NULL_TREE, entering_scope,
+				NULL_TREE, NULL_TREE,
 				tf_warning_or_error | tf_user);
+  if (entering_scope)
+    type = adjust_type_for_entering_scope (type);
 
   /* If we might be entering the scope of a partial specialization,
      find the one with the right constraints.  */
-- 
2.43.0.493.gbc7ee2e5e1


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/2] c++: requires-exprs and partial constraint subst [PR112769]
  2024-02-02 19:41 [PATCH 1/2] c++: requires-exprs and partial constraint subst [PR112769] Patrick Palka
  2024-02-02 19:41 ` [PATCH 2/2] c++: remove lookup_template_class's entering_scope flag Patrick Palka
@ 2024-02-02 19:55 ` Jason Merrill
  2024-02-02 20:01 ` Jason Merrill
  2 siblings, 0 replies; 13+ messages in thread
From: Jason Merrill @ 2024-02-02 19:55 UTC (permalink / raw)
  To: Patrick Palka, gcc-patches

On 2/2/24 14:41, Patrick Palka wrote:
> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this
> look OK for trunk?

OK.

> -- >8 --
> 
> In r11-3261-gb28b621ac67bee we made tsubst_requires_expr never partially
> substitute into a requires-expression so as to avoid checking its
> requirements out of order during e.g. generic lambda regeneration.
> 
> Unfortunately we still do need to partially substitute into a
> requires-expression in rare cases, in particular when it's used in
> associated constraints that we are directly substituting for sake of
> declaration matching or dguide constraint rewriting.  We can identify
> this situation by checking processing_constraint_expression_p, so this
> patch uses this predicate to control whether we defer substitution or
> partially substitute.  The entering_scope=true change in tsubst_baselink
> is needed to avoid ICEing from tsubst_baselink during name lookup when
> rewriting std::ranges::ref_view's dguide constraints.
> 
> 	PR c++/112769
> 	PR c++/110006
> 
> gcc/cp/ChangeLog:
> 
> 	* constraint.cc (tsubst_simple_requirement): Return a
> 	substituted _REQ node when processing_template_decl.
> 	(tsubst_type_requirement): Likewise.
> 	(tsubst_compound_requirement): Likewise.
> 	(tsubst_nested_requirement): Likewise.
> 	(tsubst_requires_expr): Don't defer partial substitution
> 	when processing_constraint_expression_p is true, in which
> 	case return a substituted REQUIRES_EXPR.
> 	* pt.cc (tsubst_baselink): Use tsubst_aggr_type with
> 	entring_scope=true instead of tsubst to substitute
> 	qualifying_scope.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp2a/class-deduction-alias18.C: New test.
> 	* g++.dg/cpp2a/concepts-friend16.C: New test.
> ---
>   gcc/cp/constraint.cc                          | 38 +++++++++++++++++--
>   gcc/cp/pt.cc                                  |  3 +-
>   .../g++.dg/cpp2a/class-deduction-alias18.C    | 13 +++++++
>   .../g++.dg/cpp2a/concepts-friend16.C          | 25 ++++++++++++
>   4 files changed, 74 insertions(+), 5 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/class-deduction-alias18.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-friend16.C
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index fef68cf7ab2..450ae548f9a 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -2028,6 +2028,8 @@ tsubst_simple_requirement (tree t, tree args, sat_info info)
>     tree expr = tsubst_valid_expression_requirement (t0, args, info);
>     if (expr == error_mark_node)
>       return error_mark_node;
> +  if (processing_template_decl)
> +    return finish_simple_requirement (EXPR_LOCATION (t), expr);
>     return boolean_true_node;
>   }
>   
> @@ -2068,6 +2070,8 @@ tsubst_type_requirement (tree t, tree args, sat_info info)
>     tree type = tsubst_type_requirement_1 (t0, args, info, EXPR_LOCATION (t));
>     if (type == error_mark_node)
>       return error_mark_node;
> +  if (processing_template_decl)
> +    return finish_type_requirement (EXPR_LOCATION (t), type);
>     return boolean_true_node;
>   }
>   
> @@ -2182,6 +2186,9 @@ tsubst_compound_requirement (tree t, tree args, sat_info info)
>   	}
>       }
>   
> +  if (processing_template_decl)
> +    return finish_compound_requirement (EXPR_LOCATION (t),
> +					expr, type, noexcept_p);
>     return boolean_true_node;
>   }
>   
> @@ -2190,6 +2197,15 @@ tsubst_compound_requirement (tree t, tree args, sat_info info)
>   static tree
>   tsubst_nested_requirement (tree t, tree args, sat_info info)
>   {
> +  if (processing_template_decl)
> +    {
> +      tree req = TREE_OPERAND (t, 0);
> +      req = tsubst_constraint (req, args, info.complain, info.in_decl);
> +      if (req == error_mark_node)
> +	return error_mark_node;
> +      return finish_nested_requirement (EXPR_LOCATION (t), req);
> +    }
> +
>     sat_info quiet (tf_none, info.in_decl);
>     tree result = constraint_satisfaction_value (t, args, quiet);
>     if (result == boolean_true_node)
> @@ -2330,18 +2346,25 @@ tsubst_requires_expr (tree t, tree args, sat_info info)
>   
>     args = add_extra_args (REQUIRES_EXPR_EXTRA_ARGS (t), args,
>   			 info.complain, info.in_decl);
> -  if (processing_template_decl)
> +  if (processing_template_decl
> +      && !processing_constraint_expression_p ())
>       {
>         /* We're partially instantiating a generic lambda.  Substituting into
>   	 this requires-expression now may cause its requirements to get
>   	 checked out of order, so instead just remember the template
> -	 arguments and wait until we can substitute them all at once.  */
> +	 arguments and wait until we can substitute them all at once.
> +
> +	 Except if this requires-expr is part of associated constraints
> +	 that we're substituting into directly (for e.g. declaration
> +	 matching or dguide constraint rewriting), in which case we need
> +	 to partially substitute.  */
>         t = copy_node (t);
>         REQUIRES_EXPR_EXTRA_ARGS (t) = build_extra_args (t, args, info.complain);
>         return t;
>       }
>   
> -  if (tree parms = REQUIRES_EXPR_PARMS (t))
> +  tree parms = REQUIRES_EXPR_PARMS (t);
> +  if (parms)
>       {
>         parms = tsubst_constraint_variables (parms, args, info);
>         if (parms == error_mark_node)
> @@ -2349,10 +2372,13 @@ tsubst_requires_expr (tree t, tree args, sat_info info)
>       }
>   
>     tree result = boolean_true_node;
> +  if (processing_template_decl)
> +    result = NULL_TREE;
>     for (tree reqs = REQUIRES_EXPR_REQS (t); reqs; reqs = TREE_CHAIN (reqs))
>       {
>         tree req = TREE_VALUE (reqs);
> -      if (tsubst_requirement (req, args, info) == error_mark_node)
> +      req = tsubst_requirement (req, args, info);
> +      if (req == error_mark_node)
>   	{
>   	  result = boolean_false_node;
>   	  if (info.diagnose_unsatisfaction_p ())
> @@ -2360,7 +2386,11 @@ tsubst_requires_expr (tree t, tree args, sat_info info)
>   	  else
>   	    break;
>   	}
> +      else if (processing_template_decl)
> +	result = tree_cons (NULL_TREE, req, result);
>       }
> +  if (processing_template_decl && result != boolean_false_node)
> +    result = finish_requires_expr (EXPR_LOCATION (t), parms, nreverse (result));
>     return result;
>   }
>   
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index 9d30a271713..17ecd4b9c5a 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -17065,7 +17065,8 @@ tsubst_baselink (tree baselink, tree object_type,
>   {
>     bool qualified_p = BASELINK_QUALIFIED_P (baselink);
>     tree qualifying_scope = BINFO_TYPE (BASELINK_ACCESS_BINFO (baselink));
> -  qualifying_scope = tsubst (qualifying_scope, args, complain, in_decl);
> +  qualifying_scope = tsubst_aggr_type (qualifying_scope, args, complain, in_decl,
> +				       /*entering_scope=*/true);
>   
>     tree optype = BASELINK_OPTYPE (baselink);
>     optype = tsubst (optype, args, complain, in_decl);
> diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias18.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias18.C
> new file mode 100644
> index 00000000000..0bb11bb944c
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias18.C
> @@ -0,0 +1,13 @@
> +// PR c++/112769
> +// { dg-do compile { target c++20 } }
> +
> +template<int I, typename T>
> +struct type
> +{
> +    type(T) requires requires { T{0}; };
> +};
> +
> +template <typename T>
> +using alias = type<0, T>;
> +
> +alias foo{123};
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend16.C b/gcc/testsuite/g++.dg/cpp2a/concepts-friend16.C
> new file mode 100644
> index 00000000000..18974eeb172
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend16.C
> @@ -0,0 +1,25 @@
> +// PR c++/110006
> +// { dg-do compile { target c++20 } }
> +
> +template<typename T>
> +class s;
> +
> +template<typename T>
> +void constraint(s<T> const&, int&);
> +
> +template<typename U, typename T2>
> +U function(s<T2> const x)
> +	requires requires (U& u) { constraint(x, u); };
> +
> +template<typename T>
> +class s
> +{
> +	template<typename U, typename T2>
> +	friend U function(s<T2> const x)
> +		requires requires (U& u) { constraint(x, u); };
> +};
> +
> +int f(s<int> q)
> +{
> +	return function<int>(q); // { dg-bogus "ambiguous" }
> +}


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/2] c++: requires-exprs and partial constraint subst [PR112769]
  2024-02-02 19:41 [PATCH 1/2] c++: requires-exprs and partial constraint subst [PR112769] Patrick Palka
  2024-02-02 19:41 ` [PATCH 2/2] c++: remove lookup_template_class's entering_scope flag Patrick Palka
  2024-02-02 19:55 ` [PATCH 1/2] c++: requires-exprs and partial constraint subst [PR112769] Jason Merrill
@ 2024-02-02 20:01 ` Jason Merrill
  2024-02-02 20:36   ` Patrick Palka
  2 siblings, 1 reply; 13+ messages in thread
From: Jason Merrill @ 2024-02-02 20:01 UTC (permalink / raw)
  To: Patrick Palka, gcc-patches

On 2/2/24 14:41, Patrick Palka wrote:
> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this
> look OK for trunk?
> 
> -- >8 --
> 
> In r11-3261-gb28b621ac67bee we made tsubst_requires_expr never partially
> substitute into a requires-expression so as to avoid checking its
> requirements out of order during e.g. generic lambda regeneration.
> 
> Unfortunately we still do need to partially substitute into a
> requires-expression in rare cases, in particular when it's used in
> associated constraints that we are directly substituting for sake of
> declaration matching or dguide constraint rewriting.  We can identify
> this situation by checking processing_constraint_expression_p, so this
> patch uses this predicate to control whether we defer substitution or
> partially substitute.  The entering_scope=true change in tsubst_baselink
> is needed to avoid ICEing from tsubst_baselink during name lookup when
> rewriting std::ranges::ref_view's dguide constraints.

Actually, I don't think we want to enter the scope when rewriting 
constraints.  Would tsubst_scope work instead?

Jason


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/2] c++: requires-exprs and partial constraint subst [PR112769]
  2024-02-02 20:01 ` Jason Merrill
@ 2024-02-02 20:36   ` Patrick Palka
  2024-02-02 20:57     ` Patrick Palka
  0 siblings, 1 reply; 13+ messages in thread
From: Patrick Palka @ 2024-02-02 20:36 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Patrick Palka, gcc-patches

On Fri, 2 Feb 2024, Jason Merrill wrote:

> On 2/2/24 14:41, Patrick Palka wrote:
> > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this
> > look OK for trunk?
> > 
> > -- >8 --
> > 
> > In r11-3261-gb28b621ac67bee we made tsubst_requires_expr never partially
> > substitute into a requires-expression so as to avoid checking its
> > requirements out of order during e.g. generic lambda regeneration.
> > 
> > Unfortunately we still do need to partially substitute into a
> > requires-expression in rare cases, in particular when it's used in
> > associated constraints that we are directly substituting for sake of
> > declaration matching or dguide constraint rewriting.  We can identify
> > this situation by checking processing_constraint_expression_p, so this
> > patch uses this predicate to control whether we defer substitution or
> > partially substitute.  The entering_scope=true change in tsubst_baselink
> > is needed to avoid ICEing from tsubst_baselink during name lookup when
> > rewriting std::ranges::ref_view's dguide constraints.
> 
> Actually, I don't think we want to enter the scope when rewriting constraints.
> Would tsubst_scope work instead?

Oops yes, because of the maybe_dependent_member_ref stuff, the handling
for which doesn't trigger here for some reason.  Ah, I think it's
because the tf_dguide flag gets dropped during tsubst_requires_expr
since it uses tf_none rather than complain & ~tf_warning_or_error
which would preserve special tsubst flags such as tf_dguide.  I'll fix...

> 
> Jason
> 
> 


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/2] c++: requires-exprs and partial constraint subst [PR112769]
  2024-02-02 20:36   ` Patrick Palka
@ 2024-02-02 20:57     ` Patrick Palka
  2024-02-02 21:05       ` Jason Merrill
  0 siblings, 1 reply; 13+ messages in thread
From: Patrick Palka @ 2024-02-02 20:57 UTC (permalink / raw)
  To: Patrick Palka; +Cc: Jason Merrill, gcc-patches

On Fri, 2 Feb 2024, Patrick Palka wrote:

> On Fri, 2 Feb 2024, Jason Merrill wrote:
> 
> > On 2/2/24 14:41, Patrick Palka wrote:
> > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this
> > > look OK for trunk?
> > > 
> > > -- >8 --
> > > 
> > > In r11-3261-gb28b621ac67bee we made tsubst_requires_expr never partially
> > > substitute into a requires-expression so as to avoid checking its
> > > requirements out of order during e.g. generic lambda regeneration.
> > > 
> > > Unfortunately we still do need to partially substitute into a
> > > requires-expression in rare cases, in particular when it's used in
> > > associated constraints that we are directly substituting for sake of
> > > declaration matching or dguide constraint rewriting.  We can identify
> > > this situation by checking processing_constraint_expression_p, so this
> > > patch uses this predicate to control whether we defer substitution or
> > > partially substitute.  The entering_scope=true change in tsubst_baselink
> > > is needed to avoid ICEing from tsubst_baselink during name lookup when
> > > rewriting std::ranges::ref_view's dguide constraints.
> > 
> > Actually, I don't think we want to enter the scope when rewriting constraints.
> > Would tsubst_scope work instead?
> 
> Oops yes, because of the maybe_dependent_member_ref stuff, the handling
> for which doesn't trigger here for some reason.  Ah, I think it's
> because the tf_dguide flag gets dropped during tsubst_requires_expr
> since it uses tf_none rather than complain & ~tf_warning_or_error
> which would preserve special tsubst flags such as tf_dguide.  I'll fix...

Like so?  Lightly tested so far, bootstrap+regtest in progress.

-- >8 --

Subject: [PATCH v2] c++: requires-exprs and partial constraint subst [PR112769]

In r11-3261-gb28b621ac67bee we made tsubst_requires_expr never partially
substitute into a requires-expression so as to avoid checking its
requirements out of order during e.g. generic lambda regeneration.

Unfortunately we still do need to partially substitute into a
requires-expression in rare cases, in particular when it's used in
associated constraints that we are directly substituting for sake of
declaration matching or dguide constraint rewriting.  We can identify
this situation by checking processing_constraint_expression_p, so this
patch uses this predicate to control whether we defer substitution or
partially substitute.

In turn, tsubst_requires_expr now needs to propagate semantic tsubst flags
rather than just using tf_none, in particular for dguide constraint rewriting
which sets tf_dguide.

	PR c++/112769
	PR c++/110006

gcc/cp/ChangeLog:

	* constraint.cc (subst_info::quiet): Accomodate non-diagnostic
	tsubst flags.
	(tsubst_valid_expression_requirement): Likewise.
	(tsubst_simple_requirement): Likewise.  Return a substituted _REQ
	node when processing_template_decl.
	(tsubst_type_requirement_1): Accomodate non-diagnostic tsubst
	flags.
	(tsubst_type_requirement): Return a substituted _REQ node when
	processing_template_decl.
	(tsubst_compound_requirement): Likewise.  Accomodate non-diagnostic
	tsubst flags.
	(tsubst_nested_requirement): Likewise.
	(tsubst_requires_expr): Don't defer partial substitution when
	processing_constraint_expression_p is true, in which case return
	a substituted REQUIRES_EXPR.
	* pt.cc (tsubst_expr) <case REQUIRES_EXPR>: Accomodate
	non-diagnostic tsubst flags.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/class-deduction-alias18.C: New test.
	* g++.dg/cpp2a/concepts-friend16.C: New test.
---
 gcc/cp/constraint.cc                          | 56 +++++++++++++++----
 gcc/cp/pt.cc                                  |  3 +-
 .../g++.dg/cpp2a/class-deduction-alias18.C    | 13 +++++
 .../g++.dg/cpp2a/concepts-friend16.C          | 25 +++++++++
 4 files changed, 84 insertions(+), 13 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/class-deduction-alias18.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-friend16.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index fef68cf7ab2..d9569013bd3 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -85,7 +85,7 @@ struct subst_info
   /* True if we should not diagnose errors.  */
   bool quiet() const
   {
-    return complain == tf_none;
+    return !(complain & tf_warning_or_error);
   }
 
   /* True if we should diagnose errors.  */
@@ -1991,8 +1991,9 @@ hash_placeholder_constraint (tree c)
 static tree
 tsubst_valid_expression_requirement (tree t, tree args, sat_info info)
 {
-  tree r = tsubst_expr (t, args, tf_none, info.in_decl);
-  if (convert_to_void (r, ICV_STATEMENT, tf_none) != error_mark_node)
+  tsubst_flags_t quiet = info.complain & ~tf_warning_or_error;
+  tree r = tsubst_expr (t, args, quiet, info.in_decl);
+  if (convert_to_void (r, ICV_STATEMENT, quiet) != error_mark_node)
     return r;
 
   if (info.diagnose_unsatisfaction_p ())
@@ -2028,6 +2029,8 @@ tsubst_simple_requirement (tree t, tree args, sat_info info)
   tree expr = tsubst_valid_expression_requirement (t0, args, info);
   if (expr == error_mark_node)
     return error_mark_node;
+  if (processing_template_decl)
+    return finish_simple_requirement (EXPR_LOCATION (t), expr);
   return boolean_true_node;
 }
 
@@ -2037,7 +2040,8 @@ tsubst_simple_requirement (tree t, tree args, sat_info info)
 static tree
 tsubst_type_requirement_1 (tree t, tree args, sat_info info, location_t loc)
 {
-  tree r = tsubst (t, args, tf_none, info.in_decl);
+  tsubst_flags_t quiet = info.complain & ~tf_warning_or_error;
+  tree r = tsubst (t, args, quiet, info.in_decl);
   if (r != error_mark_node)
     return r;
 
@@ -2068,6 +2072,8 @@ tsubst_type_requirement (tree t, tree args, sat_info info)
   tree type = tsubst_type_requirement_1 (t0, args, info, EXPR_LOCATION (t));
   if (type == error_mark_node)
     return error_mark_node;
+  if (processing_template_decl)
+    return finish_type_requirement (EXPR_LOCATION (t), type);
   return boolean_true_node;
 }
 
@@ -2124,9 +2130,11 @@ tsubst_compound_requirement (tree t, tree args, sat_info info)
 
   location_t loc = cp_expr_loc_or_input_loc (expr);
 
+  subst_info quiet (info.complain & ~tf_warning_or_error, info.in_decl);
+
   /* Check the noexcept condition.  */
   bool noexcept_p = COMPOUND_REQ_NOEXCEPT_P (t);
-  if (noexcept_p && !expr_noexcept_p (expr, tf_none))
+  if (noexcept_p && !expr_noexcept_p (expr, quiet.complain))
     {
       if (info.diagnose_unsatisfaction_p ())
 	inform (loc, "%qE is not %<noexcept%>", expr);
@@ -2139,8 +2147,6 @@ tsubst_compound_requirement (tree t, tree args, sat_info info)
   if (type == error_mark_node)
     return error_mark_node;
 
-  subst_info quiet (tf_none, info.in_decl);
-
   /* Check expression against the result type.  */
   if (type)
     {
@@ -2182,6 +2188,9 @@ tsubst_compound_requirement (tree t, tree args, sat_info info)
 	}
     }
 
+  if (processing_template_decl)
+    return finish_compound_requirement (EXPR_LOCATION (t),
+					expr, type, noexcept_p);
   return boolean_true_node;
 }
 
@@ -2190,7 +2199,16 @@ tsubst_compound_requirement (tree t, tree args, sat_info info)
 static tree
 tsubst_nested_requirement (tree t, tree args, sat_info info)
 {
-  sat_info quiet (tf_none, info.in_decl);
+  if (processing_template_decl)
+    {
+      tree req = TREE_OPERAND (t, 0);
+      req = tsubst_constraint (req, args, info.complain, info.in_decl);
+      if (req == error_mark_node)
+	return error_mark_node;
+      return finish_nested_requirement (EXPR_LOCATION (t), req);
+    }
+
+  sat_info quiet (info.complain & ~tf_warning_or_error, info.in_decl);
   tree result = constraint_satisfaction_value (t, args, quiet);
   if (result == boolean_true_node)
     return boolean_true_node;
@@ -2330,18 +2348,25 @@ tsubst_requires_expr (tree t, tree args, sat_info info)
 
   args = add_extra_args (REQUIRES_EXPR_EXTRA_ARGS (t), args,
 			 info.complain, info.in_decl);
-  if (processing_template_decl)
+  if (processing_template_decl
+      && !processing_constraint_expression_p ())
     {
       /* We're partially instantiating a generic lambda.  Substituting into
 	 this requires-expression now may cause its requirements to get
 	 checked out of order, so instead just remember the template
-	 arguments and wait until we can substitute them all at once.  */
+	 arguments and wait until we can substitute them all at once.
+
+	 Except if this requires-expr is part of associated constraints
+	 that we're substituting into directly (for e.g. declaration
+	 matching or dguide constraint rewriting), in which case we need
+	 to partially substitute.  */
       t = copy_node (t);
       REQUIRES_EXPR_EXTRA_ARGS (t) = build_extra_args (t, args, info.complain);
       return t;
     }
 
-  if (tree parms = REQUIRES_EXPR_PARMS (t))
+  tree parms = REQUIRES_EXPR_PARMS (t);
+  if (parms)
     {
       parms = tsubst_constraint_variables (parms, args, info);
       if (parms == error_mark_node)
@@ -2349,10 +2374,13 @@ tsubst_requires_expr (tree t, tree args, sat_info info)
     }
 
   tree result = boolean_true_node;
+  if (processing_template_decl)
+    result = NULL_TREE;
   for (tree reqs = REQUIRES_EXPR_REQS (t); reqs; reqs = TREE_CHAIN (reqs))
     {
       tree req = TREE_VALUE (reqs);
-      if (tsubst_requirement (req, args, info) == error_mark_node)
+      req = tsubst_requirement (req, args, info);
+      if (req == error_mark_node)
 	{
 	  result = boolean_false_node;
 	  if (info.diagnose_unsatisfaction_p ())
@@ -2360,7 +2388,11 @@ tsubst_requires_expr (tree t, tree args, sat_info info)
 	  else
 	    break;
 	}
+      else if (processing_template_decl)
+	result = tree_cons (NULL_TREE, req, result);
     }
+  if (processing_template_decl && result != boolean_false_node)
+    result = finish_requires_expr (EXPR_LOCATION (t), parms, nreverse (result));
   return result;
 }
 
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 9d30a271713..7dcdb5aaf7d 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -21705,7 +21705,8 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 
     case REQUIRES_EXPR:
       {
-	tree r = tsubst_requires_expr (t, args, tf_none, in_decl);
+	complain &= ~tf_warning_or_error;
+	tree r = tsubst_requires_expr (t, args, complain, in_decl);
 	RETURN (r);
       }
 
diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias18.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias18.C
new file mode 100644
index 00000000000..0bb11bb944c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias18.C
@@ -0,0 +1,13 @@
+// PR c++/112769
+// { dg-do compile { target c++20 } }
+
+template<int I, typename T>
+struct type
+{
+    type(T) requires requires { T{0}; };
+};
+
+template <typename T>
+using alias = type<0, T>;
+
+alias foo{123};
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend16.C b/gcc/testsuite/g++.dg/cpp2a/concepts-friend16.C
new file mode 100644
index 00000000000..18974eeb172
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend16.C
@@ -0,0 +1,25 @@
+// PR c++/110006
+// { dg-do compile { target c++20 } }
+
+template<typename T>
+class s;
+
+template<typename T>
+void constraint(s<T> const&, int&);
+
+template<typename U, typename T2>
+U function(s<T2> const x)
+	requires requires (U& u) { constraint(x, u); };
+
+template<typename T>
+class s
+{
+	template<typename U, typename T2>
+	friend U function(s<T2> const x)
+		requires requires (U& u) { constraint(x, u); };
+};
+
+int f(s<int> q)
+{
+	return function<int>(q); // { dg-bogus "ambiguous" }
+}
-- 
2.43.0.493.gbc7ee2e5e1


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/2] c++: requires-exprs and partial constraint subst [PR112769]
  2024-02-02 20:57     ` Patrick Palka
@ 2024-02-02 21:05       ` Jason Merrill
  0 siblings, 0 replies; 13+ messages in thread
From: Jason Merrill @ 2024-02-02 21:05 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches

On 2/2/24 15:57, Patrick Palka wrote:
> On Fri, 2 Feb 2024, Patrick Palka wrote:
> 
>> On Fri, 2 Feb 2024, Jason Merrill wrote:
>>
>>> On 2/2/24 14:41, Patrick Palka wrote:
>>>> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this
>>>> look OK for trunk?
>>>>
>>>> -- >8 --
>>>>
>>>> In r11-3261-gb28b621ac67bee we made tsubst_requires_expr never partially
>>>> substitute into a requires-expression so as to avoid checking its
>>>> requirements out of order during e.g. generic lambda regeneration.
>>>>
>>>> Unfortunately we still do need to partially substitute into a
>>>> requires-expression in rare cases, in particular when it's used in
>>>> associated constraints that we are directly substituting for sake of
>>>> declaration matching or dguide constraint rewriting.  We can identify
>>>> this situation by checking processing_constraint_expression_p, so this
>>>> patch uses this predicate to control whether we defer substitution or
>>>> partially substitute.  The entering_scope=true change in tsubst_baselink
>>>> is needed to avoid ICEing from tsubst_baselink during name lookup when
>>>> rewriting std::ranges::ref_view's dguide constraints.
>>>
>>> Actually, I don't think we want to enter the scope when rewriting constraints.
>>> Would tsubst_scope work instead?
>>
>> Oops yes, because of the maybe_dependent_member_ref stuff, the handling
>> for which doesn't trigger here for some reason.  Ah, I think it's
>> because the tf_dguide flag gets dropped during tsubst_requires_expr
>> since it uses tf_none rather than complain & ~tf_warning_or_error
>> which would preserve special tsubst flags such as tf_dguide.  I'll fix...
> 
> Like so?  Lightly tested so far, bootstrap+regtest in progress.

OK, thanks.

> -- >8 --
> 
> Subject: [PATCH v2] c++: requires-exprs and partial constraint subst [PR112769]
> 
> In r11-3261-gb28b621ac67bee we made tsubst_requires_expr never partially
> substitute into a requires-expression so as to avoid checking its
> requirements out of order during e.g. generic lambda regeneration.
> 
> Unfortunately we still do need to partially substitute into a
> requires-expression in rare cases, in particular when it's used in
> associated constraints that we are directly substituting for sake of
> declaration matching or dguide constraint rewriting.  We can identify
> this situation by checking processing_constraint_expression_p, so this
> patch uses this predicate to control whether we defer substitution or
> partially substitute.
> 
> In turn, tsubst_requires_expr now needs to propagate semantic tsubst flags
> rather than just using tf_none, in particular for dguide constraint rewriting
> which sets tf_dguide.
> 
> 	PR c++/112769
> 	PR c++/110006
> 
> gcc/cp/ChangeLog:
> 
> 	* constraint.cc (subst_info::quiet): Accomodate non-diagnostic
> 	tsubst flags.
> 	(tsubst_valid_expression_requirement): Likewise.
> 	(tsubst_simple_requirement): Likewise.  Return a substituted _REQ
> 	node when processing_template_decl.
> 	(tsubst_type_requirement_1): Accomodate non-diagnostic tsubst
> 	flags.
> 	(tsubst_type_requirement): Return a substituted _REQ node when
> 	processing_template_decl.
> 	(tsubst_compound_requirement): Likewise.  Accomodate non-diagnostic
> 	tsubst flags.
> 	(tsubst_nested_requirement): Likewise.
> 	(tsubst_requires_expr): Don't defer partial substitution when
> 	processing_constraint_expression_p is true, in which case return
> 	a substituted REQUIRES_EXPR.
> 	* pt.cc (tsubst_expr) <case REQUIRES_EXPR>: Accomodate
> 	non-diagnostic tsubst flags.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp2a/class-deduction-alias18.C: New test.
> 	* g++.dg/cpp2a/concepts-friend16.C: New test.
> ---
>   gcc/cp/constraint.cc                          | 56 +++++++++++++++----
>   gcc/cp/pt.cc                                  |  3 +-
>   .../g++.dg/cpp2a/class-deduction-alias18.C    | 13 +++++
>   .../g++.dg/cpp2a/concepts-friend16.C          | 25 +++++++++
>   4 files changed, 84 insertions(+), 13 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/class-deduction-alias18.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-friend16.C
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index fef68cf7ab2..d9569013bd3 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -85,7 +85,7 @@ struct subst_info
>     /* True if we should not diagnose errors.  */
>     bool quiet() const
>     {
> -    return complain == tf_none;
> +    return !(complain & tf_warning_or_error);
>     }
>   
>     /* True if we should diagnose errors.  */
> @@ -1991,8 +1991,9 @@ hash_placeholder_constraint (tree c)
>   static tree
>   tsubst_valid_expression_requirement (tree t, tree args, sat_info info)
>   {
> -  tree r = tsubst_expr (t, args, tf_none, info.in_decl);
> -  if (convert_to_void (r, ICV_STATEMENT, tf_none) != error_mark_node)
> +  tsubst_flags_t quiet = info.complain & ~tf_warning_or_error;
> +  tree r = tsubst_expr (t, args, quiet, info.in_decl);
> +  if (convert_to_void (r, ICV_STATEMENT, quiet) != error_mark_node)
>       return r;
>   
>     if (info.diagnose_unsatisfaction_p ())
> @@ -2028,6 +2029,8 @@ tsubst_simple_requirement (tree t, tree args, sat_info info)
>     tree expr = tsubst_valid_expression_requirement (t0, args, info);
>     if (expr == error_mark_node)
>       return error_mark_node;
> +  if (processing_template_decl)
> +    return finish_simple_requirement (EXPR_LOCATION (t), expr);
>     return boolean_true_node;
>   }
>   
> @@ -2037,7 +2040,8 @@ tsubst_simple_requirement (tree t, tree args, sat_info info)
>   static tree
>   tsubst_type_requirement_1 (tree t, tree args, sat_info info, location_t loc)
>   {
> -  tree r = tsubst (t, args, tf_none, info.in_decl);
> +  tsubst_flags_t quiet = info.complain & ~tf_warning_or_error;
> +  tree r = tsubst (t, args, quiet, info.in_decl);
>     if (r != error_mark_node)
>       return r;
>   
> @@ -2068,6 +2072,8 @@ tsubst_type_requirement (tree t, tree args, sat_info info)
>     tree type = tsubst_type_requirement_1 (t0, args, info, EXPR_LOCATION (t));
>     if (type == error_mark_node)
>       return error_mark_node;
> +  if (processing_template_decl)
> +    return finish_type_requirement (EXPR_LOCATION (t), type);
>     return boolean_true_node;
>   }
>   
> @@ -2124,9 +2130,11 @@ tsubst_compound_requirement (tree t, tree args, sat_info info)
>   
>     location_t loc = cp_expr_loc_or_input_loc (expr);
>   
> +  subst_info quiet (info.complain & ~tf_warning_or_error, info.in_decl);
> +
>     /* Check the noexcept condition.  */
>     bool noexcept_p = COMPOUND_REQ_NOEXCEPT_P (t);
> -  if (noexcept_p && !expr_noexcept_p (expr, tf_none))
> +  if (noexcept_p && !expr_noexcept_p (expr, quiet.complain))
>       {
>         if (info.diagnose_unsatisfaction_p ())
>   	inform (loc, "%qE is not %<noexcept%>", expr);
> @@ -2139,8 +2147,6 @@ tsubst_compound_requirement (tree t, tree args, sat_info info)
>     if (type == error_mark_node)
>       return error_mark_node;
>   
> -  subst_info quiet (tf_none, info.in_decl);
> -
>     /* Check expression against the result type.  */
>     if (type)
>       {
> @@ -2182,6 +2188,9 @@ tsubst_compound_requirement (tree t, tree args, sat_info info)
>   	}
>       }
>   
> +  if (processing_template_decl)
> +    return finish_compound_requirement (EXPR_LOCATION (t),
> +					expr, type, noexcept_p);
>     return boolean_true_node;
>   }
>   
> @@ -2190,7 +2199,16 @@ tsubst_compound_requirement (tree t, tree args, sat_info info)
>   static tree
>   tsubst_nested_requirement (tree t, tree args, sat_info info)
>   {
> -  sat_info quiet (tf_none, info.in_decl);
> +  if (processing_template_decl)
> +    {
> +      tree req = TREE_OPERAND (t, 0);
> +      req = tsubst_constraint (req, args, info.complain, info.in_decl);
> +      if (req == error_mark_node)
> +	return error_mark_node;
> +      return finish_nested_requirement (EXPR_LOCATION (t), req);
> +    }
> +
> +  sat_info quiet (info.complain & ~tf_warning_or_error, info.in_decl);
>     tree result = constraint_satisfaction_value (t, args, quiet);
>     if (result == boolean_true_node)
>       return boolean_true_node;
> @@ -2330,18 +2348,25 @@ tsubst_requires_expr (tree t, tree args, sat_info info)
>   
>     args = add_extra_args (REQUIRES_EXPR_EXTRA_ARGS (t), args,
>   			 info.complain, info.in_decl);
> -  if (processing_template_decl)
> +  if (processing_template_decl
> +      && !processing_constraint_expression_p ())
>       {
>         /* We're partially instantiating a generic lambda.  Substituting into
>   	 this requires-expression now may cause its requirements to get
>   	 checked out of order, so instead just remember the template
> -	 arguments and wait until we can substitute them all at once.  */
> +	 arguments and wait until we can substitute them all at once.
> +
> +	 Except if this requires-expr is part of associated constraints
> +	 that we're substituting into directly (for e.g. declaration
> +	 matching or dguide constraint rewriting), in which case we need
> +	 to partially substitute.  */
>         t = copy_node (t);
>         REQUIRES_EXPR_EXTRA_ARGS (t) = build_extra_args (t, args, info.complain);
>         return t;
>       }
>   
> -  if (tree parms = REQUIRES_EXPR_PARMS (t))
> +  tree parms = REQUIRES_EXPR_PARMS (t);
> +  if (parms)
>       {
>         parms = tsubst_constraint_variables (parms, args, info);
>         if (parms == error_mark_node)
> @@ -2349,10 +2374,13 @@ tsubst_requires_expr (tree t, tree args, sat_info info)
>       }
>   
>     tree result = boolean_true_node;
> +  if (processing_template_decl)
> +    result = NULL_TREE;
>     for (tree reqs = REQUIRES_EXPR_REQS (t); reqs; reqs = TREE_CHAIN (reqs))
>       {
>         tree req = TREE_VALUE (reqs);
> -      if (tsubst_requirement (req, args, info) == error_mark_node)
> +      req = tsubst_requirement (req, args, info);
> +      if (req == error_mark_node)
>   	{
>   	  result = boolean_false_node;
>   	  if (info.diagnose_unsatisfaction_p ())
> @@ -2360,7 +2388,11 @@ tsubst_requires_expr (tree t, tree args, sat_info info)
>   	  else
>   	    break;
>   	}
> +      else if (processing_template_decl)
> +	result = tree_cons (NULL_TREE, req, result);
>       }
> +  if (processing_template_decl && result != boolean_false_node)
> +    result = finish_requires_expr (EXPR_LOCATION (t), parms, nreverse (result));
>     return result;
>   }
>   
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index 9d30a271713..7dcdb5aaf7d 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -21705,7 +21705,8 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>   
>       case REQUIRES_EXPR:
>         {
> -	tree r = tsubst_requires_expr (t, args, tf_none, in_decl);
> +	complain &= ~tf_warning_or_error;
> +	tree r = tsubst_requires_expr (t, args, complain, in_decl);
>   	RETURN (r);
>         }
>   
> diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias18.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias18.C
> new file mode 100644
> index 00000000000..0bb11bb944c
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias18.C
> @@ -0,0 +1,13 @@
> +// PR c++/112769
> +// { dg-do compile { target c++20 } }
> +
> +template<int I, typename T>
> +struct type
> +{
> +    type(T) requires requires { T{0}; };
> +};
> +
> +template <typename T>
> +using alias = type<0, T>;
> +
> +alias foo{123};
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend16.C b/gcc/testsuite/g++.dg/cpp2a/concepts-friend16.C
> new file mode 100644
> index 00000000000..18974eeb172
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend16.C
> @@ -0,0 +1,25 @@
> +// PR c++/110006
> +// { dg-do compile { target c++20 } }
> +
> +template<typename T>
> +class s;
> +
> +template<typename T>
> +void constraint(s<T> const&, int&);
> +
> +template<typename U, typename T2>
> +U function(s<T2> const x)
> +	requires requires (U& u) { constraint(x, u); };
> +
> +template<typename T>
> +class s
> +{
> +	template<typename U, typename T2>
> +	friend U function(s<T2> const x)
> +		requires requires (U& u) { constraint(x, u); };
> +};
> +
> +int f(s<int> q)
> +{
> +	return function<int>(q); // { dg-bogus "ambiguous" }
> +}


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/2] c++: remove lookup_template_class's entering_scope flag
  2024-02-02 19:41 ` [PATCH 2/2] c++: remove lookup_template_class's entering_scope flag Patrick Palka
@ 2024-05-01 19:41   ` Patrick Palka
  2024-05-01 20:22     ` Jason Merrill
  0 siblings, 1 reply; 13+ messages in thread
From: Patrick Palka @ 2024-05-01 19:41 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches, jason

On Fri, 2 Feb 2024, Patrick Palka wrote:

> Bootstrapped and regtested on x86_64-pc-linux, does this look like
> an improvement?  This is not a bugfix and barely related to the previous
> patch, but the previous patch's new use of entering_scope=true motivated
> me to submit this patch since it seems like a nice simplification.

Ping, now that stage 1 is open.

> 
> -- >8 --
> 
> lookup_template_class's entering_scope flag controls whether to prefer
> returning the primary template type A<T> instead of the corresponding
> implicit instantiation A<T>.  When we want to set this flag as part of
> substitution, we need to use tsubst_aggr_type which also takes the flag,
> but this separate entry point to substitution turned out to be subtly
> problematic because it doesn't reuse typedefs like tsubst does, which
> r13-4729-gbe124477b38a71 fixed in a way that respects the flag after the
> fact, by adjusting the entering_scope=false result of
> lookup_template_class as if entering_scope=true was passed.
> 
> But if that's possible then it means lookup_template_class's the
> entering_scope flag is not necessary after all -- we can just do the
> after-the-fact adjustment everywhere that we currently pass
> entering_scope=true to it and tsubst_aggr_type.
> 
> To that end, this patch replaces this flag with an adjustment function
> adjust_type_for_entering_scope, to be used whereever we currently need
> the entering_scope=true behavior.  This means we can also get rid of
> tsubst_aggr_type, since the only reason we needed this entry point
> was to be able to pass entering_scope=true to lookup_template_class.
> 
> gcc/cp/ChangeLog:
> 
> 	* coroutines.cc (instantiate_coro_traits): Adjust call to
> 	lookup_template_class.
> 	(instantiate_coro_handle_for_promise_type): Likewise.
> 	* cp-tree.h (adjust_type_for_entering_scope): Declare.
> 	(lookup_template_class): Adjust declaration.
> 	* decl.cc (make_typename_type): Adjust call to
> 	lookup_template_class. Likewise.
> 	(get_tuple_size): Likewise.
> 	(get_tuple_element_type): Likewise.
> 	* pt.cc (adjust_type_for_entering_scope): Define.
> 	(lookup_template_class): Remove entering_scope parameter.
> 	Replace tsubst_aggr_type call with tsubst followed by
> 	adjust_type_for_entering_scope.
> 	(tsubst_aggr_type): Remove.
> 	(tsubst_aggr_type_1): Inline into tsubst.
> 	(tsubst_function_decl): Replace tsubst_aggr_type call
> 	with tsubst followed by adjust_type_for_entering_scope.
> 	(tsubst_template_decl): Likewise.
> 	(tsubst_decl): Likewise.
> 	(tsubst) <case RECORD_TYPE, UNION_TYPE, ENUMERAL_TYPE>:
> 	Inlined from tsubst_aggr_type_1.
> 	<case BOUND_TEMPLATE_TEMPLATE_PARM>: Adjust calls to
> 	lookup_template_class.
> 	<case TYPENAME_TYPE>: Replace tsubst_aggr_type call with
> 	tsubst tsubst_scope followed by adjust_type_for_entering_scope.
> 	<case UNBOUND_CLASS_TEMPLATE>: Replace tsubst_aggr_type
> 	call with tsubst followed by adjust_type_for_entering_scope.
> 	Increment processing_template_decl when substituting the
> 	context.
> 	(tsubst_baselink): Replace tsubst_aggr_type call with tsubst
> 	followed by adjust_type_for_entering_scope.
> 	(tsubst_expr) <case FIELD_DECL>: Replace tsubst_aggr_type
> 	call with tsubst followed by adjust_type_for_entering_scope.
> 	<case TEMPLATE_DECL>: Likewise.
> 	(instantiate_template): Likewise.
> 	(resolve_typename_type): Adjust lookup_template_class call
> 	and call adjust_type_for_entering_scope afterward.
> 	(listify): Adjust lookup_template_class call.
> 	(alias_ctad_tweaks): Likewise.
> 	* semantics.cc (finish_template_type): Adjust lookup_template_class
> 	call and maybe call adjust_type_for_entering_scope afterward.
> ---
>  gcc/cp/coroutines.cc |   4 +-
>  gcc/cp/cp-tree.h     |   3 +-
>  gcc/cp/decl.cc       |   4 +-
>  gcc/cp/pt.cc         | 212 +++++++++++++++++--------------------------
>  gcc/cp/semantics.cc  |   4 +-
>  5 files changed, 91 insertions(+), 136 deletions(-)
> 
> diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
> index 3194c911e8c..8dab173d5cb 100644
> --- a/gcc/cp/coroutines.cc
> +++ b/gcc/cp/coroutines.cc
> @@ -353,7 +353,7 @@ instantiate_coro_traits (tree fndecl, location_t kw)
>    tree traits_class
>      = lookup_template_class (coro_traits_templ, targ,
>  			     /*in_decl=*/NULL_TREE, /*context=*/NULL_TREE,
> -			     /*entering scope=*/false, tf_warning_or_error);
> +			     tf_warning_or_error);
>  
>    if (traits_class == error_mark_node)
>      {
> @@ -400,7 +400,7 @@ instantiate_coro_handle_for_promise_type (location_t kw, tree promise_type)
>      = lookup_template_class (coro_handle_identifier, targ,
>  			     /* in_decl=*/NULL_TREE,
>  			     /* context=*/std_node,
> -			     /* entering scope=*/false, tf_warning_or_error);
> +			     tf_warning_or_error);
>  
>    if (handle_type == error_mark_node)
>      {
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 969c7239c97..4f73a7c84b6 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -7506,8 +7506,9 @@ extern tree push_template_decl			(tree, bool is_friend = false);
>  extern tree add_inherited_template_parms	(tree, tree);
>  extern void template_parm_level_and_index	(tree, int*, int*);
>  extern bool redeclare_class_template		(tree, tree, tree);
> +extern tree adjust_type_for_entering_scope	(tree);
>  extern tree lookup_template_class		(tree, tree, tree, tree,
> -						 int, tsubst_flags_t);
> +						 tsubst_flags_t);
>  extern tree lookup_template_function		(tree, tree);
>  extern tree lookup_template_variable		(tree, tree, tsubst_flags_t);
>  extern bool uses_template_parms			(tree);
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index 3e41fd4fa31..8149aab5781 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -4569,7 +4569,6 @@ make_typename_type (tree context, tree name, enum tag_types tag_type,
>      {
>        t = lookup_template_class (t, TREE_OPERAND (fullname, 1),
>  				 NULL_TREE, context,
> -				 /*entering_scope=*/0,
>  				 complain | tf_user);
>        if (t == error_mark_node)
>  	return error_mark_node;
> @@ -9163,7 +9162,7 @@ get_tuple_size (tree type)
>    tree inst = lookup_template_class (tuple_size_identifier, args,
>  				     /*in_decl*/NULL_TREE,
>  				     /*context*/std_node,
> -				     /*entering_scope*/false, tf_none);
> +				     tf_none);
>    inst = complete_type (inst);
>    if (inst == error_mark_node
>        || !COMPLETE_TYPE_P (inst)
> @@ -9192,7 +9191,6 @@ get_tuple_element_type (tree type, unsigned i)
>    tree inst = lookup_template_class (tuple_element_identifier, args,
>  				     /*in_decl*/NULL_TREE,
>  				     /*context*/std_node,
> -				     /*entering_scope*/false,
>  				     tf_warning_or_error);
>    return make_typename_type (inst, type_identifier,
>  			     none_type, tf_warning_or_error);
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index 17ecd4b9c5a..baf9b0e6fda 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -185,8 +185,6 @@ static int unify_pack_expansion (tree, tree, tree,
>  static tree copy_template_args (tree);
>  static tree tsubst_template_parms (tree, tree, tsubst_flags_t);
>  static void tsubst_each_template_parm_constraints (tree, tree, tsubst_flags_t);
> -static tree tsubst_aggr_type (tree, tree, tsubst_flags_t, tree, int);
> -static tree tsubst_aggr_type_1 (tree, tree, tsubst_flags_t, tree, int);
>  static tree tsubst_arg_types (tree, tree, tree, tsubst_flags_t, tree);
>  static tree tsubst_function_type (tree, tree, tsubst_flags_t, tree);
>  static bool check_specialization_scope (void);
> @@ -9873,6 +9871,24 @@ maybe_get_template_decl_from_type_decl (tree decl)
>      ? CLASSTYPE_TI_TEMPLATE (TREE_TYPE (decl)) : decl;
>  }
>  
> +/* If TYPE is the generic implicit instantiation A<T>, return the primary
> +   template type A<T> (which is suitable for entering into e.g. for name
> +   lookup), otherwise return TYPE.  */
> +
> +tree
> +adjust_type_for_entering_scope (tree type)
> +{
> +  if (CLASS_TYPE_P (type)
> +      && dependent_type_p (type)
> +      && TYPE_TEMPLATE_INFO (type)
> +      /* We detect the generic implicit instantiation A<T> by inspecting
> +	 TYPE_CANONICAL, which lookup_template_class sets to the primary
> +	 template type A<T>.  */
> +      && TYPE_CANONICAL (type) == TREE_TYPE (TYPE_TI_TEMPLATE (type)))
> +    type = TYPE_CANONICAL (type);
> +  return type;
> +}
> +
>  /* Given an IDENTIFIER_NODE (or type TEMPLATE_DECL) and a chain of
>     parameters, find the desired type.
>  
> @@ -9881,9 +9897,6 @@ maybe_get_template_decl_from_type_decl (tree decl)
>     IN_DECL, if non-NULL, is the template declaration we are trying to
>     instantiate.
>  
> -   If ENTERING_SCOPE is nonzero, we are about to enter the scope of
> -   the class we are looking up.
> -
>     Issue error and warning messages under control of COMPLAIN.
>  
>     If the template class is really a local class in a template
> @@ -9899,7 +9912,7 @@ maybe_get_template_decl_from_type_decl (tree decl)
>  
>  tree
>  lookup_template_class (tree d1, tree arglist, tree in_decl, tree context,
> -		       int entering_scope, tsubst_flags_t complain)
> +		       tsubst_flags_t complain)
>  {
>    auto_timevar tv (TV_TEMPLATE_INST);
>  
> @@ -10085,9 +10098,10 @@ lookup_template_class (tree d1, tree arglist, tree in_decl, tree context,
>  	   template <class T> class C { void f(C<T>); }
>  
>  	 the `C<T>' is just the same as `C'.  Outside of the
> -	 class, however, such a reference is an instantiation.  */
> -      if (entering_scope
> -	  || !PRIMARY_TEMPLATE_P (gen_tmpl)
> +	 class, however, such a reference is an instantiation.
> +	 One can use adjust_type_for_entering_scope to make
> +	 this adjustment as needed.  */
> +      if (!PRIMARY_TEMPLATE_P (gen_tmpl)
>  	  || currently_open_class (template_type))
>  	{
>  	  tree tinfo = TYPE_TEMPLATE_INFO (template_type);
> @@ -10154,8 +10168,8 @@ lookup_template_class (tree d1, tree arglist, tree in_decl, tree context,
>  	    context = DECL_CONTEXT (templ);
>  	  else
>  	    {
> -	      context = tsubst_aggr_type (context, arglist,
> -					  complain, in_decl, true);
> +	      context = tsubst (context, arglist, complain, in_decl);
> +	      context = adjust_type_for_entering_scope (context);
>  	      /* Try completing the enclosing context if it's not already so.  */
>  	      if (context != error_mark_node
>  		  && !COMPLETE_TYPE_P (context))
> @@ -14091,94 +14105,6 @@ tsubst_each_template_parm_constraints (tree parms, tree args,
>    --processing_template_decl;
>  }
>  
> -/* Substitute the ARGS into the indicated aggregate (or enumeration)
> -   type T.  If T is not an aggregate or enumeration type, it is
> -   handled as if by tsubst.  IN_DECL is as for tsubst.  If
> -   ENTERING_SCOPE is nonzero, T is the context for a template which
> -   we are presently tsubst'ing.  Return the substituted value.  */
> -
> -static tree
> -tsubst_aggr_type (tree t,
> -		  tree args,
> -		  tsubst_flags_t complain,
> -		  tree in_decl,
> -		  int entering_scope)
> -{
> -  if (t == NULL_TREE)
> -    return NULL_TREE;
> -
> -  /* Handle typedefs via tsubst so that they get consistently reused.  */
> -  if (typedef_variant_p (t))
> -    {
> -      t = tsubst (t, args, complain, in_decl);
> -      if (t == error_mark_node)
> -	return error_mark_node;
> -
> -      /* The effect of entering_scope is that for a dependent specialization
> -	 A<T>, lookup_template_class prefers to return A's primary template
> -	 type instead of the implicit instantiation.  So when entering_scope,
> -	 we mirror this behavior by inspecting TYPE_CANONICAL appropriately,
> -	 taking advantage of the fact that lookup_template_class links the two
> -	 types by setting TYPE_CANONICAL of the latter to the former.  */
> -      if (entering_scope
> -	  && CLASS_TYPE_P (t)
> -	  && dependent_type_p (t)
> -	  && TYPE_TEMPLATE_INFO (t)
> -	  && TYPE_CANONICAL (t) == TREE_TYPE (TYPE_TI_TEMPLATE (t)))
> -	t = TYPE_CANONICAL (t);
> -
> -      return t;
> -    }
> -
> -  switch (TREE_CODE (t))
> -    {
> -      case RECORD_TYPE:
> -      case ENUMERAL_TYPE:
> -      case UNION_TYPE:
> -	return tsubst_aggr_type_1 (t, args, complain, in_decl, entering_scope);
> -
> -      default:
> -	return tsubst (t, args, complain, in_decl);
> -    }
> -}
> -
> -/* The part of tsubst_aggr_type that's shared with the RECORD_, UNION_
> -   and ENUMERAL_TYPE cases of tsubst.  */
> -
> -static tree
> -tsubst_aggr_type_1 (tree t,
> -		    tree args,
> -		    tsubst_flags_t complain,
> -		    tree in_decl,
> -		    int entering_scope)
> -{
> -  if (TYPE_TEMPLATE_INFO (t) && uses_template_parms (t))
> -    {
> -      complain &= ~tf_qualifying_scope;
> -
> -      /* Figure out what arguments are appropriate for the
> -	 type we are trying to find.  For example, given:
> -
> -	   template <class T> struct S;
> -	   template <class T, class U> void f(T, U) { S<U> su; }
> -
> -	 and supposing that we are instantiating f<int, double>,
> -	 then our ARGS will be {int, double}, but, when looking up
> -	 S we only want {double}.  */
> -      tree argvec = tsubst_template_args (TYPE_TI_ARGS (t), args,
> -					  complain, in_decl);
> -      if (argvec == error_mark_node)
> -	return error_mark_node;
> -
> -      tree r = lookup_template_class (t, argvec, in_decl, NULL_TREE,
> -				      entering_scope, complain);
> -      return cp_build_qualified_type (r, cp_type_quals (t), complain);
> -    }
> -  else
> -    /* This is not a template type, so there's nothing to do.  */
> -    return t;
> -}
> -
>  /* Map from a FUNCTION_DECL to a vec of default argument instantiations,
>     indexed in reverse order of the parameters.  */
>  
> @@ -14544,8 +14470,10 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
>      lambda_fntype = static_fn_type (lambda_fntype);
>  
>    if (member && !closure)
> -    ctx = tsubst_aggr_type (ctx, args,
> -			    complain, t, /*entering_scope=*/1);
> +    {
> +      ctx = tsubst (ctx, args, complain, t);
> +      ctx = adjust_type_for_entering_scope (ctx);
> +    }
>  
>    tree type = (lambda_fntype ? lambda_fntype
>  	       : tsubst (TREE_TYPE (t), args,
> @@ -14938,8 +14866,10 @@ tsubst_template_decl (tree t, tree args, tsubst_flags_t complain,
>  	  inner = TREE_TYPE (inner);
>  	}
>        if (class_p)
> -	inner = tsubst_aggr_type (inner, args, complain,
> -				  in_decl, /*entering*/1);
> +	{
> +	  inner = tsubst (inner, args, complain, in_decl);
> +	  inner = adjust_type_for_entering_scope (inner);
> +	}
>        else
>  	inner = tsubst_decl (inner, args, complain, /*use_spec_table=*/false);
>      }
> @@ -15443,9 +15373,8 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain,
>  	    local_p = false;
>  	    if (DECL_CLASS_SCOPE_P (t))
>  	      {
> -		ctx = tsubst_aggr_type (ctx, args,
> -					complain,
> -					in_decl, /*entering_scope=*/1);
> +		ctx = tsubst (ctx, args, complain, in_decl);
> +		ctx = adjust_type_for_entering_scope (ctx);
>  		if (DECL_SELF_REFERENCE_P (t))
>  		  /* The context and type of an injected-class-name are
>  		     the same, so we don't need to substitute both.  */
> @@ -16241,8 +16170,31 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>        /* Fall through.  */
>      case UNION_TYPE:
>      case ENUMERAL_TYPE:
> -      return tsubst_aggr_type_1 (t, args, complain, in_decl,
> -				 /*entering_scope=*/0);
> +      if (TYPE_TEMPLATE_INFO (t) && uses_template_parms (t))
> +	{
> +	  complain &= ~tf_qualifying_scope;
> +
> +	  /* Figure out what arguments are appropriate for the
> +	     type we are trying to find.  For example, given:
> +
> +	       template <class T> struct S;
> +	       template <class T, class U> void f(T, U) { S<U> su; }
> +
> +	     and supposing that we are instantiating f<int, double>,
> +	     then our ARGS will be {int, double}, but, when looking up
> +	     S we only want {double}.  */
> +	  tree argvec = tsubst_template_args (TYPE_TI_ARGS (t), args,
> +					      complain, in_decl);
> +	  if (argvec == error_mark_node)
> +	    return error_mark_node;
> +
> +	  tree r = lookup_template_class (t, argvec, in_decl, NULL_TREE,
> +					  complain);
> +	  return cp_build_qualified_type (r, cp_type_quals (t), complain);
> +	}
> +      else
> +	/* This is not a template type, so there's nothing to do.  */
> +	return t;
>  
>      case ERROR_MARK:
>      case IDENTIFIER_NODE:
> @@ -16424,7 +16376,6 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>  		r = lookup_template_class (arg,
>  					   argvec, in_decl,
>  					   DECL_CONTEXT (arg),
> -					    /*entering_scope=*/0,
>  					   complain);
>  		return cp_build_qualified_type
>  		  (r, cp_type_quals (t) | cp_type_quals (r), complain);
> @@ -16516,7 +16467,7 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>  	      if (argvec == error_mark_node)
>  		return error_mark_node;
>  	      r = lookup_template_class (tmpl, argvec, in_decl, NULL_TREE,
> -					 /*entering_scope=*/false, complain);
> +					 complain);
>  	      r = cp_build_qualified_type (r, cp_type_quals (t), complain);
>  	      break;
>  	    }
> @@ -16771,9 +16722,10 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>  	    ctx = TREE_VEC_ELT (ctx, 0);
>  	  }
>  	else
> -	  ctx = tsubst_aggr_type (ctx, args,
> -				  complain | tf_qualifying_scope,
> -				  in_decl, /*entering_scope=*/1);
> +	  {
> +	    ctx = tsubst_scope (ctx, args, complain, in_decl);
> +	    ctx = adjust_type_for_entering_scope (ctx);
> +	  }
>  	if (ctx == error_mark_node)
>  	  return error_mark_node;
>  
> @@ -16846,8 +16798,10 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>  
>      case UNBOUND_CLASS_TEMPLATE:
>        {
> -	tree ctx = tsubst_aggr_type (TYPE_CONTEXT (t), args, complain,
> -				     in_decl, /*entering_scope=*/1);
> +	++processing_template_decl;
> +	tree ctx = tsubst (TYPE_CONTEXT (t), args, complain, in_decl);
> +	ctx = adjust_type_for_entering_scope (ctx);
> +	--processing_template_decl;
>  	tree name = TYPE_IDENTIFIER (t);
>  	tree parm_list = DECL_TEMPLATE_PARMS (TYPE_NAME (t));
>  
> @@ -17065,8 +17019,8 @@ tsubst_baselink (tree baselink, tree object_type,
>  {
>    bool qualified_p = BASELINK_QUALIFIED_P (baselink);
>    tree qualifying_scope = BINFO_TYPE (BASELINK_ACCESS_BINFO (baselink));
> -  qualifying_scope = tsubst_aggr_type (qualifying_scope, args, complain, in_decl,
> -				       /*entering_scope=*/true);
> +  qualifying_scope = tsubst (qualifying_scope, args, complain, in_decl);
> +  qualifying_scope = adjust_type_for_entering_scope (qualifying_scope);
>  
>    tree optype = BASELINK_OPTYPE (baselink);
>    optype = tsubst (optype, args, complain, in_decl);
> @@ -21451,9 +21405,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>  
>  	   When we instantiate f<7>::S::g(), say, lookup_name is not
>  	   clever enough to find f<7>::a.  */
> -	enum_type
> -	  = tsubst_aggr_type (DECL_CONTEXT (t), args, complain, in_decl,
> -			      /*entering_scope=*/0);
> +	enum_type = tsubst (DECL_CONTEXT (t), args, complain, in_decl);
>  
>  	for (v = TYPE_VALUES (enum_type);
>  	     v != NULL_TREE;
> @@ -21473,8 +21425,8 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>  	{
>  	  tree ctx;
>  
> -	  ctx = tsubst_aggr_type (DECL_CONTEXT (t), args, complain, in_decl,
> -				  /*entering_scope=*/1);
> +	  ctx = tsubst (DECL_CONTEXT (t), args, complain, in_decl);
> +	  ctx = adjust_type_for_entering_scope (ctx);
>  	  if (ctx != DECL_CONTEXT (t))
>  	    {
>  	      tree r = lookup_field (ctx, DECL_NAME (t), 0, false);
> @@ -21517,8 +21469,8 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>  	     TEMPLATE_DECL with `D<T>' as its DECL_CONTEXT.  Now we
>  	     have to substitute this with one having context `D<int>'.  */
>  
> -	  tree context = tsubst_aggr_type (DECL_CONTEXT (t), args, complain,
> -					   in_decl, /*entering_scope=*/true);
> +	  tree context = tsubst (DECL_CONTEXT (t), args, complain, in_decl);
> +	  context = adjust_type_for_entering_scope (context);
>  	  RETURN (lookup_field (context, DECL_NAME(t), 0, false));
>  	}
>        else
> @@ -22060,8 +22012,10 @@ instantiate_template (tree tmpl, tree orig_args, tsubst_flags_t complain)
>  	   already non-dependent, then we might as well use it.  */
>  	ctx = DECL_CONTEXT (tmpl);
>        else
> -	ctx = tsubst_aggr_type (DECL_CONTEXT (gen_tmpl), targ_ptr,
> -				complain, gen_tmpl, true);
> +	{
> +	  ctx = tsubst (DECL_CONTEXT (gen_tmpl), targ_ptr, complain, gen_tmpl);
> +	  ctx = adjust_type_for_entering_scope (ctx);
> +	}
>        push_nested_class (ctx);
>      }
>  
> @@ -29214,8 +29168,8 @@ resolve_typename_type (tree type, bool only_current_p)
>        tree args = TREE_OPERAND (fullname, 1);
>        /* Instantiate the template.  */
>        result = lookup_template_class (tmpl, args, NULL_TREE, NULL_TREE,
> -				      /*entering_scope=*/true,
>  				      tf_error | tf_user);
> +      result = adjust_type_for_entering_scope (result);
>        if (result == error_mark_node)
>  	result = NULL_TREE;
>      }
> @@ -29443,7 +29397,7 @@ listify (tree arg)
>    TREE_VEC_ELT (argvec, 0) = arg;
>  
>    return lookup_template_class (std_init_list, argvec, NULL_TREE,
> -				NULL_TREE, 0, tf_warning_or_error);
> +				NULL_TREE, tf_warning_or_error);
>  }
>  
>  /* Replace auto in TYPE with std::initializer_list<auto>.  */
> @@ -30389,7 +30343,7 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
>  	    fprime = copy_decl (fprime);
>  	  tree fntype = TREE_TYPE (fprime);
>  	  ret = lookup_template_class (TPARMS_PRIMARY_TEMPLATE (atparms), targs,
> -				       in_decl, NULL_TREE, false, complain);
> +				       in_decl, NULL_TREE, complain);
>  	  fntype = build_function_type (ret, TYPE_ARG_TYPES (fntype));
>  	  TREE_TYPE (fprime) = fntype;
>  	  if (TREE_CODE (fprime) == TEMPLATE_DECL)
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index 3299e270446..cea819db221 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -3946,8 +3946,10 @@ finish_template_type (tree name, tree args, int entering_scope)
>    tree type;
>  
>    type = lookup_template_class (name, args,
> -				NULL_TREE, NULL_TREE, entering_scope,
> +				NULL_TREE, NULL_TREE,
>  				tf_warning_or_error | tf_user);
> +  if (entering_scope)
> +    type = adjust_type_for_entering_scope (type);
>  
>    /* If we might be entering the scope of a partial specialization,
>       find the one with the right constraints.  */
> -- 
> 2.43.0.493.gbc7ee2e5e1
> 
> 


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/2] c++: remove lookup_template_class's entering_scope flag
  2024-05-01 19:41   ` Patrick Palka
@ 2024-05-01 20:22     ` Jason Merrill
  2024-05-01 20:40       ` Patrick Palka
  0 siblings, 1 reply; 13+ messages in thread
From: Jason Merrill @ 2024-05-01 20:22 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches

On 5/1/24 12:41, Patrick Palka wrote:
> On Fri, 2 Feb 2024, Patrick Palka wrote:
> 
>> Bootstrapped and regtested on x86_64-pc-linux, does this look like
>> an improvement?  This is not a bugfix and barely related to the previous
>> patch, but the previous patch's new use of entering_scope=true motivated
>> me to submit this patch since it seems like a nice simplification.
> 
> Ping, now that stage 1 is open.

Thanks for the ping.  The earlier message isn't showing up in 
Thunderbird for some reason, though I see it in the gmail web interface...

>> @@ -16771,9 +16722,10 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>>   	    ctx = TREE_VEC_ELT (ctx, 0);
>>   	  }
>>   	else
>> -	  ctx = tsubst_aggr_type (ctx, args,
>> -				  complain | tf_qualifying_scope,
>> -				  in_decl, /*entering_scope=*/1);
>> +	  {
>> +	    ctx = tsubst_scope (ctx, args, complain, in_decl);

Why is this one tsubst_scope while the others are all plain tsubst?

Do we want a tsubst_entering_scope function?

>> +	    ctx = adjust_type_for_entering_scope (ctx);


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/2] c++: remove lookup_template_class's entering_scope flag
  2024-05-01 20:22     ` Jason Merrill
@ 2024-05-01 20:40       ` Patrick Palka
  2024-05-01 20:52         ` Jason Merrill
  0 siblings, 1 reply; 13+ messages in thread
From: Patrick Palka @ 2024-05-01 20:40 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Patrick Palka, gcc-patches

On Wed, 1 May 2024, Jason Merrill wrote:

> On 5/1/24 12:41, Patrick Palka wrote:
> > On Fri, 2 Feb 2024, Patrick Palka wrote:
> > 
> > > Bootstrapped and regtested on x86_64-pc-linux, does this look like
> > > an improvement?  This is not a bugfix and barely related to the previous
> > > patch, but the previous patch's new use of entering_scope=true motivated
> > > me to submit this patch since it seems like a nice simplification.
> > 
> > Ping, now that stage 1 is open.
> 
> Thanks for the ping.  The earlier message isn't showing up in Thunderbird for
> some reason, though I see it in the gmail web interface...

Ah, weird.  No worries, this patch was very much stage 1 material anyway.

> 
> > > @@ -16771,9 +16722,10 @@ tsubst (tree t, tree args, tsubst_flags_t
> > > complain, tree in_decl)
> > >   	    ctx = TREE_VEC_ELT (ctx, 0);
> > >   	  }
> > >   	else
> > > -	  ctx = tsubst_aggr_type (ctx, args,
> > > -				  complain | tf_qualifying_scope,
> > > -				  in_decl, /*entering_scope=*/1);
> > > +	  {
> > > +	    ctx = tsubst_scope (ctx, args, complain, in_decl);
> 
> Why is this one tsubst_scope while the others are all plain tsubst?

Ah, just because the call to tsubst_aggr_type being replace passes
tf_qualifying_scope already, so we might as well use tsubst_scope
as shorthand.

> 
> Do we want a tsubst_entering_scope function?

Which is just shorthand for tsubst + adjust_type_for_entering_scope?
Sure, though I was wondering if we eventually might want to get rid of
the distinction between the primary template type A<T> and the generic
instantiation A<T>, and we could treat this as an incremental step
towards that (then we'd just eventually remove the
adjust_type_for_entering_scope calls and keep the tsubst calls).

> 
> > > +	    ctx = adjust_type_for_entering_scope (ctx);
> 
> 


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/2] c++: remove lookup_template_class's entering_scope flag
  2024-05-01 20:40       ` Patrick Palka
@ 2024-05-01 20:52         ` Jason Merrill
  2024-05-02 17:49           ` Patrick Palka
  0 siblings, 1 reply; 13+ messages in thread
From: Jason Merrill @ 2024-05-01 20:52 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches

On 5/1/24 13:40, Patrick Palka wrote:
> On Wed, 1 May 2024, Jason Merrill wrote:
> 
>> On 5/1/24 12:41, Patrick Palka wrote:
>>> On Fri, 2 Feb 2024, Patrick Palka wrote:
>>>
>>>> Bootstrapped and regtested on x86_64-pc-linux, does this look like
>>>> an improvement?  This is not a bugfix and barely related to the previous
>>>> patch, but the previous patch's new use of entering_scope=true motivated
>>>> me to submit this patch since it seems like a nice simplification.
>>>
>>> Ping, now that stage 1 is open.
>>
>> Thanks for the ping.  The earlier message isn't showing up in Thunderbird for
>> some reason, though I see it in the gmail web interface...
> 
> Ah, weird.  No worries, this patch was very much stage 1 material anyway.
> 
>>
>>>> @@ -16771,9 +16722,10 @@ tsubst (tree t, tree args, tsubst_flags_t
>>>> complain, tree in_decl)
>>>>    	    ctx = TREE_VEC_ELT (ctx, 0);
>>>>    	  }
>>>>    	else
>>>> -	  ctx = tsubst_aggr_type (ctx, args,
>>>> -				  complain | tf_qualifying_scope,
>>>> -				  in_decl, /*entering_scope=*/1);
>>>> +	  {
>>>> +	    ctx = tsubst_scope (ctx, args, complain, in_decl);
>>
>> Why is this one tsubst_scope while the others are all plain tsubst?
> 
> Ah, just because the call to tsubst_aggr_type being replace passes
> tf_qualifying_scope already, so we might as well use tsubst_scope
> as shorthand.
> 
>> Do we want a tsubst_entering_scope function?
> 
> Which is just shorthand for tsubst + adjust_type_for_entering_scope?

That's what I was thinking.

> Sure, though I was wondering if we eventually might want to get rid of
> the distinction between the primary template type A<T> and the generic
> instantiation A<T>, and we could treat this as an incremental step
> towards that (then we'd just eventually remove the
> adjust_type_for_entering_scope calls and keep the tsubst calls).

I don't think we want that; having the distinction helps to avoid 
wrongly looking up members of the primary template in contexts that 
shouldn't be able to.

Jason


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/2] c++: remove lookup_template_class's entering_scope flag
  2024-05-01 20:52         ` Jason Merrill
@ 2024-05-02 17:49           ` Patrick Palka
  2024-05-02 18:11             ` Jason Merrill
  0 siblings, 1 reply; 13+ messages in thread
From: Patrick Palka @ 2024-05-02 17:49 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Patrick Palka, gcc-patches

On Wed, 1 May 2024, Jason Merrill wrote:

> On 5/1/24 13:40, Patrick Palka wrote:
> > On Wed, 1 May 2024, Jason Merrill wrote:
> > 
> > > On 5/1/24 12:41, Patrick Palka wrote:
> > > > On Fri, 2 Feb 2024, Patrick Palka wrote:
> > > > 
> > > > > Bootstrapped and regtested on x86_64-pc-linux, does this look like
> > > > > an improvement?  This is not a bugfix and barely related to the
> > > > > previous
> > > > > patch, but the previous patch's new use of entering_scope=true
> > > > > motivated
> > > > > me to submit this patch since it seems like a nice simplification.
> > > > 
> > > > Ping, now that stage 1 is open.
> > > 
> > > Thanks for the ping.  The earlier message isn't showing up in Thunderbird
> > > for
> > > some reason, though I see it in the gmail web interface...
> > 
> > Ah, weird.  No worries, this patch was very much stage 1 material anyway.
> > 
> > > 
> > > > > @@ -16771,9 +16722,10 @@ tsubst (tree t, tree args, tsubst_flags_t
> > > > > complain, tree in_decl)
> > > > >    	    ctx = TREE_VEC_ELT (ctx, 0);
> > > > >    	  }
> > > > >    	else
> > > > > -	  ctx = tsubst_aggr_type (ctx, args,
> > > > > -				  complain | tf_qualifying_scope,
> > > > > -				  in_decl, /*entering_scope=*/1);
> > > > > +	  {
> > > > > +	    ctx = tsubst_scope (ctx, args, complain, in_decl);
> > > 
> > > Why is this one tsubst_scope while the others are all plain tsubst?
> > 
> > Ah, just because the call to tsubst_aggr_type being replace passes
> > tf_qualifying_scope already, so we might as well use tsubst_scope
> > as shorthand.
> > 
> > > Do we want a tsubst_entering_scope function?
> > 
> > Which is just shorthand for tsubst + adjust_type_for_entering_scope?
> 
> That's what I was thinking.
> 
> > Sure, though I was wondering if we eventually might want to get rid of
> > the distinction between the primary template type A<T> and the generic
> > instantiation A<T>, and we could treat this as an incremental step
> > towards that (then we'd just eventually remove the
> > adjust_type_for_entering_scope calls and keep the tsubst calls).
> 
> I don't think we want that; having the distinction helps to avoid wrongly
> looking up members of the primary template in contexts that shouldn't be able
> to.

Makes sense.  How does the following look?

The name tsubst_entering_scope sounds confusingly similar to
tsubst_scope, but I can't think of a better name for it or for
adjust_type_for_entering_scope :/

-- >8 --

Subject: [PATCH] c++: remove lookup_template_class's entering_scope flag
gcc/cp/ChangeLog:

	* coroutines.cc (instantiate_coro_traits): Adjust call to
	lookup_template_class.
	(instantiate_coro_handle_for_promise_type): Likewise.
	* cp-tree.h (adjust_type_for_entering_scope): Declare.
	(lookup_template_class): Adjust declaration.
	* decl.cc (make_typename_type): Adjust call to
	lookup_template_class. Likewise.
	(get_tuple_size): Likewise.
	(get_tuple_element_type): Likewise.
	* pt.cc (adjust_type_for_entering_scope): Define.
	(tsubst_entering_scope): Define.
	(lookup_template_class): Remove entering_scope parameter.
	Replace tsubst_aggr_type call with tsubst_entering_scope.
	(tsubst_aggr_type): Remove.
	(tsubst_aggr_type_1): Inline into tsubst.
	(tsubst_function_decl): Replace tsubst_aggr_type call
	with tsubst_entering_scope.
	(tsubst_template_decl): Likewise.
	(tsubst_decl): Likewise.
	(tsubst) <case RECORD_TYPE, UNION_TYPE, ENUMERAL_TYPE>:
	Inlined from tsubst_aggr_type_1.
	<case BOUND_TEMPLATE_TEMPLATE_PARM>: Adjust calls to
	lookup_template_class.
	<case TYPENAME_TYPE>: Replace tsubst_aggr_type call with
	tsubst_scope followed by adjust_type_for_entering_scope.
	<case UNBOUND_CLASS_TEMPLATE>: Replace tsubst_aggr_type
	call with tsubst_entering_scope.
	Increment processing_template_decl when substituting the
	context.
	(tsubst_expr) <case FIELD_DECL>: Replace tsubst_aggr_type
	call with tsubst_entering_scope.
	<case TEMPLATE_DECL>: Likewise.
	(instantiate_template): Likewise.
	(resolve_typename_type): Adjust lookup_template_class call
	and call adjust_type_for_entering_scope afterward.
	(listify): Adjust lookup_template_class call.
	(alias_ctad_tweaks): Likewise.
	* semantics.cc (finish_template_type): Adjust lookup_template_class
	call and maybe call adjust_type_for_entering_scope afterward.
---
 gcc/cp/coroutines.cc |   4 +-
 gcc/cp/cp-tree.h     |   3 +-
 gcc/cp/decl.cc       |   4 +-
 gcc/cp/pt.cc         | 209 +++++++++++++++++--------------------------
 gcc/cp/semantics.cc  |   4 +-
 5 files changed, 90 insertions(+), 134 deletions(-)

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index b05cb9eb330..97bc211ff67 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -353,7 +353,7 @@ instantiate_coro_traits (tree fndecl, location_t kw)
   tree traits_class
     = lookup_template_class (coro_traits_templ, targ,
 			     /*in_decl=*/NULL_TREE, /*context=*/NULL_TREE,
-			     /*entering scope=*/false, tf_warning_or_error);
+			     tf_warning_or_error);
 
   if (traits_class == error_mark_node)
     {
@@ -400,7 +400,7 @@ instantiate_coro_handle_for_promise_type (location_t kw, tree promise_type)
     = lookup_template_class (coro_handle_identifier, targ,
 			     /* in_decl=*/NULL_TREE,
 			     /* context=*/std_node,
-			     /* entering scope=*/false, tf_warning_or_error);
+			     tf_warning_or_error);
 
   if (handle_type == error_mark_node)
     {
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 5d1bd6ba493..bc8e22cc39f 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7521,8 +7521,9 @@ extern tree push_template_decl			(tree, bool is_friend = false);
 extern tree add_inherited_template_parms	(tree, tree);
 extern void template_parm_level_and_index	(tree, int*, int*);
 extern bool redeclare_class_template		(tree, tree, tree);
+extern tree adjust_type_for_entering_scope	(tree);
 extern tree lookup_template_class		(tree, tree, tree, tree,
-						 int, tsubst_flags_t);
+						 tsubst_flags_t);
 extern tree lookup_template_function		(tree, tree);
 extern tree lookup_template_variable		(tree, tree, tsubst_flags_t);
 extern bool uses_template_parms			(tree);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index df855334133..876f77cbbe3 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -4574,7 +4574,6 @@ make_typename_type (tree context, tree name, enum tag_types tag_type,
     {
       t = lookup_template_class (t, TREE_OPERAND (fullname, 1),
 				 NULL_TREE, context,
-				 /*entering_scope=*/0,
 				 complain | tf_user);
       if (t == error_mark_node)
 	return error_mark_node;
@@ -9168,7 +9167,7 @@ get_tuple_size (tree type)
   tree inst = lookup_template_class (tuple_size_identifier, args,
 				     /*in_decl*/NULL_TREE,
 				     /*context*/std_node,
-				     /*entering_scope*/false, tf_none);
+				     tf_none);
   inst = complete_type (inst);
   if (inst == error_mark_node
       || !COMPLETE_TYPE_P (inst)
@@ -9197,7 +9196,6 @@ get_tuple_element_type (tree type, unsigned i)
   tree inst = lookup_template_class (tuple_element_identifier, args,
 				     /*in_decl*/NULL_TREE,
 				     /*context*/std_node,
-				     /*entering_scope*/false,
 				     tf_warning_or_error);
   return make_typename_type (inst, type_identifier,
 			     none_type, tf_warning_or_error);
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 1c3eef60c06..261d6b9f4c8 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -185,8 +185,6 @@ static int unify_pack_expansion (tree, tree, tree,
 static tree copy_template_args (tree);
 static tree tsubst_template_parms (tree, tree, tsubst_flags_t);
 static void tsubst_each_template_parm_constraints (tree, tree, tsubst_flags_t);
-static tree tsubst_aggr_type (tree, tree, tsubst_flags_t, tree, int);
-static tree tsubst_aggr_type_1 (tree, tree, tsubst_flags_t, tree, int);
 static tree tsubst_arg_types (tree, tree, tree, tsubst_flags_t, tree);
 static tree tsubst_function_type (tree, tree, tsubst_flags_t, tree);
 static bool check_specialization_scope (void);
@@ -9901,6 +9899,34 @@ maybe_get_template_decl_from_type_decl (tree decl)
     ? CLASSTYPE_TI_TEMPLATE (TREE_TYPE (decl)) : decl;
 }
 
+/* If TYPE is the generic implicit instantiation A<T>, return the primary
+   template type A<T> (which is suitable for entering into e.g. for name
+   lookup), otherwise return TYPE.  */
+
+tree
+adjust_type_for_entering_scope (tree type)
+{
+  if (CLASS_TYPE_P (type)
+      && dependent_type_p (type)
+      && TYPE_TEMPLATE_INFO (type)
+      /* We detect the generic implicit instantiation A<T> by inspecting
+	 TYPE_CANONICAL, which lookup_template_class sets to the primary
+	 template type A<T>.  */
+      && TYPE_CANONICAL (type) == TREE_TYPE (TYPE_TI_TEMPLATE (type)))
+    type = TYPE_CANONICAL (type);
+  return type;
+}
+
+/* Convenience wrapper over tsubst that does adjust_type_for_entering_scope
+   on the result.  */
+
+static tree
+tsubst_entering_scope (tree t, tree args, tsubst_flags_t complain, tree in_decl)
+{
+  t = tsubst (t, args, complain, in_decl);
+  return adjust_type_for_entering_scope (t);
+}
+
 /* Given an IDENTIFIER_NODE (or type TEMPLATE_DECL) and a chain of
    parameters, find the desired type.
 
@@ -9909,9 +9935,6 @@ maybe_get_template_decl_from_type_decl (tree decl)
    IN_DECL, if non-NULL, is the template declaration we are trying to
    instantiate.
 
-   If ENTERING_SCOPE is nonzero, we are about to enter the scope of
-   the class we are looking up.
-
    Issue error and warning messages under control of COMPLAIN.
 
    If the template class is really a local class in a template
@@ -9927,7 +9950,7 @@ maybe_get_template_decl_from_type_decl (tree decl)
 
 tree
 lookup_template_class (tree d1, tree arglist, tree in_decl, tree context,
-		       int entering_scope, tsubst_flags_t complain)
+		       tsubst_flags_t complain)
 {
   auto_timevar tv (TV_TEMPLATE_INST);
 
@@ -10139,9 +10162,10 @@ lookup_template_class (tree d1, tree arglist, tree in_decl, tree context,
 	   template <class T> class C { void f(C<T>); }
 
 	 the `C<T>' is just the same as `C'.  Outside of the
-	 class, however, such a reference is an instantiation.  */
-      if (entering_scope
-	  || !PRIMARY_TEMPLATE_P (gen_tmpl)
+	 class, however, such a reference is an instantiation.
+	 One can use adjust_type_for_entering_scope to make
+	 this adjustment as needed.  */
+      if (!PRIMARY_TEMPLATE_P (gen_tmpl)
 	  || currently_open_class (template_type))
 	{
 	  tree tinfo = TYPE_TEMPLATE_INFO (template_type);
@@ -10207,8 +10231,8 @@ lookup_template_class (tree d1, tree arglist, tree in_decl, tree context,
 	    context = DECL_CONTEXT (templ);
 	  else
 	    {
-	      context = tsubst_aggr_type (context, arglist,
-					  complain, in_decl, true);
+	      context = tsubst_entering_scope (context, arglist,
+					       complain, in_decl);
 	      /* Try completing the enclosing context if it's not already so.  */
 	      if (context != error_mark_node
 		  && !COMPLETE_TYPE_P (context))
@@ -14148,94 +14172,6 @@ tsubst_each_template_parm_constraints (tree parms, tree args,
   --processing_template_decl;
 }
 
-/* Substitute the ARGS into the indicated aggregate (or enumeration)
-   type T.  If T is not an aggregate or enumeration type, it is
-   handled as if by tsubst.  IN_DECL is as for tsubst.  If
-   ENTERING_SCOPE is nonzero, T is the context for a template which
-   we are presently tsubst'ing.  Return the substituted value.  */
-
-static tree
-tsubst_aggr_type (tree t,
-		  tree args,
-		  tsubst_flags_t complain,
-		  tree in_decl,
-		  int entering_scope)
-{
-  if (t == NULL_TREE)
-    return NULL_TREE;
-
-  /* Handle typedefs via tsubst so that they get consistently reused.  */
-  if (typedef_variant_p (t))
-    {
-      t = tsubst (t, args, complain, in_decl);
-      if (t == error_mark_node)
-	return error_mark_node;
-
-      /* The effect of entering_scope is that for a dependent specialization
-	 A<T>, lookup_template_class prefers to return A's primary template
-	 type instead of the implicit instantiation.  So when entering_scope,
-	 we mirror this behavior by inspecting TYPE_CANONICAL appropriately,
-	 taking advantage of the fact that lookup_template_class links the two
-	 types by setting TYPE_CANONICAL of the latter to the former.  */
-      if (entering_scope
-	  && CLASS_TYPE_P (t)
-	  && dependent_type_p (t)
-	  && TYPE_TEMPLATE_INFO (t)
-	  && TYPE_CANONICAL (t) == TREE_TYPE (TYPE_TI_TEMPLATE (t)))
-	t = TYPE_CANONICAL (t);
-
-      return t;
-    }
-
-  switch (TREE_CODE (t))
-    {
-      case RECORD_TYPE:
-      case ENUMERAL_TYPE:
-      case UNION_TYPE:
-	return tsubst_aggr_type_1 (t, args, complain, in_decl, entering_scope);
-
-      default:
-	return tsubst (t, args, complain, in_decl);
-    }
-}
-
-/* The part of tsubst_aggr_type that's shared with the RECORD_, UNION_
-   and ENUMERAL_TYPE cases of tsubst.  */
-
-static tree
-tsubst_aggr_type_1 (tree t,
-		    tree args,
-		    tsubst_flags_t complain,
-		    tree in_decl,
-		    int entering_scope)
-{
-  if (TYPE_TEMPLATE_INFO (t) && uses_template_parms (t))
-    {
-      complain &= ~tf_qualifying_scope;
-
-      /* Figure out what arguments are appropriate for the
-	 type we are trying to find.  For example, given:
-
-	   template <class T> struct S;
-	   template <class T, class U> void f(T, U) { S<U> su; }
-
-	 and supposing that we are instantiating f<int, double>,
-	 then our ARGS will be {int, double}, but, when looking up
-	 S we only want {double}.  */
-      tree argvec = tsubst_template_args (TYPE_TI_ARGS (t), args,
-					  complain, in_decl);
-      if (argvec == error_mark_node)
-	return error_mark_node;
-
-      tree r = lookup_template_class (t, argvec, in_decl, NULL_TREE,
-				      entering_scope, complain);
-      return cp_build_qualified_type (r, cp_type_quals (t), complain);
-    }
-  else
-    /* This is not a template type, so there's nothing to do.  */
-    return t;
-}
-
 /* Map from a FUNCTION_DECL to a vec of default argument instantiations,
    indexed in reverse order of the parameters.  */
 
@@ -14601,8 +14537,7 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
     lambda_fntype = static_fn_type (lambda_fntype);
 
   if (member && !closure)
-    ctx = tsubst_aggr_type (ctx, args,
-			    complain, t, /*entering_scope=*/1);
+    ctx = tsubst_entering_scope (ctx, args, complain, t);
 
   tree type = (lambda_fntype ? lambda_fntype
 	       : tsubst (TREE_TYPE (t), args,
@@ -14996,8 +14931,7 @@ tsubst_template_decl (tree t, tree args, tsubst_flags_t complain,
 	  inner = TREE_TYPE (inner);
 	}
       if (class_p)
-	inner = tsubst_aggr_type (inner, args, complain,
-				  in_decl, /*entering*/1);
+	inner = tsubst_entering_scope (inner, args, complain, in_decl);
       else
 	inner = tsubst_decl (inner, args, complain, /*use_spec_table=*/false);
     }
@@ -15501,9 +15435,7 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain,
 	    local_p = false;
 	    if (DECL_CLASS_SCOPE_P (t))
 	      {
-		ctx = tsubst_aggr_type (ctx, args,
-					complain,
-					in_decl, /*entering_scope=*/1);
+		ctx = tsubst_entering_scope (ctx, args, complain, in_decl);
 		if (DECL_SELF_REFERENCE_P (t))
 		  /* The context and type of an injected-class-name are
 		     the same, so we don't need to substitute both.  */
@@ -16299,8 +16231,31 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
       /* Fall through.  */
     case UNION_TYPE:
     case ENUMERAL_TYPE:
-      return tsubst_aggr_type_1 (t, args, complain, in_decl,
-				 /*entering_scope=*/0);
+      if (TYPE_TEMPLATE_INFO (t) && uses_template_parms (t))
+	{
+	  complain &= ~tf_qualifying_scope;
+
+	  /* Figure out what arguments are appropriate for the
+	     type we are trying to find.  For example, given:
+
+	       template <class T> struct S;
+	       template <class T, class U> void f(T, U) { S<U> su; }
+
+	     and supposing that we are instantiating f<int, double>,
+	     then our ARGS will be {int, double}, but, when looking up
+	     S we only want {double}.  */
+	  tree argvec = tsubst_template_args (TYPE_TI_ARGS (t), args,
+					      complain, in_decl);
+	  if (argvec == error_mark_node)
+	    return error_mark_node;
+
+	  tree r = lookup_template_class (t, argvec, in_decl, NULL_TREE,
+					  complain);
+	  return cp_build_qualified_type (r, cp_type_quals (t), complain);
+	}
+      else
+	/* This is not a template type, so there's nothing to do.  */
+	return t;
 
     case ERROR_MARK:
     case IDENTIFIER_NODE:
@@ -16492,7 +16447,6 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 		r = lookup_template_class (arg,
 					   argvec, in_decl,
 					   DECL_CONTEXT (arg),
-					    /*entering_scope=*/0,
 					   complain);
 		return cp_build_qualified_type
 		  (r, cp_type_quals (t) | cp_type_quals (r), complain);
@@ -16584,7 +16538,7 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	      if (argvec == error_mark_node)
 		return error_mark_node;
 	      r = lookup_template_class (tmpl, argvec, in_decl, NULL_TREE,
-					 /*entering_scope=*/false, complain);
+					 complain);
 	      r = cp_build_qualified_type (r, cp_type_quals (t), complain);
 	      break;
 	    }
@@ -16839,9 +16793,10 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	    ctx = TREE_VEC_ELT (ctx, 0);
 	  }
 	else
-	  ctx = tsubst_aggr_type (ctx, args,
-				  complain | tf_qualifying_scope,
-				  in_decl, /*entering_scope=*/1);
+	  {
+	    ctx = tsubst_scope (ctx, args, complain, in_decl);
+	    ctx = adjust_type_for_entering_scope (ctx);
+	  }
 	if (ctx == error_mark_node)
 	  return error_mark_node;
 
@@ -16914,8 +16869,10 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 
     case UNBOUND_CLASS_TEMPLATE:
       {
-	tree ctx = tsubst_aggr_type (TYPE_CONTEXT (t), args, complain,
-				     in_decl, /*entering_scope=*/1);
+	++processing_template_decl;
+	tree ctx = tsubst_entering_scope (TYPE_CONTEXT (t), args,
+					  complain, in_decl);
+	--processing_template_decl;
 	tree name = TYPE_IDENTIFIER (t);
 	tree parm_list = DECL_TEMPLATE_PARMS (TYPE_NAME (t));
 
@@ -21564,9 +21521,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 
 	   When we instantiate f<7>::S::g(), say, lookup_name is not
 	   clever enough to find f<7>::a.  */
-	enum_type
-	  = tsubst_aggr_type (DECL_CONTEXT (t), args, complain, in_decl,
-			      /*entering_scope=*/0);
+	enum_type = tsubst (DECL_CONTEXT (t), args, complain, in_decl);
 
 	for (v = TYPE_VALUES (enum_type);
 	     v != NULL_TREE;
@@ -21586,8 +21541,8 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	{
 	  tree ctx;
 
-	  ctx = tsubst_aggr_type (DECL_CONTEXT (t), args, complain, in_decl,
-				  /*entering_scope=*/1);
+	  ctx = tsubst_entering_scope (DECL_CONTEXT (t), args,
+				       complain, in_decl);
 	  if (ctx != DECL_CONTEXT (t))
 	    {
 	      tree r = lookup_field (ctx, DECL_NAME (t), 0, false);
@@ -21630,8 +21585,8 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	     TEMPLATE_DECL with `D<T>' as its DECL_CONTEXT.  Now we
 	     have to substitute this with one having context `D<int>'.  */
 
-	  tree context = tsubst_aggr_type (DECL_CONTEXT (t), args, complain,
-					   in_decl, /*entering_scope=*/true);
+	  tree context = tsubst_entering_scope (DECL_CONTEXT (t), args,
+						complain, in_decl);
 	  RETURN (lookup_field (context, DECL_NAME(t), 0, false));
 	}
       else
@@ -22161,8 +22116,8 @@ instantiate_template (tree tmpl, tree orig_args, tsubst_flags_t complain)
 	   already non-dependent, then we might as well use it.  */
 	ctx = DECL_CONTEXT (tmpl);
       else
-	ctx = tsubst_aggr_type (DECL_CONTEXT (gen_tmpl), targ_ptr,
-				complain, gen_tmpl, true);
+	ctx = tsubst_entering_scope (DECL_CONTEXT (gen_tmpl), targ_ptr,
+				     complain, gen_tmpl);
       push_nested_class (ctx);
     }
 
@@ -29324,8 +29279,8 @@ resolve_typename_type (tree type, bool only_current_p)
       tree args = TREE_OPERAND (fullname, 1);
       /* Instantiate the template.  */
       result = lookup_template_class (tmpl, args, NULL_TREE, NULL_TREE,
-				      /*entering_scope=*/true,
 				      tf_error | tf_user);
+      result = adjust_type_for_entering_scope (result);
       if (result == error_mark_node)
 	result = NULL_TREE;
     }
@@ -29564,7 +29519,7 @@ listify (tree arg)
   TREE_VEC_ELT (argvec, 0) = arg;
 
   return lookup_template_class (std_init_list, argvec, NULL_TREE,
-				NULL_TREE, 0, tf_warning_or_error);
+				NULL_TREE, tf_warning_or_error);
 }
 
 /* Replace auto in TYPE with std::initializer_list<auto>.  */
@@ -30510,7 +30465,7 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
 	    fprime = copy_decl (fprime);
 	  tree fntype = TREE_TYPE (fprime);
 	  ret = lookup_template_class (TPARMS_PRIMARY_TEMPLATE (atparms), targs,
-				       in_decl, NULL_TREE, false, complain);
+				       in_decl, NULL_TREE, complain);
 	  fntype = build_function_type (ret, TYPE_ARG_TYPES (fntype));
 	  TREE_TYPE (fprime) = fntype;
 	  if (TREE_CODE (fprime) == TEMPLATE_DECL)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 2dde65a970b..d0fa324fe5c 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -4011,8 +4011,10 @@ finish_template_type (tree name, tree args, int entering_scope)
   tree type;
 
   type = lookup_template_class (name, args,
-				NULL_TREE, NULL_TREE, entering_scope,
+				NULL_TREE, NULL_TREE,
 				tf_warning_or_error | tf_user);
+  if (entering_scope)
+    type = adjust_type_for_entering_scope (type);
 
   /* If we might be entering the scope of a partial specialization,
      find the one with the right constraints.  */
-- 
2.45.0.31.gd4cc1ec35f


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/2] c++: remove lookup_template_class's entering_scope flag
  2024-05-02 17:49           ` Patrick Palka
@ 2024-05-02 18:11             ` Jason Merrill
  0 siblings, 0 replies; 13+ messages in thread
From: Jason Merrill @ 2024-05-02 18:11 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches

On 5/2/24 13:49, Patrick Palka wrote:
> On Wed, 1 May 2024, Jason Merrill wrote:
> 
>> On 5/1/24 13:40, Patrick Palka wrote:
>>> On Wed, 1 May 2024, Jason Merrill wrote:
>>>
>>>> On 5/1/24 12:41, Patrick Palka wrote:
>>>>> On Fri, 2 Feb 2024, Patrick Palka wrote:
>>>>>
>>>>>> Bootstrapped and regtested on x86_64-pc-linux, does this look like
>>>>>> an improvement?  This is not a bugfix and barely related to the
>>>>>> previous
>>>>>> patch, but the previous patch's new use of entering_scope=true
>>>>>> motivated
>>>>>> me to submit this patch since it seems like a nice simplification.
>>>>>
>>>>> Ping, now that stage 1 is open.
>>>>
>>>> Thanks for the ping.  The earlier message isn't showing up in Thunderbird
>>>> for
>>>> some reason, though I see it in the gmail web interface...
>>>
>>> Ah, weird.  No worries, this patch was very much stage 1 material anyway.
>>>
>>>>
>>>>>> @@ -16771,9 +16722,10 @@ tsubst (tree t, tree args, tsubst_flags_t
>>>>>> complain, tree in_decl)
>>>>>>     	    ctx = TREE_VEC_ELT (ctx, 0);
>>>>>>     	  }
>>>>>>     	else
>>>>>> -	  ctx = tsubst_aggr_type (ctx, args,
>>>>>> -				  complain | tf_qualifying_scope,
>>>>>> -				  in_decl, /*entering_scope=*/1);
>>>>>> +	  {
>>>>>> +	    ctx = tsubst_scope (ctx, args, complain, in_decl);
>>>>
>>>> Why is this one tsubst_scope while the others are all plain tsubst?
>>>
>>> Ah, just because the call to tsubst_aggr_type being replace passes
>>> tf_qualifying_scope already, so we might as well use tsubst_scope
>>> as shorthand.
>>>
>>>> Do we want a tsubst_entering_scope function?
>>>
>>> Which is just shorthand for tsubst + adjust_type_for_entering_scope?
>>
>> That's what I was thinking.
>>
>>> Sure, though I was wondering if we eventually might want to get rid of
>>> the distinction between the primary template type A<T> and the generic
>>> instantiation A<T>, and we could treat this as an incremental step
>>> towards that (then we'd just eventually remove the
>>> adjust_type_for_entering_scope calls and keep the tsubst calls).
>>
>> I don't think we want that; having the distinction helps to avoid wrongly
>> looking up members of the primary template in contexts that shouldn't be able
>> to.
> 
> Makes sense.  How does the following look?
> 
> The name tsubst_entering_scope sounds confusingly similar to
> tsubst_scope, but I can't think of a better name for it or for
> adjust_type_for_entering_scope :/
> 
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index 1c3eef60c06..261d6b9f4c8 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -185,8 +185,6 @@ static int unify_pack_expansion (tree, tree, tree,
>   static tree copy_template_args (tree);
>   static tree tsubst_template_parms (tree, tree, tsubst_flags_t);
>   static void tsubst_each_template_parm_constraints (tree, tree, tsubst_flags_t);
> -static tree tsubst_aggr_type (tree, tree, tsubst_flags_t, tree, int);
> -static tree tsubst_aggr_type_1 (tree, tree, tsubst_flags_t, tree, int);
>   static tree tsubst_arg_types (tree, tree, tree, tsubst_flags_t, tree);
>   static tree tsubst_function_type (tree, tree, tsubst_flags_t, tree);
>   static bool check_specialization_scope (void);
> @@ -9901,6 +9899,34 @@ maybe_get_template_decl_from_type_decl (tree decl)
>       ? CLASSTYPE_TI_TEMPLATE (TREE_TYPE (decl)) : decl;
>   }
>   
> +/* If TYPE is the generic implicit instantiation A<T>, return the primary
> +   template type A<T> (which is suitable for entering into e.g. for name
> +   lookup), otherwise return TYPE.  */

Maybe "e.g. for defining a member of A<T>"

OK either way.

> +
> +tree
> +adjust_type_for_entering_scope (tree type)
> +{
> +  if (CLASS_TYPE_P (type)
> +      && dependent_type_p (type)
> +      && TYPE_TEMPLATE_INFO (type)
> +      /* We detect the generic implicit instantiation A<T> by inspecting
> +	 TYPE_CANONICAL, which lookup_template_class sets to the primary
> +	 template type A<T>.  */
> +      && TYPE_CANONICAL (type) == TREE_TYPE (TYPE_TI_TEMPLATE (type)))
> +    type = TYPE_CANONICAL (type);
> +  return type;
> +}


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2024-05-02 18:11 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-02-02 19:41 [PATCH 1/2] c++: requires-exprs and partial constraint subst [PR112769] Patrick Palka
2024-02-02 19:41 ` [PATCH 2/2] c++: remove lookup_template_class's entering_scope flag Patrick Palka
2024-05-01 19:41   ` Patrick Palka
2024-05-01 20:22     ` Jason Merrill
2024-05-01 20:40       ` Patrick Palka
2024-05-01 20:52         ` Jason Merrill
2024-05-02 17:49           ` Patrick Palka
2024-05-02 18:11             ` Jason Merrill
2024-02-02 19:55 ` [PATCH 1/2] c++: requires-exprs and partial constraint subst [PR112769] Jason Merrill
2024-02-02 20:01 ` Jason Merrill
2024-02-02 20:36   ` Patrick Palka
2024-02-02 20:57     ` Patrick Palka
2024-02-02 21:05       ` Jason Merrill

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).