]> granicus.if.org Git - clang/commitdiff
Fix a crash bug when comparing overload quality of conversion operators with conversi...
authorSebastian Redl <sebastian.redl@getdesigned.at>
Thu, 16 Apr 2009 17:51:27 +0000 (17:51 +0000)
committerSebastian Redl <sebastian.redl@getdesigned.at>
Thu, 16 Apr 2009 17:51:27 +0000 (17:51 +0000)
Remove an atrocious amount of trailing whitespace in the overloaded operator mangler. Sorry, couldn't help myself.
Change the DeclType parameter of Sema::CheckReferenceInit to be passed by value instead of reference. It wasn't changed anywhere.
Let the parser handle C++'s irregular grammar around assignment-expression and conditional-expression.
And finally, the reason for all this stuff: implement C++ semantics for the conditional operator. The implementation is complete except for determining lvalueness.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@69299 91177308-0d34-0410-b5e6-96231b3b80d8

12 files changed:
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Basic/OperatorKinds.def
lib/CodeGen/Mangle.cpp
lib/Parse/ParseExpr.cpp
lib/Sema/Sema.h
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaExprCXX.cpp
lib/Sema/SemaOverload.cpp
lib/Sema/SemaOverload.h
test/SemaCXX/conditional-expr.cpp [new file with mode: 0644]
test/SemaCXX/conversion-function.cpp

index a6586a8246b43862db9c74d2a23cf91f3d693f9b..3d9073d8294a29eabc70cf5f2fdf730458c17d96 100644 (file)
@@ -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">;
index 0836faf0f369b7d439e36dc01521515386cacbce..d011e9d39ff8cc44ecae951ecc81465d1331b820 100644 (file)
@@ -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
index c8d9de2d3304af561c6b7ffbe556d74c81026b6c..e42b6fdb7917f6d476ff9ce55c517249ac82c38a 100644 (file)
@@ -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;
index d41586ca919cd0ae64b7452fe2038fde029f10f2..3280730564fd364609086e9b6a5d46da2428334e 100644 (file)
@@ -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);
 
index dc80bd98ec2daef36c27c916d1dea5189b54ca78..fd708516f4e66a3b6dfe9198052aa6dee880acdd 100644 (file)
@@ -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,
index 3cfa8aed32d36428cf0f195846d1f628b52a85bc..8e2302ed64f8c4206954fe34a83d901317758ef5 100644 (file)
@@ -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) {
index ef2bf91cb79d4e7d547aa85cd1bc47c451c87257..f903e8f2535ef187566668118e67ad3c0f19c5a0 100644 (file)
@@ -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.
index e2a21f842df2d6073e464e09d4aebaf202264603..8f41c003f95b9cc9609eda97665eb9d2dc683f73 100644 (file)
@@ -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();
+}
index 70c9ee7ef9a2796197887d826e996b19bcb53ac7..6e2e72040ed3d3981db9ed17234e2d717f65b904 100644 (file)
@@ -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;
 
index e8fad6f01ba83baab00f028de23da1a6500a29ae..9de380657612b05de4a9ec6c2882829105830b60 100644 (file)
@@ -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 (file)
index 0000000..cbf3969
--- /dev/null
@@ -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
+  // for rvalues, hierarchy takes precedence over other conversions
+  (void)(i1 ? BadBase() : BadDerived());
+  (void)(i1 ? BadDerived() : BadBase());
+
+  // b2.1 (hierarchy stuff)
+  const Base constret();
+  const Derived constder();
+  // should use const overload
+  A a1((i1 ? constret() : Base()).trick());
+  A a2((i1 ? Base() : constret()).trick());
+  A a3((i1 ? constret() : Derived()).trick());
+  A a4((i1 ? Derived() : constret()).trick());
+  // should use non-const overload
+  i1 = (i1 ? Base() : Base()).trick();
+  i1 = (i1 ? Base() : Base()).trick();
+  i1 = (i1 ? Base() : Derived()).trick();
+  i1 = (i1 ? Derived() : Base()).trick();
+  // 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
+  (void)(i1 ? Base() : Priv()); // xpected-error private base
+  (void)(i1 ? Priv() : Base()); // xpected-error private base
+  (void)(i1 ? Base() : Fin()); // xpected-error ambiguous base
+  (void)(i1 ? Fin() : Base()); // xpected-error ambiguous base
+
+  // b2.2 (non-hierarchy)
+  i1 = i1 ? I() : i1;
+  i1 = i1 ? i1 : I();
+  I i2(i1 ? I() : J());
+  I i3(i1 ? J() : I());
+  // "the type [it] woud have if E2 were converted to an rvalue"
+  vfn pfn = i1 ? F() : test;
+  pfn = i1 ? test : F();
+  // these are ambiguous - better messages would be nice
+  (void)(i1 ? A() : B()); // expected-error {{incompatible operand types}}
+  (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}}
+
+  // 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;
+
+  // p5 (conversion to built-in types)
+  // GCC 4.3 fails these
+  double d1 = i1 ? I() : K();
+  pfn = i1 ? F() : G();
+
+  // p6 (final conversions)
+  i1 = i1 ? i1 : ir1;
+  int *pi1 = i1 ? &i1 : 0;
+  pi1 = i1 ? 0 : &i1;
+  i1 = i1 ? i1 : EVal;
+  i1 = i1 ? EVal : i1;
+  d1 = i1 ? 'c' : 4.0;
+  d1 = i1 ? 4.0 : 'c';
+
+  // Note the thing that this does not test: since DR446, various situations
+  // *must* create a separate temporary copy of class objects. This can only
+  // be properly tested at runtime, though.
+}
index b5dca080d44bd88d5fba32ba7da28789f9f996da..1ecea89aab0726960950e365e1c427bea77687f7 100644 (file)
@@ -50,3 +50,14 @@ public:
   operator const void() const; // expected-warning{{conversion function converting 'class B' to 'void const' will never be used}}
   operator const B(); // expected-warning{{conversion function converting 'class B' to itself will never be used}}
 };
+
+// This used to crash Clang.
+struct Flip;
+struct Flop {
+  Flop();
+  Flop(const Flip&);
+};
+struct Flip {
+  operator Flop() const;
+};
+Flop flop = Flip(); // expected-error {{cannot initialize 'flop' with an rvalue of type 'struct Flip'}}