mirror of https://github.com/microsoft/clang.git
[c++20] P0515R3: Parsing support and basic AST construction for operator <=>.
Adding the new enumerator forced a bunch more changes into this patch than I would have liked. The -Wtautological-compare warning was extended to properly check the new comparison operator, clang-format needed updating because it uses precedence levels as weights for determining where to break lines (and several operators increased their precedence levels with this change), thread-safety analysis needed changes to build its own IL properly for the new operator. All "real" semantic checking for this operator has been deferred to a future patch. For now, we use the relational comparison rules and arbitrarily give the builtin form of the operator a return type of 'void'. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@320707 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
f19939b529
commit
96881be75c
|
@ -3072,7 +3072,7 @@ public:
|
|||
static bool isEqualityOp(Opcode Opc) { return Opc == BO_EQ || Opc == BO_NE; }
|
||||
bool isEqualityOp() const { return isEqualityOp(getOpcode()); }
|
||||
|
||||
static bool isComparisonOp(Opcode Opc) { return Opc >= BO_LT && Opc<=BO_NE; }
|
||||
static bool isComparisonOp(Opcode Opc) { return Opc >= BO_Cmp && Opc<=BO_NE; }
|
||||
bool isComparisonOp() const { return isComparisonOp(getOpcode()); }
|
||||
|
||||
static Opcode negateComparisonOp(Opcode Opc) {
|
||||
|
|
|
@ -332,7 +332,8 @@ CAST_OPERATION(IntToOCLSampler)
|
|||
|
||||
//===- Binary Operations -------------------------------------------------===//
|
||||
// Operators listed in order of precedence.
|
||||
// Note that additions to this should also update the StmtVisitor class.
|
||||
// Note that additions to this should also update the StmtVisitor class and
|
||||
// BinaryOperator::getOverloadedOperator.
|
||||
|
||||
// [C++ 5.5] Pointer-to-member operators.
|
||||
BINARY_OPERATION(PtrMemD, ".*")
|
||||
|
@ -347,6 +348,8 @@ BINARY_OPERATION(Sub, "-")
|
|||
// [C99 6.5.7] Bitwise shift operators.
|
||||
BINARY_OPERATION(Shl, "<<")
|
||||
BINARY_OPERATION(Shr, ">>")
|
||||
// C++20 [expr.spaceship] Three-way comparison operator.
|
||||
BINARY_OPERATION(Cmp, "<=>")
|
||||
// [C99 6.5.8] Relational operators.
|
||||
BINARY_OPERATION(LT, "<")
|
||||
BINARY_OPERATION(GT, ">")
|
||||
|
@ -382,7 +385,8 @@ BINARY_OPERATION(Comma, ",")
|
|||
|
||||
|
||||
//===- Unary Operations ---------------------------------------------------===//
|
||||
// Note that additions to this should also update the StmtVisitor class.
|
||||
// Note that additions to this should also update the StmtVisitor class and
|
||||
// UnaryOperator::getOverloadedOperator.
|
||||
|
||||
// [C99 6.5.2.4] Postfix increment and decrement
|
||||
UNARY_OPERATION(PostInc, "++")
|
||||
|
|
|
@ -63,8 +63,8 @@
|
|||
OPERATOR(PtrMemD) OPERATOR(PtrMemI) OPERATOR(Mul) OPERATOR(Div) \
|
||||
OPERATOR(Rem) OPERATOR(Add) OPERATOR(Sub) OPERATOR(Shl) OPERATOR(Shr) \
|
||||
OPERATOR(LT) OPERATOR(GT) OPERATOR(LE) OPERATOR(GE) OPERATOR(EQ) \
|
||||
OPERATOR(NE) OPERATOR(And) OPERATOR(Xor) OPERATOR(Or) OPERATOR(LAnd) \
|
||||
OPERATOR(LOr) OPERATOR(Assign) OPERATOR(Comma)
|
||||
OPERATOR(NE) OPERATOR(Cmp) OPERATOR(And) OPERATOR(Xor) OPERATOR(Or) \
|
||||
OPERATOR(LAnd) OPERATOR(LOr) OPERATOR(Assign) OPERATOR(Comma)
|
||||
|
||||
// All compound assign operators.
|
||||
#define CAO_LIST() \
|
||||
|
|
|
@ -65,6 +65,7 @@ public:
|
|||
case BO_GE: DISPATCH(BinGE, BinaryOperator);
|
||||
case BO_EQ: DISPATCH(BinEQ, BinaryOperator);
|
||||
case BO_NE: DISPATCH(BinNE, BinaryOperator);
|
||||
case BO_Cmp: DISPATCH(BinCmp, BinaryOperator);
|
||||
|
||||
case BO_And: DISPATCH(BinAnd, BinaryOperator);
|
||||
case BO_Xor: DISPATCH(BinXor, BinaryOperator);
|
||||
|
@ -132,6 +133,8 @@ public:
|
|||
|
||||
BINOP_FALLBACK(LT) BINOP_FALLBACK(GT) BINOP_FALLBACK(LE)
|
||||
BINOP_FALLBACK(GE) BINOP_FALLBACK(EQ) BINOP_FALLBACK(NE)
|
||||
BINOP_FALLBACK(Cmp)
|
||||
|
||||
BINOP_FALLBACK(And) BINOP_FALLBACK(Xor) BINOP_FALLBACK(Or)
|
||||
BINOP_FALLBACK(LAnd) BINOP_FALLBACK(LOr)
|
||||
|
||||
|
|
|
@ -92,6 +92,7 @@ enum TIL_BinaryOpcode : unsigned char {
|
|||
BOP_Neq, // !=
|
||||
BOP_Lt, // <
|
||||
BOP_Leq, // <=
|
||||
BOP_Cmp, // <=>
|
||||
BOP_LogicAnd, // && (no short-circuit)
|
||||
BOP_LogicOr // || (no short-circuit)
|
||||
};
|
||||
|
|
|
@ -5937,25 +5937,25 @@ def note_typecheck_assign_const : Note<
|
|||
"%select{|nested }1data member %2 declared const here}0">;
|
||||
|
||||
def warn_unsigned_always_true_comparison : Warning<
|
||||
"comparison of %select{%3|unsigned expression}0 %2 "
|
||||
"%select{unsigned expression|%3}0 is always %select{false|true}4">,
|
||||
"result of comparison of %select{%3|unsigned expression}0 %2 "
|
||||
"%select{unsigned expression|%3}0 is always %4">,
|
||||
InGroup<TautologicalUnsignedZeroCompare>;
|
||||
def warn_unsigned_enum_always_true_comparison : Warning<
|
||||
"comparison of %select{%3|unsigned enum expression}0 %2 "
|
||||
"%select{unsigned enum expression|%3}0 is always %select{false|true}4">,
|
||||
"result of comparison of %select{%3|unsigned enum expression}0 %2 "
|
||||
"%select{unsigned enum expression|%3}0 is always %4">,
|
||||
InGroup<TautologicalUnsignedEnumZeroCompare>;
|
||||
def warn_tautological_constant_compare : Warning<
|
||||
"comparison %select{%3|%1}0 %2 "
|
||||
"%select{%1|%3}0 is always %select{false|true}4">,
|
||||
"result of comparison %select{%3|%1}0 %2 "
|
||||
"%select{%1|%3}0 is always %4">,
|
||||
InGroup<TautologicalConstantCompare>;
|
||||
|
||||
def warn_mixed_sign_comparison : Warning<
|
||||
"comparison of integers of different signs: %0 and %1">,
|
||||
InGroup<SignCompare>, DefaultIgnore;
|
||||
def warn_out_of_range_compare : Warning<
|
||||
"comparison of %select{constant %0|true|false}1 with "
|
||||
"%select{expression of type %2|boolean expression}3 is always "
|
||||
"%select{false|true}4">, InGroup<TautologicalOutOfRangeCompare>;
|
||||
"result of comparison of %select{constant %0|true|false}1 with "
|
||||
"%select{expression of type %2|boolean expression}3 is always %4">,
|
||||
InGroup<TautologicalOutOfRangeCompare>;
|
||||
def warn_tautological_bool_compare : Warning<warn_out_of_range_compare.Text>,
|
||||
InGroup<TautologicalConstantCompare>;
|
||||
def warn_comparison_of_mixed_enum_types : Warning<
|
||||
|
@ -7289,7 +7289,7 @@ def ext_cxx17_attr : Extension<
|
|||
"use of the %0 attribute is a C++17 extension">, InGroup<CXX17>;
|
||||
|
||||
def warn_unused_comparison : Warning<
|
||||
"%select{%select{|in}1equality|relational}0 comparison result unused">,
|
||||
"%select{equality|inequality|relational|three-way}0 comparison result unused">,
|
||||
InGroup<UnusedComparison>;
|
||||
def note_inequality_comparison_to_or_assign : Note<
|
||||
"use '|=' to turn this inequality comparison into an or-assignment">;
|
||||
|
|
|
@ -36,10 +36,11 @@ namespace prec {
|
|||
And = 8, // &
|
||||
Equality = 9, // ==, !=
|
||||
Relational = 10, // >=, <=, >, <
|
||||
Shift = 11, // <<, >>
|
||||
Additive = 12, // -, +
|
||||
Multiplicative = 13, // *, /, %
|
||||
PointerToMember = 14 // .*, ->*
|
||||
Spaceship = 11, // <=>
|
||||
Shift = 12, // <<, >>
|
||||
Additive = 13, // -, +
|
||||
Multiplicative = 14, // *, /, %
|
||||
PointerToMember = 15 // .*, ->*
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1780,6 +1780,7 @@ BinaryOperator::getOverloadedOpcode(OverloadedOperatorKind OO) {
|
|||
case OO_Amp: return BO_And;
|
||||
case OO_Pipe: return BO_Or;
|
||||
case OO_Equal: return BO_Assign;
|
||||
case OO_Spaceship: return BO_Cmp;
|
||||
case OO_Less: return BO_LT;
|
||||
case OO_Greater: return BO_GT;
|
||||
case OO_PlusEqual: return BO_AddAssign;
|
||||
|
@ -1811,6 +1812,7 @@ OverloadedOperatorKind BinaryOperator::getOverloadedOperator(Opcode Opc) {
|
|||
OO_Star, OO_Slash, OO_Percent,
|
||||
OO_Plus, OO_Minus,
|
||||
OO_LessLess, OO_GreaterGreater,
|
||||
OO_Spaceship,
|
||||
OO_Less, OO_Greater, OO_LessEqual, OO_GreaterEqual,
|
||||
OO_EqualEqual, OO_ExclaimEqual,
|
||||
OO_Amp,
|
||||
|
|
|
@ -10471,6 +10471,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
|
|||
case BO_AndAssign:
|
||||
case BO_XorAssign:
|
||||
case BO_OrAssign:
|
||||
case BO_Cmp: // FIXME: Re-enable once we can evaluate this.
|
||||
// C99 6.6/3 allows assignments within unevaluated subexpressions of
|
||||
// constant expressions, but they can never be ICEs because an ICE cannot
|
||||
// contain an lvalue operand.
|
||||
|
|
|
@ -505,6 +505,7 @@ til::SExpr *SExprBuilder::translateBinaryOperator(const BinaryOperator *BO,
|
|||
case BO_GE: return translateBinOp(til::BOP_Leq, BO, Ctx, true);
|
||||
case BO_EQ: return translateBinOp(til::BOP_Eq, BO, Ctx);
|
||||
case BO_NE: return translateBinOp(til::BOP_Neq, BO, Ctx);
|
||||
case BO_Cmp: return translateBinOp(til::BOP_Cmp, BO, Ctx);
|
||||
case BO_And: return translateBinOp(til::BOP_BitAnd, BO, Ctx);
|
||||
case BO_Xor: return translateBinOp(til::BOP_BitXor, BO, Ctx);
|
||||
case BO_Or: return translateBinOp(til::BOP_BitOr, BO, Ctx);
|
||||
|
|
|
@ -38,6 +38,7 @@ StringRef til::getBinaryOpcodeString(TIL_BinaryOpcode Op) {
|
|||
case BOP_Neq: return "!=";
|
||||
case BOP_Lt: return "<";
|
||||
case BOP_Leq: return "<=";
|
||||
case BOP_Cmp: return "<=>";
|
||||
case BOP_LogicAnd: return "&&";
|
||||
case BOP_LogicOr: return "||";
|
||||
}
|
||||
|
|
|
@ -63,6 +63,7 @@ prec::Level getBinOpPrecedence(tok::TokenKind Kind, bool GreaterThanIsOperator,
|
|||
case tok::lessequal:
|
||||
case tok::less:
|
||||
case tok::greaterequal: return prec::Relational;
|
||||
case tok::spaceship: return prec::Spaceship;
|
||||
case tok::lessless: return prec::Shift;
|
||||
case tok::plus:
|
||||
case tok::minus: return prec::Additive;
|
||||
|
|
|
@ -3922,6 +3922,7 @@ LValue CodeGenFunction::EmitCompoundAssignmentLValue(
|
|||
case BO_GE:
|
||||
case BO_EQ:
|
||||
case BO_NE:
|
||||
case BO_Cmp:
|
||||
case BO_And:
|
||||
case BO_Xor:
|
||||
case BO_Or:
|
||||
|
|
|
@ -3408,6 +3408,7 @@ static std::pair<bool, RValue> emitOMPAtomicRMW(CodeGenFunction &CGF, LValue X,
|
|||
case BO_GE:
|
||||
case BO_EQ:
|
||||
case BO_NE:
|
||||
case BO_Cmp:
|
||||
case BO_AddAssign:
|
||||
case BO_SubAssign:
|
||||
case BO_AndAssign:
|
||||
|
|
|
@ -323,7 +323,8 @@ bool ContinuationIndenter::mustBreak(const LineState &State) {
|
|||
// We need special cases for ">>" which we have split into two ">" while
|
||||
// lexing in order to make template parsing easier.
|
||||
bool IsComparison = (Previous.getPrecedence() == prec::Relational ||
|
||||
Previous.getPrecedence() == prec::Equality) &&
|
||||
Previous.getPrecedence() == prec::Equality ||
|
||||
Previous.getPrecedence() == prec::Spaceship) &&
|
||||
Previous.Previous &&
|
||||
Previous.Previous->isNot(TT_BinaryOperator); // For >>.
|
||||
bool LHSIsBinaryExpr =
|
||||
|
@ -536,7 +537,8 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
|
|||
(P->is(TT_ConditionalExpr) && P->is(tok::colon))) &&
|
||||
!P->isOneOf(TT_OverloadedOperator, TT_CtorInitializerComma) &&
|
||||
P->getPrecedence() != prec::Assignment &&
|
||||
P->getPrecedence() != prec::Relational) {
|
||||
P->getPrecedence() != prec::Relational &&
|
||||
P->getPrecedence() != prec::Spaceship) {
|
||||
bool BreakBeforeOperator =
|
||||
P->MustBreakBefore || P->is(tok::lessless) ||
|
||||
(P->is(TT_BinaryOperator) &&
|
||||
|
|
|
@ -2074,6 +2074,7 @@ LangOptions getFormattingLangOpts(const FormatStyle &Style) {
|
|||
LangOpts.CPlusPlus11 = Style.Standard == FormatStyle::LS_Cpp03 ? 0 : 1;
|
||||
LangOpts.CPlusPlus14 = Style.Standard == FormatStyle::LS_Cpp03 ? 0 : 1;
|
||||
LangOpts.CPlusPlus17 = Style.Standard == FormatStyle::LS_Cpp03 ? 0 : 1;
|
||||
LangOpts.CPlusPlus2a = Style.Standard == FormatStyle::LS_Cpp03 ? 0 : 1;
|
||||
LangOpts.LineComment = 1;
|
||||
bool AlternativeOperators = Style.isCpp();
|
||||
LangOpts.CXXOperatorNames = AlternativeOperators ? 1 : 0;
|
||||
|
|
|
@ -2153,7 +2153,7 @@ unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line,
|
|||
if (Left.isOneOf(tok::plus, tok::comma) && Left.Previous &&
|
||||
Left.Previous->isLabelString() &&
|
||||
(Left.NextOperator || Left.OperatorIndex != 0))
|
||||
return 45;
|
||||
return 50;
|
||||
if (Right.is(tok::plus) && Left.isLabelString() &&
|
||||
(Right.NextOperator || Right.OperatorIndex != 0))
|
||||
return 25;
|
||||
|
|
|
@ -66,12 +66,16 @@ using namespace clang;
|
|||
/// shift-expression '<<' additive-expression
|
||||
/// shift-expression '>>' additive-expression
|
||||
///
|
||||
/// relational-expression: [C99 6.5.8]
|
||||
/// compare-expression: [C++20 expr.spaceship]
|
||||
/// shift-expression
|
||||
/// relational-expression '<' shift-expression
|
||||
/// relational-expression '>' shift-expression
|
||||
/// relational-expression '<=' shift-expression
|
||||
/// relational-expression '>=' shift-expression
|
||||
/// compare-expression '<=>' shift-expression
|
||||
///
|
||||
/// relational-expression: [C99 6.5.8]
|
||||
/// compare-expression
|
||||
/// relational-expression '<' compare-expression
|
||||
/// relational-expression '>' compare-expression
|
||||
/// relational-expression '<=' compare-expression
|
||||
/// relational-expression '>=' compare-expression
|
||||
///
|
||||
/// equality-expression: [C99 6.5.9]
|
||||
/// relational-expression
|
||||
|
@ -267,7 +271,8 @@ bool Parser::diagnoseUnknownTemplateId(ExprResult LHS, SourceLocation Less) {
|
|||
}
|
||||
|
||||
bool Parser::isFoldOperator(prec::Level Level) const {
|
||||
return Level > prec::Unknown && Level != prec::Conditional;
|
||||
return Level > prec::Unknown && Level != prec::Conditional &&
|
||||
Level != prec::Spaceship;
|
||||
}
|
||||
|
||||
bool Parser::isFoldOperator(tok::TokenKind Kind) const {
|
||||
|
|
|
@ -7279,8 +7279,8 @@ static bool CheckMemorySizeofForComparison(Sema &S, const Expr *E,
|
|||
if (!Size)
|
||||
return false;
|
||||
|
||||
// if E is binop and op is >, <, >=, <=, ==, &&, ||:
|
||||
if (!Size->isComparisonOp() && !Size->isEqualityOp() && !Size->isLogicalOp())
|
||||
// if E is binop and op is <=>, >, <, >=, <=, ==, &&, ||:
|
||||
if (!Size->isComparisonOp() && !Size->isLogicalOp())
|
||||
return false;
|
||||
|
||||
SourceRange SizeRange = Size->getSourceRange();
|
||||
|
@ -8433,6 +8433,8 @@ static IntRange GetExprRange(ASTContext &C, const Expr *E, unsigned MaxWidth) {
|
|||
|
||||
if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
|
||||
switch (BO->getOpcode()) {
|
||||
case BO_Cmp:
|
||||
llvm_unreachable("builtin <=> should have class type");
|
||||
|
||||
// Boolean-valued operations are single-bit and positive.
|
||||
case BO_LAnd:
|
||||
|
@ -8747,9 +8749,18 @@ struct PromotedRange {
|
|||
llvm_unreachable("impossible compare result");
|
||||
}
|
||||
|
||||
static llvm::Optional<bool> constantValue(BinaryOperatorKind Op,
|
||||
ComparisonResult R,
|
||||
bool ConstantOnRHS) {
|
||||
static llvm::Optional<StringRef>
|
||||
constantValue(BinaryOperatorKind Op, ComparisonResult R, bool ConstantOnRHS) {
|
||||
if (Op == BO_Cmp) {
|
||||
ComparisonResult LTFlag = LT, GTFlag = GT;
|
||||
if (ConstantOnRHS) std::swap(LTFlag, GTFlag);
|
||||
|
||||
if (R & EQ) return StringRef("'std::strong_ordering::equal'");
|
||||
if (R & LTFlag) return StringRef("'std::strong_ordering::less'");
|
||||
if (R & GTFlag) return StringRef("'std::strong_ordering::greater'");
|
||||
return llvm::None;
|
||||
}
|
||||
|
||||
ComparisonResult TrueFlag, FalseFlag;
|
||||
if (Op == BO_EQ) {
|
||||
TrueFlag = EQ;
|
||||
|
@ -8769,9 +8780,9 @@ struct PromotedRange {
|
|||
std::swap(TrueFlag, FalseFlag);
|
||||
}
|
||||
if (R & TrueFlag)
|
||||
return true;
|
||||
return StringRef("true");
|
||||
if (R & FalseFlag)
|
||||
return false;
|
||||
return StringRef("false");
|
||||
return llvm::None;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -11375,6 +11375,7 @@ BinaryOperatorKind Sema::ConvertTokenKindToBinaryOpcode(tok::TokenKind Kind) {
|
|||
case tok::greater: Opc = BO_GT; break;
|
||||
case tok::exclaimequal: Opc = BO_NE; break;
|
||||
case tok::equalequal: Opc = BO_EQ; break;
|
||||
case tok::spaceship: Opc = BO_Cmp; break;
|
||||
case tok::amp: Opc = BO_And; break;
|
||||
case tok::caret: Opc = BO_Xor; break;
|
||||
case tok::pipe: Opc = BO_Or; break;
|
||||
|
@ -11683,6 +11684,13 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
|
|||
ConvertHalfVec = true;
|
||||
ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc, false);
|
||||
break;
|
||||
case BO_Cmp:
|
||||
// FIXME: Implement proper semantic checking of '<=>'.
|
||||
ConvertHalfVec = true;
|
||||
ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc, true);
|
||||
if (!ResultTy.isNull())
|
||||
ResultTy = Context.VoidTy;
|
||||
break;
|
||||
case BO_And:
|
||||
checkObjCPointerIntrospection(*this, LHS, RHS, OpLoc);
|
||||
LLVM_FALLTHROUGH;
|
||||
|
|
|
@ -10343,6 +10343,7 @@ static bool ActOnOMPReductionKindClause(
|
|||
case BO_GE:
|
||||
case BO_EQ:
|
||||
case BO_NE:
|
||||
case BO_Cmp:
|
||||
case BO_AndAssign:
|
||||
case BO_XorAssign:
|
||||
case BO_OrAssign:
|
||||
|
|
|
@ -127,34 +127,47 @@ void Sema::ActOnForEachDeclStmt(DeclGroupPtrTy dg) {
|
|||
/// warning from firing.
|
||||
static bool DiagnoseUnusedComparison(Sema &S, const Expr *E) {
|
||||
SourceLocation Loc;
|
||||
bool IsNotEqual, CanAssign, IsRelational;
|
||||
bool CanAssign;
|
||||
enum { Equality, Inequality, Relational, ThreeWay } Kind;
|
||||
|
||||
if (const BinaryOperator *Op = dyn_cast<BinaryOperator>(E)) {
|
||||
if (!Op->isComparisonOp())
|
||||
return false;
|
||||
|
||||
IsRelational = Op->isRelationalOp();
|
||||
if (Op->getOpcode() == BO_EQ)
|
||||
Kind = Equality;
|
||||
else if (Op->getOpcode() == BO_NE)
|
||||
Kind = Inequality;
|
||||
else if (Op->getOpcode() == BO_Cmp)
|
||||
Kind = ThreeWay;
|
||||
else {
|
||||
assert(Op->isRelationalOp());
|
||||
Kind = Relational;
|
||||
}
|
||||
Loc = Op->getOperatorLoc();
|
||||
IsNotEqual = Op->getOpcode() == BO_NE;
|
||||
CanAssign = Op->getLHS()->IgnoreParenImpCasts()->isLValue();
|
||||
} else if (const CXXOperatorCallExpr *Op = dyn_cast<CXXOperatorCallExpr>(E)) {
|
||||
switch (Op->getOperator()) {
|
||||
default:
|
||||
return false;
|
||||
case OO_EqualEqual:
|
||||
Kind = Equality;
|
||||
break;
|
||||
case OO_ExclaimEqual:
|
||||
IsRelational = false;
|
||||
Kind = Inequality;
|
||||
break;
|
||||
case OO_Less:
|
||||
case OO_Greater:
|
||||
case OO_GreaterEqual:
|
||||
case OO_LessEqual:
|
||||
IsRelational = true;
|
||||
Kind = Relational;
|
||||
break;
|
||||
case OO_Spaceship:
|
||||
Kind = ThreeWay;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
Loc = Op->getOperatorLoc();
|
||||
IsNotEqual = Op->getOperator() == OO_ExclaimEqual;
|
||||
CanAssign = Op->getArg(0)->IgnoreParenImpCasts()->isLValue();
|
||||
} else {
|
||||
// Not a typo-prone comparison.
|
||||
|
@ -167,15 +180,15 @@ static bool DiagnoseUnusedComparison(Sema &S, const Expr *E) {
|
|||
return false;
|
||||
|
||||
S.Diag(Loc, diag::warn_unused_comparison)
|
||||
<< (unsigned)IsRelational << (unsigned)IsNotEqual << E->getSourceRange();
|
||||
<< (unsigned)Kind << E->getSourceRange();
|
||||
|
||||
// If the LHS is a plausible entity to assign to, provide a fixit hint to
|
||||
// correct common typos.
|
||||
if (!IsRelational && CanAssign) {
|
||||
if (IsNotEqual)
|
||||
if (CanAssign) {
|
||||
if (Kind == Inequality)
|
||||
S.Diag(Loc, diag::note_inequality_comparison_to_or_assign)
|
||||
<< FixItHint::CreateReplacement(Loc, "|=");
|
||||
else
|
||||
else if (Kind == Equality)
|
||||
S.Diag(Loc, diag::note_equality_comparison_to_assign)
|
||||
<< FixItHint::CreateReplacement(Loc, "=");
|
||||
}
|
||||
|
|
|
@ -255,7 +255,10 @@ void FindIdenticalExprVisitor::checkComparisonOp(const BinaryOperator *B) {
|
|||
PathDiagnosticLocation ELoc =
|
||||
PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager());
|
||||
StringRef Message;
|
||||
if (((Op == BO_EQ) || (Op == BO_LE) || (Op == BO_GE)))
|
||||
if (Op == BO_Cmp)
|
||||
Message = "comparison of identical expressions always evaluates to "
|
||||
"'equal'";
|
||||
else if (((Op == BO_EQ) || (Op == BO_LE) || (Op == BO_GE)))
|
||||
Message = "comparison of identical expressions always evaluates to true";
|
||||
else
|
||||
Message = "comparison of identical expressions always evaluates to false";
|
||||
|
|
|
@ -1541,7 +1541,7 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr,
|
|||
// For non-assignment operations, we require that we can understand
|
||||
// both the LHS and RHS.
|
||||
if (LhsString.empty() || RhsString.empty() ||
|
||||
!BinaryOperator::isComparisonOp(Op))
|
||||
!BinaryOperator::isComparisonOp(Op) || Op == BO_Cmp)
|
||||
return nullptr;
|
||||
|
||||
// Should we invert the strings if the LHS is not a variable name?
|
||||
|
|
|
@ -396,7 +396,9 @@ bool RangeConstraintManager::canReasonAbout(SVal X) const {
|
|||
}
|
||||
|
||||
if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(SE)) {
|
||||
if (BinaryOperator::isComparisonOp(SSE->getOpcode())) {
|
||||
// FIXME: Handle <=> here.
|
||||
if (BinaryOperator::isEqualityOp(SSE->getOpcode()) ||
|
||||
BinaryOperator::isRelationalOp(SSE->getOpcode())) {
|
||||
// We handle Loc <> Loc comparisons, but not (yet) NonLoc <> NonLoc.
|
||||
if (Loc::isLocType(SSE->getLHS()->getType())) {
|
||||
assert(Loc::isLocType(SSE->getRHS()->getType()));
|
||||
|
|
|
@ -33,7 +33,7 @@ ProgramStateRef RangedConstraintManager::assumeSym(ProgramStateRef State,
|
|||
// We can only simplify expressions whose RHS is an integer.
|
||||
|
||||
BinaryOperator::Opcode op = SIE->getOpcode();
|
||||
if (BinaryOperator::isComparisonOp(op)) {
|
||||
if (BinaryOperator::isComparisonOp(op) && op != BO_Cmp) {
|
||||
if (!Assumption)
|
||||
op = BinaryOperator::negateComparisonOp(op);
|
||||
|
||||
|
|
|
@ -679,7 +679,7 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state,
|
|||
if (SymbolRef rSym = rhs.getAsLocSymbol()) {
|
||||
// We can only build expressions with symbols on the left,
|
||||
// so we need a reversible operator.
|
||||
if (!BinaryOperator::isComparisonOp(op))
|
||||
if (!BinaryOperator::isComparisonOp(op) || op == BO_Cmp)
|
||||
return UnknownVal();
|
||||
|
||||
const llvm::APSInt &lVal = lhs.castAs<loc::ConcreteInt>().getValue();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// RUN: %clang_cc1 -std=c++2a -emit-llvm %s -o - -triple %itanium_abi_triple | FileCheck %s --check-prefix=ITANIUM
|
||||
// RUN: not %clang_cc1 -std=c++2a -emit-llvm %s -o - -triple %ms_abi_triple 2>&1 | FileCheck %s --check-prefix=MSABI
|
||||
// RUN: not %clang_cc1 -std=c++2a -emit-llvm %s -o - -triple %itanium_abi_triple -DBUILTIN 2>&1 | FileCheck %s --check-prefix=BUILTIN
|
||||
// MSABI: cannot mangle this three-way comparison operator yet
|
||||
|
||||
struct A {
|
||||
|
@ -11,3 +12,18 @@ void A::operator<=>(int) {}
|
|||
|
||||
// ITANIUM: define {{.*}}@_Zssi1A(
|
||||
void operator<=>(int, A) {}
|
||||
|
||||
int operator<=>(A, A);
|
||||
|
||||
// ITANIUM: define {{.*}}_Z1f1A(
|
||||
int f(A a) {
|
||||
// ITANIUM: %[[RET:.*]] = call {{.*}}_Zss1AS_(
|
||||
// ITANIUM: ret i32 %[[RET]]
|
||||
return a <=> a;
|
||||
}
|
||||
|
||||
#ifdef BUILTIN
|
||||
void builtin(int a) {
|
||||
a <=> a; // BUILTIN: cannot compile this scalar expression yet
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
// RUN: %clang_cc1 -std=c++2a -verify %s
|
||||
|
||||
template<int> struct X {};
|
||||
|
||||
X<1> operator<<(X<0>, X<0>);
|
||||
X<2> operator<=>(X<0>, X<1>);
|
||||
X<2> operator<=>(X<1>, X<0>);
|
||||
X<3> operator<(X<0>, X<2>);
|
||||
X<3> operator<(X<2>, X<0>);
|
||||
|
||||
void f(X<0> x0, X<1> x1) {
|
||||
X<2> a = x0 <=> x0 << x0;
|
||||
X<2> b = x0 << x0 <=> x0; // expected-warning {{overloaded operator << has higher precedence than comparison operator}} expected-note 2{{}}
|
||||
X<3> c = x0 < x0 <=> x1;
|
||||
X<3> d = x1 <=> x0 < x0;
|
||||
X<3> e = x0 < x0 <=> x0 << x0;
|
||||
X<3> f = x0 << x0 <=> x0 < x0; // expected-warning {{overloaded operator << has higher precedence than comparison operator}} expected-note 2{{}}
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
// Force x86-64 because some of our heuristics are actually based
|
||||
// on integer sizes.
|
||||
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin -fsyntax-only -pedantic -verify -Wsign-compare -std=c++2a %s
|
||||
|
||||
void test0(long a, unsigned long b) {
|
||||
enum EnumA {A};
|
||||
enum EnumB {B};
|
||||
enum EnumC {C = 0x10000};
|
||||
// (a,b)
|
||||
|
||||
// FIXME: <=> should never produce -Wsign-compare warnings. All the possible error
|
||||
// cases involve narrowing conversions and so are ill-formed.
|
||||
(void)(a <=> (unsigned long) b); // expected-warning {{comparison of integers of different signs}}
|
||||
(void)(a <=> (unsigned int) b);
|
||||
(void)(a <=> (unsigned short) b);
|
||||
(void)(a <=> (unsigned char) b);
|
||||
(void)((long) a <=> b); // expected-warning {{comparison of integers of different signs}}
|
||||
(void)((int) a <=> b); // expected-warning {{comparison of integers of different signs}}
|
||||
(void)((short) a <=> b); // expected-warning {{comparison of integers of different signs}}
|
||||
(void)((signed char) a <=> b); // expected-warning {{comparison of integers of different signs}}
|
||||
(void)((long) a <=> (unsigned long) b); // expected-warning {{comparison of integers of different signs}}
|
||||
(void)((int) a <=> (unsigned int) b); // expected-warning {{comparison of integers of different signs}}
|
||||
(void)((short) a <=> (unsigned short) b);
|
||||
(void)((signed char) a <=> (unsigned char) b);
|
||||
|
||||
#if 0
|
||||
// (A,b)
|
||||
(void)(A <=> (unsigned long) b);
|
||||
(void)(A <=> (unsigned int) b);
|
||||
(void)(A <=> (unsigned short) b);
|
||||
(void)(A <=> (unsigned char) b);
|
||||
(void)((long) A <=> b);
|
||||
(void)((int) A <=> b);
|
||||
(void)((short) A <=> b);
|
||||
(void)((signed char) A <=> b);
|
||||
(void)((long) A <=> (unsigned long) b);
|
||||
(void)((int) A <=> (unsigned int) b);
|
||||
(void)((short) A <=> (unsigned short) b);
|
||||
(void)((signed char) A <=> (unsigned char) b);
|
||||
|
||||
// (a,B)
|
||||
(void)(a <=> (unsigned long) B);
|
||||
(void)(a <=> (unsigned int) B);
|
||||
(void)(a <=> (unsigned short) B);
|
||||
(void)(a <=> (unsigned char) B);
|
||||
(void)((long) a <=> B);
|
||||
(void)((int) a <=> B);
|
||||
(void)((short) a <=> B);
|
||||
(void)((signed char) a <=> B);
|
||||
(void)((long) a <=> (unsigned long) B);
|
||||
(void)((int) a <=> (unsigned int) B);
|
||||
(void)((short) a <=> (unsigned short) B);
|
||||
(void)((signed char) a <=> (unsigned char) B);
|
||||
|
||||
// (C,b)
|
||||
(void)(C <=> (unsigned long) b);
|
||||
(void)(C <=> (unsigned int) b);
|
||||
(void)(C <=> (unsigned short) b); // expected-warning {{comparison of constant 'C' (65536) with expression of type 'unsigned short' is always 'std::strong_ordering::greater'}}
|
||||
(void)(C <=> (unsigned char) b); // expected-warning {{comparison of constant 'C' (65536) with expression of type 'unsigned char' is always 'std::strong_ordering::greater'}}
|
||||
(void)((long) C <=> b);
|
||||
(void)((int) C <=> b);
|
||||
(void)((short) C <=> b);
|
||||
(void)((signed char) C <=> b);
|
||||
(void)((long) C <=> (unsigned long) b);
|
||||
(void)((int) C <=> (unsigned int) b);
|
||||
(void)((short) C <=> (unsigned short) b);
|
||||
(void)((signed char) C <=> (unsigned char) b);
|
||||
|
||||
// (a,C)
|
||||
(void)(a <=> (unsigned long) C);
|
||||
(void)(a <=> (unsigned int) C);
|
||||
(void)(a <=> (unsigned short) C);
|
||||
(void)(a <=> (unsigned char) C);
|
||||
(void)((long) a <=> C);
|
||||
(void)((int) a <=> C);
|
||||
(void)((short) a <=> C); // expected-warning {{comparison of constant 'C' (65536) with expression of type 'short' is always 'std::strong_ordering::less'}}
|
||||
(void)((signed char) a <=> C); // expected-warning {{comparison of constant 'C' (65536) with expression of type 'signed char' is always 'std::strong_ordering::less'}}
|
||||
(void)((long) a <=> (unsigned long) C);
|
||||
(void)((int) a <=> (unsigned int) C);
|
||||
(void)((short) a <=> (unsigned short) C);
|
||||
(void)((signed char) a <=> (unsigned char) C);
|
||||
#endif
|
||||
|
||||
// (0x80000,b)
|
||||
(void)(0x80000 <=> (unsigned long) b);
|
||||
(void)(0x80000 <=> (unsigned int) b);
|
||||
(void)(0x80000 <=> (unsigned short) b); // expected-warning {{result of comparison of constant 524288 with expression of type 'unsigned short' is always 'std::strong_ordering::greater'}}
|
||||
(void)(0x80000 <=> (unsigned char) b); // expected-warning {{result of comparison of constant 524288 with expression of type 'unsigned char' is always 'std::strong_ordering::greater'}}
|
||||
(void)((long) 0x80000 <=> b);
|
||||
(void)((int) 0x80000 <=> b);
|
||||
(void)((short) 0x80000 <=> b);
|
||||
(void)((signed char) 0x80000 <=> b);
|
||||
(void)((long) 0x80000 <=> (unsigned long) b);
|
||||
(void)((int) 0x80000 <=> (unsigned int) b);
|
||||
(void)((short) 0x80000 <=> (unsigned short) b);
|
||||
(void)((signed char) 0x80000 <=> (unsigned char) b);
|
||||
|
||||
// (a,0x80000)
|
||||
(void)(a <=> (unsigned long) 0x80000); // expected-warning {{comparison of integers of different signs}}
|
||||
(void)(a <=> (unsigned int) 0x80000);
|
||||
(void)(a <=> (unsigned short) 0x80000);
|
||||
(void)(a <=> (unsigned char) 0x80000);
|
||||
(void)((long) a <=> 0x80000);
|
||||
(void)((int) a <=> 0x80000);
|
||||
(void)((short) a <=> 0x80000); // expected-warning {{comparison of constant 524288 with expression of type 'short' is always 'std::strong_ordering::less'}}
|
||||
(void)((signed char) a <=> 0x80000); // expected-warning {{comparison of constant 524288 with expression of type 'signed char' is always 'std::strong_ordering::less'}}
|
||||
(void)((long) a <=> (unsigned long) 0x80000); // expected-warning {{comparison of integers of different signs}}
|
||||
(void)((int) a <=> (unsigned int) 0x80000); // expected-warning {{comparison of integers of different signs}}
|
||||
(void)((short) a <=> (unsigned short) 0x80000);
|
||||
(void)((signed char) a <=> (unsigned char) 0x80000);
|
||||
}
|
||||
|
||||
void test5(bool b) {
|
||||
(void) (b <=> -10); // expected-warning{{comparison of constant -10 with expression of type 'bool' is always 'std::strong_ordering::greater'}}
|
||||
(void) (b <=> -1); // expected-warning{{comparison of constant -1 with expression of type 'bool' is always 'std::strong_ordering::greater'}}
|
||||
(void) (b <=> 0);
|
||||
(void) (b <=> 1);
|
||||
(void) (b <=> 2); // expected-warning{{comparison of constant 2 with expression of type 'bool' is always 'std::strong_ordering::less'}}
|
||||
(void) (b <=> 10); // expected-warning{{comparison of constant 10 with expression of type 'bool' is always 'std::strong_ordering::less'}}
|
||||
}
|
||||
|
||||
void test6(signed char sc) {
|
||||
(void)(sc <=> 200); // expected-warning{{comparison of constant 200 with expression of type 'signed char' is always 'std::strong_ordering::less'}}
|
||||
(void)(200 <=> sc); // expected-warning{{comparison of constant 200 with expression of type 'signed char' is always 'std::strong_ordering::greater'}}
|
||||
}
|
||||
|
||||
// Test many signedness combinations.
|
||||
void test7(unsigned long other) {
|
||||
// Common unsigned, other unsigned, constant unsigned
|
||||
(void)((unsigned)other <=> (unsigned long)(0x1'ffff'ffff)); // expected-warning{{less}}
|
||||
(void)((unsigned)other <=> (unsigned long)(0xffff'ffff));
|
||||
(void)((unsigned long)other <=> (unsigned)(0x1'ffff'ffff));
|
||||
(void)((unsigned long)other <=> (unsigned)(0xffff'ffff));
|
||||
|
||||
// Common unsigned, other signed, constant unsigned
|
||||
(void)((int)other <=> (unsigned long)(0xffff'ffff'ffff'ffff)); // expected-warning{{different signs}}
|
||||
(void)((int)other <=> (unsigned long)(0x0000'0000'ffff'ffff)); // expected-warning{{different signs}}
|
||||
(void)((int)other <=> (unsigned long)(0x0000'0000'0fff'ffff)); // expected-warning{{different signs}}
|
||||
(void)((int)other <=> (unsigned)(0x8000'0000)); // expected-warning{{different signs}}
|
||||
|
||||
// Common unsigned, other unsigned, constant signed
|
||||
(void)((unsigned long)other <=> (int)(0xffff'ffff)); // expected-warning{{different signs}}
|
||||
|
||||
// Common unsigned, other signed, constant signed
|
||||
// Should not be possible as the common type should also be signed.
|
||||
|
||||
// Common signed, other signed, constant signed
|
||||
(void)((int)other <=> (long)(0xffff'ffff)); // expected-warning{{less}}
|
||||
(void)((int)other <=> (long)(0xffff'ffff'0000'0000)); // expected-warning{{greater}}
|
||||
(void)((int)other <=> (long)(0x0fff'ffff));
|
||||
(void)((int)other <=> (long)(0xffff'ffff'f000'0000));
|
||||
|
||||
// Common signed, other signed, constant unsigned
|
||||
(void)((int)other <=> (unsigned char)(0xffff));
|
||||
(void)((int)other <=> (unsigned char)(0xff));
|
||||
|
||||
// Common signed, other unsigned, constant signed
|
||||
(void)((unsigned char)other <=> (int)(0xff));
|
||||
(void)((unsigned char)other <=> (int)(0xffff)); // expected-warning{{less}}
|
||||
|
||||
// Common signed, other unsigned, constant unsigned
|
||||
(void)((unsigned char)other <=> (unsigned short)(0xff));
|
||||
(void)((unsigned char)other <=> (unsigned short)(0x100)); // expected-warning{{less}}
|
||||
(void)((unsigned short)other <=> (unsigned char)(0xff));
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// RUN: %clang_cc1 -std=c++2a -verify %s -fcxx-exceptions -triple=x86_64-linux-gnu
|
||||
|
||||
namespace ThreeWayComparison {
|
||||
struct A {
|
||||
int n;
|
||||
constexpr friend int operator<=>(const A &a, const A &b) {
|
||||
return a.n < b.n ? -1 : a.n > b.n ? 1 : 0;
|
||||
}
|
||||
};
|
||||
static_assert(A{1} <=> A{2} < 0);
|
||||
static_assert(A{2} <=> A{1} > 0);
|
||||
static_assert(A{2} <=> A{2} == 0);
|
||||
|
||||
// Note: not yet supported.
|
||||
static_assert(1 <=> 2 < 0); // expected-error {{invalid operands}}
|
||||
static_assert(2 <=> 1 > 0); // expected-error {{invalid operands}}
|
||||
static_assert(1 <=> 1 == 0); // expected-error {{invalid operands}}
|
||||
constexpr int k = (1 <=> 1, 0);
|
||||
// expected-error@-1 {{constexpr variable 'k' must be initialized by a constant expression}}
|
||||
// expected-warning@-2 {{three-way comparison result unused}}
|
||||
|
||||
constexpr void f() { // expected-error {{constant expression}}
|
||||
void(1 <=> 1); // expected-note {{constant expression}}
|
||||
}
|
||||
|
||||
// TODO: defaulted operator <=>
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
// RUN: %clang_cc1 -std=c++1z -verify %s
|
||||
// RUN: %clang_cc1 -std=c++2a -verify %s
|
||||
|
||||
template<typename ...T> constexpr auto sum(T ...t) { return (... + t); }
|
||||
template<typename ...T> constexpr auto product(T ...t) { return (t * ...); }
|
||||
|
@ -76,3 +77,18 @@ template<typename T, typename ...Ts> constexpr decltype(auto) apply(T &t, Ts ...
|
|||
return (t.*....*ts);
|
||||
}
|
||||
static_assert(&apply(a, &A::b, &A::B::c, &A::B::C::d, &A::B::C::D::e) == &a.b.c.d.e);
|
||||
|
||||
#if __cplusplus > 201703L
|
||||
// The <=> operator is unique among binary operators in not being a
|
||||
// fold-operator.
|
||||
// FIXME: This diagnostic is not great.
|
||||
template<typename ...T> constexpr auto spaceship1(T ...t) { return (t <=> ...); } // expected-error {{expected expression}}
|
||||
template<typename ...T> constexpr auto spaceship2(T ...t) { return (... <=> t); } // expected-error {{expected expression}}
|
||||
template<typename ...T> constexpr auto spaceship3(T ...t) { return (t <=> ... <=> 0); } // expected-error {{expected expression}}
|
||||
#endif
|
||||
|
||||
// The GNU binary conditional operator ?: is not recognized as a fold-operator.
|
||||
// FIXME: Why not? This seems like it would be useful.
|
||||
template<typename ...T> constexpr auto binary_conditional1(T ...t) { return (t ?: ...); } // expected-error {{expected expression}}
|
||||
template<typename ...T> constexpr auto binary_conditional2(T ...t) { return (... ?: t); } // expected-error {{expected expression}}
|
||||
template<typename ...T> constexpr auto binary_conditional3(T ...t) { return (t ?: ... ?: 0); } // expected-error {{expected expression}}
|
||||
|
|
|
@ -3072,6 +3072,10 @@ TEST_F(FormatTest, LineBreakingInBinaryExpressions) {
|
|||
"if (aaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaa(\n"
|
||||
" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) == 5) {\n"
|
||||
"}");
|
||||
verifyFormat(
|
||||
"if (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n"
|
||||
" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) <=> 5) {\n"
|
||||
"}");
|
||||
// Even explicit parentheses stress the precedence enough to make the
|
||||
// additional break unnecessary.
|
||||
verifyFormat("if ((aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +\n"
|
||||
|
@ -3091,6 +3095,10 @@ TEST_F(FormatTest, LineBreakingInBinaryExpressions) {
|
|||
" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ==\n"
|
||||
" 5) {\n"
|
||||
"}");
|
||||
verifyFormat("if (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +\n"
|
||||
" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa <=>\n"
|
||||
" 5) {\n"
|
||||
"}");
|
||||
|
||||
FormatStyle OnePerLine = getLLVMStyle();
|
||||
OnePerLine.BinPackParameters = false;
|
||||
|
|
Loading…
Reference in New Issue