]> granicus.if.org Git - clang/commitdiff
Implement support for operator overloading using candidate operator
authorDouglas Gregor <dgregor@apple.com>
Wed, 12 Nov 2008 17:17:38 +0000 (17:17 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 12 Nov 2008 17:17:38 +0000 (17:17 +0000)
functions for built-in operators, e.g., the builtin

  bool operator==(int const*, int const*)

can be used for the expression "x1 == x2" given:

  struct X {
    operator int const*();
  } x1, x2;

The scheme for handling these built-in operators is relatively simple:
for each candidate required by the standard, create a special kind of
candidate function for the built-in. If overload resolution picks the
built-in operator, we perform the appropriate conversions on the
arguments and then let the normal built-in operator take care of it.

There may be some optimization opportunity left: if we can reduce the
number of built-in operator overloads we generate, overload resolution
for these cases will go faster. However, one must be careful when
doing this: GCC generates too few operator overloads in our little
test program, and fails to compile it because none of the overloads it
generates match.

Note that we only support operator overload for non-member binary
operators at the moment. The other operators will follow.

As part of this change, ImplicitCastExpr can now be an lvalue.

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

14 files changed:
Driver/RewriteObjC.cpp
include/clang/AST/Expr.h
include/clang/AST/TypeOrdering.h
include/clang/Basic/DiagnosticKinds.def
lib/AST/Expr.cpp
lib/AST/StmtSerialization.cpp
lib/Sema/Sema.cpp
lib/Sema/Sema.h
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaOverload.cpp
lib/Sema/SemaOverload.h
test/SemaCXX/overloaded-builtin-operators.cpp [new file with mode: 0644]

index 9d78c42d0253bc569162c573c9761e40c15d3795..65e8841ec79340480d8ef00e7d4aeb507a0c3e55 100644 (file)
@@ -1519,7 +1519,8 @@ CallExpr *RewriteObjC::SynthesizeCallToFunctionDecl(
                                      
   // Now, we cast the reference to a pointer to the objc_msgSend type.
   QualType pToFunc = Context->getPointerType(msgSendType);
-  ImplicitCastExpr *ICE = new ImplicitCastExpr(pToFunc, DRE);
+  ImplicitCastExpr *ICE = new ImplicitCastExpr(pToFunc, DRE, 
+                                               /*isLvalue=*/false);
   
   const FunctionType *FT = msgSendType->getAsFunctionType();
   
index efab1a14e140ebfe3556ead4c0808550b7dd7525..6fe205cd08481fe0a9118a871e53454441d45a2f 100644 (file)
@@ -834,14 +834,23 @@ public:
 /// (*f)(), float->double, short->int, etc.
 ///
 class ImplicitCastExpr : public CastExpr {
+  /// LvalueCast - Whether this cast produces an lvalue.
+  bool LvalueCast;
+
 public:
-  ImplicitCastExpr(QualType ty, Expr *op) : 
-    CastExpr(ImplicitCastExprClass, ty, op) {}
+  ImplicitCastExpr(QualType ty, Expr *op, bool Lvalue) : 
+    CastExpr(ImplicitCastExprClass, ty, op), LvalueCast(Lvalue) {}
 
   virtual SourceRange getSourceRange() const {
     return getSubExpr()->getSourceRange();
   }
 
+  /// isLvalueCast - Whether this cast produces an lvalue.
+  bool isLvalueCast() const { return LvalueCast; }
+
+  /// setLvalueCast - Set whether this cast produces an lvalue.
+  void setLvalueCast(bool Lvalue) { LvalueCast = Lvalue; }
+
   static bool classof(const Stmt *T) { 
     return T->getStmtClass() == ImplicitCastExprClass; 
   }
index a21655b58054af270ee62c20bb956ffe994fea16..a23ca75ff063312050da33a301c0d4e6b13fe7db 100644 (file)
@@ -7,9 +7,9 @@
 //
 //===----------------------------------------------------------------------===//
 //
-//  This file provides a function object that gives a total ordering
-//  on QualType values, so that they can be sorted, used in std::maps
-//  and std::sets, and so on.
+//  This file provides a function objects and specializations that
+//  allow QualType values to be sorted, used in std::maps, std::sets,
+//  llvm::DenseMaps, and llvm::DenseSets.
 //
 //===----------------------------------------------------------------------===//
 
@@ -17,6 +17,7 @@
 #define LLVM_CLANG_TYPE_ORDERING_H
 
 #include "clang/AST/Type.h"
+#include "llvm/ADT/DenseMap.h"
 #include <functional>
 
 namespace clang {
@@ -31,4 +32,30 @@ struct QualTypeOrdering : std::binary_function<QualType, QualType, bool> {
 
 }
 
+namespace llvm {
+  template<> struct DenseMapInfo<clang::QualType> {
+    static inline clang::QualType getEmptyKey() { return clang::QualType(); }
+
+    static inline clang::QualType getTombstoneKey() { 
+      using clang::QualType;
+      return QualType::getFromOpaquePtr(reinterpret_cast<clang::Type *>(-1));
+    }
+
+    static unsigned getHashValue(clang::QualType Val) {
+      return (unsigned)Val.getAsOpaquePtr();
+    }
+
+    static bool isEqual(clang::QualType LHS, clang::QualType RHS) {
+      return LHS == RHS;
+    }
+
+    static bool isPod() { 
+      // QualType isn't *technically* a POD type. However, we can get
+      // away with calling it a POD type since its copy constructor,
+      // copy assignment operator, and destructor are all trivial.
+      return true; 
+    }
+  };
+}
+
 #endif
index 8d821e4362b56eab3925f3aa75b43112b294235b..451e8de630b955c41c5bff8848f45adc868299c4 100644 (file)
@@ -867,6 +867,8 @@ DIAG(err_ovl_ambiguous_call, ERROR,
      "call to '%0' is ambiguous; candidates are:")
 DIAG(err_ovl_candidate, NOTE,
      "candidate function")
+DIAG(err_ovl_builtin_candidate, NOTE,
+     "built-in candidate function '%0'")
 DIAG(err_ovl_no_viable_function_in_init, ERROR,
      "no matching constructor for initialization of '%0'.")
 DIAG(err_ovl_no_viable_function_in_init_with_cands, ERROR,
index fbb225613fd2c729b0a14c55f31da24ab757d149..c54fc40ecb268bb5e943b2904bb98149aa47127f 100644 (file)
@@ -389,8 +389,28 @@ Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const {
         cast<UnaryOperator>(this)->getOpcode() == UnaryOperator::Extension)
       return cast<UnaryOperator>(this)->getSubExpr()->isLvalue(Ctx);  // GNU.
     break;
+  case ImplicitCastExprClass:
+    return cast<ImplicitCastExpr>(this)->isLvalueCast()? LV_Valid 
+                                                       : LV_InvalidExpression;
   case ParenExprClass: // C99 6.5.1p5
     return cast<ParenExpr>(this)->getSubExpr()->isLvalue(Ctx);
+  case BinaryOperatorClass:
+  case CompoundAssignOperatorClass: {
+    const BinaryOperator *BinOp = cast<BinaryOperator>(this);
+    if (BinOp->isAssignmentOp()) {
+      if (Ctx.getLangOptions().CPlusPlus)
+        // C++ [expr.ass]p1: 
+        //   The result of an assignment operation [...] is an lvalue.
+        return LV_Valid;
+      else 
+        // C99 6.5.16:
+        //   An assignment expression [...] is not an lvalue.
+        return LV_InvalidExpression;
+    } else
+      return LV_InvalidExpression;
+
+    break;
+  }
   case CallExprClass: {
     // C++ [expr.call]p10:
     //   A function call is an lvalue if and only if the result type
index f71b88b46f1d3279a920fd9a0bd9bcc7efd9e183..667f597b8ab5ecf30627b33a038b81eb1953dee9 100644 (file)
@@ -662,12 +662,14 @@ ImaginaryLiteral* ImaginaryLiteral::CreateImpl(Deserializer& D, ASTContext& C) {
 void ImplicitCastExpr::EmitImpl(Serializer& S) const {
   S.Emit(getType());
   S.EmitOwnedPtr(getSubExpr());
+  S.Emit(LvalueCast);
 }
 
 ImplicitCastExpr* ImplicitCastExpr::CreateImpl(Deserializer& D, ASTContext& C) {
   QualType t = QualType::ReadVal(D);
   Expr* Op = D.ReadOwnedPtr<Expr>(C);
-  return new ImplicitCastExpr(t,Op);
+  bool isLvalue = D.ReadBool();
+  return new ImplicitCastExpr(t,Op,isLvalue);
 }
 
 void IndirectGotoStmt::EmitImpl(Serializer& S) const {
index 9c8d0c8bcb414ed348bd86e166102fe7de6c800b..b78336b6cd2d6b75aa8c946f4ea3b5d31fbf7784 100644 (file)
@@ -124,7 +124,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer)
 
 /// ImpCastExprToType - If Expr is not of type 'Type', insert an implicit cast. 
 /// If there is already an implicit cast, merge into the existing one.
-void Sema::ImpCastExprToType(Expr *&Expr, QualType Ty) {
+  /// If isLvalue, the result of the cast is an lvalue.
+void Sema::ImpCastExprToType(Expr *&Expr, QualType Ty, bool isLvalue) {
   QualType ExprTy = Context.getCanonicalType(Expr->getType());
   QualType TypeTy = Context.getCanonicalType(Ty);
   
@@ -143,10 +144,11 @@ void Sema::ImpCastExprToType(Expr *&Expr, QualType Ty) {
     }
   }
   
-  if (ImplicitCastExpr *ImpCast = dyn_cast<ImplicitCastExpr>(Expr))
+  if (ImplicitCastExpr *ImpCast = dyn_cast<ImplicitCastExpr>(Expr)) {
     ImpCast->setType(Ty);
-  else 
-    Expr = new ImplicitCastExpr(Ty, Expr);
+    ImpCast->setLvalueCast(isLvalue);
+  } else 
+    Expr = new ImplicitCastExpr(Ty, Expr, isLvalue);
 }
 
 void Sema::DeleteExpr(ExprTy *E) {
index 7cd507a0aa30919d62fc2be837d9c25c288967bf..5b66694e045dfe58cff0efe71ef1b42b00bed284 100644 (file)
@@ -435,6 +435,12 @@ public:
   void AddConversionCandidate(CXXConversionDecl *Conversion,
                               Expr *From, QualType ToType,
                               OverloadCandidateSet& CandidateSet);
+  void AddBuiltinCandidate(QualType ResultTy, QualType *ParamTys, 
+                           Expr **Args, unsigned NumArgs,
+                           OverloadCandidateSet& CandidateSet);
+  void AddBuiltinBinaryOperatorCandidates(OverloadedOperatorKind Op, 
+                                          Expr **Args, 
+                                          OverloadCandidateSet& CandidateSet);
   void AddOverloadCandidates(const OverloadedFunctionDecl *Ovl, 
                              Expr **Args, unsigned NumArgs,
                              OverloadCandidateSet& CandidateSet,
@@ -1052,16 +1058,13 @@ public:
 
   /// ImpCastExprToType - If Expr is not of type 'Type', insert an implicit
   /// cast.  If there is already an implicit cast, merge into the existing one.
-  void ImpCastExprToType(Expr *&Expr, QualType Type);
+  /// If isLvalue, the result of the cast is an lvalue.
+  void ImpCastExprToType(Expr *&Expr, QualType Type, bool isLvalue = false);
 
   // UsualUnaryConversions - promotes integers (C99 6.3.1.1p2) and converts
   // functions and arrays to their respective pointers (C99 6.3.2.1).
   Expr *UsualUnaryConversions(Expr *&expr); 
 
-  // UsualUnaryConversionType - Same as UsualUnaryConversions, but works
-  // on types instead of expressions
-  QualType UsualUnaryConversionType(QualType Ty); 
-  
   // DefaultFunctionArrayConversion - converts functions and arrays
   // to their respective pointers (C99 6.3.2.1). 
   void DefaultFunctionArrayConversion(Expr *&expr);
@@ -1079,6 +1082,12 @@ public:
   QualType UsualArithmeticConversions(Expr *&lExpr, Expr *&rExpr,
                                       bool isCompAssign = false);
   
+  /// UsualArithmeticConversionsType - handles the various conversions
+  /// that are common to binary operators (C99 6.3.1.8, C++ [expr]p9)
+  /// and returns the result type of that conversion.
+  QualType UsualArithmeticConversionsType(QualType lhs, QualType rhs);
+                                          
+
   /// AssignConvertType - All of the 'assignment' semantic checks return this
   /// enum to indicate whether the assignment was allowed.  These checks are
   /// done for simple assignments, as well as initialization, return from
index fc412a605edfe7df38880035ad0861740e8e7498..aaceb4f55911676c75872ef66fc45f52a8a5668c 100644 (file)
@@ -3021,7 +3021,8 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, DeclTy *EnumDeclX,
     ECD->setInitVal(InitVal);
     
     // Adjust the Expr initializer and type.
-    ECD->setInitExpr(new ImplicitCastExpr(NewTy, ECD->getInitExpr()));
+    ECD->setInitExpr(new ImplicitCastExpr(NewTy, ECD->getInitExpr(), 
+                                          /*isLvalue=*/false));
     ECD->setType(NewTy);
   }
   
index 2f43e5fa9eccf2785019625cd311d021849309f7..128df113858907db2f6ed74c49dadf7ea5b6af5f 100644 (file)
@@ -1597,7 +1597,7 @@ Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType,
       // Perform the conversion.
       // FIXME: Binding to a subobject of the lvalue is going to require
       // more AST annotation than this.
-      ImpCastExprToType(Init, T1);    
+      ImpCastExprToType(Init, T1, /*isLvalue=*/true);    
     }
   }
 
@@ -1656,7 +1656,7 @@ Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType,
         // Perform the conversion.
         // FIXME: Binding to a subobject of the lvalue is going to require
         // more AST annotation than this.
-        ImpCastExprToType(Init, T1);
+        ImpCastExprToType(Init, T1, /*isLvalue=*/true);
       }
       break;
 
@@ -1741,7 +1741,7 @@ Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType,
     } else {
       // FIXME: Binding to a subobject of the rvalue is going to require
       // more AST annotation than this.
-      ImpCastExprToType(Init, T1);
+      ImpCastExprToType(Init, T1, /*isLvalue=*/true);
     }
     return false;
   }
index 92efa68cc434ad74da2a35153a05b764a13b833c..21bf990fba1773febcd65f15402b3eb3d6e40ffa 100644 (file)
@@ -99,13 +99,47 @@ QualType Sema::UsualArithmeticConversions(Expr *&lhsExpr, Expr *&rhsExpr,
     UsualUnaryConversions(lhsExpr);
     UsualUnaryConversions(rhsExpr);
   }
+
   // For conversion purposes, we ignore any qualifiers. 
   // For example, "const float" and "float" are equivalent.
   QualType lhs =
     Context.getCanonicalType(lhsExpr->getType()).getUnqualifiedType();
   QualType rhs = 
     Context.getCanonicalType(rhsExpr->getType()).getUnqualifiedType();
-  
+
+  // If both types are identical, no conversion is needed.
+  if (lhs == rhs)
+    return lhs;
+
+  // If either side is a non-arithmetic type (e.g. a pointer), we are done.
+  // The caller can deal with this (e.g. pointer + int).
+  if (!lhs->isArithmeticType() || !rhs->isArithmeticType())
+    return lhs;
+
+  QualType destType = UsualArithmeticConversionsType(lhs, rhs);
+  if (!isCompAssign) {
+    ImpCastExprToType(lhsExpr, destType);
+    ImpCastExprToType(rhsExpr, destType);
+  }
+  return destType;
+}
+
+QualType Sema::UsualArithmeticConversionsType(QualType lhs, QualType rhs) {
+  // Perform the usual unary conversions. We do this early so that
+  // integral promotions to "int" can allow us to exit early, in the
+  // lhs == rhs check. Also, for conversion purposes, we ignore any
+  // qualifiers.  For example, "const float" and "float" are
+  // equivalent.
+  if (lhs->isPromotableIntegerType())
+    lhs = Context.IntTy;
+  else
+    lhs = Context.getCanonicalType(lhs).getUnqualifiedType();
+
+  if (rhs->isPromotableIntegerType())
+    rhs = Context.IntTy;
+  else
+    rhs = Context.getCanonicalType(rhs).getUnqualifiedType();
+
   // If both types are identical, no conversion is needed.
   if (lhs == rhs)
     return lhs;
@@ -122,12 +156,10 @@ QualType Sema::UsualArithmeticConversions(Expr *&lhsExpr, Expr *&rhsExpr,
     // if we have an integer operand, the result is the complex type.
     if (rhs->isIntegerType() || rhs->isComplexIntegerType()) { 
       // convert the rhs to the lhs complex type.
-      if (!isCompAssign) ImpCastExprToType(rhsExpr, lhs);
       return lhs;
     }
     if (lhs->isIntegerType() || lhs->isComplexIntegerType()) { 
       // convert the lhs to the rhs complex type.
-      if (!isCompAssign) ImpCastExprToType(lhsExpr, rhs);
       return rhs;
     }
     // This handles complex/complex, complex/float, or float/complex.
@@ -144,24 +176,16 @@ QualType Sema::UsualArithmeticConversions(Expr *&lhsExpr, Expr *&rhsExpr,
     
     if (result > 0) { // The left side is bigger, convert rhs. 
       rhs = Context.getFloatingTypeOfSizeWithinDomain(lhs, rhs);
-      if (!isCompAssign)
-        ImpCastExprToType(rhsExpr, rhs);
     } else if (result < 0) { // The right side is bigger, convert lhs. 
       lhs = Context.getFloatingTypeOfSizeWithinDomain(rhs, lhs);
-      if (!isCompAssign)
-        ImpCastExprToType(lhsExpr, lhs);
     } 
     // At this point, lhs and rhs have the same rank/size. Now, make sure the
     // domains match. This is a requirement for our implementation, C99
     // does not require this promotion.
     if (lhs != rhs) { // Domains don't match, we have complex/float mix.
       if (lhs->isRealFloatingType()) { // handle "double, _Complex double".
-        if (!isCompAssign)
-          ImpCastExprToType(lhsExpr, rhs);
         return rhs;
       } else { // handle "_Complex double, double".
-        if (!isCompAssign)
-          ImpCastExprToType(rhsExpr, lhs);
         return lhs;
       }
     }
@@ -172,12 +196,10 @@ QualType Sema::UsualArithmeticConversions(Expr *&lhsExpr, Expr *&rhsExpr,
     // if we have an integer operand, the result is the real floating type.
     if (rhs->isIntegerType() || rhs->isComplexIntegerType()) { 
       // convert rhs to the lhs floating point type.
-      if (!isCompAssign) ImpCastExprToType(rhsExpr, lhs);
       return lhs;
     }
     if (lhs->isIntegerType() || lhs->isComplexIntegerType()) { 
       // convert lhs to the rhs floating point type.
-      if (!isCompAssign) ImpCastExprToType(lhsExpr, rhs);
       return rhs;
     }
     // We have two real floating types, float/complex combos were handled above.
@@ -185,14 +207,12 @@ QualType Sema::UsualArithmeticConversions(Expr *&lhsExpr, Expr *&rhsExpr,
     int result = Context.getFloatingTypeOrder(lhs, rhs);
     
     if (result > 0) { // convert the rhs
-      if (!isCompAssign) ImpCastExprToType(rhsExpr, lhs);
       return lhs;
     }
     if (result < 0) { // convert the lhs
-      if (!isCompAssign) ImpCastExprToType(lhsExpr, rhs); // convert the lhs
       return rhs;
     }
-    assert(0 && "Sema::UsualArithmeticConversions(): illegal float comparison");
+    assert(0 && "Sema::UsualArithmeticConversionsType(): illegal float comparison");
   }
   if (lhs->isComplexIntegerType() || rhs->isComplexIntegerType()) {
     // Handle GCC complex int extension.
@@ -203,19 +223,14 @@ QualType Sema::UsualArithmeticConversions(Expr *&lhsExpr, Expr *&rhsExpr,
       if (Context.getIntegerTypeOrder(lhsComplexInt->getElementType(), 
                                       rhsComplexInt->getElementType()) >= 0) {
         // convert the rhs
-        if (!isCompAssign) ImpCastExprToType(rhsExpr, lhs);
         return lhs;
       }
-      if (!isCompAssign) 
-        ImpCastExprToType(lhsExpr, rhs); // convert the lhs
       return rhs;
     } else if (lhsComplexInt && rhs->isIntegerType()) {
       // convert the rhs to the lhs complex type.
-      if (!isCompAssign) ImpCastExprToType(rhsExpr, lhs);
       return lhs;
     } else if (rhsComplexInt && lhs->isIntegerType()) {
       // convert the lhs to the rhs complex type.
-      if (!isCompAssign) ImpCastExprToType(lhsExpr, rhs);
       return rhs;
     }
   }
@@ -244,10 +259,6 @@ QualType Sema::UsualArithmeticConversions(Expr *&lhsExpr, Expr *&rhsExpr,
     // to the signed type.
     destType = Context.getCorrespondingUnsignedType(lhsSigned ? lhs : rhs);
   }
-  if (!isCompAssign) {
-    ImpCastExprToType(lhsExpr, destType);
-    ImpCastExprToType(rhsExpr, destType);
-  }
   return destType;
 }
 
@@ -2765,6 +2776,14 @@ Action::ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
   if (getLangOptions().CPlusPlus &&
       (lhs->getType()->isRecordType() || lhs->getType()->isEnumeralType() ||
        rhs->getType()->isRecordType() || rhs->getType()->isEnumeralType())) {
+    // If this is one of the assignment operators, we only perform
+    // overload resolution if the left-hand side is a class or
+    // enumeration type (C++ [expr.ass]p3).
+    if (Opc >= BinaryOperator::Assign && Opc <= BinaryOperator::OrAssign &&
+        !(lhs->getType()->isRecordType() || lhs->getType()->isEnumeralType())) {
+      return CreateBuiltinBinOp(TokLoc, Opc, lhs, rhs);
+    }
+
     // C++ [over.binary]p1:
     //   A binary operator shall be implemented either by a non-static
     //   member function (9.3) with one parameter or by a non-member
@@ -2809,40 +2828,57 @@ Action::ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
                = dyn_cast_or_null<OverloadedFunctionDecl>(D))
       AddOverloadCandidates(Ovl, Args, 2, CandidateSet);
 
-    // FIXME: Add builtin overload candidates (C++ [over.built]).
+    // Add builtin overload candidates (C++ [over.built]).
+    AddBuiltinBinaryOperatorCandidates(OverOp, Args, CandidateSet);
 
     // Perform overload resolution.
     OverloadCandidateSet::iterator Best;
     switch (BestViableFunction(CandidateSet, Best)) {
     case OR_Success: {
-      // FIXME: We might find a built-in candidate here.
+      // We found a built-in operator or an overloaded operator.
       FunctionDecl *FnDecl = Best->Function;
 
-      // Convert the arguments.
-      // FIXME: Conversion will be different for member operators.
-      if (PerformCopyInitialization(lhs, FnDecl->getParamDecl(0)->getType(),
-                                    "passing") ||
-          PerformCopyInitialization(rhs, FnDecl->getParamDecl(1)->getType(),
-                                    "passing"))
-        return true;
-
-      // Determine the result type
-      QualType ResultTy 
-        = FnDecl->getType()->getAsFunctionType()->getResultType();
-      ResultTy = ResultTy.getNonReferenceType();
-
-      // Build the actual expression node.
-      // FIXME: We lose the fact that we have a function here!
-      if (Opc > BinaryOperator::Assign && Opc <= BinaryOperator::OrAssign)
-        return new CompoundAssignOperator(lhs, rhs, Opc, ResultTy, ResultTy,
-                                          TokLoc);
-      else
-        return new BinaryOperator(lhs, rhs, Opc, ResultTy, TokLoc);
+      if (FnDecl) {
+        // We matched an overloaded operator. Build a call to that
+        // operator.
+
+        // Convert the arguments.
+        // FIXME: Conversion will be different for member operators.
+        if (PerformCopyInitialization(lhs, FnDecl->getParamDecl(0)->getType(),
+                                      "passing") ||
+            PerformCopyInitialization(rhs, FnDecl->getParamDecl(1)->getType(),
+                                      "passing"))
+          return true;
+
+        // Determine the result type
+        QualType ResultTy 
+          = FnDecl->getType()->getAsFunctionType()->getResultType();
+        ResultTy = ResultTy.getNonReferenceType();
+        
+        // Build the actual expression node.
+        // FIXME: We lose the fact that we have a function here!
+        if (Opc > BinaryOperator::Assign && Opc <= BinaryOperator::OrAssign)
+          return new CompoundAssignOperator(lhs, rhs, Opc, ResultTy, ResultTy,
+                                            TokLoc);
+        else
+          return new BinaryOperator(lhs, rhs, Opc, ResultTy, TokLoc);
+      } else {
+        // We matched a built-in operator. Convert the arguments, then
+        // break out so that we will build the appropriate built-in
+        // operator node.
+        if (PerformCopyInitialization(lhs, Best->BuiltinTypes.ParamTypes[0],
+                                      "passing") ||
+            PerformCopyInitialization(rhs, Best->BuiltinTypes.ParamTypes[1],
+                                      "passing"))
+          return true;
+
+        break;
+      } 
     }
 
     case OR_No_Viable_Function:
       // No viable function; fall through to handling this as a
-      // built-in operator.
+      // built-in operator, which will produce an error message for us.
       break;
 
     case OR_Ambiguous:
@@ -2854,7 +2890,9 @@ Action::ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
       return true;
     }
 
-    // There was no viable overloaded operator; fall through.
+    // Either we found no viable overloaded operator or we matched a
+    // built-in operator. In either case, fall through to trying to
+    // build a built-in operation.
   } 
 
   
index b60e55492ba9e98147c7ad5f42ba9606b7de86c1..9229fa35d295aeacc725c3ed230e0c1051d61d8d 100644 (file)
 #include "Sema.h"
 #include "SemaInherit.h"
 #include "clang/Basic/Diagnostic.h"
+#include "clang/Lex/Preprocessor.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Expr.h"
+#include "clang/AST/TypeOrdering.h"
+#include "llvm/ADT/DenseSet.h"
 #include "llvm/Support/Compiler.h"
 #include <algorithm>
 
@@ -413,6 +416,7 @@ Sema::IsStandardConversion(Expr* From, QualType ToType,
     return false;
 
   // Standard conversions (C++ [conv])
+  SCS.setAsIdentityConversion();
   SCS.Deprecated = false;
   SCS.FromTypePtr = FromType.getAsOpaquePtr();
   SCS.CopyConstructor = 0;
@@ -1530,7 +1534,7 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion,
   DeclRefExpr ConversionRef(Conversion, Conversion->getType(), 
                             SourceLocation());
   ImplicitCastExpr ConversionFn(Context.getPointerType(Conversion->getType()),
-                                &ConversionRef);
+                                &ConversionRef, false);
   CallExpr Call(&ConversionFn, 0, 0, 
                 Conversion->getConversionType().getNonReferenceType(),
                 SourceLocation());
@@ -1550,6 +1554,562 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion,
   }
 }
 
+/// AddBuiltinCandidate - Add a candidate for a built-in
+/// operator. ResultTy and ParamTys are the result and parameter types
+/// of the built-in candidate, respectively. Args and NumArgs are the
+/// arguments being passed to the candidate.
+void Sema::AddBuiltinCandidate(QualType ResultTy, QualType *ParamTys, 
+                               Expr **Args, unsigned NumArgs,
+                               OverloadCandidateSet& CandidateSet) {
+  // Add this candidate
+  CandidateSet.push_back(OverloadCandidate());
+  OverloadCandidate& Candidate = CandidateSet.back();
+  Candidate.Function = 0;
+  Candidate.BuiltinTypes.ResultTy = ResultTy;
+  for (unsigned ArgIdx = 0; ArgIdx < NumArgs; ++ArgIdx)
+    Candidate.BuiltinTypes.ParamTypes[ArgIdx] = ParamTys[ArgIdx];
+
+  // Determine the implicit conversion sequences for each of the
+  // arguments.
+  Candidate.Viable = true;
+  Candidate.Conversions.resize(NumArgs);
+  for (unsigned ArgIdx = 0; ArgIdx < NumArgs; ++ArgIdx) {
+    Candidate.Conversions[ArgIdx] 
+      = TryCopyInitialization(Args[ArgIdx], ParamTys[ArgIdx], false);
+    if (Candidate.Conversions[ArgIdx].ConversionKind 
+        == ImplicitConversionSequence::BadConversion)
+      Candidate.Viable = false;
+  }
+}
+
+/// BuiltinCandidateTypeSet - A set of types that will be used for the
+/// candidate operator functions for built-in operators (C++
+/// [over.built]). The types are separated into pointer types and
+/// enumeration types.
+class BuiltinCandidateTypeSet  {
+  /// TypeSet - A set of types.
+  typedef llvm::DenseSet<QualType> TypeSet;
+
+  /// PointerTypes - The set of pointer types that will be used in the
+  /// built-in candidates.
+  TypeSet PointerTypes;
+
+  /// EnumerationTypes - The set of enumeration types that will be
+  /// used in the built-in candidates.
+  TypeSet EnumerationTypes;
+
+  /// Context - The AST context in which we will build the type sets.
+  ASTContext &Context;
+
+  bool AddWithMoreQualifiedTypeVariants(QualType Ty);
+
+public:
+  /// iterator - Iterates through the types that are part of the set.
+  typedef TypeSet::iterator iterator;
+
+  BuiltinCandidateTypeSet(ASTContext &Context) : Context(Context) { }
+
+  void AddTypesConvertedFrom(QualType Ty, bool AllowUserConversions = true);
+
+  /// pointer_begin - First pointer type found;
+  iterator pointer_begin() { return PointerTypes.begin(); }
+
+  /// pointer_end - Last pointer type found;
+  iterator pointer_end() { return PointerTypes.end(); }
+
+  /// enumeration_begin - First enumeration type found;
+  iterator enumeration_begin() { return EnumerationTypes.begin(); }
+
+  /// enumeration_end - Last enumeration type found;
+  iterator enumeration_end() { return EnumerationTypes.end(); }
+};
+
+/// AddWithMoreQualifiedTypeVariants - Add the pointer type @p Ty to
+/// the set of pointer types along with any more-qualified variants of
+/// that type. For example, if @p Ty is "int const *", this routine
+/// will add "int const *", "int const volatile *", "int const
+/// restrict *", and "int const volatile restrict *" to the set of
+/// pointer types. Returns true if the add of @p Ty itself succeeded,
+/// false otherwise.
+bool BuiltinCandidateTypeSet::AddWithMoreQualifiedTypeVariants(QualType Ty) {
+  // Insert this type.
+  if (!PointerTypes.insert(Ty).second)
+    return false;
+
+  if (const PointerType *PointerTy = Ty->getAsPointerType()) {
+    QualType PointeeTy = PointerTy->getPointeeType();
+    // FIXME: Optimize this so that we don't keep trying to add the same types.
+
+    // FIXME: Do we have to add CVR qualifiers at *all* levels to deal
+    // with all pointer conversions that don't cast away constness?
+    if (!PointeeTy.isConstQualified())
+      AddWithMoreQualifiedTypeVariants
+        (Context.getPointerType(PointeeTy.withConst()));
+    if (!PointeeTy.isVolatileQualified())
+      AddWithMoreQualifiedTypeVariants
+        (Context.getPointerType(PointeeTy.withVolatile()));
+    if (!PointeeTy.isRestrictQualified())
+      AddWithMoreQualifiedTypeVariants
+        (Context.getPointerType(PointeeTy.withRestrict()));
+  }
+
+  return true;
+}
+
+/// AddTypesConvertedFrom - Add each of the types to which the type @p
+/// Ty can be implicit converted to the given set of @p Types. We're
+/// primarily interested in pointer types, enumeration types,
+void BuiltinCandidateTypeSet::AddTypesConvertedFrom(QualType Ty,
+                                                    bool AllowUserConversions) {
+  // Only deal with canonical types.
+  Ty = Context.getCanonicalType(Ty);
+
+  // Look through reference types; they aren't part of the type of an
+  // expression for the purposes of conversions.
+  if (const ReferenceType *RefTy = Ty->getAsReferenceType())
+    Ty = RefTy->getPointeeType();
+
+  // We don't care about qualifiers on the type.
+  Ty = Ty.getUnqualifiedType();
+
+  if (const PointerType *PointerTy = Ty->getAsPointerType()) {
+    QualType PointeeTy = PointerTy->getPointeeType();
+
+    // Insert our type, and its more-qualified variants, into the set
+    // of types.
+    if (!AddWithMoreQualifiedTypeVariants(Ty))
+      return;
+
+    // Add 'cv void*' to our set of types.
+    if (!Ty->isVoidType()) {
+      QualType QualVoid 
+        = Context.VoidTy.getQualifiedType(PointeeTy.getCVRQualifiers());
+      AddWithMoreQualifiedTypeVariants(Context.getPointerType(QualVoid));
+    }
+
+    // If this is a pointer to a class type, add pointers to its bases
+    // (with the same level of cv-qualification as the original
+    // derived class, of course).
+    if (const RecordType *PointeeRec = PointeeTy->getAsRecordType()) {
+      CXXRecordDecl *ClassDecl = cast<CXXRecordDecl>(PointeeRec->getDecl());
+      for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin();
+           Base != ClassDecl->bases_end(); ++Base) {
+        QualType BaseTy = Context.getCanonicalType(Base->getType());
+        BaseTy = BaseTy.getQualifiedType(PointeeTy.getCVRQualifiers());
+
+        // Add the pointer type, recursively, so that we get all of
+        // the indirect base classes, too.
+        AddTypesConvertedFrom(Context.getPointerType(BaseTy), false);
+      }
+    }
+  } else if (Ty->isEnumeralType()) {
+    EnumerationTypes.insert(Ty);
+  } else if (AllowUserConversions) {
+    if (const RecordType *TyRec = Ty->getAsRecordType()) {
+      CXXRecordDecl *ClassDecl = cast<CXXRecordDecl>(TyRec->getDecl());
+      // FIXME: Visit conversion functions in the base classes, too.
+      OverloadedFunctionDecl *Conversions 
+        = ClassDecl->getConversionFunctions();
+      for (OverloadedFunctionDecl::function_iterator Func 
+             = Conversions->function_begin();
+           Func != Conversions->function_end(); ++Func) {
+        CXXConversionDecl *Conv = cast<CXXConversionDecl>(*Func);
+        AddTypesConvertedFrom(Conv->getConversionType(), false);
+      }
+    }
+  }
+}
+
+/// AddBuiltinCandidates - Add the appropriate built-in operator
+/// overloads to the candidate set (C++ [over.built]), based on the
+/// operator @p Op and the arguments given. For example, if the
+/// operator is a binary '+', this routine might add
+///   "int operator+(int, int)"
+/// to cover integer addition.
+void
+Sema::AddBuiltinBinaryOperatorCandidates(OverloadedOperatorKind Op, 
+                                         Expr **Args, 
+                                         OverloadCandidateSet& CandidateSet) {
+  // The set of "promoted arithmetic types", which are the arithmetic
+  // types are that preserved by promotion (C++ [over.built]p2). Note
+  // that the first few of these types are the promoted integral
+  // types; these types need to be first.
+  // FIXME: What about complex?
+  const unsigned FirstIntegralType = 0;
+  const unsigned LastIntegralType = 13;
+  const unsigned FirstPromotedIntegralType = 7, 
+                 LastPromotedIntegralType = 13;
+  const unsigned FirstPromotedArithmeticType = 7,
+                 LastPromotedArithmeticType = 16;
+  const unsigned NumArithmeticTypes = 16;
+  QualType ArithmeticTypes[NumArithmeticTypes] = {
+    Context.BoolTy, Context.CharTy, Context.WCharTy,
+    Context.SignedCharTy, Context.ShortTy,
+    Context.UnsignedCharTy, Context.UnsignedShortTy,
+    Context.IntTy, Context.LongTy, Context.LongLongTy,
+    Context.UnsignedIntTy, Context.UnsignedLongTy, Context.UnsignedLongLongTy,
+    Context.FloatTy, Context.DoubleTy, Context.LongDoubleTy
+  };
+
+  // Find all of the types that the arguments can convert to, but only
+  // if the operator we're looking at has built-in operator candidates
+  // that make use of these types.
+  BuiltinCandidateTypeSet CandidateTypes(Context);
+  if (Op == OO_Less || Op == OO_Greater || Op == OO_LessEqual ||
+      Op == OO_GreaterEqual || Op == OO_EqualEqual || Op == OO_ExclaimEqual ||
+      Op == OO_Plus || Op == OO_Minus || Op == OO_Equal ||
+      Op == OO_PlusEqual || Op == OO_MinusEqual || Op == OO_Subscript ||
+      Op == OO_ArrowStar) {
+    for (unsigned ArgIdx = 0; ArgIdx < 2; ++ArgIdx)
+      CandidateTypes.AddTypesConvertedFrom(Args[ArgIdx]->getType());
+  }
+
+  bool isComparison = false;
+  switch (Op) {
+  case OO_None:
+  case NUM_OVERLOADED_OPERATORS:
+    assert(false && "Expected an overloaded operator");
+    break;
+
+  case OO_New:
+  case OO_Delete:
+  case OO_Array_New:
+  case OO_Array_Delete:
+  case OO_Tilde:
+  case OO_Exclaim:
+  case OO_PlusPlus:
+  case OO_MinusMinus:
+  case OO_Arrow:
+  case OO_Call:
+    assert(false && "Expected a binary operator");
+    break;
+
+  case OO_Comma:
+    // C++ [over.match.oper]p3:
+    //   -- For the operator ',', the unary operator '&', or the
+    //      operator '->', the built-in candidates set is empty.
+    // We don't check '&' or '->' here, since they are unary operators.
+    break;
+
+  case OO_Less:
+  case OO_Greater:
+  case OO_LessEqual:
+  case OO_GreaterEqual:
+  case OO_EqualEqual:
+  case OO_ExclaimEqual:
+    // C++ [over.built]p15:
+    //
+    //   For every pointer or enumeration type T, there exist
+    //   candidate operator functions of the form
+    //     
+    //        bool       operator<(T, T);
+    //        bool       operator>(T, T);
+    //        bool       operator<=(T, T);
+    //        bool       operator>=(T, T);
+    //        bool       operator==(T, T);
+    //        bool       operator!=(T, T);
+    for (BuiltinCandidateTypeSet::iterator Ptr = CandidateTypes.pointer_begin();
+         Ptr != CandidateTypes.pointer_end(); ++Ptr) {
+      QualType ParamTypes[2] = { *Ptr, *Ptr };
+      AddBuiltinCandidate(Context.BoolTy, ParamTypes, Args, 2, CandidateSet);
+    }
+    for (BuiltinCandidateTypeSet::iterator Enum 
+           = CandidateTypes.enumeration_begin();
+         Enum != CandidateTypes.enumeration_end(); ++Enum) {
+      QualType ParamTypes[2] = { *Enum, *Enum };
+      AddBuiltinCandidate(Context.BoolTy, ParamTypes, Args, 2, CandidateSet);
+    }
+
+    // Fall through.
+    isComparison = true;
+
+  case OO_Plus:
+  case OO_Minus:
+    if (!isComparison) {
+      // We didn't fall through, so we must have OO_Plus or OO_Minus.
+
+      // C++ [over.built]p13:
+      //
+      //   For every cv-qualified or cv-unqualified object type T
+      //   there exist candidate operator functions of the form
+      //    
+      //      T*         operator+(T*, ptrdiff_t);
+      //      T&         operator[](T*, ptrdiff_t);    [BELOW]
+      //      T*         operator-(T*, ptrdiff_t);
+      //      T*         operator+(ptrdiff_t, T*);
+      //      T&         operator[](ptrdiff_t, T*);    [BELOW]
+      //
+      // C++ [over.built]p14:
+      //
+      //   For every T, where T is a pointer to object type, there
+      //   exist candidate operator functions of the form
+      //
+      //      ptrdiff_t  operator-(T, T);
+      for (BuiltinCandidateTypeSet::iterator Ptr 
+             = CandidateTypes.pointer_begin();
+           Ptr != CandidateTypes.pointer_end(); ++Ptr) {
+        QualType ParamTypes[2] = { *Ptr, Context.getPointerDiffType() };
+
+        // operator+(T*, ptrdiff_t) or operator-(T*, ptrdiff_t)
+        AddBuiltinCandidate(*Ptr, ParamTypes, Args, 2, CandidateSet);
+
+        if (Op == OO_Plus) {
+          // T* operator+(ptrdiff_t, T*);
+          ParamTypes[0] = ParamTypes[1];
+          ParamTypes[1] = *Ptr;
+          AddBuiltinCandidate(*Ptr, ParamTypes, Args, 2, CandidateSet);
+        } else {
+          // ptrdiff_t operator-(T, T);
+          ParamTypes[1] = *Ptr;
+          AddBuiltinCandidate(Context.getPointerDiffType(), ParamTypes,
+                              Args, 2, CandidateSet);
+        }
+      }
+    }
+    // Fall through
+
+  case OO_Star:
+  case OO_Slash:
+    // C++ [over.built]p12:
+    //
+    //   For every pair of promoted arithmetic types L and R, there
+    //   exist candidate operator functions of the form
+    //
+    //        LR         operator*(L, R);
+    //        LR         operator/(L, R);
+    //        LR         operator+(L, R);
+    //        LR         operator-(L, R);
+    //        bool       operator<(L, R);
+    //        bool       operator>(L, R);
+    //        bool       operator<=(L, R);
+    //        bool       operator>=(L, R);
+    //        bool       operator==(L, R);
+    //        bool       operator!=(L, R);
+    //
+    //   where LR is the result of the usual arithmetic conversions
+    //   between types L and R.
+    for (unsigned Left = FirstPromotedArithmeticType; 
+         Left < LastPromotedArithmeticType; ++Left) {
+      for (unsigned Right = FirstPromotedArithmeticType; 
+           Right < LastPromotedArithmeticType; ++Right) {
+        QualType LandR[2] = { ArithmeticTypes[Left], ArithmeticTypes[Right] };
+        QualType Result 
+          = isComparison? Context.BoolTy 
+                        : UsualArithmeticConversionsType(LandR[0], LandR[1]);
+        AddBuiltinCandidate(Result, LandR, Args, 2, CandidateSet);
+      }
+    }
+    break;
+
+  case OO_Percent:
+  case OO_Amp:
+  case OO_Caret:
+  case OO_Pipe:
+  case OO_LessLess:
+  case OO_GreaterGreater:
+    // C++ [over.built]p17:
+    //
+    //   For every pair of promoted integral types L and R, there
+    //   exist candidate operator functions of the form
+    //
+    //      LR         operator%(L, R);
+    //      LR         operator&(L, R);
+    //      LR         operator^(L, R);
+    //      LR         operator|(L, R);
+    //      L          operator<<(L, R);
+    //      L          operator>>(L, R);
+    //
+    //   where LR is the result of the usual arithmetic conversions
+    //   between types L and R.
+    for (unsigned Left = FirstPromotedIntegralType; 
+         Left < LastPromotedIntegralType; ++Left) {
+      for (unsigned Right = FirstPromotedIntegralType; 
+           Right < LastPromotedIntegralType; ++Right) {
+        QualType LandR[2] = { ArithmeticTypes[Left], ArithmeticTypes[Right] };
+        QualType Result = (Op == OO_LessLess || Op == OO_GreaterGreater)
+            ? LandR[0]
+            : UsualArithmeticConversionsType(LandR[0], LandR[1]);
+        AddBuiltinCandidate(Result, LandR, Args, 2, CandidateSet);
+      }
+    }
+    break;
+
+  case OO_Equal:
+    // C++ [over.built]p20:
+    //
+    //   For every pair (T, VQ), where T is an enumeration or
+    //   (FIXME:) pointer to member type and VQ is either volatile or
+    //   empty, there exist candidate operator functions of the form
+    //
+    //        VQ T&      operator=(VQ T&, T);
+    for (BuiltinCandidateTypeSet::iterator Enum 
+           = CandidateTypes.enumeration_begin();
+         Enum != CandidateTypes.enumeration_end(); ++Enum) {
+      QualType ParamTypes[2];
+
+      // T& operator=(T&, T)
+      ParamTypes[0] = Context.getReferenceType(*Enum);
+      ParamTypes[1] = *Enum;
+      AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet);
+
+      // volatile T& operator=(volatile T&, T)
+      ParamTypes[0] = Context.getReferenceType(Enum->withVolatile());
+      ParamTypes[1] = *Enum;
+      AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet);
+    }
+    // Fall through.
+
+  case OO_PlusEqual:
+  case OO_MinusEqual:
+    // C++ [over.built]p19:
+    //
+    //   For every pair (T, VQ), where T is any type and VQ is either
+    //   volatile or empty, there exist candidate operator functions
+    //   of the form
+    //
+    //        T*VQ&      operator=(T*VQ&, T*);
+    //
+    // C++ [over.built]p21:
+    //
+    //   For every pair (T, VQ), where T is a cv-qualified or
+    //   cv-unqualified object type and VQ is either volatile or
+    //   empty, there exist candidate operator functions of the form
+    //
+    //        T*VQ&      operator+=(T*VQ&, ptrdiff_t);
+    //        T*VQ&      operator-=(T*VQ&, ptrdiff_t);
+    for (BuiltinCandidateTypeSet::iterator Ptr = CandidateTypes.pointer_begin();
+         Ptr != CandidateTypes.pointer_end(); ++Ptr) {
+      QualType ParamTypes[2];
+      ParamTypes[1] = (Op == OO_Equal)? *Ptr : Context.getPointerDiffType();
+
+      // non-volatile version
+      ParamTypes[0] = Context.getReferenceType(*Ptr);
+      AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet);
+
+      // volatile version
+      ParamTypes[0] = Context.getReferenceType(Ptr->withVolatile());
+      AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet);
+    }
+    // Fall through.
+
+  case OO_StarEqual:
+  case OO_SlashEqual:
+    // C++ [over.built]p18:
+    //
+    //   For every triple (L, VQ, R), where L is an arithmetic type,
+    //   VQ is either volatile or empty, and R is a promoted
+    //   arithmetic type, there exist candidate operator functions of
+    //   the form
+    //
+    //        VQ L&      operator=(VQ L&, R);
+    //        VQ L&      operator*=(VQ L&, R);
+    //        VQ L&      operator/=(VQ L&, R);
+    //        VQ L&      operator+=(VQ L&, R);
+    //        VQ L&      operator-=(VQ L&, R);
+    for (unsigned Left = 0; Left < NumArithmeticTypes; ++Left) {
+      for (unsigned Right = FirstPromotedArithmeticType; 
+           Right < LastPromotedArithmeticType; ++Right) {
+        QualType ParamTypes[2];
+        ParamTypes[1] = ArithmeticTypes[Right];
+
+        // Add this built-in operator as a candidate (VQ is empty).
+        ParamTypes[0] = Context.getReferenceType(ArithmeticTypes[Left]);
+        AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet);
+
+        // Add this built-in operator as a candidate (VQ is 'volatile').
+        ParamTypes[0] = ArithmeticTypes[Left].withVolatile();
+        ParamTypes[0] = Context.getReferenceType(ParamTypes[0]);
+        AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet);
+      }
+    }
+    break;
+
+  case OO_PercentEqual:
+  case OO_LessLessEqual:
+  case OO_GreaterGreaterEqual:
+  case OO_AmpEqual:
+  case OO_CaretEqual:
+  case OO_PipeEqual:
+    // C++ [over.built]p22:
+    //
+    //   For every triple (L, VQ, R), where L is an integral type, VQ
+    //   is either volatile or empty, and R is a promoted integral
+    //   type, there exist candidate operator functions of the form
+    //
+    //        VQ L&       operator%=(VQ L&, R);
+    //        VQ L&       operator<<=(VQ L&, R);
+    //        VQ L&       operator>>=(VQ L&, R);
+    //        VQ L&       operator&=(VQ L&, R);
+    //        VQ L&       operator^=(VQ L&, R);
+    //        VQ L&       operator|=(VQ L&, R);
+    for (unsigned Left = FirstIntegralType; Left < LastIntegralType; ++Left) {
+      for (unsigned Right = FirstPromotedIntegralType; 
+           Right < LastPromotedIntegralType; ++Right) {
+        QualType ParamTypes[2];
+        ParamTypes[1] = ArithmeticTypes[Right];
+
+        // Add this built-in operator as a candidate (VQ is empty).
+        // FIXME: We should be caching these declarations somewhere,
+        // rather than re-building them every time.
+        ParamTypes[0] = Context.getReferenceType(ArithmeticTypes[Left]);
+        AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet);
+
+        // Add this built-in operator as a candidate (VQ is 'volatile').
+        ParamTypes[0] = ArithmeticTypes[Left];
+        ParamTypes[0].addVolatile();
+        ParamTypes[0] = Context.getReferenceType(ParamTypes[0]);
+        AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet);
+      }
+    }
+    break;
+
+  case OO_AmpAmp:
+  case OO_PipePipe: {
+    // C++ [over.operator]p23:
+    //
+    //   There also exist candidate operator functions of the form
+    //
+    //        bool        operator!(bool);            [In Unary version]
+    //        bool        operator&&(bool, bool);
+    //        bool        operator||(bool, bool);
+    QualType ParamTypes[2] = { Context.BoolTy, Context.BoolTy };
+    AddBuiltinCandidate(Context.BoolTy, ParamTypes, Args, 2, CandidateSet);
+    break;
+  }
+
+  case OO_Subscript:
+    // C++ [over.built]p13:
+    //
+    //   For every cv-qualified or cv-unqualified object type T there
+    //   exist candidate operator functions of the form
+    //    
+    //        T*         operator+(T*, ptrdiff_t);     [ABOVE]
+    //        T&         operator[](T*, ptrdiff_t);
+    //        T*         operator-(T*, ptrdiff_t);     [ABOVE]
+    //        T*         operator+(ptrdiff_t, T*);     [ABOVE]
+    //        T&         operator[](ptrdiff_t, T*);
+    for (BuiltinCandidateTypeSet::iterator Ptr = CandidateTypes.pointer_begin();
+         Ptr != CandidateTypes.pointer_end(); ++Ptr) {
+      QualType ParamTypes[2] = { *Ptr, Context.getPointerDiffType() };
+      QualType PointeeType = (*Ptr)->getAsPointerType()->getPointeeType();
+      QualType ResultTy = Context.getReferenceType(PointeeType);
+
+      // T& operator[](T*, ptrdiff_t)
+      AddBuiltinCandidate(ResultTy, ParamTypes, Args, 2, CandidateSet);
+
+      // T& operator[](ptrdiff_t, T*);
+      ParamTypes[0] = ParamTypes[1];
+      ParamTypes[1] = *Ptr;
+      AddBuiltinCandidate(ResultTy, ParamTypes, Args, 2, CandidateSet);
+    }
+    break;
+
+  case OO_ArrowStar:
+    // FIXME: No support for pointer-to-members yet.
+    break;
+  }
+}
+
 /// AddOverloadCandidates - Add all of the function overloads in Ovl
 /// to the candidate set.
 void 
