This is an archive of the discontinued LLVM Phabricator instance.

PR24115: Don't instantiate constexpr function templates in decltype
AbandonedPublic

Authored by sberg on Oct 2 2015, 8:05 AM.

Details

Summary

As discussed in "llvm-nm fails to build with gcc 5.1's libstdc++," both llvm-nm and LibreOffice fail to build against GCC 5.1 libstdc++, due to Clang trying (and failing) to instantiate constexpr function templates referenced from within a decltype.
See "Implicit instantiation of constexpr function template specialization called in decltype expression?" for a stripped-down test case and a discussion why the Clang behaviour is considered wrong.
There is a comment in test/SemaTemplate/constexpr-instantiate.cpp claiming this behaviour is in line with g++, but I cannot reproduce that neither with some GCC 5.1.1 nor with a recent GCC trunk build. The comment also says "FIXME: None of this is required by the C++ standard. The rules in this area are poorly specified, so this is subject to change.", and from the above build breakage I think it would be best to change the behaviour to not instantiate constexpr function templates merely because they are referenced from within a decltype. (Doing so of course breaks the test corresponding to that comment, so turn that test around to check the changed behaviour now.)

Diff Detail

Event Timeline

sberg updated this revision to Diff 36366.Oct 2 2015, 8:05 AM
sberg retitled this revision from to PR24115: Don't instantiate constexpr function templates in decltype.
sberg updated this object.
sberg added reviewers: cfe-commits, rsmith.
EricWF added a subscriber: EricWF.Oct 4 2015, 3:51 AM
rsmith edited edge metadata.Oct 5 2015, 3:06 PM

Sorry, this is not OK; we need to eagerly instantiate within a decltype expression in some cases, and we should not be treating decltype as being different from any other unevaluated operand here. Example:

template<typename T> constexpr T f() { return 0; }
decltype(char{f<int>()}) x;

... will be rejected due to a narrowing conversion unless f<int>() is instantiated. As far as I'm aware, GCC gets around this by triggering instantiation from within the constant expression evaluator, but that is a significant layering / phases of translation violation, and we don't want to do that.

In discussion with Jason Merrill, the front-runner approach for handling this is to separate 'unevaluated contexts' into 'unevaluated contexts' and 'constant-evaluated contexts' (clang already somewhat has this distinction -- see Sema::ExpressionEvaluationContext's Unevaluated versus ConstantEvaluated -- but it doesn't quite line up with what we'd want here). The latter would cover things like:

  • a template argument,
  • an expression within a braced-init-list,
  • an expression inside an array declarator

... and so on (these are the contexts that can occur within an unevaluated context, and where constant evaluation is necessary). We would then trigger instantiation of a constexpr function if it's either odr-used or referenced in a constant-evaluated context.

sberg abandoned this revision.Oct 6 2015, 12:03 AM

Ah, I see. I'll wait for that approach to be implemented then.