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()) {
}
if (lType->isArithmeticType() && rType->isArithmeticType())
- return Context.IntTy;
+ return ResultTy;
}
bool LHSIsNull = lex->isNullPointerConstant(Context);
<< 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()) {
<< 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()) ||
<< lex->getSourceRange() << rex->getSourceRange();
}
ImpCastExprToType(rex, lType); // promote the pointer to pointer
- return Context.IntTy;
+ return ResultTy;
}
if ((lType->isObjCQualifiedIdType() || rType->isObjCQualifiedIdType())) {
<< 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;
}
}
}
<< 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())) {
<< 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()) {
<< 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)
<< 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);
}
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 &&
}
}
+/// 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
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);
+ }
}
}
// 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)) {
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));
(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 {
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'}}
+}