c++: dependence of constrained memfn from current inst [PR105842]

Here we incorrectly deem the calls to func1, func2 and tmpl2 as
ambiguous ahead of time ultimately because we mishandle dependence
of a constrained member function from the current instantiation.

In type_dependent_expression_p, we already consider dependence of a
TEMPLATE_DECL's constraints (via uses_outer_template_parms), but
neglect to do the same for a FUNCTION_DECL (such as that for func1).

And in satisfy_declaration_constraints, we give up if _any_ template
argument is dependent, but for non-dependent member functions from
the current instantiation (such as func2 and tmpl2), we can and must
check constraints as long as the innermost arguments aren't dependent.

	PR c++/105842

gcc/cp/ChangeLog:

	* constraint.cc (satisfy_declaration_constraints): Refine early
	exit test for argument dependence.
	* cp-tree.h (uses_outer_template_parms_in_constraints): Declare.
	* pt.cc (template_class_depth): Handle TI_TEMPLATE being a
	FIELD_DECL.
	(usse_outer_template_parms): Factor out constraint dependence
	test into ...
	(uses_outer_template_parms_in_constraints): ... here.
	(type_dependent_expression_p): Use it for FUNCTION_DECL.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/concepts-memtmpl6.C: New test.

(cherry picked from commit f07778f6f9)
This commit is contained in:
Patrick Palka 2022-07-13 14:01:28 -04:00
parent 670ef5b108
commit 5d6286903f
4 changed files with 84 additions and 12 deletions

View File

@ -3179,9 +3179,15 @@ satisfy_declaration_constraints (tree t, sat_info info)
args = regen_args;
}
/* If any arguments depend on template parameters, we can't
check constraints. Pretend they're satisfied for now. */
if (uses_template_parms (args))
/* If the innermost arguments are dependent, or if the outer arguments
are dependent and are needed by the constraints, we can't check
satisfaction yet so pretend they're satisfied for now. */
if (uses_template_parms (args)
&& ((DECL_TEMPLATE_INFO (t)
&& PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t))
&& (TMPL_ARGS_DEPTH (args) == 1
|| uses_template_parms (INNERMOST_TEMPLATE_ARGS (args))))
|| uses_outer_template_parms_in_constraints (t)))
return boolean_true_node;
/* Get the normalized constraints. */
@ -3243,9 +3249,13 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info)
else
args = add_outermost_template_args (t, args);
/* If any arguments depend on template parameters, we can't
check constraints. Pretend they're satisfied for now. */
if (uses_template_parms (args))
/* If the innermost arguments are dependent, or if the outer arguments
are dependent and are needed by the constraints, we can't check
satisfaction yet so pretend they're satisfied for now. */
if (uses_template_parms (args)
&& (TMPL_ARGS_DEPTH (args) == 1
|| uses_template_parms (INNERMOST_TEMPLATE_ARGS (args))
|| uses_outer_template_parms_in_constraints (t)))
return boolean_true_node;
tree result = boolean_true_node;

View File

@ -7317,6 +7317,7 @@ extern tree lookup_template_function (tree, tree);
extern tree lookup_template_variable (tree, tree);
extern int uses_template_parms (tree);
extern bool uses_template_parms_level (tree, int);
extern bool uses_outer_template_parms_in_constraints (tree);
extern bool in_template_function (void);
extern bool need_generic_capture (void);
extern tree instantiate_class_template (tree);

View File

@ -389,7 +389,9 @@ template_class_depth (tree type)
{
tree tinfo = get_template_info (type);
if (tinfo && PRIMARY_TEMPLATE_P (TI_TEMPLATE (tinfo))
if (tinfo
&& TREE_CODE (TI_TEMPLATE (tinfo)) == TEMPLATE_DECL
&& PRIMARY_TEMPLATE_P (TI_TEMPLATE (tinfo))
&& uses_template_parms (INNERMOST_TEMPLATE_ARGS (TI_ARGS (tinfo))))
++depth;
@ -10953,7 +10955,7 @@ uses_template_parms_level (tree t, int level)
/* Returns true if the signature of DECL depends on any template parameter from
its enclosing class. */
bool
static bool
uses_outer_template_parms (tree decl)
{
int depth = template_class_depth (CP_DECL_CONTEXT (decl));
@ -10984,13 +10986,27 @@ uses_outer_template_parms (tree decl)
return true;
}
}
if (uses_outer_template_parms_in_constraints (decl))
return true;
return false;
}
/* Returns true if the constraints of DECL depend on any template parameters
from its enclosing scope. */
bool
uses_outer_template_parms_in_constraints (tree decl)
{
tree ci = get_constraints (decl);
if (ci)
ci = CI_ASSOCIATED_CONSTRAINTS (ci);
if (ci && for_each_template_parm (ci, template_parm_outer_level,
&depth, NULL, /*nondeduced*/true))
return true;
return false;
if (!ci)
return false;
int depth = template_class_depth (CP_DECL_CONTEXT (decl));
if (depth == 0)
return false;
return for_each_template_parm (ci, template_parm_outer_level,
&depth, NULL, /*nondeduced*/true);
}
/* Returns TRUE iff INST is an instantiation we don't need to do in an
@ -27982,6 +27998,17 @@ type_dependent_expression_p (tree expression)
return false;
}
/* Otherwise, its constraints could still depend on outer template parameters
from its (dependent) scope. */
if (TREE_CODE (expression) == FUNCTION_DECL
/* As an optimization, check this cheaper sufficient condition first.
(At this point we've established that we're looking at a member of
a dependent class, so it makes sense to start treating say undeduced
auto as dependent.) */
&& !dependent_type_p (TREE_TYPE (expression))
&& uses_outer_template_parms_in_constraints (expression))
return true;
/* Always dependent, on the number of arguments if nothing else. */
if (TREE_CODE (expression) == EXPR_PACK_EXPANSION)
return true;

View File

@ -0,0 +1,34 @@
// PR c++/105842
// { dg-do compile { target c++20 } }
template<class T>
struct S {
static void func1() requires __is_same(T, int);
static void func1() requires (!__is_same(T, int));
static void func2() requires false && false;
static void func2() requires false;
template<class...> static void tmpl1() requires __is_same(T, int);
template<class...> static void tmpl1() requires (!__is_same(T, int));
template<class... Us> static void tmpl2() requires (sizeof...(Us) == 1);
template<class... Us> static void tmpl2() requires (sizeof...(Us) == 2);
static void foo() {
// Both calls resolve to the first overload at instantiation time.
func1();
tmpl1();
}
static void bar() {
// We can check and reject both calls ahead of time since the functions'
// constraints don't depend on outer template parameters.
func2(); // { dg-error "no match" }
tmpl2(); // { dg-error "no match" }
}
};
int main() {
S<int>::foo();
}