12

Please see the update for a better sample of the problem. The original code has a mix of issues which muddies the picture:

This question Why can I call a non-constexpr function inside a constexpr function? presented the following code

#include <stdio.h>

constexpr int f()
{
    return printf("a side effect!\n");
}

int main()
{
    char a[f()];
    printf("%zd\n", sizeof a);
}

Which as I answer is ill-formed but gcc 4.8.2 allows it (see it live).

But, if we use the -fno-builtin flag gcc generates an error (see it live):

error: call to non-constexpr function 'int printf(const char*, ...)'
     return printf("a side effect!\n");
                                     ^

so it seems that gcc is considering its builtin version of printf to be a constant expression. gcc documents builtins here but does not document this case where a builtin of a non-constexpr function can be considered a constant expression.

If this is indeed the case:

  • Is a compiler allowed to to do this?
  • If they are allowed, don't they have to document it to be conformant?
  • Can this be considered an extension, if so, it seems like this would require a warning as the C++ draft standard section 1.4 Implementation compliance paragraph 8 says (emphasis mine):

A conforming implementation may have extensions (including additional library functions), provided they do not alter the behavior of any well-formed program. Implementations are required to diagnose programs that use such extensions that are ill-formed according to this International Standard. Having done so, however, they can compile and execute such programs.

Update

As Casey points out there are a few things going on in the original problem that makes it a poor example. A simple example would be using std::pow which is not a constexpr function:

#include <cmath>
#include <cstdio>

constexpr double f()
{
    return std::pow( 2.0, 2.0 ) ;
}

int main()
{
    constexpr double x = f() ;

    printf( "%f\n", x ) ;
}

Compiles and builds with no warnings or error (see it live) but adding -fno-builtin makes it generates an error (see it live). Note: why math functions are not constexpr in C++11:

error: call to non-constexpr function 'double pow(double, double)'
     return std::pow( 2.0, 2.0 ) ;
                               ^
