Issue 11851

Has there been discussion about this issue, related to the
instantiation of constexpr functions?

http://llvm.org/bugs/show_bug.cgi?id=11851

I updated yesterday and it still seems to be a problem. This is how
I've reproduced it -- at least, I hope this is the same problem.

template <typename T>
  constexpr bool Type() { return true; }

template <typename T, bool = Type<T>()>
  void f(T x) { }

int main() { f(3); }

Digging through the compiler, it looks like Type<int>() is not being
evaluated as a constant expression because the function definition
hasn't been instantiated by the time the call to f is overloaded. That
would jive with Jeffrey's (on the bug report) explanation of
un-commenting a statement that results instantiation of the function
template prior to its use in overload resolution.

I was going to try to fix this problem, but I'm not sure where to
start. What's the right way to ensure that the definition of an
instantiated template is available in this context i.e., not deferred?

Andrew

I was going to try to fix this problem, but I'm not sure where to
start. What's the right way to ensure that the definition of an
instantiated template is available in this context i.e., not deferred?

This solves the problem, but causes a couple regressions.

So, it seems that the problem is that constexpr function template
calls in the signatures of function templates cannot be evaluated
because their uses occur in dependent contexts (apparently
instantiating a function template signature as part of overload
resolution occurs within the context of the function template being
instantiated?). The result is that body of the constexpr function
template isn't instantiated in time to be evaluated, hence the
substitution failures.

This patch just ensures that definitions are emitted for those calls.
I tested against the examples given in the bug report, and they seemed
to compile just fine. I also ran the patch against some of my own
examples, and they work well. I neglected to include those in the
patch though.

Unfortunately the patch causes definitions to be emitted for some
functions that aren't actually used, so I didn't quite get it right.
Is there a deeper problem, or do the criteria for generating
definitions just need to be adjusted?

Errors from the test suite are:

SemaCXX/undefined-internal.cpp
error: 'warning' diagnostics expected but not seen:
  Line 9: not needed and will not be emitted
1 error generated.

SemaCXX/warn-unused-filescoped.cpp
error: 'warning' diagnostics seen but not expected:
  Line 177: function 'OverloadUse::<anonymous namespace>::f' has
internal linkage but is not defined
error: 'note' diagnostics expected but not seen:
  Line 181: used here
error: 'note' diagnostics seen but not expected:
  Line 182: used here
  Line 182: used here
4 errors generated.

SemaCXX/warn-unused-filescoped.cpp
error: 'warning' diagnostics expected but not seen:
  Line 83: not needed and will not be emitted
1 error generated.

Andrew

constexpr-patch (1.02 KB)

Hi Andrew,

I was going to try to fix this problem, but I’m not sure where to
start. What’s the right way to ensure that the definition of an
instantiated template is available in this context i.e., not deferred?

This solves the problem, but causes a couple regressions.

So, it seems that the problem is that constexpr function template
calls in the signatures of function templates cannot be evaluated
because their uses occur in dependent contexts (apparently
instantiating a function template signature as part of overload
resolution occurs within the context of the function template being
instantiated?). The result is that body of the constexpr function
template isn’t instantiated in time to be evaluated, hence the
substitution failures.

This patch just ensures that definitions are emitted for those calls.
I tested against the examples given in the bug report, and they seemed
to compile just fine. I also ran the patch against some of my own
examples, and they work well. I neglected to include those in the
patch though.

Unfortunately the patch causes definitions to be emitted for some
functions that aren’t actually used, so I didn’t quite get it right.
Is there a deeper problem, or do the criteria for generating
definitions just need to be adjusted?

I don’t think this patch is quite the right approach – we should not be instantiating function template specializations which are called within unevaluated operands, even if they’re constexpr.

One problem is in IsPotentiallyEvaluatedContext: we do not instantiate functions and variables if they are used within a dependent context. This causes us to incorrectly reject code such as:

template struct S {
static const int n;
};
template const int S::n = sizeof(T);
template void g() {
int arr[S<int>::n] = { 1, 2, 3, 4 };
}

It looks like fixing that is sufficient for us to correctly handle instantiations of constexpr functions triggered by explicitly-specified template arguments (we treat such substitutions as being in a dependent context). Interestingly, my testing indicates that g++ instantiates non-dependent specializations which are odr-used in dependent contexts in C++11 mode, and does not instantiate them in C++98 mode.

Another problem is that we treat some of the relevant contexts as unevaluated contexts (in particular, when substituting deduced template arguments into a declaration), when they are not. This also causes other bugs, such as our incorrectly accepting this:

template void f(T) {
static_assert(sizeof(T) == 100, “”);
}
template<typename T, void(*)(T) = f> struct S {};
template S g(T);
void h() { g(0); }

The substitution of T=int into S is incorrectly being treated as an unevaluated context, resulting in us failing to notice that f is odr-used and thus failing to instantiate it.

  • Richard