diff options
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 10 | ||||
-rw-r--r-- | include/clang/Basic/OperatorKinds.def | 3 | ||||
-rw-r--r-- | lib/CodeGen/Mangle.cpp | 111 | ||||
-rw-r--r-- | lib/Parse/ParseExpr.cpp | 13 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 4 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 2 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 16 | ||||
-rw-r--r-- | lib/Sema/SemaExprCXX.cpp | 290 | ||||
-rw-r--r-- | lib/Sema/SemaOverload.cpp | 61 | ||||
-rw-r--r-- | lib/Sema/SemaOverload.h | 2 | ||||
-rw-r--r-- | test/SemaCXX/conditional-expr.cpp | 144 | ||||
-rw-r--r-- | test/SemaCXX/conversion-function.cpp | 11 |
12 files changed, 587 insertions, 80 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index a6586a8246..3d9073d829 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1123,11 +1123,19 @@ def err_bad_memptr_rhs : Error< "right hand operand to %0 has non pointer-to-member type %1">; def err_memptr_rhs_incomplete : Error< "right hand operand is a pointer to member of incomplete type %0">; - def err_bad_memptr_lhs : Error< "left hand operand to %0 must be a %select{|pointer to }1class " "compatible with the right hand operand, but is %2">; +def err_conditional_void_nonvoid : Error< + "%select{left|right}1 operand to ? is void, but %select{right|left}1 operand " + "is of type %0">; +def err_conditional_ambiguous : Error< + "conditional expression is ambiguous; %0 can be converted to %1 " + "and vice versa">; +def err_conditional_ambiguous_ovl : Error< + "conditional expression is ambiguous; %0 and %1 can be converted to several " + "common types">; def err_invalid_use_of_function_type : Error< "a function type is not allowed here">; diff --git a/include/clang/Basic/OperatorKinds.def b/include/clang/Basic/OperatorKinds.def index 0836faf0f3..d011e9d39f 100644 --- a/include/clang/Basic/OperatorKinds.def +++ b/include/clang/Basic/OperatorKinds.def @@ -98,6 +98,9 @@ OVERLOADED_OPERATOR(ArrowStar , "->*" , arrowstar , false, t OVERLOADED_OPERATOR(Arrow , "->" , arrow , true , false, true) OVERLOADED_OPERATOR_MULTI(Call , "()" , true , true , true) OVERLOADED_OPERATOR_MULTI(Subscript , "[]" , false, true , true) +// ?: can *not* be overloaded, but we need the overload +// resolution machinery for it. +OVERLOADED_OPERATOR_MULTI(Conditional , "?" , false, true , false) #undef OVERLOADED_OPERATOR_MULTI #undef OVERLOADED_OPERATOR diff --git a/lib/CodeGen/Mangle.cpp b/lib/CodeGen/Mangle.cpp index c8d9de2d33..e42b6fdb79 100644 --- a/lib/CodeGen/Mangle.cpp +++ b/lib/CodeGen/Mangle.cpp @@ -283,97 +283,98 @@ void CXXNameMangler::manglePrefix(const DeclContext *DC) { void CXXNameMangler::mangleOperatorName(OverloadedOperatorKind OO, unsigned Arity) { switch (OO) { - // <operator-name> ::= nw # new + // <operator-name> ::= nw # new case OO_New: Out << "nw"; break; // ::= na # new[] case OO_Array_New: Out << "na"; break; - // ::= dl # delete + // ::= dl # delete case OO_Delete: Out << "dl"; break; - // ::= da # delete[] + // ::= da # delete[] case OO_Array_Delete: Out << "da"; break; // ::= ps # + (unary) // ::= pl # + case OO_Plus: Out << (Arity == 1? "ps" : "pl"); break; - // ::= ng # - (unary) - // ::= mi # - + // ::= ng # - (unary) + // ::= mi # - case OO_Minus: Out << (Arity == 1? "ng" : "mi"); break; - // ::= ad # & (unary) - // ::= an # & + // ::= ad # & (unary) + // ::= an # & case OO_Amp: Out << (Arity == 1? "ad" : "an"); break; - // ::= de # * (unary) - // ::= ml # * + // ::= de # * (unary) + // ::= ml # * case OO_Star: Out << (Arity == 1? "de" : "ml"); break; - // ::= co # ~ + // ::= co # ~ case OO_Tilde: Out << "co"; break; - // ::= dv # / + // ::= dv # / case OO_Slash: Out << "dv"; break; - // ::= rm # % + // ::= rm # % case OO_Percent: Out << "rm"; break; - // ::= or # | - case OO_Pipe: Out << "or"; break; - // ::= eo # ^ + // ::= or # | + case OO_Pipe: Out << "or"; break; + // ::= eo # ^ case OO_Caret: Out << "eo"; break; - // ::= aS # = + // ::= aS # = case OO_Equal: Out << "aS"; break; - // ::= pL # += + // ::= pL # += case OO_PlusEqual: Out << "pL"; break; - // ::= mI # -= + // ::= mI # -= case OO_MinusEqual: Out << "mI"; break; - // ::= mL # *= + // ::= mL # *= case OO_StarEqual: Out << "mL"; break; - // ::= dV # /= + // ::= dV # /= case OO_SlashEqual: Out << "dV"; break; - // ::= rM # %= - case OO_PercentEqual: Out << "rM"; break; - // ::= aN # &= - case OO_AmpEqual: Out << "aN"; break; - // ::= oR # |= - case OO_PipeEqual: Out << "oR"; break; - // ::= eO # ^= - case OO_CaretEqual: Out << "eO"; break; - // ::= ls # << + // ::= rM # %= + case OO_PercentEqual: Out << "rM"; break; + // ::= aN # &= + case OO_AmpEqual: Out << "aN"; break; + // ::= oR # |= + case OO_PipeEqual: Out << "oR"; break; + // ::= eO # ^= + case OO_CaretEqual: Out << "eO"; break; + // ::= ls # << case OO_LessLess: Out << "ls"; break; - // ::= rs # >> - case OO_GreaterGreater: Out << "rs"; break; - // ::= lS # <<= - case OO_LessLessEqual: Out << "lS"; break; - // ::= rS # >>= - case OO_GreaterGreaterEqual: Out << "rS"; break; + // ::= rs # >> + case OO_GreaterGreater: Out << "rs"; break; + // ::= lS # <<= + case OO_LessLessEqual: Out << "lS"; break; + // ::= rS # >>= + case OO_GreaterGreaterEqual: Out << "rS"; break; // ::= eq # == case OO_EqualEqual: Out << "eq"; break; - // ::= ne # != - case OO_ExclaimEqual: Out << "ne"; break; - // ::= lt # < + // ::= ne # != + case OO_ExclaimEqual: Out << "ne"; break; + // ::= lt # < case OO_Less: Out << "lt"; break; - // ::= gt # > + // ::= gt # > case OO_Greater: Out << "gt"; break; - // ::= le # <= + // ::= le # <= case OO_LessEqual: Out << "le"; break; - // ::= ge # >= + // ::= ge # >= case OO_GreaterEqual: Out << "ge"; break; - // ::= nt # ! + // ::= nt # ! case OO_Exclaim: Out << "nt"; break; - // ::= aa # && + // ::= aa # && case OO_AmpAmp: Out << "aa"; break; - // ::= oo # || - case OO_PipePipe: Out << "oo"; break; - // ::= pp # ++ - case OO_PlusPlus: Out << "pp"; break; - // ::= mm # -- + // ::= oo # || + case OO_PipePipe: Out << "oo"; break; + // ::= pp # ++ + case OO_PlusPlus: Out << "pp"; break; + // ::= mm # -- case OO_MinusMinus: Out << "mm"; break; - // ::= cm # , - case OO_Comma: Out << "cm"; break; - // ::= pm # ->* + // ::= cm # , + case OO_Comma: Out << "cm"; break; + // ::= pm # ->* case OO_ArrowStar: Out << "pm"; break; - // ::= pt # -> + // ::= pt # -> case OO_Arrow: Out << "pt"; break; - // ::= cl # () + // ::= cl # () case OO_Call: Out << "cl"; break; - // ::= ix # [] + // ::= ix # [] case OO_Subscript: Out << "ix"; break; // UNSUPPORTED: ::= qu # ? - case OO_None: + case OO_None: + case OO_Conditional: case NUM_OVERLOADED_OPERATORS: assert(false && "Not an overloaded operator"); break; diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index d41586ca91..3280730564 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -186,6 +186,7 @@ static prec::Level getBinOpPrecedence(tok::TokenKind Kind, /// logical-OR-expression /// logical-OR-expression '?' expression ':' conditional-expression /// [GNU] logical-OR-expression '?' ':' conditional-expression +/// [C++] the third operand is an assignment-expression /// /// assignment-expression: [C99 6.5.16] /// conditional-expression @@ -332,7 +333,17 @@ Parser::ParseRHSOfBinaryExpression(OwningExprResult LHS, unsigned MinPrec) { } // Parse another leaf here for the RHS of the operator. - OwningExprResult RHS(ParseCastExpression(false)); + // ParseCastExpression works here because all RHS expressions in C have it + // as a prefix, at least. However, in C++, an assignment-expression could + // be a throw-expression, which is not a valid cast-expression. + // Therefore we need some special-casing here. + // Also note that the third operand of the conditional operator is + // an assignment-expression in C++. + OwningExprResult RHS(Actions); + if (getLang().CPlusPlus && NextTokPrec <= prec::Conditional) + RHS = ParseAssignmentExpression(); + else + RHS = ParseCastExpression(false); if (RHS.isInvalid()) return move(RHS); diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index dc80bd98ec..fd708516f4 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -2382,6 +2382,8 @@ public: Expr *lex, Expr *&rex, SourceLocation OpLoc); QualType CheckConditionalOperands( // C99 6.5.15 Expr *&cond, Expr *&lhs, Expr *&rhs, SourceLocation questionLoc); + QualType CXXCheckConditionalOperands( // C++ 5.16 + Expr *&cond, Expr *&lhs, Expr *&rhs, SourceLocation questionLoc); /// type checking for vector binary operators. inline QualType CheckVectorOperands(SourceLocation l, Expr *&lex, Expr *&rex); @@ -2435,7 +2437,7 @@ public: ReferenceCompareResult CompareReferenceRelationship(QualType T1, QualType T2, bool& DerivedToBase); - bool CheckReferenceInit(Expr *&simpleInit_or_initList, QualType &declType, + bool CheckReferenceInit(Expr *&simpleInit_or_initList, QualType declType, ImplicitConversionSequence *ICS = 0, bool SuppressUserConversions = false, bool AllowExplicit = false, diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 3cfa8aed32..8e2302ed64 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -2018,7 +2018,7 @@ Sema::CompareReferenceRelationship(QualType T1, QualType T2, /// conversion functions. /// When @p ForceRValue, we unconditionally treat the initializer as an rvalue. bool -Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, +Sema::CheckReferenceInit(Expr *&Init, QualType DeclType, ImplicitConversionSequence *ICS, bool SuppressUserConversions, bool AllowExplicit, bool ForceRValue) { diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index ef2bf91cb7..f903e8f253 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -2588,6 +2588,10 @@ Sema::ActOnCastExpr(SourceLocation LParenLoc, TypeTy *Ty, /// C99 6.5.15 QualType Sema::CheckConditionalOperands(Expr *&Cond, Expr *&LHS, Expr *&RHS, SourceLocation QuestionLoc) { + // C++ is sufficiently different to merit its own checker. + if (getLangOptions().CPlusPlus) + return CXXCheckConditionalOperands(Cond, LHS, RHS, QuestionLoc); + UsualUnaryConversions(Cond); UsualUnaryConversions(LHS); UsualUnaryConversions(RHS); @@ -2596,17 +2600,13 @@ QualType Sema::CheckConditionalOperands(Expr *&Cond, Expr *&LHS, Expr *&RHS, QualType RHSTy = RHS->getType(); // first, check the condition. - if (!Cond->isTypeDependent()) { - if (!CondTy->isScalarType()) { // C99 6.5.15p2 - Diag(Cond->getLocStart(), diag::err_typecheck_cond_expect_scalar) - << CondTy; - return QualType(); - } + if (!CondTy->isScalarType()) { // C99 6.5.15p2 + Diag(Cond->getLocStart(), diag::err_typecheck_cond_expect_scalar) + << CondTy; + return QualType(); } // Now check the two expressions. - if ((LHS && LHS->isTypeDependent()) || (RHS && RHS->isTypeDependent())) - return Context.DependentTy; // If both operands have arithmetic type, do the usual arithmetic conversions // to find a common type: C99 6.5.15p3,5. diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index e2a21f842d..8f41c003f9 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -1008,3 +1008,293 @@ QualType Sema::CheckPointerToMemberOperands( Result.addVolatile(); return Result; } + +/// \brief Get the target type of a standard or user-defined conversion. +static QualType TargetType(const ImplicitConversionSequence &ICS) { + assert((ICS.ConversionKind == + ImplicitConversionSequence::StandardConversion || + ICS.ConversionKind == + ImplicitConversionSequence::UserDefinedConversion) && + "function only valid for standard or user-defined conversions"); + if (ICS.ConversionKind == ImplicitConversionSequence::StandardConversion) + return QualType::getFromOpaquePtr(ICS.Standard.ToTypePtr); + return QualType::getFromOpaquePtr(ICS.UserDefined.After.ToTypePtr); +} + +/// \brief Try to convert a type to another according to C++0x 5.16p3. +/// +/// This is part of the parameter validation for the ? operator. If either +/// value operand is a class type, the two operands are attempted to be +/// converted to each other. This function does the conversion in one direction. +/// It emits a diagnostic and returns true only if it finds an ambiguous +/// conversion. +static bool TryClassUnification(Sema &Self, Expr *From, Expr *To, + SourceLocation QuestionLoc, + ImplicitConversionSequence &ICS) +{ + // C++0x 5.16p3 + // The process for determining whether an operand expression E1 of type T1 + // can be converted to match an operand expression E2 of type T2 is defined + // as follows: + // -- If E2 is an lvalue: + if (To->isLvalue(Self.Context) == Expr::LV_Valid) { + // E1 can be converted to match E2 if E1 can be implicitly converted to + // type "lvalue reference to T2", subject to the constraint that in the + // conversion the reference must bind directly to E1. + if (!Self.CheckReferenceInit(From, + Self.Context.getLValueReferenceType(To->getType()), + &ICS)) + { + assert((ICS.ConversionKind == + ImplicitConversionSequence::StandardConversion || + ICS.ConversionKind == + ImplicitConversionSequence::UserDefinedConversion) && + "expected a definite conversion"); + bool DirectBinding = + ICS.ConversionKind == ImplicitConversionSequence::StandardConversion ? + ICS.Standard.DirectBinding : ICS.UserDefined.After.DirectBinding; + if (DirectBinding) + return false; + } + } + ICS.ConversionKind = ImplicitConversionSequence::BadConversion; + // -- If E2 is an rvalue, or if the conversion above cannot be done: + // -- if E1 and E2 have class type, and the underlying class types are + // the same or one is a base class of the other: + QualType FTy = From->getType(); + QualType TTy = To->getType(); + const RecordType *FRec = FTy->getAsRecordType(); + const RecordType *TRec = TTy->getAsRecordType(); + bool FDerivedFromT = FRec && TRec && Self.IsDerivedFrom(FTy, TTy); + if (FRec && TRec && (FRec == TRec || + FDerivedFromT || Self.IsDerivedFrom(TTy, FTy))) { + // E1 can be converted to match E2 if the class of T2 is the + // same type as, or a base class of, the class of T1, and + // [cv2 > cv1]. + if ((FRec == TRec || FDerivedFromT) && TTy.isAtLeastAsQualifiedAs(FTy)) { + // Could still fail if there's no copy constructor. + // FIXME: Is this a hard error then, or just a conversion failure? The + // standard doesn't say. + ICS = Self.TryCopyInitialization(From, TTy); + } + } else { + // -- Otherwise: E1 can be converted to match E2 if E1 can be + // implicitly converted to the type that expression E2 would have + // if E2 were converted to an rvalue. + // First find the decayed type. + if (TTy->isFunctionType()) + TTy = Self.Context.getPointerType(TTy); + else if(TTy->isArrayType()) + TTy = Self.Context.getArrayDecayedType(TTy); + + // Now try the implicit conversion. + // FIXME: This doesn't detect ambiguities. + ICS = Self.TryImplicitConversion(From, TTy); + } + return false; +} + +/// \brief Try to find a common type for two according to C++0x 5.16p5. +/// +/// This is part of the parameter validation for the ? operator. If either +/// value operand is a class type, overload resolution is used to find a +/// conversion to a common type. +static bool FindConditionalOverload(Sema &Self, Expr *&LHS, Expr *&RHS, + SourceLocation Loc) { + Expr *Args[2] = { LHS, RHS }; + OverloadCandidateSet CandidateSet; + Self.AddBuiltinOperatorCandidates(OO_Conditional, Args, 2, CandidateSet); + + OverloadCandidateSet::iterator Best; + switch (Self.BestViableFunction(CandidateSet, Best)) { + case Sema::OR_Success: + // We found a match. Perform the conversions on the arguments and move on. + if (Self.PerformImplicitConversion(LHS, Best->BuiltinTypes.ParamTypes[0], + Best->Conversions[0], "converting") || + Self.PerformImplicitConversion(RHS, Best->BuiltinTypes.ParamTypes[1], + Best->Conversions[1], "converting")) + break; + return false; + + case Sema::OR_No_Viable_Function: + Self.Diag(Loc, diag::err_typecheck_cond_incompatible_operands) + << LHS->getType() << RHS->getType() + << LHS->getSourceRange() << RHS->getSourceRange(); + return true; + + case Sema::OR_Ambiguous: + Self.Diag(Loc, diag::err_conditional_ambiguous_ovl) + << LHS->getType() << RHS->getType() + << LHS->getSourceRange() << RHS->getSourceRange(); + // FIXME: Print the possible common types by printing the return types + // of the viable candidates. + break; + + case Sema::OR_Deleted: + assert(false && "Conditional operator has only built-in overloads"); + break; + } + return true; +} + +/// \brief Check the operands of ?: under C++ semantics. +/// +/// See C++ [expr.cond]. Note that LHS is never null, even for the GNU x ?: y +/// extension. In this case, LHS == Cond. (But they're not aliases.) +QualType Sema::CXXCheckConditionalOperands(Expr *&Cond, Expr *&LHS, Expr *&RHS, + SourceLocation QuestionLoc) { + // FIXME: Handle C99's complex types, vector types, block pointers and + // Obj-C++ interface pointers. + + // C++0x 5.16p1 + // The first expression is contextually converted to bool. + if (!Cond->isTypeDependent()) { + if (CheckCXXBooleanCondition(Cond)) + return QualType(); + } + + // Either of the arguments dependent? + if (LHS->isTypeDependent() || RHS->isTypeDependent()) + return Context.DependentTy; + + // C++0x 5.16p2 + // If either the second or the third operand has type (cv) void, ... + QualType LTy = LHS->getType(); + QualType RTy = RHS->getType(); + bool LVoid = LTy->isVoidType(); + bool RVoid = RTy->isVoidType(); + if (LVoid || RVoid) { + // ... then the [l2r] conversions are performed on the second and third + // operands ... + DefaultFunctionArrayConversion(LHS); + DefaultFunctionArrayConversion(RHS); + LTy = LHS->getType(); + RTy = RHS->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 an rvalue. + bool LThrow = isa<CXXThrowExpr>(LHS); + bool RThrow = isa<CXXThrowExpr>(RHS); + 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 an rvalue. + if (LVoid && RVoid) + return Context.VoidTy; + + // Neither holds, error. + Diag(QuestionLoc, diag::err_conditional_void_nonvoid) + << (LVoid ? RTy : LTy) << (LVoid ? 0 : 1) + << LHS->getSourceRange() << RHS->getSourceRange(); + return QualType(); + } + + // Neither is void. + + // C++0x 5.16p3 + // Otherwise, if the second and third operand have different types, and + // either has (cv) class type, and attempt is made to convert each of those + // operands to the other. + if (Context.getCanonicalType(LTy) != Context.getCanonicalType(RTy) && + (LTy->isRecordType() || RTy->isRecordType())) { + ImplicitConversionSequence ICSLeftToRight, ICSRightToLeft; + // These return true if a single direction is already ambiguous. + if (TryClassUnification(*this, LHS, RHS, QuestionLoc, ICSLeftToRight)) + return QualType(); + if (TryClassUnification(*this, RHS, LHS, QuestionLoc, ICSRightToLeft)) + return QualType(); + + bool HaveL2R = ICSLeftToRight.ConversionKind != + ImplicitConversionSequence::BadConversion; + bool HaveR2L = ICSRightToLeft.ConversionKind != + ImplicitConversionSequence::BadConversion; + // If both can be converted, [...] the program is ill-formed. + if (HaveL2R && HaveR2L) { + Diag(QuestionLoc, diag::err_conditional_ambiguous) + << LTy << RTy << LHS->getSourceRange() << RHS->getSourceRange(); + return QualType(); + } + + // If exactly one conversion is possible, that conversion is applied to + // 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")) + return QualType(); + LTy = LHS->getType(); + } else if (HaveR2L) { + if (PerformImplicitConversion(RHS, TargetType(ICSRightToLeft), + ICSRightToLeft, "converting")) + return QualType(); + RTy = RHS->getType(); + } + } + + // C++0x 5.16p4 + // If the second and third operands are lvalues and have the same type, + // the result is of that type [...] + bool Same = Context.getCanonicalType(LTy) == Context.getCanonicalType(RTy); + if (Same && LHS->isLvalue(Context) == Expr::LV_Valid && + RHS->isLvalue(Context) == Expr::LV_Valid) + return LTy; + + // C++0x 5.16p5 + // Otherwise, the result is an rvalue. If the second and third operands + // do not have the same type, and either has (cv) class type, ... + if (!Same && (LTy->isRecordType() || RTy->isRecordType())) { + // ... overload resolution is used to determine the conversions (if any) + // to be applied to the operands. If the overload resolution fails, the + // program is ill-formed. + if (FindConditionalOverload(*this, LHS, RHS, QuestionLoc)) + return QualType(); + } + + // C++0x 5.16p6 + // LValue-to-rvalue, array-to-pointer, and function-to-pointer standard + // conversions are performed on the second and third operands. + DefaultFunctionArrayConversion(LHS); + DefaultFunctionArrayConversion(RHS); + LTy = LHS->getType(); + RTy = RHS->getType(); + + // After those conversions, one of the following shall hold: + // -- The second and third operands have the same type; the result + // is of that type. + if (Context.getCanonicalType(LTy) == Context.getCanonicalType(RTy)) + return LTy; + + // -- The second and third operands have arithmetic or enumeration type; + // the usual arithmetic conversions are performed to bring them to a + // common type, and the result is of that type. + if (LTy->isArithmeticType() && RTy->isArithmeticType()) { + UsualArithmeticConversions(LHS, RHS); + return LHS->getType(); + } + + // -- The second and third operands have pointer type, or one has pointer + // type and the other is a null pointer constant; pointer conversions + // and qualification conversions are performed to bring them to their + // composite pointer type. The result is of the composite pointer type. + // Fourth bullet is same for pointers-to-member. + if ((LTy->isPointerType() || LTy->isMemberPointerType()) && + RHS->isNullPointerConstant(Context)) { + ImpCastExprToType(RHS, LTy); // promote the null to a pointer. + return LTy; + } + if ((RTy->isPointerType() || RTy->isMemberPointerType()) && + LHS->isNullPointerConstant(Context)) { + ImpCastExprToType(LHS, RTy); // promote the null to a pointer. + return RTy; + } + + // FIXME: Handle the case where both are pointers. + Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands) + << LHS->getType() << RHS->getType() + << LHS->getSourceRange() << RHS->getSourceRange(); + return QualType(); +} diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 70c9ee7ef9..6e2e72040e 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -2026,15 +2026,19 @@ Sema::AddOverloadCandidate(FunctionDecl *Function, "Use AddConversionCandidate for conversion functions"); if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Function)) { - // If we get here, it's because we're calling a member function - // that is named without a member access expression (e.g., - // "this->f") that was either written explicitly or created - // implicitly. This can happen with a qualified call to a member - // function, e.g., X::f(). We use a NULL object as the implied - // object argument (C++ [over.call.func]p3). - AddMethodCandidate(Method, 0, Args, NumArgs, CandidateSet, - SuppressUserConversions, ForceRValue); - return; + if (!isa<CXXConstructorDecl>(Method)) { + // If we get here, it's because we're calling a member function + // that is named without a member access expression (e.g., + // "this->f") that was either written explicitly or created + // implicitly. This can happen with a qualified call to a member + // function, e.g., X::f(). We use a NULL object as the implied + // object argument (C++ [over.call.func]p3). + AddMethodCandidate(Method, 0, Args, NumArgs, CandidateSet, + SuppressUserConversions, ForceRValue); + return; + } + // We treat a constructor like a non-member function, since its object + // argument doesn't participate in overload resolution. } @@ -2127,8 +2131,10 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, Expr *Object, const FunctionProtoType* Proto = dyn_cast<FunctionProtoType>(Method->getType()->getAsFunctionType()); assert(Proto && "Methods without a prototype cannot be overloaded"); - assert(!isa<CXXConversionDecl>(Method) && + assert(!isa<CXXConversionDecl>(Method) && "Use AddConversionCandidate for conversion functions"); + assert(!isa<CXXConstructorDecl>(Method) && + "Use AddOverloadCandidate for constructors"); // Add this candidate CandidateSet.push_back(OverloadCandidate()); @@ -2664,7 +2670,7 @@ Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, Op == OO_Plus || (Op == OO_Minus && NumArgs == 2) || Op == OO_Equal || Op == OO_PlusEqual || Op == OO_MinusEqual || Op == OO_Subscript || Op == OO_ArrowStar || Op == OO_PlusPlus || Op == OO_MinusMinus || - (Op == OO_Star && NumArgs == 1)) { + (Op == OO_Star && NumArgs == 1) || Op == OO_Conditional) { for (unsigned ArgIdx = 0; ArgIdx < NumArgs; ++ArgIdx) CandidateTypes.AddTypesConvertedFrom(Args[ArgIdx]->getType(), true, @@ -2939,6 +2945,7 @@ Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, case OO_Slash: BinaryStar: + Conditional: // C++ [over.built]p12: // // For every pair of promoted arithmetic types L and R, there @@ -2957,6 +2964,17 @@ Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, // // where LR is the result of the usual arithmetic conversions // between types L and R. + // + // C++ [over.built]p24: + // + // For every pair of promoted arithmetic types L and R, there exist + // candidate operator functions of the form + // + // LR operator?(bool, L, R); + // + // where LR is the result of the usual arithmetic conversions + // between types L and R. + // Our candidates ignore the first parameter. for (unsigned Left = FirstPromotedArithmeticType; Left < LastPromotedArithmeticType; ++Left) { for (unsigned Right = FirstPromotedArithmeticType; @@ -3201,6 +3219,25 @@ Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, case OO_ArrowStar: // FIXME: No support for pointer-to-members yet. break; + + case OO_Conditional: + // Note that we don't consider the first argument, since it has been + // contextually converted to bool long ago. The candidates below are + // therefore added as binary. + // + // C++ [over.built]p24: + // For every type T, where T is a pointer or pointer-to-member type, + // there exist candidate operator functions of the form + // + // T operator?(bool, T, T); + // + // FIXME: pointer-to-member + for (BuiltinCandidateTypeSet::iterator Ptr = CandidateTypes.pointer_begin(), + E = CandidateTypes.pointer_end(); Ptr != E; ++Ptr) { + QualType ParamTypes[2] = { *Ptr, *Ptr }; + AddBuiltinCandidate(*Ptr, ParamTypes, Args, 2, CandidateSet); + } + goto Conditional; } } @@ -3852,7 +3889,7 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc, // Perform overload resolution. OverloadCandidateSet::iterator Best; switch (BestViableFunction(CandidateSet, Best)) { - case OR_Success: { + case OR_Success: { // We found a built-in operator or an overloaded operator. FunctionDecl *FnDecl = Best->Function; diff --git a/lib/Sema/SemaOverload.h b/lib/Sema/SemaOverload.h index e8fad6f01b..9de3806576 100644 --- a/lib/Sema/SemaOverload.h +++ b/lib/Sema/SemaOverload.h @@ -169,7 +169,7 @@ namespace clang { /// ImplicitConversionSequence - Represents an implicit conversion /// sequence, which may be a standard conversion sequence - // (C++ 13.3.3.1.1), user-defined conversion sequence (C++ 13.3.3.1.2), + /// (C++ 13.3.3.1.1), user-defined conversion sequence (C++ 13.3.3.1.2), /// or an ellipsis conversion sequence (C++ 13.3.3.1.3). struct ImplicitConversionSequence { /// Kind - The kind of implicit conversion sequence. BadConversion diff --git a/test/SemaCXX/conditional-expr.cpp b/test/SemaCXX/conditional-expr.cpp new file mode 100644 index 0000000000..cbf396936b --- /dev/null +++ b/test/SemaCXX/conditional-expr.cpp @@ -0,0 +1,144 @@ +// RUN: clang-cc -fsyntax-only -verify -std=c++0x %s + +// C++ rules for ?: are a lot stricter than C rules, and have to take into +// account more conversion options. +// This test runs in C++0x mode for the contextual conversion of the condition. + +struct ToBool { explicit operator bool(); }; + +struct B; +struct A { A(); A(const B&); }; +struct B { operator A() const; }; +struct I { operator int(); }; +struct J { operator I(); }; +struct K { operator double(); }; +typedef void (*vfn)(); +struct F { operator vfn(); }; +struct G { operator vfn(); }; + +struct Base { + int trick(); + A trick() const; +}; +struct Derived : Base {}; +struct Convertible { operator Base&(); }; +struct Priv : private Base {}; +struct Mid : Base {}; +struct Fin : Mid, Derived {}; + +struct BadDerived; +struct BadBase { operator BadDerived&(); }; +struct BadDerived : BadBase {}; + +struct Fields { + int i1, i2, b1 : 3, b2 : 3; +}; + +enum Enum { EVal }; + +struct Ambig { + operator short(); + operator signed char(); +}; + +void test() +{ + // This function tests C++0x 5.16 + + // p1 (contextually convert to bool) + int i1 = ToBool() ? 0 : 1; + + // p2 (one or both void, and throwing) + i1 ? throw 0 : throw 1; + i1 ? test() : throw 1; + i1 ? throw 0 : test(); + i1 ? test() : test(); + i1 = i1 ? throw 0 : 0; + 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}} + + // p3 (one or both class type, convert to each other) + // b1 (lvalues) + 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); + // these are ambiguous + BadBase bb; + BadDerived bd; + (void)(i1 ? bb : bd); // expected-error {{conditional expression is ambiguous; 'struct BadBase' can be converted to 'struct BadDerived' and vice versa}} + (void)(i1 ? bd : bb); // expected-error {{conditional expression is ambiguous}} + // curiously enough (and a defect?), these are not |