not-constexpr variable in if constexpr – clang vs. GCC - c++

struct A{
constexpr operator bool()const{ return true; }
};
int main(){
auto f = [](auto v){ if constexpr(v){} };
A a;
f(a);
}
clang 6 accepts the Code, GCC 8 rejects it with:
$ g++ -std=c++17 main.cpp
main.cpp: In lambda function:
main.cpp:6:37: error: 'v' is not a constant expression
auto f = [](auto v){ if constexpr(v){} };
^
Who is correct and why?
When I take the parameter per reference, both reject the code:
struct A{
constexpr operator bool()const{ return true; }
};
int main(){
auto f = [](auto& v){ if constexpr(v){} };
constexpr A a;
f(a);
}
Compiled with clang 6:
$ clang++ -std=c++17 main.cpp
main.cpp:6:40: error: constexpr if condition is not a constant expression
auto f = [](auto& v){ if constexpr(v){} };
^
main.cpp:8:6: note: in instantiation of function template specialization
'main()::(anonymous class)::operator()<const A>' requested here
f(a);
^
1 error generated.
When I copy the parameter into a local variable both accept the code:
struct A{
constexpr operator bool()const{ return true; }
};
int main(){
auto f = [](auto v){ auto x = v; if constexpr(x){} };
A a;
f(a);
}
Edit: I am sure that the second and third cases will be handled correctly by both compilers. I don't know what the rule is, though.
In the first case I suspect that clang is right, because the case resembles the second. I would like to know if in the first case clang or GCC is correct and which rules in the second case makes the use of the not-constexpr variable v invalid and in the third case x valid.
Edit 2: First Question is clear now:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84421
clang was right, GCC 7 accepted the code as well. The bug will be fixed in the final version of GCC 8.

