Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 927
Rumburak Avatar asked Nov 14 '16 16:11

Rumburak


1 Answers

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).

like image 64
M.M Avatar answered Sep 30 '22 16:09

M.M