]> granicus.if.org Git - clang/commitdiff
Built-in equality and relational operators have return type "bool" in C++,
authorDouglas Gregor <dgregor@apple.com>
Wed, 19 Nov 2008 03:25:36 +0000 (03:25 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 19 Nov 2008 03:25:36 +0000 (03:25 +0000)
not "int".

Fix a typo in the promotion of enumeration types that was causing some
integral promotions to look like integral conversions (leading to
extra ambiguities in overload resolution).

Check for "acceptable" overloaded operators based on the types of the
arguments. This is a somewhat odd check that is specified by the
standard, but I can't see why it actually matters: the overload
candidates it suppresses don't seem like they would ever be picked as
the best candidates.

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

lib/Sema/SemaExpr.cpp
lib/Sema/SemaOverload.cpp
test/SemaCXX/overloaded-builtin-operators.cpp
test/SemaCXX/overloaded-operator.cpp

index 960c99af1f25bbb792082288cf4dc2f1501950b7..0e62e26e8b5df30b2dc7bbf5908a2f3cb5fa6823 100644 (file)
@@ -2145,9 +2145,12 @@ QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation Loc,
           Diag(Loc, diag::warn_selfcomparison);      
   }
   
+  // The result of comparisons is 'bool' in C++, 'int' in C.
+  QualType ResultTy = getLangOptions().CPlusPlus? Context.BoolTy : Context.IntTy;
+
   if (isRelational) {
     if (lType->isRealType() && rType->isRealType())
-      return Context.IntTy;
+      return ResultTy;
   } else {
     // Check for comparisons of floating point operands using != and ==.
     if (lType->isFloatingType()) {
@@ -2156,7 +2159,7 @@ QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation Loc,
     }
     
     if (lType->isArithmeticType() && rType->isArithmeticType())
-      return Context.IntTy;
+      return ResultTy;
   }
   
   bool LHSIsNull = lex->isNullPointerConstant(Context);
@@ -2181,7 +2184,7 @@ QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation Loc,
         << lex->getSourceRange() << rex->getSourceRange();
     }
     ImpCastExprToType(rex, lType); // promote the pointer to pointer
-    return Context.IntTy;
+    return ResultTy;
   }
   // Handle block pointer types.
   if (lType->isBlockPointerType() && rType->isBlockPointerType()) {
@@ -2195,7 +2198,7 @@ QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation Loc,
         << lex->getSourceRange() << rex->getSourceRange();
     }
     ImpCastExprToType(rex, lType); // promote the pointer to pointer
-    return Context.IntTy;
+    return ResultTy;
   }
   // Allow block pointers to be compared with null pointer constants.
   if ((lType->isBlockPointerType() && rType->isPointerType()) ||
@@ -2206,7 +2209,7 @@ QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation Loc,
         << lex->getSourceRange() << rex->getSourceRange();
     }
     ImpCastExprToType(rex, lType); // promote the pointer to pointer
-    return Context.IntTy;
+    return ResultTy;
   }
 
   if ((lType->isObjCQualifiedIdType() || rType->isObjCQualifiedIdType())) {
@@ -2224,21 +2227,21 @@ QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation Loc,
           << lType.getAsString() << rType.getAsString()
           << lex->getSourceRange() << rex->getSourceRange();
         ImpCastExprToType(rex, lType);
-        return Context.IntTy;
+        return ResultTy;
       }
       ImpCastExprToType(rex, lType);
-      return Context.IntTy;
+      return ResultTy;
     }
     if (ObjCQualifiedIdTypesAreCompatible(lType, rType, true)) {
       ImpCastExprToType(rex, lType);
-      return Context.IntTy;
+      return ResultTy;
     } else {
       if ((lType->isObjCQualifiedIdType() && rType->isObjCQualifiedIdType())) {
         Diag(Loc, diag::warn_incompatible_qualified_id_operands)
           << lType.getAsString() << rType.getAsString()
           << lex->getSourceRange() << rex->getSourceRange();
         ImpCastExprToType(rex, lType);
-        return Context.IntTy;
+        return ResultTy;
       }
     }
   }