Clang is correct in all cases. [Full disclosure: I'm a Clang developer]
The question in all cases reduces to this: can we call a constexpr member function on v within a constant expression?
To answer this question, we need to look at [expr.const]p2, which says:
An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (6.8.1), would evaluate one of the following expressions:
...
an id-expression that refers to a variable or data member of reference type unless the reference has a
preceding initialization and either
it is initialized with a constant expression or
its lifetime began within the evaluation of e;
...
None of the other rules prohibit any of your examples. In particular, you are allowed to name local variables in a constant expression if they are not of reference type. (You are not allowed to perform lvalue-to-rvalue conversions on them -- that is, read their values -- unless their value is known (for instance, because they're constexpr), and you're not allowed to end up referring to the address of such a variable, but you are allowed to name them.)
The reason that the rules are different for entities of reference type is that merely naming an entity of reference type causes the reference to be immediately resolved, even if you don't do anything with the result, and resolving a reference requires knowing what it's bound to.
So: the first example is valid. The *this value of the constexpr member function is bound to the local variable a. It doesn't matter that we don't know what object that is, because the evaluation doesn't care.
The second example (where v is of reference type) is ill-formed. Merely naming v requires resolving it to the object it's bound to, which can't be done as part of the constant expression evaluation because we have no idea what it'll end up being bound to. It doesn't matter that the later evaluation steps won't use the resulting object; references are resolved immediately when they're named.
The third example is valid for the same reason as the first. Notably, the third example remains valid even if you change v to be of reference type:
auto f = [](auto &v) { auto x = v; if constexpr (x) {} };
A a;
f(a);
... because x is, once again, a local variable that we can name within a constant expression.

Related

Clang fails with "static_assert expression is not an integral constant expression" for a static constexpr method of an object within another class [duplicate]

Accessing static class member functions or variables, can be done in two ways: through an object (obj.member_fun() or obj.member_var) or through the class (Class::member_fun() or Class::member_var). However, in constexpr functions, Clang gives an error on the object access and requires to use class access:
struct S
{
constexpr static auto s_v = 42;
constexpr static auto v() { return s_v; }
};
#define TEST 1
constexpr auto foo(S const& s [[maybe_unused]])
{
#if TEST
constexpr auto v = s.v(); // ERROR for clang, OK for gcc
#else
constexpr auto v = S::v(); // OK for clang and gcc
#endif
return v;
}
constexpr auto bar(S const& s [[maybe_unused]])
{
#if TEST
constexpr auto v = s.s_v; // ERROR for clang, OK for gcc
#else
constexpr auto v = S::s_v; // OK for clang and gcc
#endif
return v;
}
int main() {}
Live Example compiled with -std=c++1z and #define TEST 1 for Clang 5.0 SVN, with error message:
Start
prog.cc:12:24: error: constexpr variable 'v' must be initialized by a constant expression
constexpr auto v = s.v(); // ERROR for clang, OK for gcc
^~~~~
prog.cc:22:24: error: constexpr variable 'v' must be initialized by a constant expression
constexpr auto v = s.s_v; // ERROR for clang, OK for gcc
^~~~~
2 errors generated.
1
Finish
Question: is this is a Clang bug, or is gcc too liberal in accepting both syntax forms for static member access in a constexpr function?
Clang seems to be in the right. When accessing a static member with the member access syntax [class.static/1]:
A static member s of class X may be referred to using the qualified-id
expression X​::​s; it is not necessary to use the class member access
syntax to refer to a static member. A static member may be referred to
using the class member access syntax, in which case the object
expression is evaluated.
So s.v() will cause s to be evaluated. Now, according to [expr.const/2.11], s is not a constant expression:
2 An expression e is a core constant expression unless the evaluation
of e, following the rules of the abstract machine, would evaluate one
of the following expressions:
[...]
an id-expression that refers to a variable or data member of reference
type unless the reference has a preceding initialization and either:
(2.11.1) - it is initialized with a constant expression or
(2.11.2) - its lifetime began within the evaluation of e;
s doesn't have a preceding initialization with a constant expression, not in the scope of foo.
If you want to access the static members based of a function parameter, without hard-coding the type, the way forward is std::remove_reference_t<decltype(s)>. This is accepted by Clang and GCC both:
#include <type_traits>
struct S
{
constexpr static auto s_v = 42;
constexpr static auto v() { return s_v; }
};
constexpr auto foo(S const& s)
{
constexpr auto v = std::remove_reference_t<decltype(s)>::v();
return v;
}
constexpr auto bar(S const& s)
{
constexpr auto v = std::remove_reference_t<decltype(s)>::s_v;
return v;
}
int main() {}
constexpr auto v = s.v(); // ERROR for clang, OK for gcc
I guess it depends on whether you compile in C++11 or C++14 mode. If you look over at cppreference, you will find (emphasis added by me):
A core constant expression is any expression that does not have any one of the following
(...)
6) The this pointer, except if used for class member access inside a non-static member function (until C++14)
6) The this pointer, except in a constexpr function or a constexpr constructor that is being evaluated as part of the expression (since C++14)
So, in C++11, whatever happens inside s.v() would not be considered a constant expression, since it uses the this pointer, but it is not a non-static member function (it's static) accessing a class member.
Per C++14, however, it would be, since it is evaluating a constexpr function as part of the expression, so the "except if" clause on the "does not have any of" set of rules catches.
Now don't ask me whether that makes any sense or whether anyone is supposed to understand that... :-)

C++: Perfectly forwarded integral_constant can't be evaluated at compile-time with template function [duplicate]

I'm trying to figure out whether GCC or Clang interpret the C++17 standard differently / wrong here.
This is my code, which does compile using GCC 8, but not using Clang 6:
struct BoolHolder {
constexpr static bool b = true;
};
template<bool b>
class Foo {};
int main() {
BoolHolder b;
Foo<b.b> f; // Works
BoolHolder & br = b;
Foo<br.b> f2; // Doesn't work
}
I wonder why that is. Obviously, b.b is a valid constexpr (or the first Foo<b.b> wouldn't be valid). Is br.b not a valid constexpr? Why? The object or the reference itself should have nothing to do with it, since we're accessing a static constexpr member here, right?
If this is really not valid C++17, should the fact that GCC doesn't even warn me (even though I enabled -Wall -Wextra -pedantic) be considered a bug?
Clang is correct. References are evaluated "eagerly" in constant expressions, so to speak. [expr.const]/2.11:
An expression e is a core constant expression unless the evaluation
of e, following the rules of the abstract machine, would evaluate one
of the following expressions:
[...]
an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and
either
it is initialized with a constant expression or
its lifetime began within the evaluation of e;
[...]

Using a constexpr static member of a reference as template argument

I'm trying to figure out whether GCC or Clang interpret the C++17 standard differently / wrong here.
This is my code, which does compile using GCC 8, but not using Clang 6:
struct BoolHolder {
constexpr static bool b = true;
};
template<bool b>
class Foo {};
int main() {
BoolHolder b;
Foo<b.b> f; // Works
BoolHolder & br = b;
Foo<br.b> f2; // Doesn't work
}
I wonder why that is. Obviously, b.b is a valid constexpr (or the first Foo<b.b> wouldn't be valid). Is br.b not a valid constexpr? Why? The object or the reference itself should have nothing to do with it, since we're accessing a static constexpr member here, right?
If this is really not valid C++17, should the fact that GCC doesn't even warn me (even though I enabled -Wall -Wextra -pedantic) be considered a bug?
Clang is correct. References are evaluated "eagerly" in constant expressions, so to speak. [expr.const]/2.11:
An expression e is a core constant expression unless the evaluation
of e, following the rules of the abstract machine, would evaluate one
of the following expressions:
[...]
an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and
either
it is initialized with a constant expression or
its lifetime began within the evaluation of e;
[...]

Is a function pointer odr-used if it is called

This question is sparked by a comment here
Consider the following code
template <typename T, typename C>
void g(T, C) {}
template <typename T, typename C>
struct G
{
static constexpr void (*m) (T, C) = &g;
};
void foo()
{
auto l = [](int){return 42;};
G<int, decltype(l)>::m(420, l);
}
This is legal in C++17 everywhere, G::m is defined within G via inlined variables and all that.
What's weird is in C++14 and C++11 gcc rejects this stating m is used but never defined, while clang accepts it. Live
Is m odr-used? Or is this a gcc bug?
This is not a GCC bug. This is a interpretation of the c++14.
One and only one definition of every non-inline function or variable
that is odr-used (see below) is required to appear in the entire
program (including any standard and user-defined libraries). The
compiler is not required to diagnose this violation, but the behavior
of the program that violates it is undefined.
The error is
source:7:29: error: 'constexpr void (* const G<int,
foo()::<lambda(int)> >::m)(int, foo()::<lambda(int)>)', declared using
local type 'foo()::<lambda(int)>', is used but never defined
[-fpermissive]
And it is true that the type foo()::<lambda(int)> is never defined (by the programmer, it is actually defined by the compiler) since every lambda has a unique type.
replacing line static constexpr void (*m) (T, C) = &g; by static inline constexpr void (*m) (T, C) = &g; makes the error go away.
Which is clearly an indication that m is odr-used if not tag as inline.
I believe this message is a way of warning you that
auto l = [](int){return 42;};
G<int, decltype(l)>::m(420, l);
G<int, decltype(l)>::m(420, [](int){return 42;});
will result in the following error (even with using -std=c++1z and GCC or CLANG)
source: In function 'void foo()': <source>:15:34: error: could not
convert 'g' from 'foo()::<lambda(int)>' to 'foo()::<lambda(int)>'
G<int, decltype(l)>::m(420, g);
because the lambda types are unique.
However GCC transforms the error into a warning if you use -fpermissive which is really a way of knowing that it is not a GCC bug but an over-protection meant to discourage certain practices.
One way of lifting any ambiguity without using -fpermissive is to do what GCC recommends and declare the prototype.
template <typename T, typename C>
void g(T, C) {}
template <typename T, typename C>
struct G
{
static constexpr void (*m) (T, C) = &g;
};
typedef int (*return_int)(int);
void foo()
{
auto l = [](int){return 42;};
G<int, return_int>::m(420, l);
G<int, return_int>::m(420, [](int){return 41;});
//Or even better if the aim is to use embedded template parameters
return_int ln = [](int){return 42;};
G<int, decltype(ln)>::m(420, ln);
}
This compile fines.
Last, why did the GCC team make this protection pop with C++17. I don't know maybe they receive complaints that the behavior was over protective and that cast errors of lambda types were sufficient. But seriously the error
error: could not
convert 'g' from 'foo()::<lambda(int)>' to 'foo()::<lambda(int)>'
Is a little bit off-putting the first time… When getting this error your mind wonders, until you come to the conclusion that decltype([](int){return 42;}) is different from decltype([](int){return 42;}) ! When you try this...
#include <random>
#include <iostream>
int main()
{
auto l = [](int) {return 42; };
std::cout << typeid(decltype(l)).name() << std::endl;
auto m = [](int) {return 42; };
std::cout << typeid(decltype(m)).name() << std::endl;
return 0;
}
which outputs (under visual studio)
class <lambda_37799c61f9e31cc7b5f51a1bd0a09621>
class <lambda_818eb0a43a553fc43d3adadd7480d71e>
TLDR m isn't odr-used, this is indeed a GCC bug.
odr-use
Intuitively, variables needs to be stored in memory somewhere. The only exception is when the value of the variable can be optimized out by the compiler and never used in another way. Odr-usage formalizes this idea: a variable requires a definition only if it is odr-used.
Odr-usage is defined by [basic.def.odr]
A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion to x yields a constant expression that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion is applied to e, or e is a discarded-value expression.
In other words, x isn't odr-used if either
x doesn't appear as a potentially-evaluated expression (e.g. decltype(x)).
x isn't an object (e.g. a reference) and applying the lvalue-to-rvalue conversion to x yields a constant expression.
x is an object and applying the lvalue-to-rvalue conversion to x yields a constant expression. Furthermore, there is a "coupled" expression e that either has the lvalue-to-rvalue conversion applied to it or is discarded.
The "coupling" refers to the intuition that there is some enclosing expression closely related to x that can only be used in certain ways without needing x be stored in memory. This notion is formalized by the definition of potential results [basic.def.odr]
The set of potential results of an expression e is defined as follows:
If e is an id-expression, the set contains only e.
If e is a subscripting operation with an array operand, the set contains the potential results of that operand.
If e is a class member access expression, the set contains the potential results of the object expression.
If e is a pointer-to-member expression whose second operand is a constant expression, the set contains the potential results of the object expression.
If e has the form (e1), the set contains the potential results of e1.
If e is a glvalue conditional expression, the set is the union of the sets of potential results of the second and third operands.
If e is a comma expression, the set contains the potential results of the right operand.
Otherwise, the set is empty.
An expression ex that is within the potential results of e is "coupled" with e.
The question
Applying the definitions
m is potentially evaluated.
Applying the lvalue-to-rvalue conversion to m yields a constant expression.
m is an object.
m is an expression whose potential results includes m and has the lvalue-to-rvalue conversion applied to.
We thus conclude m isn't odr-used.

constexpr member functions that don't use this?

Please consider the following two C++14 programs:
Program 1:
struct S { constexpr int f() const { return 42; } };
S s;
int main() { constexpr int x = s.f(); return x; }
Program 2:
struct S { constexpr int f() const { return 42; } };
int g(S s) { constexpr int x = s.f(); return x; }
int main() { S s; return g(s); }
Are neither, either or both of these programs ill-formed?
Why/why not?
Both programs are well-formed. The C++14 standard requires that s.f() be a constant expression because it is being used to initialize a constexpr variable, and in fact it is a core constant expression because there's no reason for it not to be. The reasons that an expression might not be a core constant expression are listed in section 5.19 p2. In particular, it states that the evaluation of the expression would have to do one of several things, none of which are done in your examples.
This may be surprising since, in some contexts, passing a non-constant expression to a constexpr function can cause the result to be a non-constant expression even if the argument isn't used. For example:
constexpr int f(int) { return 42; }
int main()
{
int x = 5;
constexpr auto y = f(x); // ill-formed
}
However, the reason this is ill-formed is because of the lvalue-to-rvalue conversion of a non-constant expression, which is one of the things that the evaluation of the expression is not allowed to do. An lvalue-to-rvalue conversion doesn't occur in the case of calling s.f().
I can't seem to find a compelling passage or example in the standard that directly addresses the issue of calling a constexpr member function on a non-constexpr instance, but here are some that may be of help (from draft N4140):
[C++14: 7.1.5/5]:
For a non-template, non-defaulted constexpr function or a non-template, non-defaulted, non-inheriting
constexpr constructor, if no argument values exist such that an invocation of the function or constructor
could be an evaluated subexpression of a core constant expression (5.19), the program is ill-formed; no
diagnostic required.
constexpr int f(bool b)
{ return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required
From this I take that the program is not outright ill-formed just because a constexpr function has a possible non-constexpr path.
[C++14: 5.19]:
int x; // not constant
struct A {
constexpr A(bool b) : m(b?42:x) { }
int m;
};
constexpr int v = A(true).m; // OK: constructor call initializes
// m with the value 42
constexpr int w = A(false).m; // error: initializer for m is
// x, which is non-constant
This is somewhat closer to your example programs, here a constexpr constructor may reference a non-constexpr variable depending on the value of the argument, but there is no error if this path is not actually taken.
So I don't think either program you presented should be ill-formed, but I cannot offer convincing proof :)
This sounds like a quiz question, and not presented by a student, but the professor testing the public on stackoverflow, but let's see...
Let's start with the One Definition Rule. It's clear neither version violates that, so they both pass that part.
Then, to syntax. Neither have syntax failures, they'll both compile without issue if you don't mind the potential blend of a syntax and semantic issue.
First, the simpler semantic issue. This isn't a syntax problem, but f(), in both versions, is the member of a struct, and the function clearly makes no change to the owning struct, it's returning a constant. Although the function is declared constexpr, it is not declared as const, which means if there were some reason to call this as a runtime function, it would generate an error if that attempt were made on a const S. That affects both versions.
Now, the potentially ambiguous return g(S()); Clearly the outer g is a function call, but S may not be so clear as it would be if written return g(S{}); With {} initializing S, there would be no ambiguity in the future should struct S be expanded with an operator() (the struct nearly resembles a functor already). The constructor invoked is automatically generated now, and there is no operator() to create confusion for the compiler at this version, but modern C++14 is supposed to offer clearer alternatives to avoid the "Most Vexing Parse", which g(S()) resembles.
So, I'd have to say that based on semantic rules, they both fail (not so badly though).