[cfe-commits] r69299 - in /cfe/trunk: 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 test/SemaCXX/conversion-function.cpp

Sebastian Redl sebastian.redl at getdesigned.at
Thu Apr 16 10:51:27 PDT 2009


Author: cornedbee
Date: Thu Apr 16 12:51:27 2009
New Revision: 69299

URL: http://llvm.org/viewvc/llvm-project?rev=69299&view=rev
Log:
Fix a crash bug when comparing overload quality of conversion operators with conversion constructors.
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.

Added:
    cfe/trunk/test/SemaCXX/conditional-expr.cpp
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Basic/OperatorKinds.def
    cfe/trunk/lib/CodeGen/Mangle.cpp
    cfe/trunk/lib/Parse/ParseExpr.cpp
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/lib/Sema/SemaExprCXX.cpp
    cfe/trunk/lib/Sema/SemaOverload.cpp
    cfe/trunk/lib/Sema/SemaOverload.h
    cfe/trunk/test/SemaCXX/conversion-function.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=69299&r1=69298&r2=69299&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Thu Apr 16 12:51:27 2009
@@ -1123,11 +1123,19 @@
   "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">;

Modified: cfe/trunk/include/clang/Basic/OperatorKinds.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/OperatorKinds.def?rev=69299&r1=69298&r2=69299&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/OperatorKinds.def (original)
+++ cfe/trunk/include/clang/Basic/OperatorKinds.def Thu Apr 16 12:51:27 2009
@@ -98,6 +98,9 @@
 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

Modified: cfe/trunk/lib/CodeGen/Mangle.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/Mangle.cpp?rev=69299&r1=69298&r2=69299&view=diff

==============================================================================
--- cfe/trunk/lib/CodeGen/Mangle.cpp (original)
+++ cfe/trunk/lib/CodeGen/Mangle.cpp Thu Apr 16 12:51:27 2009
@@ -283,97 +283,98 @@
 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;

Modified: cfe/trunk/lib/Parse/ParseExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseExpr.cpp?rev=69299&r1=69298&r2=69299&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseExpr.cpp (original)
+++ cfe/trunk/lib/Parse/ParseExpr.cpp Thu Apr 16 12:51:27 2009
@@ -186,6 +186,7 @@
 ///         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 @@
     }
 
     // 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);
 

Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=69299&r1=69298&r2=69299&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Thu Apr 16 12:51:27 2009
@@ -2382,6 +2382,8 @@
     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 @@
   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,

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=69299&r1=69298&r2=69299&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Thu Apr 16 12:51:27 2009
@@ -2018,7 +2018,7 @@
 /// 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) {

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=69299&r1=69298&r2=69299&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Thu Apr 16 12:51:27 2009
@@ -2588,6 +2588,10 @@
 /// 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 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.

Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=69299&r1=69298&r2=69299&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Thu Apr 16 12:51:27 2009
@@ -1008,3 +1008,293 @@
     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();
+}

Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=69299&r1=69298&r2=69299&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Thu Apr 16 12:51:27 2009
@@ -2026,15 +2026,19 @@
          "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 @@
   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 @@
       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 @@
 
   case OO_Slash:
   BinaryStar:
+  Conditional:
     // C++ [over.built]p12:
     //
     //   For every pair of promoted arithmetic types L and R, there
@@ -2957,6 +2964,17 @@
     //
     //   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 @@
   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 @@
   // 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;
 

Modified: cfe/trunk/lib/Sema/SemaOverload.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.h?rev=69299&r1=69298&r2=69299&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.h (original)
+++ cfe/trunk/lib/Sema/SemaOverload.h Thu Apr 16 12:51:27 2009
@@ -169,7 +169,7 @@
 
   /// 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

Added: cfe/trunk/test/SemaCXX/conditional-expr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/conditional-expr.cpp?rev=69299&view=auto

==============================================================================
--- cfe/trunk/test/SemaCXX/conditional-expr.cpp (added)
+++ cfe/trunk/test/SemaCXX/conditional-expr.cpp Thu Apr 16 12:51:27 2009
@@ -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.
+}

Modified: cfe/trunk/test/SemaCXX/conversion-function.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/conversion-function.cpp?rev=69299&r1=69298&r2=69299&view=diff

==============================================================================
--- cfe/trunk/test/SemaCXX/conversion-function.cpp (original)
+++ cfe/trunk/test/SemaCXX/conversion-function.cpp Thu Apr 16 12:51:27 2009
@@ -50,3 +50,14 @@
   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'}}





More information about the cfe-commits mailing list