Why can I call a non-constexpr function inside a constexpr function?
Asked Answered
M

2

19

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?

Marmion answered 4/3, 2014 at 15:57 Comment(18)
clang 3.5 gives me an error: note: non-constexpr function 'printf' cannot be used in a constant expressionClubhaul
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]Dovap
@GabiMe Whatever compiler ideone uses :)Marmion
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.Jenjena
@Jenjena 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.Dovap
@Dovap 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?Jenjena
@Dovap 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.Jenjena
@Jenjena Actually, I think you are right. I thought calling f() was legal in a non-constexpr context. I have to check this though.Dovap
@Jenjena 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.Dovap
@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.Fixity
@Fixity 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.Clubhaul
@Jenjena it depends in this case the standard says it is ill-formed and no diagnostic is required.Clubhaul
@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.Fixity
@Fixity 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.Clubhaul
@Fixity I posted a follow-up question on whether gcc is allowed to do this or not.Clubhaul
@ShafikYaghmour Feel free to retag.Marmion
I know this is late, but generally I rely on using std::array<int, f()> a; to test constant expressions rather than VLA's. ExampleShep
I updated my answer to reflect some new information and some information I should have added back then but forgot to.Clubhaul
C
15

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.

Clubhaul answered 4/3, 2014 at 16:25 Comment(0)
M
0

The program is legal since C++23.

C++23 allows you to mark (almost) any function as constexpr, even if it's impossible to evaluate at compile-time. In your case the function will run at runtime. You will only get an error if you try to force it to run at compile-time, e.g. by assigning the result to a constexpr variable.

Madlynmadman answered 17/1 at 4:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.