From: Sebastian Redl Date: Fri, 17 Apr 2009 16:30:52 +0000 (+0000) Subject: Implement lvalue test for conditional expressions. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=76458501a8963fa11b91c9337a487de6871169b4;p=clang Implement lvalue test for conditional expressions. Add a few commented lines to the test case that point out things that don't work yet. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@69354 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index 9936965bbb..e73162d2fe 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -136,6 +136,7 @@ public: /// - *e, the type of e cannot be a function type /// - string-constant /// - reference type [C++ [expr]] + /// - b ? x : y, where x and y are lvalues of suitable types [C++] /// enum isLvalueResult { LV_Valid, diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 083bf3b808..83efc75bae 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -772,6 +772,32 @@ Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const { case CXXTypeidExprClass: // C++ 5.2.8p1: The result of a typeid expression is an lvalue of ... return LV_Valid; + case ConditionalOperatorClass: { + // Complicated handling is only for C++. + if (!Ctx.getLangOptions().CPlusPlus) + return LV_InvalidExpression; + + // Sema should have taken care to ensure that a CXXTemporaryObjectExpr is + // everywhere there's an object converted to an rvalue. Also, any other + // casts should be wrapped by ImplicitCastExprs. There's just the special + // case involving throws to work out. + const ConditionalOperator *Cond = cast(this); + Expr *LHS = Cond->getLHS(); + Expr *RHS = Cond->getRHS(); + // C++0x 5.16p2 + // If either the second or the third operand has type (cv) void, [...] + // the result [...] is an rvalue. + if (LHS->getType()->isVoidType() || RHS->getType()->isVoidType()) + return LV_InvalidExpression; + + // Both sides must be lvalues for the result to be an lvalue. + if (LHS->isLvalue(Ctx) != LV_Valid || RHS->isLvalue(Ctx) != LV_Valid) + return LV_InvalidExpression; + + // That's it. + return LV_Valid; + } + default: break; } diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 9fd62269f2..734b6ce3a0 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -2120,6 +2120,7 @@ Sema::CheckReferenceInit(Expr *&Init, QualType DeclType, ICS->Standard.ReferenceBinding = true; ICS->Standard.DirectBinding = true; ICS->Standard.RRefBinding = false; + ICS->Standard.CopyConstructor = 0; // Nothing more to do: the inaccessibility/ambiguity check for // derived-to-base conversions is suppressed when we're @@ -2273,6 +2274,7 @@ Sema::CheckReferenceInit(Expr *&Init, QualType DeclType, ICS->Standard.ReferenceBinding = true; ICS->Standard.DirectBinding = false; ICS->Standard.RRefBinding = isRValRef; + ICS->Standard.CopyConstructor = 0; } else { // FIXME: Binding to a subobject of the rvalue is going to require // more AST annotation than this. diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 8f41c003f9..0539caa712 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -1137,6 +1137,35 @@ static bool FindConditionalOverload(Sema &Self, Expr *&LHS, Expr *&RHS, return true; } +/// \brief Perform an "extended" implicit conversion as returned by +/// TryClassUnification. +/// +/// TryClassUnification generates ICSs that include reference bindings. +/// PerformImplicitConversion is not suitable for this; it chokes if the +/// second part of a standard conversion is ICK_DerivedToBase. This function +/// handles the reference binding specially. +static bool ConvertForConditional(Sema &Self, Expr *&E, + const ImplicitConversionSequence &ICS) +{ + if (ICS.ConversionKind == ImplicitConversionSequence::StandardConversion && + ICS.Standard.ReferenceBinding) { + assert(ICS.Standard.DirectBinding && + "TryClassUnification should never generate indirect ref bindings"); + Self.ImpCastExprToType(E, TargetType(ICS), true); + return false; + } + if (ICS.ConversionKind == ImplicitConversionSequence::UserDefinedConversion && + ICS.UserDefined.After.ReferenceBinding) { + assert(ICS.UserDefined.After.DirectBinding && + "TryClassUnification should never generate indirect ref bindings"); + Self.ImpCastExprToType(E, TargetType(ICS), true); + return false; + } + if (Self.PerformImplicitConversion(E, TargetType(ICS), ICS, "converting")) + return true; + return false; +} + /// \brief Check the operands of ?: under C++ semantics. /// /// See C++ [expr.cond]. Note that LHS is never null, even for the GNU x ?: y @@ -1223,13 +1252,11 @@ QualType Sema::CXXCheckConditionalOperands(Expr *&Cond, Expr *&LHS, Expr *&RHS, // the chosen operand and the converted operands are used in place of the // original operands for the remainder of this section. if (HaveL2R) { - if (PerformImplicitConversion(LHS, TargetType(ICSLeftToRight), - ICSLeftToRight, "converting")) + if (ConvertForConditional(*this, LHS, ICSLeftToRight)) return QualType(); LTy = LHS->getType(); } else if (HaveR2L) { - if (PerformImplicitConversion(RHS, TargetType(ICSRightToLeft), - ICSRightToLeft, "converting")) + if (ConvertForConditional(*this, RHS, ICSRightToLeft)) return QualType(); RTy = RHS->getType(); } diff --git a/test/SemaCXX/conditional-expr.cpp b/test/SemaCXX/conditional-expr.cpp index cbf396936b..3a4da2afdd 100644 --- a/test/SemaCXX/conditional-expr.cpp +++ b/test/SemaCXX/conditional-expr.cpp @@ -19,12 +19,17 @@ struct G { operator vfn(); }; struct Base { int trick(); A trick() const; + void fn1(); +}; +struct Derived : Base { + void fn2(); }; -struct Derived : Base {}; struct Convertible { operator Base&(); }; struct Priv : private Base {}; struct Mid : Base {}; struct Fin : Mid, Derived {}; +typedef void (Derived::*DFnPtr)(); +struct ToMemPtr { operator DFnPtr(); }; struct BadDerived; struct BadBase { operator BadDerived&(); }; @@ -65,11 +70,10 @@ void test() Base base; Derived derived; Convertible conv; - // FIXME: lvalueness - /*Base &bar1 =*/(void)( i1 ? base : derived); - /*Base &bar2 =*/(void)( i1 ? derived : base); - /*Base &bar3 =*/(void)( i1 ? base : conv); - /*Base &bar4 =*/(void)( i1 ? conv : base); + Base &bar1 = i1 ? base : derived; + Base &bar2 = i1 ? derived : base; + Base &bar3 = i1 ? base : conv; + Base &bar4 = i1 ? conv : base; // these are ambiguous BadBase bb; BadDerived bd; @@ -96,7 +100,7 @@ void test() // should fail: const lost (void)(i1 ? Base() : constder()); // expected-error {{incompatible operand types ('struct Base' and 'struct Derived const')}} (void)(i1 ? constder() : Base()); // expected-error {{incompatible operand types ('struct Derived const' and 'struct Base')}} - // should fail: private or ambiguous base + // FIXME: should fail: private or ambiguous base (void)(i1 ? Base() : Priv()); // xpected-error private base (void)(i1 ? Priv() : Base()); // xpected-error private base (void)(i1 ? Base() : Fin()); // xpected-error ambiguous base @@ -115,19 +119,24 @@ void test() (void)(i1 ? B() : A()); // expected-error {{incompatible operand types}} (void)(i1 ? 1 : Ambig()); // expected-error {{incompatible operand types}} (void)(i1 ? Ambig() : 1); // expected-error {{incompatible operand types}} + // By the way, this isn't an lvalue: + &(i1 ? i1 : i2); // expected-error {{address expression must be an lvalue or a function designator}} // p4 (lvalue, same type) - //Fields flds; - int &ir1 = i1; - //int &ir1 = i1 ? flds.i1 : flds.i2; - //(i1 ? flds.b1 : flds.i2) = 0; - //(i1 ? flds.i1 : flds.b2) = 0; - //(i1 ? flds.b1 : flds.b2) = 0; + Fields flds; + int &ir1 = i1 ? flds.i1 : flds.i2; + (i1 ? flds.b1 : flds.i2) = 0; + (i1 ? flds.i1 : flds.b2) = 0; + (i1 ? flds.b1 : flds.b2) = 0; // p5 (conversion to built-in types) // GCC 4.3 fails these double d1 = i1 ? I() : K(); pfn = i1 ? F() : G(); + DFnPtr pfm; + // FIXME: Overload resolution won't choose the member pointer yet. + //pfm = i1 ? DFnPtr() : &Base::fn1; + //pfm = i1 ? &Base::fn1 : DFnPtr(); // p6 (final conversions) i1 = i1 ? i1 : ir1; @@ -137,6 +146,16 @@ void test() i1 = i1 ? EVal : i1; d1 = i1 ? 'c' : 4.0; d1 = i1 ? 4.0 : 'c'; + pfm = i1 ? &Derived::fn2 : 0; + pfm = i1 ? 0 : &Derived::fn2; + // FIXME: pointer conversions don't work yet. + //Base *pb = i1 ? (Base*)0 : (Derived*)0; + //Base *pb = i1 ? (Derived*)0 : (Base*)0; + //pfm = i1 ? &Base::fn1 : &Derived::fn2; + //pfm = i1 ? &Derived::fn2 : &Base::fn1; + // Conversion of primitives does not result in an lvalue. + &(i1 ? i1 : d1); // expected-error {{address expression must be an lvalue or a function designator}} + // Note the thing that this does not test: since DR446, various situations // *must* create a separate temporary copy of class objects. This can only