5

It seems that the rules for the compile-time keywords: constexpr, consteval and constinit are defined well enough for compilers to warn if you misapply the label.

It would make sense that (much like inline) the compiler can, in all places, apply rules to determine if, in fact, code could have one of the compile-time keywords applied and, be forced, per language specification, to compile as much as possible as if the compile-time keywords had been applied.

Or, at a minimum, if a compile-time keyword is applied to a function and the code would have qualified with had the correct compile-time keywords been applied. Compile that function, as if all the functions called had the correct compile-time keywords.

Here is a simple example:

#include <tuple>

consteval std::tuple<int, int> init1(int x, int y)
{
    return {x,y};
}

std::tuple<int, int>& foo1()
{
    static constinit std::tuple<int, int> x=init1(1,2);
    return x;
}

std::tuple<int, int> init2(int x, int y)
{
    return {x,y};
}

std::tuple<int, int>& foo2()
{
    static std::tuple<int, int> x=init2(1,2);
    return x;
}

static std::tuple<int, int> x3=init2(1,2);

std::tuple<int, int>& foo3()
{
    return x3;
}

see it here: https://godbolt.org/z/KrzGfnEo7

Note that init1/foo1 are fully specified with compile-time keywords and there is no need for a guard variable, since it is initialized completely (not just 0 filled).

The question is about why the compiler doesn't do the same in the cases of init2/foo2 or x3/foo3? Or more precisely why the compiler is allowed NOT to initialize fully at compile time.

EDIT (as per comment) see (Why do we need to mark functions as constexpr?) constexpr would be required. I am concerned more with cases of consteval and constinit. Could the specification be modified to require trying to resolve code at compile-time and not failing because of the absence of constexpr? This would allow interface contracts to just use constexpr as per the related question.

Glenn Teitelbaum
  • 10,108
  • 3
  • 36
  • 80
  • 4
    Are you asking why `constexpr` isn't applied automatically? (read https://stackoverflow.com/q/14472359/103167) Or whether the compiler can perform compile-time optimization on code not using `constexpr`? (It can.) – Ben Voigt Nov 08 '21 at 16:04
  • Yes, I understand since constexpr is optional. I was concerned more for places where I apply consteval or constinit and the compiler could allow it, independent of the existence of constexpr, etc., sorry if that wasn't clear. I'll see if I can edit for that to be clearer. – Glenn Teitelbaum Nov 08 '21 at 16:16
  • a compiler is allowed to apply any optimization as long as you cannot tell the difference. When a function can be evaluated at compile time then why should it not? See https://stackoverflow.com/questions/15718262/what-exactly-is-the-as-if-rule. – 463035818_is_not_an_ai Nov 08 '21 at 16:16
  • 1
    The question wasn't "Can the compiler?", but rather "Why isn't the compiler required?" – Glenn Teitelbaum Nov 08 '21 at 16:23
  • @GlennTeitelbaum: "*Could the specification be modified to require trying to resolve code at compile-time and not failing because of the absence of `constexpr`?*" That is a matter of quality-of-implementation, not language specification. – Nicol Bolas Nov 08 '21 at 16:33
  • @GlennTeitelbaum: The compiler isn't required because of the Halting Problem. Determining whether the evaluation could be completed at compile time (if allowed unlimited compile time) is in general undecidable. Furthermore in cases where it is decidable, having to wait for the compiler to try and give up would be very wasteful and slow down the rebuild cycle horribly. – Ben Voigt Nov 08 '21 at 16:34
  • @NicolBolas: No, the failure of the compiler to optimize this particular case is QoI. That the specification cannot say "try" is a well-known result of computer science. – Ben Voigt Nov 08 '21 at 16:35
  • @BenVoigt I am saying specifically, in the case where the evaluation of a constinit fails solely because of the lack of a constexpr or consteval keyword, why not have it succeed? It is going down no other path than had the keyword been there so this is not a Halting Problem, it is simply a syntax difference. No more or less calculations on the part of the compiler. – Glenn Teitelbaum Nov 08 '21 at 16:37
  • @GlennTeitelbaum: I'm pretty sure you were asking why a variable not marked `constinit` doesn't have the initializer evaluated at compile time "if at all possible under the as-if rule", not why a variable that is marked `constinit` causes a compile-time error. That one is a simple matter of contract. The provider of the non-`constexpr`-marked function has reserved the right to do non-compile-time things in future implementations. – Ben Voigt Nov 08 '21 at 16:38
  • And now you've changed the question / moved the goalposts. – Ben Voigt Nov 08 '21 at 16:40
  • @BenVoigt I was trying to clarify, but yes. I think it has changed, I will revert. – Glenn Teitelbaum Nov 08 '21 at 16:42
  • If it stays the way it is now I will close it as a duplicate of the first answer I linked in my first comment. Which you should actually read. – Ben Voigt Nov 08 '21 at 16:43
  • Reverted to before edit.... (and yes I read) – Glenn Teitelbaum Nov 08 '21 at 16:44
  • @GlennTeitelbaum: "*in the case where the evaluation of a constinit fails solely because of the lack of a constexpr or consteval keyword, why not have it succeed?*" Because that is literally why `constinit` was added to the language. That compile error is the whole point. – Nicol Bolas Nov 08 '21 at 16:45
  • @NicolBolas thank you, that gave me the point of view I was missing. Unfortunate, but testing a contract makes sense. Make that an answer and I'll accept it. – Glenn Teitelbaum Nov 08 '21 at 16:50
  • @GlennTeitelbaum: I did make it an answer. It's at the bottom of my answer. – Nicol Bolas Nov 08 '21 at 16:51

1 Answers1

5

Or, at a minimum, if a compile-time keyword is applied to a function and the code would have qualified with had the correct compile-time keywords been applied.

The basis of your question is the assumption that these keywords are just variations on a theme, that a function which could have some of them ought to have a specific one based on the properties within the function alone.

That's not reasonable.

For functions, any function which could be constexpr could also be consteval. The difference is that one can be called during constant expression evaluation, and the other must be called only during constant expression evaluation. This is not an intrinsic property of the function definition; it is about how the function is to be used.

The compiler should not override the decision of a programmer to be able to use a function at runtime just because the function definition just so happens to also be legit for consteval.

It should also be noted that there is the requirements of a function definition for a consteval function are the same as for a constexpr function.

It is also not possible to know if a function definition ought to be constexpr or not. Remember: while a constexpr function explicitly forbids certain constructs from appearing in the definition, it also permits you to have certain constructs in the definition that aren't allowed to be evaluated during constant evaluation... so long as you never actually allow those code paths to be evaluated during constant evaluation. So there are many function definitions that just so happen to be valid for constexpr even if the programmer didn't intend for them to be evaluated at compile-time.

Lambdas get implicit constexpr definitions only because space in a lambda expression is at a premium.

For variables, implicit constexpr makes even less sense. A variable ought to be constexpr if you intend to use it in a constant expression later on. This requires it to have an initializer that is a constant expression. Implicit constexpr is a problem because you might start relying on the implicit constexpr rule, then change the initializer to no longer be a constant expression and get a strange error in the place where you used its constexpr properties.

It's better to make the user be explicit up-front than to misjudge what the user was trying to do and make a user think something is supposed to be valid when it wasn't intended.

And constinit is solely about ensuring that a user never accidentally uses a non-constant expression to initialize the variable. Making it implicit would make absolutely no sense, as changing the expression to no longer be a constant expression wouldn't cause compilation to fail. Compilers are smart enough to know when a variable is initialized with a constant expression and can decide how to handle that on its own.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982