forked from OSchip/llvm-project
PR17052 / DR1560 (+DR1550): In a conditional expression between a glvalue and a
throw-expression, the result is also a glvalue and isn't unnecessarily coerced to a prvalue. llvm-svn: 200189
This commit is contained in:
parent
3bb1de7885
commit
6a6a4bbdd4
|
@ -541,10 +541,21 @@ static Cl::Kinds ClassifyConditional(ASTContext &Ctx, const Expr *True,
|
|||
"This is only relevant for C++.");
|
||||
|
||||
// C++ [expr.cond]p2
|
||||
// If either the second or the third operand has type (cv) void, [...]
|
||||
// the result [...] is a prvalue.
|
||||
if (True->getType()->isVoidType() || False->getType()->isVoidType())
|
||||
// If either the second or the third operand has type (cv) void,
|
||||
// one of the following shall hold:
|
||||
if (True->getType()->isVoidType() || False->getType()->isVoidType()) {
|
||||
// The second or the third operand (but not both) is a (possibly
|
||||
// parenthesized) throw-expression; the result is of the [...] value
|
||||
// category of the other.
|
||||
bool TrueIsThrow = isa<CXXThrowExpr>(True->IgnoreParenImpCasts());
|
||||
bool FalseIsThrow = isa<CXXThrowExpr>(False->IgnoreParenImpCasts());
|
||||
if (const Expr *NonThrow = TrueIsThrow ? (FalseIsThrow ? 0 : False)
|
||||
: (FalseIsThrow ? True : 0))
|
||||
return ClassifyInternal(Ctx, NonThrow);
|
||||
|
||||
// [Otherwise] the result [...] is a prvalue.
|
||||
return Cl::CL_PRValue;
|
||||
}
|
||||
|
||||
// Note that at this point, we have already performed all conversions
|
||||
// according to [expr.cond]p3.
|
||||
|
|
|
@ -4185,20 +4185,21 @@ static Expr *EvalAddr(Expr *E, SmallVectorImpl<DeclRefExpr *> &refVars,
|
|||
ConditionalOperator *C = cast<ConditionalOperator>(E);
|
||||
|
||||
// Handle the GNU extension for missing LHS.
|
||||
if (Expr *lhsExpr = C->getLHS()) {
|
||||
// In C++, we can have a throw-expression, which has 'void' type.
|
||||
if (!lhsExpr->getType()->isVoidType())
|
||||
if (Expr* LHS = EvalAddr(lhsExpr, refVars, ParentDecl))
|
||||
// FIXME: That isn't a ConditionalOperator, so doesn't get here.
|
||||
if (Expr *LHSExpr = C->getLHS()) {
|
||||
// In C++, we can have a throw-expression, which has 'void' type.
|
||||
if (!LHSExpr->getType()->isVoidType())
|
||||
if (Expr *LHS = EvalAddr(LHSExpr, refVars, ParentDecl))
|
||||
return LHS;
|
||||
}
|
||||
|
||||
// In C++, we can have a throw-expression, which has 'void' type.
|
||||
if (C->getRHS()->getType()->isVoidType())
|
||||
return NULL;
|
||||
return 0;
|
||||
|
||||
return EvalAddr(C->getRHS(), refVars, ParentDecl);
|
||||
}
|
||||
|
||||
|
||||
case Stmt::BlockExprClass:
|
||||
if (cast<BlockExpr>(E)->getBlockDecl()->hasCaptures())
|
||||
return E; // local block.
|
||||
|
@ -4338,9 +4339,16 @@ do {
|
|||
ConditionalOperator *C = cast<ConditionalOperator>(E);
|
||||
|
||||
// Handle the GNU extension for missing LHS.
|
||||
if (Expr *lhsExpr = C->getLHS())
|
||||
if (Expr *LHS = EvalVal(lhsExpr, refVars, ParentDecl))
|
||||
return LHS;
|
||||
if (Expr *LHSExpr = C->getLHS()) {
|
||||
// In C++, we can have a throw-expression, which has 'void' type.
|
||||
if (!LHSExpr->getType()->isVoidType())
|
||||
if (Expr *LHS = EvalVal(LHSExpr, refVars, ParentDecl))
|
||||
return LHS;
|
||||
}
|
||||
|
||||
// In C++, we can have a throw-expression, which has 'void' type.
|
||||
if (C->getRHS()->getType()->isVoidType())
|
||||
return 0;
|
||||
|
||||
return EvalVal(C->getRHS(), refVars, ParentDecl);
|
||||
}
|
||||
|
|
|
@ -4399,42 +4399,21 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS,
|
|||
bool LVoid = LTy->isVoidType();
|
||||
bool RVoid = RTy->isVoidType();
|
||||
if (LVoid || RVoid) {
|
||||
// ... then the [l2r] conversions are performed on the second and third
|
||||
// operands ...
|
||||
LHS = DefaultFunctionArrayLvalueConversion(LHS.take());
|
||||
RHS = DefaultFunctionArrayLvalueConversion(RHS.take());
|
||||
if (LHS.isInvalid() || RHS.isInvalid())
|
||||
return QualType();
|
||||
|
||||
// Finish off the lvalue-to-rvalue conversion by copy-initializing a
|
||||
// temporary if necessary. DefaultFunctionArrayLvalueConversion doesn't
|
||||
// do this part for us.
|
||||
ExprResult &NonVoid = LVoid ? RHS : LHS;
|
||||
if (NonVoid.get()->getType()->isRecordType() &&
|
||||
NonVoid.get()->isGLValue()) {
|
||||
if (RequireNonAbstractType(QuestionLoc, NonVoid.get()->getType(),
|
||||
diag::err_allocation_of_abstract_type))
|
||||
return QualType();
|
||||
InitializedEntity Entity =
|
||||
InitializedEntity::InitializeTemporary(NonVoid.get()->getType());
|
||||
NonVoid = PerformCopyInitialization(Entity, SourceLocation(), NonVoid);
|
||||
if (NonVoid.isInvalid())
|
||||
return QualType();
|
||||
// ... one of the following shall hold:
|
||||
// -- The second or the third operand (but not both) is a (possibly
|
||||
// parenthesized) throw-expression; the result is of the type
|
||||
// and value category of the other.
|
||||
bool LThrow = isa<CXXThrowExpr>(LHS.get()->IgnoreParenImpCasts());
|
||||
bool RThrow = isa<CXXThrowExpr>(RHS.get()->IgnoreParenImpCasts());
|
||||
if (LThrow != RThrow) {
|
||||
Expr *NonThrow = LThrow ? RHS.get() : LHS.get();
|
||||
VK = NonThrow->getValueKind();
|
||||
// DR (no number yet): the result is a bit-field if the
|
||||
// non-throw-expression operand is a bit-field.
|
||||
OK = NonThrow->getObjectKind();
|
||||
return NonThrow->getType();
|
||||
}
|
||||
|
||||
LTy = LHS.get()->getType();
|
||||
RTy = RHS.get()->getType();
|
||||
|
||||
// ... and one of the following shall hold:
|
||||
// -- The second or the third operand (but not both) is a throw-
|
||||
// expression; the result is of the type of the other and is a prvalue.
|
||||
bool LThrow = isa<CXXThrowExpr>(LHS.get()->IgnoreParenCasts());
|
||||
bool RThrow = isa<CXXThrowExpr>(RHS.get()->IgnoreParenCasts());
|
||||
if (LThrow && !RThrow)
|
||||
return RTy;
|
||||
if (RThrow && !LThrow)
|
||||
return LTy;
|
||||
|
||||
// -- Both the second and third operands have type void; the result is of
|
||||
// type void and is a prvalue.
|
||||
if (LVoid && RVoid)
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
||||
// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
||||
// RUN: %clang_cc1 -std=c++1y -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
||||
|
||||
// expected-no-diagnostics
|
||||
|
||||
namespace DR1550 { // dr1550: yes
|
||||
int f(bool b, int n) {
|
||||
return (b ? (throw 0) : n) + (b ? n : (throw 0));
|
||||
}
|
||||
}
|
||||
|
||||
namespace DR1560 { // dr1560: 3.5
|
||||
void f(bool b, int n) {
|
||||
(b ? throw 0 : n) = (b ? n : throw 0) = 0;
|
||||
}
|
||||
class X { X(const X&); };
|
||||
const X &get();
|
||||
const X &x = true ? get() : throw 0;
|
||||
}
|
|
@ -67,3 +67,16 @@ int test6(bool x, bool y, int z) {
|
|||
//
|
||||
// end:
|
||||
// CHECK: ret i32
|
||||
|
||||
namespace DR1560 {
|
||||
struct A {
|
||||
~A();
|
||||
};
|
||||
extern bool b;
|
||||
A get();
|
||||
// CHECK-LABEL: @_ZN6DR15601bE
|
||||
const A &r = b ? get() : throw 0;
|
||||
// CHECK-NOT: call {{.*}}@_ZN6DR15601AD1Ev
|
||||
// CHECK: call {{.*}} @__cxa_atexit({{.*}} @_ZN6DR15601AD1Ev {{.*}} @_ZGRN6DR15601rE
|
||||
// CHECK-NOT: call {{.*}}@_ZN6DR15601AD1Ev
|
||||
}
|
||||
|
|
|
@ -75,6 +75,7 @@ void test()
|
|||
int i1 = ToBool() ? 0 : 1;
|
||||
|
||||
// p2 (one or both void, and throwing)
|
||||
Fields flds;
|
||||
i1 ? throw 0 : throw 1;
|
||||
i1 ? test() : throw 1;
|
||||
i1 ? throw 0 : test();
|
||||
|
@ -85,8 +86,16 @@ void test()
|
|||
i1 = i1 ? 0 : (throw 0);
|
||||
i1 ? 0 : test(); // expected-error {{right operand to ? is void, but left operand is of type 'int'}}
|
||||
i1 ? test() : 0; // expected-error {{left operand to ? is void, but right operand is of type 'int'}}
|
||||
(i1 ? throw 0 : i1) = 0; // expected-error {{expression is not assignable}}
|
||||
(i1 ? i1 : throw 0) = 0; // expected-error {{expression is not assignable}}
|
||||
(i1 ? throw 0 : i1) = 0;
|
||||
(i1 ? i1 : throw 0) = 0;
|
||||
(i1 ? (throw 0) : i1) = 0;
|
||||
(i1 ? i1 : (throw 0)) = 0;
|
||||
(i1 ? (void)(throw 0) : i1) = 0; // expected-error {{left operand to ? is void, but right operand is of type 'int'}}
|
||||
(i1 ? i1 : (void)(throw 0)) = 0; // expected-error {{right operand to ? is void, but left operand is of type 'int'}}
|
||||
int &throwRef1 = (i1 ? flds.i1 : throw 0);
|
||||
int &throwRef2 = (i1 ? throw 0 : flds.i1);
|
||||
int &throwRef3 = (i1 ? flds.b1 : throw 0); // expected-error {{non-const reference cannot bind to bit-field}}
|
||||
int &throwRef4 = (i1 ? throw 0 : flds.b1); // expected-error {{non-const reference cannot bind to bit-field}}
|
||||
|
||||
// p3 (one or both class type, convert to each other)
|
||||
// b1 (lvalues)
|
||||
|
@ -151,7 +160,6 @@ void test()
|
|||
&(i1 ? i1 : i2); // expected-error {{cannot take the address of an rvalue}}
|
||||
|
||||
// p4 (lvalue, same type)
|
||||
Fields flds;
|
||||
int &ir1 = i1 ? flds.i1 : flds.i2;
|
||||
(i1 ? flds.b1 : flds.i2) = 0;
|
||||
(i1 ? flds.i1 : flds.b2) = 0;
|
||||
|
@ -219,8 +227,8 @@ void test()
|
|||
// *must* create a separate temporary copy of class objects. This can only
|
||||
// be properly tested at runtime, though.
|
||||
|
||||
const Abstract &a = true ? static_cast<const Abstract&>(Derived1()) : Derived2(); // expected-error {{allocating an object of abstract class type 'const Abstract'}}
|
||||
true ? static_cast<const Abstract&>(Derived1()) : throw 3; // expected-error {{allocating an object of abstract class type 'const Abstract'}}
|
||||
const Abstract &abstract1 = true ? static_cast<const Abstract&>(Derived1()) : Derived2(); // expected-error {{allocating an object of abstract class type 'const Abstract'}}
|
||||
const Abstract &abstract2 = true ? static_cast<const Abstract&>(Derived1()) : throw 3; // ok
|
||||
}
|
||||
|
||||
namespace PR6595 {
|
||||
|
@ -367,3 +375,12 @@ namespace DR587 {
|
|||
const volatile int &cvir2 = b ? cvi : vi;
|
||||
const volatile int &cvir3 = b ? ci : vi; // expected-error{{volatile lvalue reference to type 'const volatile int' cannot bind to a temporary of type 'int'}}
|
||||
}
|
||||
|
||||
namespace PR17052 {
|
||||
struct X {
|
||||
int i_;
|
||||
bool b_;
|
||||
|
||||
int &test() { return b_ ? i_ : throw 1; }
|
||||
};
|
||||
}
|
||||
|
|
|
@ -520,20 +520,19 @@ void expr_cond(bool cond)
|
|||
// 5.16 Conditional operator [expr.cond]
|
||||
//
|
||||
// 2 If either the second or the third operand has type (possibly
|
||||
// cv-qualified) void, then the lvalue-to-rvalue (4.1),
|
||||
// array-to-pointer (4.2), and function-to-pointer (4.3) standard
|
||||
// conversions are performed on the second and third operands, and one
|
||||
// of the following shall hold:
|
||||
// cv-qualified) void, one of the following shall hold:
|
||||
//
|
||||
// - The second or the third operand (but not both) is a
|
||||
// throw-expression (15.1); the result is of the type of the other and
|
||||
// is an rvalue.
|
||||
// (possibly parenthesized) throw-expression (15.1); the result
|
||||
// is of the type and value category of the other.
|
||||
|
||||
Class classLvalue;
|
||||
ASSERT_RVALUE(cond ? throw 1 : (void)0);
|
||||
ASSERT_RVALUE(cond ? (void)0 : throw 1);
|
||||
ASSERT_RVALUE(cond ? throw 1 : classLvalue);
|
||||
ASSERT_RVALUE(cond ? classLvalue : throw 1);
|
||||
ASSERT_RVALUE(cond ? throw 1 : 0);
|
||||
ASSERT_RVALUE(cond ? 0 : throw 1);
|
||||
ASSERT_LVALUE(cond ? throw 1 : classLvalue);
|
||||
ASSERT_LVALUE(cond ? classLvalue : throw 1);
|
||||
|
||||
// - Both the second and the third operands have type void; the result
|
||||
// is of type void and is an rvalue. [Note: this includes the case
|
||||
|
|
|
@ -9115,7 +9115,7 @@ and <I>POD class</I></td>
|
|||
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1550">1550</a></td>
|
||||
<td>DRWP</td>
|
||||
<td>Parenthesized <I>throw-expression</I> operand of <I>conditional-expression</I></td>
|
||||
<td class="none" align="center">Unknown</td>
|
||||
<td class="full" align="center">Yes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1551">1551</a></td>
|
||||
|
@ -9175,7 +9175,7 @@ and <I>POD class</I></td>
|
|||
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1560">1560</a></td>
|
||||
<td>DRWP</td>
|
||||
<td>Gratuitous lvalue-to-rvalue conversion in <I>conditional-expression</I> with <I>throw-expression</I> operand</td>
|
||||
<td class="none" align="center">Unknown</td>
|
||||
<td class="svn" align="center">SVN</td>
|
||||
</tr>
|
||||
<tr class="open">
|
||||
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#1561">1561</a></td>
|
||||
|
|
Loading…
Reference in New Issue