Should decltype(foo(1)) instantiate the constexpr function template foo?
Asked Answered
C

1

12

The following code compiles with with gcc and MSVC, but fails using clang I tested with clang-3.5 and current trunk).

template <typename T>
constexpr auto wrong = false;

template <typename T>
constexpr auto foo(const T t) -> int
{
  static_assert(wrong<T>, "");
  return {};
}

using F = decltype(foo(1));

int main() {}

clang instantiates the function body and stumbles over the static_assert. gcc and MSVC just look at the function declaration and ignore the static_assert in the body.

If you remove the constexpr, all compilers compile the code just fine.

Question:
Is decltype allowed to look into the function body if the return type is declared?

I am looking for a reference to the respective section in the standard.

Crumhorn answered 14/11, 2016 at 16:32 Comment(5)
I think a better way to ask this is: does decltype trigger a template instantiation?Grizel
@BoPersson The compiler cannot tell without instantiating the body. There could be a specialization of wrong that is true.Crumhorn
@RyanHaining In most cases, it does not. As written in the question: If you remove the constexpr, no compiler instantiates the body.Crumhorn
"[temp.inst]/3 ... the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist." That would seem to suggest that, if a piece of code would compile with only the declaration of a function template available but not the definition, then that piece of code shouldn't trigger an implicit instantiation. Clang violates this invariant - works with declaration alone, fails with definition. Looks like a bug to my untrained eye.Loreanloredana
Searching the clang bug database, I learned that there is an unresolved core issue on this topic: open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1581Crumhorn
P
5

History: As noted in comments, this issue was raised as CWG issue 1581. In a isocpp.org thread, Columbo argued that the code was valid because templates are never instantiated inside unevaluated operands, however on a clang bug report, "rsmith" countered that some decltype expressions definitely did require template instantiation.

The clang thread temporarily resolved the issue by coming up with their own (non-standard) criteria for when decltype would instantiate a constexpr template. Since version 4.0, clang does compile the code successfully.


Richard Smith of WG21 has started to address the issue as of November 2017, with P0859. This adds new text to [expr.const] which implement the behaviour of clang as discussed above:

An expression is potentially constant evaluated if it is:

  • a potentially-evaluated expression ([basic.def.odr]),
  • a constraint-expression, including one formed from the constraint-logical-or-expression of a requires-clause,
  • an immediate subexpression of a braced-init-list[ Footnote: Constant evaluation may be necessary to determine whether a narrowing conversion is performed ([dcl.init.list]). ],
  • an expression of the form & cast-expression that occurs within a templated entity[ Footnote: Constant evaluation may be necessary to determine whether such an expression is value-dependent ([temp.dep.constexpr]). ], or
  • a subexpression of one of the above that is not a subexpression of a nested unevaluated operand.

A function or variable is needed for constant evaluation if it is:

  • a constexpr function that is named by an expression ([basic.def.odr]) that is potentially constant evaluated, or
  • a variable whose name appears as a potentially constant evaluated expression that is either a constexpr variable or is of non-volatile const-qualified integral type or of reference type.

And [temp.inst] is modified to say that a template specialization is instantiated if its definition affects the semantics of the program, which means that it's needed for constant evaluation as defined above, even if it isn't actually needed, so to speak.

The ODR is modified to avoid Columbo's objection.


The recommended changes on that proposal do appear in N4727, which is a post-C++17 draft. So I assume they have been accepted even though the P0859 link and the CWG defect list don't say so yet.

Under these changes, your code decltype(foo(1)), the expression foo(1) is NOT potentially constant evaluated (because it does not match any of the bullet points above), so the template must not be instantiated, and the code, with a modification to avoid [dcl.constexpr]/6, should compile successfully.

(C++17 dcl.constexpr/6 says that a template is ill-formed NDR if there are no valid specializations; which is true for foo, however this can be fixed by adding template <> constexpr auto wrong<float> = true; for example).

Punchball answered 20/5, 2018 at 21:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.