Community
  • 1
  • 1
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • 2
    What exactly do you think gcc violates here? Isn't it [dcl.constexpr]/5 "For a constexpr function, if no function argument values exist such that the function invocation substitution would produce a constant expression (5.19), the program is ill-formed; no diagnostic required."? – dyp Mar 04 '14 at 22:53
  • 1
    Note: I don't quite understand why you say "gcc 4.8.2 allows it" when gcc rejects it, the invocation `f()` being *not a constant expression*. What it doesn't reject however is the definition of the function `f` itself, for which AFAIK, no diagnostic is required. – dyp Mar 04 '14 at 22:56
  • I'm not convinced it treats the built-in as a constant expression. You didn't provide an example that proves it (and the invocation of `f()`, leading to invocation of `printf("a side effect!\n")` is *not* considered a constant expression). – dyp Mar 04 '14 at 23:02
  • @dyp that is why I purposely used the word `seems` as opposed to `prove` but the behavior is consistent across the builtins and `gcc` does produce an error when not using a builtin. So it seems much more likely they are just considering the builtins to be constant expressions which seems odd. [This conversation](http://marc.info/?l=cfe-dev&m=135070979226303&w=2) I just found sounds pretty close to the behavior we are seeing. The questions are still valid questions. – Shafik Yaghmour Mar 04 '14 at 23:25
  • @dyp ok, I reworded to allow for your doubts, which are fair points. – Shafik Yaghmour Mar 04 '14 at 23:31
  • Hmmm I don't understand what you mean with *seems .. considering .. constant expression*. What I observe is this (when using built-ins): gcc does not emit a message that the function is ill-formed. The Standard doesn't require a diagnostic (for the function). gcc rejects *invoking* the function in a context where a constant expression is required (with a diagnostic; AFAIK this is required). gcc does not reject invoking the function in a context where it allows non-constant-expressions, but emits a diagnostic (that's an extension). [to be continued..] – dyp Mar 04 '14 at 23:38
  • 2
    [continued..] When switching to `-fno-builtin`, it *also* diagnoses the ill-formedness of `f` itself. Is the question now: "Is it conforming that gcc shows this different behaviour with different switches?" or is it "Is it conforming that gcc accepts the non-constant-expression as an array bound?" or is it "Is gcc conforming when *not* to rejecting `f`?" – dyp Mar 04 '14 at 23:39
  • @dyp it seems most likely this is considered an extension in which case it seems to me they should be generating a warning and it should be documented. – Shafik Yaghmour Mar 05 '14 at 05:10
  • 1
    Hmm this example is better IMO. `f` is still ill-formed, no diagnostic required, but `x = f()` is ill-formed *and I think a diagnostic is required*. Accepting the program is an extension, not producing any message about it is nonconforming. – dyp Mar 05 '14 at 11:47
  • @dyp this just seems like such an unexpected result and non-portable. You would have no way of knowing until you tried it with another compiler or turned off builtins, seems kind of evil. – Shafik Yaghmour Mar 05 '14 at 13:43
  • It's another question whether gcc *should* emit a warning. It might be subtle, as using the built-in directly doesn't need to be portable, so the warning/diagnostic should only be emitted when using the built-in via an alias where the alias implementation w/o built-ins would be ill-formed (or even: where the *invocation* of the no-built-in implementation for some arguments would be ill-formed when a constant expression is required). – dyp Mar 05 '14 at 13:51
  • @dyp added a self-answer, I recently discovered the answer to this question researching another topic. I don't know how I missed the bug report I link in my answer when I wrote this perhaps it was much further down in search back then. – Shafik Yaghmour Jan 12 '15 at 16:58

2 Answers2

6

Yes, gcc is considering some builtin functions as constexpr even if the standard does not explicitly mark them as such. We can find the discussion that pertains specifically to the math function found in cmath in the gcc bug report [C++0x] sinh vs asinh vs constexpr which says:

LWG 2013 does seem to allow GCC to treat these functions as constexpr. So, fixed for 4.7

which is referring to LWG issue 2013 whose original proposed resolution was to add the following to section 17.6.5.6 [constexpr.functions] (emphasis mine going forward):

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

but after C++11 the resolution was reversed and the final resolution ended up as:

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

So this is currently(in C++14) a explicitly non-conforming extension and as far as I can tell this was non-conforming in C++11 since it alters observable behavior and therefore would not be allowed via the as-if rule.

Jonathan Wakely points out a libstdc++ mailing list discussion: PR libstdc++/49813 revisited: constexpr on functions (and builtins) where reopening the bug report mentioned above was discussed due to the issues laid out above:

I believe we should re-open the bug in light of the actual resolution of LWG 2013 (adding constexpr is forbidden).

The FE should not treat builtins as constexpr in strict-conformance mode.

We should either remove _GLIBCXX_CONSTEXPR from <cmath> entirely or make it conditional on __STRICT_ANSI__.

Community
  • 1
  • 1
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
4

GCC does not consider f() to be a constant expression. Look at the diagnostics for the first sample program you linked:

main.cpp: In function 'int main()':
main.cpp:10:19: warning: ISO C++ forbids variable length array 'a' [-Wvla]
         char a[f()];
                   ^

The compiler doesn't think f() is a constant expression, the program is in fact using GCC's extension that allows variable length arrays - arrays with non-constant size.

If you change the program to force f() into a constant expression:

int main() {
    constexpr int size = f();
    char a[size];
    printf("%zd\n", sizeof a);
}

GCC does report an error:

main.cpp: In function 'int main()':
main.cpp:10:32:   in constexpr expansion of 'f()'
main.cpp:5:41: error: 'printf(((const char*)"a side effect!\012"))' is not a constant expression
         return printf("a side effect!\n");
                                         ^
Casey
  • 41,449
  • 7
  • 95
  • 125
  • [this is a simpler](http://coliru.stacked-crooked.com/a/236de6511453a86d) example with `strlen`, I have to look at the other example again. – Shafik Yaghmour Mar 05 '14 at 02:34
  • @ShafikYaghmour `strlen` has no side effects. It very well could be `constexpr`. [Considering that this compiles](http://coliru.stacked-crooked.com/a/fe68fdf40c91374c), I'd say for all purposes it *is* `constexpr` for that particular flavor of gcc+args. Interestingly, C++1y (well, N3936 anyway) 17.6.5.6 [constexpr.functions]/1 probably forbids this behavior: "This standard explicitly requires that certain standard library functions are constexpr (7.1.5). An implementation shall not declare any standard library function signature as constexpr except for those where it is explicitly required." – Casey Mar 05 '14 at 02:51
  • If [you use -fno-builtin](http://coliru.stacked-crooked.com/a/ec518e0424db5ef1) it generates an error: `error: call to non-constexpr function 'size_t strlen(const char*)'` – Shafik Yaghmour Mar 05 '14 at 03:07
  • @ShafikYaghmour Yes, I'm aware - hence my statement "for that particular flavor of gcc + args". – Casey Mar 05 '14 at 03:19
  • +1 The original example has too many things going on so I updated my question and added a simpler example using `std::pow` since we know that it can not be a constexpr. We also know what `gcc` is doing here is using the builtin to generate a constant at compile time. It is still not clear to me that this is valid. – Shafik Yaghmour Mar 05 '14 at 04:24
  • I thought I mentioned it before but I self-answered due to new information I found much later on. – Shafik Yaghmour Mar 23 '15 at 14:42