@@ -2249,7 +2252,7 @@ QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation Loc,
         << lType.getAsString() << rType.getAsString()
         << lex->getSourceRange() << rex->getSourceRange();
     ImpCastExprToType(rex, lType); // promote the integer to pointer
-    return Context.IntTy;
+    return ResultTy;
   }
   if (lType->isIntegerType() && 
       (rType->isPointerType() || rType->isObjCQualifiedIdType())) {
@@ -2258,7 +2261,7 @@ QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation Loc,
         << lType.getAsString() << rType.getAsString()
         << lex->getSourceRange() << rex->getSourceRange();
     ImpCastExprToType(lex, rType); // promote the integer to pointer
-    return Context.IntTy;
+    return ResultTy;
   }
   // Handle block pointers.
   if (lType->isBlockPointerType() && rType->isIntegerType()) {
@@ -2267,7 +2270,7 @@ QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation Loc,
         << lType.getAsString() << rType.getAsString()
         << lex->getSourceRange() << rex->getSourceRange();
     ImpCastExprToType(rex, lType); // promote the integer to pointer
-    return Context.IntTy;
+    return ResultTy;
   }
   if (lType->isIntegerType() && rType->isBlockPointerType()) {
     if (!LHSIsNull)
@@ -2275,7 +2278,7 @@ QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation Loc,
         << lType.getAsString() << rType.getAsString()
         << lex->getSourceRange() << rex->getSourceRange();
     ImpCastExprToType(lex, rType); // promote the integer to pointer
-    return Context.IntTy;
+    return ResultTy;
   }
   return InvalidOperands(Loc, lex, rex);
 }
index 749b916e0d62af8fb98900ff13285795a38d620e..98a32ef26791cd395d0419f2eb2b926bbfb2da91 100644 (file)
@@ -637,7 +637,7 @@ bool Sema::IsIntegralPromotion(Expr *From, QualType FromType, QualType ToType)
       Context.IntTy, Context.UnsignedIntTy, 
       Context.LongTy, Context.UnsignedLongTy 
     };