@@ -1609,7 +2169,8 @@ Sema::isBetterOverloadCandidate(const OverloadCandidate& Cand1,
   if (HasBetterConversion)
     return true;
 
-  // FIXME: Several other bullets in (C++ 13.3.3p1) need to be implemented.
+  // FIXME: Several other bullets in (C++ 13.3.3p1) need to be
+  // implemented, but they require template support.
 
   // C++ [over.match.best]p1b4:
   //
@@ -1688,8 +2249,24 @@ Sema::PrintOverloadCandidates(OverloadCandidateSet& CandidateSet,
   OverloadCandidateSet::iterator Cand = CandidateSet.begin(),
                              LastCand = CandidateSet.end();
   for (; Cand != LastCand; ++Cand) {
-    if (Cand->Viable ||!OnlyViable)
-      Diag(Cand->Function->getLocation(), diag::err_ovl_candidate);
+    if (Cand->Viable || !OnlyViable) {
+      if (Cand->Function) {
+        // Normal function
+        Diag(Cand->Function->getLocation(), diag::err_ovl_candidate);
+      } else {
+        // FIXME: We need to get the identifier in here
+        // FIXME: Do we want the error message to point at the 
+        // operator? (built-ins won't have a location)
+        QualType FnType 
+          = Context.getFunctionType(Cand->BuiltinTypes.ResultTy,
+                                    Cand->BuiltinTypes.ParamTypes,
+                                    Cand->Conversions.size(),
+                                    false, 0);
+
+        Diag(SourceLocation(), diag::err_ovl_builtin_candidate,
+             FnType.getAsString());
+      }
+    }
   }
 }
 
index 00a81859c8c340d5c83e5abde7ba040349fd5c8f..24b796b21143124abac8f5e5fbfe615f2b7ffb5e 100644 (file)
@@ -198,8 +198,16 @@ namespace clang {
 
   /// OverloadCandidate - A single candidate in an overload set (C++ 13.3).
   struct OverloadCandidate {
-    /// Function - The actual function that this candidate represents.
+    /// Function - The actual function that this candidate
+    /// represents. When NULL, this is a built-in candidate.
     FunctionDecl *Function;
+    
+    // BuiltinTypes - Provides the return and parameter types of a
+    // built-in overload candidate. Only valid when Function is NULL.
+    struct {
+      QualType ResultTy;
+      QualType ParamTypes[3];
+    } BuiltinTypes;
 
     /// Conversions - The conversion sequences used to convert the
     /// function arguments to the function parameters.
@@ -217,7 +225,7 @@ namespace clang {
 
   /// OverloadCandidateSet - A set of overload candidates, used in C++
   /// overload resolution (C++ 13.3).
-  typedef llvm::SmallVector<OverloadCandidate, 4> OverloadCandidateSet;
+  typedef llvm::SmallVector<OverloadCandidate, 16> OverloadCandidateSet;
 } // end namespace clang
 
 #endif // LLVM_CLANG_SEMA_OVERLOAD_H
diff --git a/test/SemaCXX/overloaded-builtin-operators.cpp b/test/SemaCXX/overloaded-builtin-operators.cpp
new file mode 100644 (file)
index 0000000..aabe8dd
--- /dev/null
@@ -0,0 +1,66 @@
+// RUN: clang -fsyntax-only -verify %s 
+struct yes;
+struct no;
+
+struct Short {
+  operator short();
+};
+
+struct Long {
+  operator long();
+};
+
+yes& islong(long);
+no& islong(int);
+
+void f(Short s, Long l) {
+  // C++ [over.built]p12
+  (void)static_cast<yes&>(islong(s + l));
+  (void)static_cast<no&>(islong(s + s));
+
+  // C++ [over.built]p17
+  (void)static_cast<yes&>(islong(s % l));
+  (void)static_cast<yes&>(islong(l << s));
+  (void)static_cast<no&>(islong(s << l));
+}
+
+struct ShortRef {
+  operator short&();
+};
+
+struct LongRef {
+  operator volatile long&();
+};
+
+void g(ShortRef sr, LongRef lr) {
+  // C++ [over.built]p18
+  short& sr1 = (sr *= lr);
+  volatile long& lr1 = (lr *= sr);
+
+  // C++ [over.built]p22
+  short& sr2 = (sr %= lr);
+  volatile long& lr2 = (lr <<= sr);
+
+  bool b1 = (sr && lr) || (sr || lr);
+}
+
+struct VolatileIntPtr {
+  operator int volatile *();
+};
+
+struct ConstIntPtr {
+  operator int const *();
+};
+
+void test_with_ptrs(VolatileIntPtr vip, ConstIntPtr cip, ShortRef sr) {
+#if 0
+  // FIXME: Enable these tests once we have operator overloading for
+  // operator[].
+  const int& cir1 = cip[sr];
+  const int& cir2 = sr[cip];
+  volatile int& vir1 = vip[sr];
+  volatile int& vir2 = sr[vip];
+#endif
+  bool b1 = (vip == cip);
+  long p1 = vip - cip;
+}