18

Consider 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);
}

I would have expected the compiler to complain about the call to printf inside f, because f is supposed to be constexpr, but printf is not. Why does the program compile and print 15?

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • 3
    clang `3.5` gives me an error: `note: non-constexpr function 'printf' cannot be used in a constant expression` – Shafik Yaghmour Mar 04 '14 at 15:58
  • Could it be that your compiler allows VLAs? This fails on GC 4.8.2 with reasonable compiler flags. `error: ISO C++ forbids variable length array 'a' [-Wvla]` – juanchopanza Mar 04 '14 at 15:59
  • @GabiMe Whatever compiler ideone uses :) – fredoverflow Mar 04 '14 at 16:00
  • On gcc 4.8.2 with `-pedantic` this gives an error. Apparently, gcc treats this as a VLA and never tries to check if this is really `constexpr`. Tring to use `f()` as a non-type template argument will fail. – pmr Mar 04 '14 at 16:00
  • @pmr I would say it does check, and it finds isn't `constexpr`. Then it complains about VLAs, because that is something it supports by default. – juanchopanza Mar 04 '14 at 16:05
  • 2
    @juanchopanza I think this boils down to: When should an invalid `constexpr` be rejected: (a) always, like clang does, (b) when its compile-time evaluation is required? – pmr Mar 04 '14 at 16:06
  • @juanchopanza I agree. `clang` seems to be over-eager to reject this and this should probably be a warning instead of an error. It should be an error when `f()` is used as a `constexpr`. Unfortunately, I cannot easily back this up with a quote. – pmr Mar 04 '14 at 16:10
  • @pmr Actually, I think you are right. I thought calling `f()` was legal in a non-constexpr context. I have to check this though. – juanchopanza Mar 04 '14 at 16:10
  • 1
    @pmr It looks like a bug in GCC. If I replace the call to `printf` with one to `int foo() {return 42;}` it fails to compile. It should do the same with `printf` because neither is `constexpr`. – juanchopanza Mar 04 '14 at 16:24
  • 3
    @juanchopanza: Maybe this is happening because `strlen` in GCC on a character literal is `constexpr` and GCC substitutes `printf` with `puts` (or `puts`+`strlen` if you consume the return value) if the format string contains no format specifiers. So apart from the obvious side effect, one could _believe_ that this is a mighty fine `constexpr` function. – Damon Mar 04 '14 at 16:28
  • @Damon hmmm, I don't think the compiler is allowed to do that ... and if we use this `constexpr int x = f();` in `gcc` it produces an error. – Shafik Yaghmour Mar 04 '14 at 16:40
  • @pmr it depends in this case the standard says it is ill-formed and no diagnostic is required. – Shafik Yaghmour Mar 04 '14 at 16:59
  • @ShafikYaghmour: I agree that it is certainly illegal, but I can see how the compiler might _believe_ that it is, due to those substitutions. `constexpr int x = strlen("foo");` compiles just fine, and if you disassemble the binary, you never find a single call to `strlen` anywhere, only the literal (assuming you use it, so it isn't optimized out). Ironically, this is **not** true for built-in macros such as `__func__`, which, to you and me, are quite obviously `constexpr`, but the compiler will claim that they're not. – Damon Mar 04 '14 at 17:28
  • @Damon huh, that is good for thought, if you use `-fno-builtin` these all become errors in `gcc`. So that makes more sense, I still think the compiler is not allowed to do this though. – Shafik Yaghmour Mar 04 '14 at 17:34
  • @Damon I posted a [follow-up question](http://stackoverflow.com/questions/22182432/gcc-considering-builtins-of-non-constant-expression-functions-to-be-constant-exp) on whether `gcc` is allowed to do this or not. – Shafik Yaghmour Mar 04 '14 at 20:35
  • @ShafikYaghmour Feel free to retag. – fredoverflow Mar 04 '14 at 20:47
  • I know this is late, but generally I rely on using `std::array a;` to test constant expressions rather than VLA's. [Example](http://coliru.stacked-crooked.com/a/f83344965403611d) –  Mar 05 '14 at 00:00
  • I updated my answer to reflect some new information and some information I should have added back then but forgot to. – Shafik Yaghmour Jan 05 '15 at 19:08

1 Answers1

14

The program is ill-formed and requires no diagnostic according to the C++11 draft standard section 7.1.5 The constexpr specifier paragraph 5 which says:

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.

and provides the following example:

constexpr int f(bool b)
  { return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required

and section 5.19 paragraph 2 says:

A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression [...]

and includes:

— an invocation of a function other than a constexpr constructor for a literal class or a constexpr function [ Note: Overload resolution (13.3) is applied as usual —end note ];

We would probably prefer a diagnostic in this case, it could just be an oversight, I have a bug report for a similar situation where gcc does not produce an error but we would probably like it to: Is the compiler allowed leeway in what it considers undefined behavior in a constant expression?.

Update

Using the -fno-builtin flag will cause gcc to generate the following error:

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

So gcc does consider this ill-formed it is just ignores it when it is using the builtin version of printf.

Although somewhat inconsistently using the -pedantic produces the following warning:

warning: ISO C++ forbids variable length array 'a' [-Wvla]
 char a[f()];
           ^

Note that using f() to initialized a constexpr variable:

constexpr int x = f() ;

does generate an error:

error: 'printf(((const char*)"a side effect!\012"))' is not a constant expression

Note that additionally in the more general case a compiler is not allowed mark standard library functions as constexpr unless explicitly allowed by the standard.

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