c++: unnecessary instantiation of constexpr var [PR99130]

Here the use of 'value' from within an unevaluated context causes us
to overeagerly instantiate it, via maybe_instantiate_decl called from
mark_used, despite the use occurring in a context that doesn't require
a definition.

This seems to only affect constexpr variable specializations, though
we used to have the same issue for constexpr function specializations
until r6-1309-g81371eff9bc7ef made us delay their instantiation until
necessary during constexpr evaluation.

This patch expands upon the r6-1309 fix to make mark_used avoid
unnecessarily instantiating constexpr variable specializations too,
by pulling out from maybe_instantiate_decl the condition

  (decl_maybe_constant_var_p (decl)
   || (TREE_CODE (decl) == FUNCTION_DECL
       && DECL_OMP_DECLARE_REDUCTION_P (decl))
   || undeduced_auto_decl (decl))

into each of its three callers (including mark_used), removing the
problematic first test from mark_used, and simplifying accordingly.
The net result is that only mark_used is changed because the other two
callers, resolve_address_of_overloaded_function and decl_constant_var_p,
already guard the call appropriately.  (This relaxation of mark_used
seems to be safe because during constexpr evaluation we already take
care to instantiate a constexpr variable as necessary via
decl_constant_value etc).

	PR c++/99130

gcc/cp/ChangeLog:

	* decl2.cc (maybe_instantiate_decl): Adjust function comment.
	Check VAR_OR_FUNCTION_DECL_P.  Pull out the disjunction into ...
	(mark_used): ... here, removing the decl_maybe_constant_var_p
	part of it.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp0x/constexpr-decltype5.C: New test.
This commit is contained in:
Patrick Palka 2022-09-08 09:45:45 -04:00
parent 95c7d58995
commit 4db3cb781c
2 changed files with 34 additions and 22 deletions

View File

@ -5381,24 +5381,15 @@ possibly_inlined_p (tree decl)
return true;
}
/* Normally, we can wait until instantiation-time to synthesize DECL.
However, if DECL is a static data member initialized with a constant
or a constexpr function, we need it right now because a reference to
such a data member or a call to such function is not value-dependent.
For a function that uses auto in the return type, we need to instantiate
it to find out its type. For OpenMP user defined reductions, we need
them instantiated for reduction clauses which inline them by hand
directly. */
/* If DECL is a function or variable template specialization, instantiate
its definition now. */
void
maybe_instantiate_decl (tree decl)
{
if (DECL_LANG_SPECIFIC (decl)
if (VAR_OR_FUNCTION_DECL_P (decl)
&& DECL_LANG_SPECIFIC (decl)
&& DECL_TEMPLATE_INFO (decl)
&& (decl_maybe_constant_var_p (decl)
|| (TREE_CODE (decl) == FUNCTION_DECL
&& DECL_OMP_DECLARE_REDUCTION_P (decl))
|| undeduced_auto_decl (decl))
&& !DECL_DECLARED_CONCEPT_P (decl)
&& !uses_template_parms (DECL_TI_ARGS (decl)))
{
@ -5700,15 +5691,13 @@ mark_used (tree decl, tsubst_flags_t complain)
return false;
}
/* Normally, we can wait until instantiation-time to synthesize DECL.
However, if DECL is a static data member initialized with a constant
or a constexpr function, we need it right now because a reference to
such a data member or a call to such function is not value-dependent.
For a function that uses auto in the return type, we need to instantiate
it to find out its type. For OpenMP user defined reductions, we need
them instantiated for reduction clauses which inline them by hand
directly. */
maybe_instantiate_decl (decl);
/* If DECL has a deduced return type, we need to instantiate it now to
find out its type. For OpenMP user defined reductions, we need them
instantiated for reduction clauses which inline them by hand directly. */
if (undeduced_auto_decl (decl)
|| (TREE_CODE (decl) == FUNCTION_DECL
&& DECL_OMP_DECLARE_REDUCTION_P (decl)))
maybe_instantiate_decl (decl);
if (processing_template_decl || in_template_function ())
return true;

View File

@ -0,0 +1,23 @@
// PR c++/99130
// { dg-do compile { target c++11 } }
template<class T>
struct A {
static constexpr int value = T::nonexistent;
};
using type = const int;
using type = decltype(A<int>::value);
#if __cpp_variable_templates
struct B {
template<class T>
static constexpr int value = T::nonexistent;
};
template<class T>
constexpr int value = T::nonexistent;
using type = decltype(B::value<int>);
using type = decltype(value<int>);
#endif