44

gcc compiles the following code without warning:

#include <cmath>

struct foo {
  static constexpr double a = std::cos(3.);
  static constexpr double c = std::exp(3.);
  static constexpr double d = std::log(3.);
  static constexpr double e1 = std::asin(1.);
  static constexpr double h = std::sqrt(.1);
  static constexpr double p = std::pow(1.3,-0.75);
};

int main()
{
}

None of the standard library functions used above are constexpr functions, we are allowed to use them where a constant expression is required from both the draft C++11 standard and draft C++14 standard section 7.1.5 [dcl.constexpr]:

[...]If it is initialized by a constructor call, that call shall be a constant expression (5.19). Otherwise, or if a constexpr specifier is used in a reference declaration, every full expression that appears in its initializer shall be a constant expression.[...]

Even when using -std=c++14 -pedantic or -std=c++11 -pedantic no warnings are generated (see it live). Using -fno-builtin produces errors (see it live) which indicates that the builtin version of these standard library functions are being treated as if they where constexpr

While clang does not allow the code with any combination of flags I have tried.

So this is a gcc extension to treat at least some builtin functions as if they were constexpr functions even though the standard does not explicitly require them to be. I would have expected to at least receive a warning in strict conformance mode, is this a conforming extension?

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740

1 Answers1

44

TL;DR

In C++14 this is explicitly not allowed, although in 2011 it appeared like this case would be explicitly allowed. It is unclear if for C++11 this fell under the as-if rule, I don't believe it does since it alters observable behavior but that point was not clarified in the issue I reference below.

Details

The answer to this question has shifted with the evolving status of LWG issue 2013 which opens with:

Suppose that a particular function is not tagged as constexpr in the standard, but that, in some particular implementation, it is possible to write it within the constexpr constraints. If an implementer tags such a function as constexpr, is that a violation of the standard or is it a conforming extension?

In C++11 it was unclear if the as-if rule permitted this but the orignal proposal would have explicitly allowed it once it was accepted and we can see below in the gcc bug report I reference, this was the assumption made by the gcc team.

The consensus to allow this shifted in 2012 and the proposal changed and in C++14 this is a non-conforming extension. This is reflected in the draft C++14 standard section 17.6.5.6 [constexpr.functions] which says:

[...]An implementation shall not declare any standard library function signature as constexpr except for those where it is explicitly required.[..]

and although a strict reading of this seems to leave some wiggle room for treating a builtin implicitly as if it were a constexpr we can see from the following quote in the issue that the intention was to prevent divergence in implementations since identical code could produce different behavior when using SFINAE (emphasis mine):

Some concern expressed when presented to full committee for the vote to WP status that this issue had been resolved without sufficient thought of the consequences for diverging library implementations, as users may use SFINAE to observe different behavior from otherwise identical code.

We can see from the gcc bug report [C++0x] sinh vs asinh vs constexpr that the team relied on the earlier proposed resolution of LWG 2013 which says:

[...]Additionally, an implementation may declare any function to be constexpr if that function's definition satisfies the necessary constraints[...]

when deciding whether this change for the math functions was allowed in strict conformance mode.

As far as I can tell this would become conforming if this we received a warning in strict conformance mode i.e. using -std=c++11 -pedantic or if it was disabled in this mode.

Note, I added a comment to the bug report explaining that the resolution changed since this issue was originally addressed.

Jonathan Wakely pointed out in another question a more recent discussion and it seems likely the gcc bug report will be reopened to address this conformance issue.

What about intrinsics

Compiler intrinsics are not covered by the standard and so as far as I can tell they should be exempt from this rule, so using:

static constexpr double a = __builtin_cos(3.);

should be allowed. This question came up in the bug report and opinion of Daniel Krügler was:

[...]Library functions and other intrinsics can probably be considered as exceptions, because they are not required to be "explainable" by normal language rules.

Community
  • 1
  • 1
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • @Walter I added a `LT;DR` section. Let me know if there are other point I can clarify. – Shafik Yaghmour Jan 02 '15 at 16:01
  • What about intrinsics? – Columbo Jan 02 '15 at 17:12
  • I don't think it's helpful to talk about compiler intrinsics. All names starting with `__` are reserved for the implementation, and the implementation defines their semantics. It is completely reasonable for the semantics of implementation-specific functions, whether they are intrinisics or not, to be defined as `constexpr`, purely at the discretion of the implementer. – Ben Voigt Jan 05 '15 at 16:06
  • And the as-if rule certainly would not permit this. As-if only allows varying implementations that have the same observable behavior, and SFINAE changes are very much observable differences. – Ben Voigt Jan 05 '15 at 16:08
  • @BenVoigt well the topic of intrinsics was brought up in the `gcc` bug report as well as a comment here and so it seemed relevant to mention it even though the answer would seem obvious. I agree with you on the *as-if* rule, I should have made that more obvious, it is updated now. – Shafik Yaghmour Jan 05 '15 at 18:32
  • @BenVoigt: SFINAE changes may be observable changes, but it nonetheless seems that there should be some constructs which compilers would be allowed, but not required, to regard as substitution failures. If there were an intrinsic "require expression to be constexpr as defined by some particular version of the Standard", but other places that would otherwise require constexpr were allowed to accept (or not) things whose value the compiler can infer, that would make it practical for the language to evolve in favor of usefully recognizing more forms of constant expression in more contexts. – supercat Apr 11 '18 at 20:57