constexpr difference between gcc and clang

The following compiles fine in macports gcc 4.7, but does not in clang svn 161307. I believe the key is the const reference in combination with the ?: operator.

#include

struct A {
constexpr A(int a) : value(a) {}

constexpr int get() { return value; }

private:
int value;
};

constexpr A someFn(const A& a) {
return a.get() == 0 ? throw std::exception() : a;
}

int main(int argc, char** argv) {
constexpr A a(4);
static_assert( someFn(a).get() == 4, “error” );
}

Some digging on the internets reveals http://stackoverflow.com/questions/5605142/stdmax-and-stdmin-not-constexpr, which seems to point to lvalues, glvalues and memory allocation, although I did not follow it very well. There is also the particularly interesting comment :

The C++ committee have suggested that it was intended to be possible for function invocation substitution to produce an lvalue referring to a temporary. g++ behaves that way, but clang currently implements the standard as written.

So, I have two questions. 1) Is the way clang handles this in fact conformant to the spec, and gcc is lax? 2) Can someone take another crack at explaining why it is that this doesn’t work?

Thanks
–David

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

an invocation of a constexpr function with arguments that, when
substituted by function invocation
substitution (7.1.5), do not produce a constant expression

someFn(a) produces the address of the local variable a, which is not a
"constant expression" in the language of [expr.const].

-Eli

Hi David,

The following compiles fine in macports gcc 4.7, but does not in clang svn
161307. I believe the key is the const reference in combination with the ?:
operator.

#include

struct A {
constexpr A(int a) : value(a) {}

constexpr int get() { return value; }

private:
int value;
};

constexpr A someFn(const A& a) {
return a.get() == 0 ? throw std::exception() : a;
}

int main(int argc, char** argv) {
constexpr A a(4);
static_assert( someFn(a).get() == 4, “error” );
}

Some digging on the internets reveals
http://stackoverflow.com/questions/5605142/stdmax-and-stdmin-not-constexpr,
which seems to point to lvalues, glvalues and memory allocation, although I
did not follow it very well. There is also the particularly interesting
comment :

The C++ committee have suggested that it was intended to be possible for
function invocation substitution to produce an lvalue referring to a
temporary. g++ behaves that way, but clang currently implements the standard
as written.

That comment is out of date. At the most recent C++ standards committee meeting, we decided to allow such cases, and Clang now supports them.

So, I have two questions. 1) Is the way clang handles this in fact
conformant to the spec, and gcc is lax?

This code is well-formed, we’re incorrect to reject it. Please file a bug at llvm.org/bugs.

We are rejecting it because we have produced a broken AST:

(CompoundStmt 0x4024650 <:10:32, line:12:1>
(ReturnStmt 0x4024630 <line:11:3, col:35>
(CXXConstructExpr 0x40245f8 <col:10, col:35> ‘struct A’‘void (const struct A &) noexcept’ elidable
(MaterializeTemporaryExpr 0x40244d0 <col:10, col:35> ‘const struct A’ lvalue
(ConditionalOperator 0x4024128 <col:10, col:35> ‘const struct A’
(BinaryOperator 0x4024098 <col:10, col:21> ‘_Bool’ ‘==’
(CXXMemberCallExpr 0x4024050 <col:10, col:16> ‘int’
(MemberExpr 0x4024020 <col:10, col:12> ‘’ .get 0x4023af0
(DeclRefExpr 0x4023ff8 col:10 ‘const struct A’ lvalue ParmVar 0x4023e90 ‘a’ ‘const struct A &’)))
(IntegerLiteral 0x4024078 col:21 ‘int’ 0))
(CXXThrowExpr 0x40240e0 <col:25, col:31> ‘void’
(IntegerLiteral 0x40240c0 col:31 ‘int’ 0))
(DeclRefExpr 0x4024100 col:35 ‘const struct A’ lvalue ParmVar 0x4023e90 ‘a’ ‘const struct A &’))))))

Note that the ConditionalOperator is an rvalue, but its third operand is an lvalue, with no intervening CXXConstructExpr. That’s wrong – we’re supposed to perform an lvalue-to-rvalue conversion on this operand (see [expr.cond]p2).

Err, oops, you're right; I somehow misread the testcase.

-Eli

Fixed in r161450.