-    for (int Idx = 0; Idx < 0; ++Idx) {
+    for (int Idx = 0; Idx < 4; ++Idx) {
       uint64_t ToSize = Context.getTypeSize(PromoteTypes[Idx]);
       if (FromSize < ToSize ||
           (FromSize == ToSize && 
@@ -1712,6 +1712,42 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion,
   }
 }
 
+/// IsAcceptableNonMemberOperatorCandidate - Determine whether Fn is
+/// an acceptable non-member overloaded operator for a call whose
+/// arguments have types T1 (and, if non-empty, T2). This routine
+/// implements the check in C++ [over.match.oper]p3b2 concerning
+/// enumeration types.
+static bool 
+IsAcceptableNonMemberOperatorCandidate(FunctionDecl *Fn,
+                                       QualType T1, QualType T2,
+                                       ASTContext &Context) {
+  if (T1->isRecordType() || (!T2.isNull() && T2->isRecordType()))
+    return true;
+
+  const FunctionTypeProto *Proto = Fn->getType()->getAsFunctionTypeProto();
+  if (Proto->getNumArgs() < 1)
+    return false;
+
+  if (T1->isEnumeralType()) {
+    QualType ArgType = Proto->getArgType(0).getNonReferenceType();
+    if (Context.getCanonicalType(T1).getUnqualifiedType()
+          == Context.getCanonicalType(ArgType).getUnqualifiedType())
+      return true;
+  }
+
+  if (Proto->getNumArgs() < 2)
+    return false;
+
+  if (!T2.isNull() && T2->isEnumeralType()) {
+    QualType ArgType = Proto->getArgType(1).getNonReferenceType();
+    if (Context.getCanonicalType(T2).getUnqualifiedType()
+          == Context.getCanonicalType(ArgType).getUnqualifiedType())
+      return true;
+  }
+
+  return false;
+}
+
 /// AddOperatorCandidates - Add the overloaded operator candidates for
 /// the operator Op that was used in an operator expression such as "x
 /// Op y". S is the scope in which the expression occurred (used for
@@ -1790,18 +1826,19 @@ void Sema::AddOperatorCandidates(OverloadedOperatorKind Op, Scope *S,
       break;
     }
 
-    // FIXME: check that strange "However" condition above. It's going
-    // to need a special test.
-    if (FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(NonMemberOps))
-      AddOverloadCandidate(FD, Args, NumArgs, CandidateSet,
-                           /*SuppressUserConversions=*/false);
-    else if (OverloadedFunctionDecl *Ovl
-               = dyn_cast_or_null<OverloadedFunctionDecl>(NonMemberOps)) {
+    if (FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(NonMemberOps)) {
+      if (IsAcceptableNonMemberOperatorCandidate(FD, T1, T2, Context))
+        AddOverloadCandidate(FD, Args, NumArgs, CandidateSet,
+                             /*SuppressUserConversions=*/false);
+    } else if (OverloadedFunctionDecl *Ovl
+                 = dyn_cast_or_null<OverloadedFunctionDecl>(NonMemberOps)) {
       for (OverloadedFunctionDecl::function_iterator F = Ovl->function_begin(),
                                                   FEnd = Ovl->function_end();
-           F != FEnd; ++F)
-        AddOverloadCandidate(*F, Args, NumArgs, CandidateSet, 
-                             /*SuppressUserConversions=*/false);
+           F != FEnd; ++F) {
+        if (IsAcceptableNonMemberOperatorCandidate(*F, T1, T2, Context)) 
+          AddOverloadCandidate(*F, Args, NumArgs, CandidateSet, 
+                               /*SuppressUserConversions=*/false);
+      }
     }
   }
 
@@ -2476,7 +2513,8 @@ Sema::isBetterOverloadCandidate(const OverloadCandidate& Cand1,
   //      the type of the entity being initialized) is a better
   //      conversion sequence than the standard conversion sequence
   //      from the return type of F2 to the destination type.
-  if (isa<CXXConversionDecl>(Cand1.Function) && 
+  if (Cand1.Function && Cand2.Function && 
+      isa<CXXConversionDecl>(Cand1.Function) && 
       isa<CXXConversionDecl>(Cand2.Function)) {
     switch (CompareStandardConversionSequences(Cand1.FinalConversion,
                                                Cand2.FinalConversion)) {
index aabe8dde1b1ee75e40bd7505af2a40a1df2d1ab7..af328da212bd84d8a1ebaaacd9cf76704bc8e287 100644 (file)
@@ -10,10 +10,21 @@ struct Long {
   operator long();
 };
 
+enum E1 { };
+struct Enum1 {
+  operator E1();
+};
+
+enum E2 { };
+struct Enum2 {
+  operator E2();
+};
+
 yes& islong(long);
+yes& islong(unsigned long); // FIXME: shouldn't be needed
 no& islong(int);
 
-void f(Short s, Long l) {
+void f(Short s, Long l, Enum1 e1, Enum2 e2) {
   // C++ [over.built]p12
   (void)static_cast<yes&>(islong(s + l));
   (void)static_cast<no&>(islong(s + s));
@@ -22,6 +33,8 @@ void f(Short s, Long l) {
   (void)static_cast<yes&>(islong(s % l));
   (void)static_cast<yes&>(islong(l << s));
   (void)static_cast<no&>(islong(s << l));
+  (void)static_cast<yes&>(islong(e1 % l));
+  // FIXME: should pass (void)static_cast<no&>(islong(e1 % e2));
 }
 
 struct ShortRef {
index 71d949d750a0764f9706858a151df14e3f463785..c540c2dafcbbd131704ae968b8f533fa79baffc4 100644 (file)
@@ -48,3 +48,24 @@ struct B {
     make_A() == z;
   }
 };
+
+enum Enum1 { };
+enum Enum2 { };
+
+struct E1 {
+  E1(Enum1) { }
+};
+
+struct E2 {
+  E2(Enum2);
+};
+
+// C++ [over.match.oper]p3 - enum restriction.
+float& operator==(E1, E2); 
+
+void enum_test(Enum1 enum1, Enum2 enum2, E1 e1, E2 e2) {
+  float &f1 = (e1 == e2);
+  float &f2 = (enum1 == e2); 
+  float &f3 = (e1 == enum2); 
+  float &f4 = (enum1 == enum2);  // expected-error{{non-const reference to type 'float' cannot be initialized with a temporary of type '_Bool'}}
+}