aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2019-10-19 00:04:38 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2019-10-19 00:04:38 +0000
commitea521aa6024431138f7890d486a6e3500822b3d5 (patch)
treefdac644533e0b19bd5e54c53170aaea83d86f668
parent3b03365d5fddd6485581747d7a7ba1799d94241f (diff)
downloadclang-ea521aa6024431138f7890d486a6e3500822b3d5.tar.gz
[c++20] Add CXXRewrittenBinaryOperator to represent a comparison
operator that is rewritten as a call to multiple other operators. No functionality change yet: nothing creates these expressions. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@375305 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/AST/ExprCXX.h90
-rw-r--r--include/clang/AST/RecursiveASTVisitor.h9
-rw-r--r--include/clang/AST/Stmt.h10
-rw-r--r--include/clang/Basic/StmtNodes.td1
-rw-r--r--include/clang/Serialization/ASTBitCodes.h3
-rw-r--r--lib/AST/Expr.cpp1
-rw-r--r--lib/AST/ExprCXX.cpp70
-rw-r--r--lib/AST/ExprClassification.cpp4
-rw-r--r--lib/AST/ExprConstant.cpp7
-rw-r--r--lib/AST/ItaniumMangle.cpp11
-rw-r--r--lib/AST/StmtPrinter.cpp9
-rw-r--r--lib/AST/StmtProfile.cpp10
-rw-r--r--lib/CodeGen/CGExpr.cpp2
-rw-r--r--lib/CodeGen/CGExprAgg.cpp3
-rw-r--r--lib/CodeGen/CGExprComplex.cpp4
-rw-r--r--lib/CodeGen/CGExprScalar.cpp4
-rw-r--r--lib/Sema/SemaExceptionSpec.cpp1
-rw-r--r--lib/Sema/TreeTransform.h34
-rw-r--r--lib/Serialization/ASTReaderStmt.cpp11
-rw-r--r--lib/Serialization/ASTWriter.cpp1
-rw-r--r--lib/Serialization/ASTWriterStmt.cpp8
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngine.cpp1
-rw-r--r--tools/libclang/CXCursor.cpp1
23 files changed, 295 insertions, 0 deletions
diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h
index 9c05de6fdc..2b3085183c 100644
--- a/include/clang/AST/ExprCXX.h
+++ b/include/clang/AST/ExprCXX.h
@@ -220,6 +220,96 @@ public:
}
};
+/// A rewritten comparison expression that was originally written using
+/// operator syntax.
+///
+/// In C++20, the following rewrites are performed:
+/// - <tt>a == b</tt> -> <tt>b == a</tt>
+/// - <tt>a != b</tt> -> <tt>!(a == b)</tt>
+/// - <tt>a != b</tt> -> <tt>!(b == a)</tt>
+/// - For \c \@ in \c <, \c <=, \c >, \c >=, \c <=>:
+/// - <tt>a @ b<tt> -> <tt>(a <=> b) @ 0</tt>
+/// - <tt>a @ b<tt> -> <tt>0 @ (b <=> a)</tt>
+///
+/// This expression provides access to both the original syntax and the
+/// rewritten expression.
+///
+/// Note that the rewritten calls to \c ==, \c <=>, and \c \@ are typically
+/// \c CXXOperatorCallExprs, but could theoretically be \c BinaryOperators.
+class CXXRewrittenBinaryOperator : public Expr {
+ friend class ASTStmtReader;
+
+ /// The rewritten semantic form.
+ Stmt *SemanticForm;
+
+public:
+ CXXRewrittenBinaryOperator(Expr *SemanticForm, bool IsReversed)
+ : Expr(CXXRewrittenBinaryOperatorClass, SemanticForm->getType(),
+ SemanticForm->getValueKind(), SemanticForm->getObjectKind(),
+ SemanticForm->isTypeDependent(), SemanticForm->isValueDependent(),
+ SemanticForm->isInstantiationDependent(),
+ SemanticForm->containsUnexpandedParameterPack()),
+ SemanticForm(SemanticForm) {
+ CXXRewrittenBinaryOperatorBits.IsReversed = IsReversed;
+ }
+ CXXRewrittenBinaryOperator(EmptyShell Empty)
+ : Expr(CXXRewrittenBinaryOperatorClass, Empty), SemanticForm() {}
+
+ /// Get an equivalent semantic form for this expression.
+ Expr *getSemanticForm() { return cast<Expr>(SemanticForm); }
+ const Expr *getSemanticForm() const { return cast<Expr>(SemanticForm); }
+
+ struct DecomposedForm {
+ /// The original opcode, prior to rewriting.
+ BinaryOperatorKind Opcode;
+ /// The original left-hand side.
+ const Expr *LHS;
+ /// The original right-hand side.
+ const Expr *RHS;
+ /// The inner \c == or \c <=> operator expression.
+ const Expr *InnerBinOp;
+ };
+
+ /// Decompose this operator into its syntactic form.
+ DecomposedForm getDecomposedForm() const LLVM_READONLY;
+
+ /// Determine whether this expression was rewritten in reverse form.
+ bool isReversed() const { return CXXRewrittenBinaryOperatorBits.IsReversed; }
+
+ BinaryOperatorKind getOperator() const { return getDecomposedForm().Opcode; }
+ const Expr *getLHS() const { return getDecomposedForm().LHS; }
+ const Expr *getRHS() const { return getDecomposedForm().RHS; }
+
+ SourceLocation getOperatorLoc() const LLVM_READONLY {
+ return getDecomposedForm().InnerBinOp->getExprLoc();
+ }
+ SourceLocation getExprLoc() const LLVM_READONLY { return getOperatorLoc(); }
+
+ /// Compute the begin and end locations from the decomposed form.
+ /// The locations of the semantic form are not reliable if this is
+ /// a reversed expression.
+ //@{
+ SourceLocation getBeginLoc() const LLVM_READONLY {
+ return getDecomposedForm().LHS->getBeginLoc();
+ }
+ SourceLocation getEndLoc() const LLVM_READONLY {
+ return getDecomposedForm().RHS->getEndLoc();
+ }
+ SourceRange getSourceRange() const LLVM_READONLY {
+ DecomposedForm DF = getDecomposedForm();
+ return SourceRange(DF.LHS->getBeginLoc(), DF.RHS->getEndLoc());
+ }
+ //@}
+
+ child_range children() {
+ return child_range(&SemanticForm, &SemanticForm + 1);
+ }
+
+ static bool classof(const Stmt *T) {
+ return T->getStmtClass() == CXXRewrittenBinaryOperatorClass;
+ }
+};
+
/// Represents a call to a CUDA kernel function.
class CUDAKernelCallExpr final : public CallExpr {
friend class ASTStmtReader;
diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h
index c01a2da926..5b58eab95d 100644
--- a/include/clang/AST/RecursiveASTVisitor.h
+++ b/include/clang/AST/RecursiveASTVisitor.h
@@ -2606,6 +2606,15 @@ DEF_TRAVERSE_STMT(SEHLeaveStmt, {})
DEF_TRAVERSE_STMT(CapturedStmt, { TRY_TO(TraverseDecl(S->getCapturedDecl())); })
DEF_TRAVERSE_STMT(CXXOperatorCallExpr, {})
+DEF_TRAVERSE_STMT(CXXRewrittenBinaryOperator, {
+ if (!getDerived().shouldVisitImplicitCode()) {
+ CXXRewrittenBinaryOperator::DecomposedForm Decomposed =
+ S->getDecomposedForm();
+ TRY_TO(TraverseStmt(const_cast<Expr*>(Decomposed.LHS)));
+ TRY_TO(TraverseStmt(const_cast<Expr*>(Decomposed.RHS)));
+ ShouldVisitChildren = false;
+ }
+})
DEF_TRAVERSE_STMT(OpaqueValueExpr, {})
DEF_TRAVERSE_STMT(TypoExpr, {})
DEF_TRAVERSE_STMT(CUDAKernelCallExpr, {})
diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h
index 403b88ac3a..7aebbf2cb6 100644
--- a/include/clang/AST/Stmt.h
+++ b/include/clang/AST/Stmt.h
@@ -604,6 +604,15 @@ protected:
unsigned FPFeatures : 3;
};
+ class CXXRewrittenBinaryOperatorBitfields {
+ friend class ASTStmtReader;
+ friend class CXXRewrittenBinaryOperator;
+
+ unsigned : NumCallExprBits;
+
+ unsigned IsReversed : 1;
+ };
+
class CXXBoolLiteralExprBitfields {
friend class CXXBoolLiteralExpr;
@@ -978,6 +987,7 @@ protected:
// C++ Expressions
CXXOperatorCallExprBitfields CXXOperatorCallExprBits;
+ CXXRewrittenBinaryOperatorBitfields CXXRewrittenBinaryOperatorBits;
CXXBoolLiteralExprBitfields CXXBoolLiteralExprBits;
CXXNullPtrLiteralExprBitfields CXXNullPtrLiteralExprBits;
CXXThisExprBitfields CXXThisExprBits;
diff --git a/include/clang/Basic/StmtNodes.td b/include/clang/Basic/StmtNodes.td
index 8d972f75e4..59444b2919 100644
--- a/include/clang/Basic/StmtNodes.td
+++ b/include/clang/Basic/StmtNodes.td
@@ -114,6 +114,7 @@ def GNUNullExpr : DStmt<Expr>;
// C++ Expressions.
def CXXOperatorCallExpr : DStmt<CallExpr>;
def CXXMemberCallExpr : DStmt<CallExpr>;
+def CXXRewrittenBinaryOperator : DStmt<Expr>;
def CXXNamedCastExpr : DStmt<ExplicitCastExpr, 1>;
def CXXStaticCastExpr : DStmt<CXXNamedCastExpr>;
def CXXDynamicCastExpr : DStmt<CXXNamedCastExpr>;
diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h
index b95c281a58..f310572754 100644
--- a/include/clang/Serialization/ASTBitCodes.h
+++ b/include/clang/Serialization/ASTBitCodes.h
@@ -1845,6 +1845,9 @@ namespace serialization {
/// A CXXMemberCallExpr record.
EXPR_CXX_MEMBER_CALL,
+ /// A CXXRewrittenBinaryOperator record.
+ EXPR_CXX_REWRITTEN_BINARY_OPERATOR,
+
/// A CXXConstructExpr record.
EXPR_CXX_CONSTRUCT,
diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp
index f2e625124c..3438c3aadc 100644
--- a/lib/AST/Expr.cpp
+++ b/lib/AST/Expr.cpp
@@ -3473,6 +3473,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
case ArrayInitLoopExprClass:
case ParenListExprClass:
case CXXPseudoDestructorExprClass:
+ case CXXRewrittenBinaryOperatorClass:
case CXXStdInitializerListExprClass:
case SubstNonTypeTemplateParmExprClass:
case MaterializeTemporaryExprClass:
diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp
index a9b7c00a8b..0b0d3c6880 100644
--- a/lib/AST/ExprCXX.cpp
+++ b/lib/AST/ExprCXX.cpp
@@ -58,6 +58,76 @@ bool CXXOperatorCallExpr::isInfixBinaryOp() const {
}
}
+CXXRewrittenBinaryOperator::DecomposedForm
+CXXRewrittenBinaryOperator::getDecomposedForm() const {
+ DecomposedForm Result = {};
+ const Expr *E = getSemanticForm()->IgnoreImplicit();
+
+ // Remove an outer '!' if it exists (only happens for a '!=' rewrite).
+ bool SkippedNot = false;
+ if (auto *NotEq = dyn_cast<UnaryOperator>(E)) {
+ assert(NotEq->getOpcode() == UO_LNot);
+ E = NotEq->getSubExpr()->IgnoreImplicit();
+ SkippedNot = true;
+ }
+
+ // Decompose the outer binary operator.
+ if (auto *BO = dyn_cast<BinaryOperator>(E)) {
+ assert(!SkippedNot || BO->getOpcode() == BO_EQ);
+ Result.Opcode = SkippedNot ? BO_NE : BO->getOpcode();
+ Result.LHS = BO->getLHS();
+ Result.RHS = BO->getRHS();
+ Result.InnerBinOp = BO;
+ } else if (auto *BO = dyn_cast<CXXOperatorCallExpr>(E)) {
+ assert(!SkippedNot || BO->getOperator() == OO_Equal);
+ assert(BO->isInfixBinaryOp());
+ switch (BO->getOperator()) {
+ case OO_Less: Result.Opcode = BO_LT; break;
+ case OO_LessEqual: Result.Opcode = BO_LE; break;
+ case OO_Greater: Result.Opcode = BO_GT; break;
+ case OO_GreaterEqual: Result.Opcode = BO_GE; break;
+ case OO_Spaceship: Result.Opcode = BO_Cmp; break;
+ case OO_EqualEqual: Result.Opcode = SkippedNot ? BO_NE : BO_EQ; break;
+ default: llvm_unreachable("unexpected binop in rewritten operator expr");
+ }
+ Result.LHS = BO->getArg(0);
+ Result.RHS = BO->getArg(1);
+ Result.InnerBinOp = BO;
+ } else {
+ llvm_unreachable("unexpected rewritten operator form");
+ }
+
+ // Put the operands in the right order for == and !=, and canonicalize the
+ // <=> subexpression onto the LHS for all other forms.
+ if (isReversed())
+ std::swap(Result.LHS, Result.RHS);
+
+ // If this isn't a spaceship rewrite, we're done.
+ if (Result.Opcode == BO_EQ || Result.Opcode == BO_NE)
+ return Result;
+
+ // Otherwise, we expect a <=> to now be on the LHS.
+ E = Result.InnerBinOp->IgnoreImplicit();
+ if (auto *BO = dyn_cast<BinaryOperator>(E)) {
+ assert(BO->getOpcode() == BO_Cmp);
+ Result.LHS = BO->getLHS();
+ Result.RHS = BO->getRHS();
+ Result.InnerBinOp = BO;
+ } else if (auto *BO = dyn_cast<CXXOperatorCallExpr>(E)) {
+ assert(BO->getOperator() == OO_Spaceship);
+ Result.LHS = BO->getArg(0);
+ Result.RHS = BO->getArg(1);
+ Result.InnerBinOp = BO;
+ } else {
+ llvm_unreachable("unexpected rewritten operator form");
+ }
+
+ // Put the comparison operands in the right order.
+ if (isReversed())
+ std::swap(Result.LHS, Result.RHS);
+ return Result;
+}
+
bool CXXTypeidExpr::isPotentiallyEvaluated() const {
if (isTypeOperand())
return false;
diff --git a/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp
index d109cd3d03..9dbf6fe9e0 100644
--- a/lib/AST/ExprClassification.cpp
+++ b/lib/AST/ExprClassification.cpp
@@ -307,6 +307,10 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
case Expr::CUDAKernelCallExprClass:
return ClassifyUnnamed(Ctx, cast<CallExpr>(E)->getCallReturnType(Ctx));
+ case Expr::CXXRewrittenBinaryOperatorClass:
+ return ClassifyInternal(
+ Ctx, cast<CXXRewrittenBinaryOperator>(E)->getSemanticForm());
+
// __builtin_choose_expr is equivalent to the chosen expression.
case Expr::ChooseExprClass:
return ClassifyInternal(Ctx, cast<ChooseExpr>(E)->getChosenSubExpr());
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index a379a335b2..42c746e602 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -6765,6 +6765,10 @@ public:
}
}
+ bool VisitCXXRewrittenBinaryOperator(const CXXRewrittenBinaryOperator *E) {
+ return StmtVisitorTy::Visit(E->getSemanticForm());
+ }
+
bool VisitBinaryConditionalOperator(const BinaryConditionalOperator *E) {
// Evaluate and cache the common expression. We treat it as a temporary,
// even though it's not quite the same thing.
@@ -13945,6 +13949,9 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
return CheckEvalInICE(E, Ctx);
return ICEDiag(IK_NotICE, E->getBeginLoc());
}
+ case Expr::CXXRewrittenBinaryOperatorClass:
+ return CheckICE(cast<CXXRewrittenBinaryOperator>(E)->getSemanticForm(),
+ Ctx);
case Expr::DeclRefExprClass: {
if (isa<EnumConstantDecl>(cast<DeclRefExpr>(E)->getDecl()))
return NoDiag();
diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp
index 534a92d2cb..c55a901375 100644
--- a/lib/AST/ItaniumMangle.cpp
+++ b/lib/AST/ItaniumMangle.cpp
@@ -4090,6 +4090,17 @@ recurse:
break;
}
+ case Expr::CXXRewrittenBinaryOperatorClass: {
+ // The mangled form represents the original syntax.
+ CXXRewrittenBinaryOperator::DecomposedForm Decomposed =
+ cast<CXXRewrittenBinaryOperator>(E)->getDecomposedForm();
+ mangleOperatorName(BinaryOperator::getOverloadedOperator(Decomposed.Opcode),
+ /*Arity=*/2);
+ mangleExpression(Decomposed.LHS);
+ mangleExpression(Decomposed.RHS);
+ break;
+ }
+
case Expr::ConditionalOperatorClass: {
const ConditionalOperator *CO = cast<ConditionalOperator>(E);
mangleOperatorName(OO_Conditional, /*Arity=*/3);
diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp
index 5b92ca0ac9..7759ff6c13 100644
--- a/lib/AST/StmtPrinter.cpp
+++ b/lib/AST/StmtPrinter.cpp
@@ -1697,6 +1697,15 @@ void StmtPrinter::VisitCUDAKernelCallExpr(CUDAKernelCallExpr *Node) {
OS << ")";
}
+void StmtPrinter::VisitCXXRewrittenBinaryOperator(
+ CXXRewrittenBinaryOperator *Node) {
+ CXXRewrittenBinaryOperator::DecomposedForm Decomposed =
+ Node->getDecomposedForm();
+ PrintExpr(const_cast<Expr*>(Decomposed.LHS));
+ OS << ' ' << BinaryOperator::getOpcodeStr(Decomposed.Opcode) << ' ';
+ PrintExpr(const_cast<Expr*>(Decomposed.RHS));
+}
+
void StmtPrinter::VisitCXXNamedCastExpr(CXXNamedCastExpr *Node) {
OS << Node->getCastName() << '<';
Node->getTypeAsWritten().print(OS, Policy);
diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp
index 0c4916e6ab..d1e8565389 100644
--- a/lib/AST/StmtProfile.cpp
+++ b/lib/AST/StmtProfile.cpp
@@ -1557,6 +1557,16 @@ void StmtProfiler::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *S) {
ID.AddInteger(S->getOperator());
}
+void StmtProfiler::VisitCXXRewrittenBinaryOperator(
+ const CXXRewrittenBinaryOperator *S) {
+ // If a rewritten operator were ever to be type-dependent, we should profile
+ // it following its syntactic operator.
+ assert(!S->isTypeDependent() &&
+ "resolved rewritten operator should never be type-dependent");
+ ID.AddBoolean(S->isReversed());
+ VisitExpr(S->getSemanticForm());
+}
+
#if defined(_MSC_VER) && !defined(__clang__)
#if _MSC_VER == 1911
#pragma optimize("", on)
diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp
index 2bd1b0ba7e..dcd365c8ea 100644
--- a/lib/CodeGen/CGExpr.cpp
+++ b/lib/CodeGen/CGExpr.cpp
@@ -1267,6 +1267,8 @@ LValue CodeGenFunction::EmitLValue(const Expr *E) {
case Expr::CXXOperatorCallExprClass:
case Expr::UserDefinedLiteralClass:
return EmitCallExprLValue(cast<CallExpr>(E));
+ case Expr::CXXRewrittenBinaryOperatorClass:
+ return EmitLValue(cast<CXXRewrittenBinaryOperator>(E)->getSemanticForm());
case Expr::VAArgExprClass:
return EmitVAArgExprLValue(cast<VAArgExpr>(E));
case Expr::DeclRefExprClass:
diff --git a/lib/CodeGen/CGExprAgg.cpp b/lib/CodeGen/CGExprAgg.cpp
index ab51512ef9..2f0e493761 100644
--- a/lib/CodeGen/CGExprAgg.cpp
+++ b/lib/CodeGen/CGExprAgg.cpp
@@ -150,6 +150,9 @@ public:
void VisitBinAssign(const BinaryOperator *E);
void VisitBinComma(const BinaryOperator *E);
void VisitBinCmp(const BinaryOperator *E);
+ void VisitCXXRewrittenBinaryOperator(CXXRewrittenBinaryOperator *E) {
+ Visit(E->getSemanticForm());
+ }
void VisitObjCMessageExpr(ObjCMessageExpr *E);
void VisitObjCIvarRefExpr(ObjCIvarRefExpr *E) {
diff --git a/lib/CodeGen/CGExprComplex.cpp b/lib/CodeGen/CGExprComplex.cpp
index 6a5fb45ba2..385f87f12a 100644
--- a/lib/CodeGen/CGExprComplex.cpp
+++ b/lib/CodeGen/CGExprComplex.cpp
@@ -279,6 +279,10 @@ public:
return EmitBinDiv(EmitBinOps(E));
}
+ ComplexPairTy VisitCXXRewrittenBinaryOperator(CXXRewrittenBinaryOperator *E) {
+ return Visit(E->getSemanticForm());
+ }
+
// Compound assignments.
ComplexPairTy VisitBinAddAssign(const CompoundAssignOperator *E) {
return EmitCompoundAssign(E, &ComplexExprEmitter::EmitBinAdd);
diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp
index f1396137d7..55a413a2a7 100644
--- a/lib/CodeGen/CGExprScalar.cpp
+++ b/lib/CodeGen/CGExprScalar.cpp
@@ -817,6 +817,10 @@ public:
Value *VisitBinPtrMemD(const Expr *E) { return EmitLoadOfLValue(E); }
Value *VisitBinPtrMemI(const Expr *E) { return EmitLoadOfLValue(E); }
+ Value *VisitCXXRewrittenBinaryOperator(CXXRewrittenBinaryOperator *E) {
+ return Visit(E->getSemanticForm());
+ }
+
// Other Operators.
Value *VisitBlockExpr(const BlockExpr *BE);
Value *VisitAbstractConditionalOperator(const AbstractConditionalOperator *);
diff --git a/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp
index 76fd10d2e6..b334ba568a 100644
--- a/lib/Sema/SemaExceptionSpec.cpp
+++ b/lib/Sema/SemaExceptionSpec.cpp
@@ -1201,6 +1201,7 @@ CanThrowResult Sema::canThrow(const Expr *E) {
case Expr::CoyieldExprClass:
case Expr::CXXConstCastExprClass:
case Expr::CXXReinterpretCastExprClass:
+ case Expr::CXXRewrittenBinaryOperatorClass:
case Expr::BuiltinBitCastExprClass:
case Expr::CXXStdInitializerListExprClass:
case Expr::DesignatedInitExprClass:
diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h
index f8549471c5..461cd909c2 100644
--- a/lib/Sema/TreeTransform.h
+++ b/lib/Sema/TreeTransform.h
@@ -2353,6 +2353,17 @@ public:
return getSema().BuildBinOp(/*Scope=*/nullptr, OpLoc, Opc, LHS, RHS);
}
+ /// Build a new rewritten operator expression.
+ ///
+ /// By default, builds the rewritten operator without performing any semantic
+ /// analysis. Subclasses may override this routine to provide different
+ /// behavior.
+ ExprResult RebuildCXXRewrittenBinaryOperator(Expr *SemanticForm,
+ bool IsReversed) {
+ return new (getSema().Context)
+ CXXRewrittenBinaryOperator(SemanticForm, IsReversed);
+ }
+
/// Build a new conditional operator expression.
///
/// By default, performs semantic analysis to build the new expression.
@@ -9769,6 +9780,29 @@ TreeTransform<Derived>::TransformBinaryOperator(BinaryOperator *E) {
LHS.get(), RHS.get());
}
+template <typename Derived>
+ExprResult TreeTransform<Derived>::TransformCXXRewrittenBinaryOperator(
+ CXXRewrittenBinaryOperator *E) {
+ // FIXME: C++ [temp.deduct]p7 "The substitution proceeds in lexical order and
+ // stops when a condition that causes deduction to fail is encountered."
+ // requires us to substitute into the LHS before the RHS, even in a rewrite
+ // that reversed the operand order.
+ //
+ // We can't decompose back to a binary operator here, because that would lose
+ // the unqualified lookup results from the phase 1 name lookup.
+
+ ExprResult SemanticForm = getDerived().TransformExpr(E->getSemanticForm());
+ if (SemanticForm.isInvalid())
+ return ExprError();
+
+ if (!getDerived().AlwaysRebuild() &&
+ SemanticForm.get() == E->getSemanticForm())
+ return E;
+
+ return getDerived().RebuildCXXRewrittenBinaryOperator(SemanticForm.get(),
+ E->isReversed());
+}
+
template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformCompoundAssignOperator(
diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp
index 509e206c81..a275e0c305 100644
--- a/lib/Serialization/ASTReaderStmt.cpp
+++ b/lib/Serialization/ASTReaderStmt.cpp
@@ -1440,6 +1440,13 @@ void ASTStmtReader::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E) {
E->Range = Record.readSourceRange();
}
+void ASTStmtReader::VisitCXXRewrittenBinaryOperator(
+ CXXRewrittenBinaryOperator *E) {
+ VisitExpr(E);
+ E->CXXRewrittenBinaryOperatorBits.IsReversed = Record.readInt();
+ E->SemanticForm = Record.readSubExpr();
+}
+
void ASTStmtReader::VisitCXXConstructExpr(CXXConstructExpr *E) {
VisitExpr(E);
@@ -3252,6 +3259,10 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
Context, /*NumArgs=*/Record[ASTStmtReader::NumExprFields], Empty);
break;
+ case EXPR_CXX_REWRITTEN_BINARY_OPERATOR:
+ S = new (Context) CXXRewrittenBinaryOperator(Empty);
+ break;
+
case EXPR_CXX_CONSTRUCT:
S = CXXConstructExpr::CreateEmpty(
Context,
diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp
index 6f3b367aed..c6b05c9a17 100644
--- a/lib/Serialization/ASTWriter.cpp
+++ b/lib/Serialization/ASTWriter.cpp
@@ -1024,6 +1024,7 @@ static void AddStmtsExprs(llvm::BitstreamWriter &Stream,
RECORD(STMT_CXX_FOR_RANGE);
RECORD(EXPR_CXX_OPERATOR_CALL);
RECORD(EXPR_CXX_MEMBER_CALL);
+ RECORD(EXPR_CXX_REWRITTEN_BINARY_OPERATOR);
RECORD(EXPR_CXX_CONSTRUCT);
RECORD(EXPR_CXX_TEMPORARY_OBJECT);
RECORD(EXPR_CXX_STATIC_CAST);
diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp
index 84a532496d..c39d4d39bc 100644
--- a/lib/Serialization/ASTWriterStmt.cpp
+++ b/lib/Serialization/ASTWriterStmt.cpp
@@ -1375,6 +1375,14 @@ void ASTStmtWriter::VisitCXXMemberCallExpr(CXXMemberCallExpr *E) {
Code = serialization::EXPR_CXX_MEMBER_CALL;
}
+void ASTStmtWriter::VisitCXXRewrittenBinaryOperator(
+ CXXRewrittenBinaryOperator *E) {
+ VisitExpr(E);
+ Record.push_back(E->isReversed());
+ Record.AddStmt(E->getSemanticForm());
+ Code = serialization::EXPR_CXX_REWRITTEN_BINARY_OPERATOR;
+}
+
void ASTStmtWriter::VisitCXXConstructExpr(CXXConstructExpr *E) {
VisitExpr(E);
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index a148088c2b..e92e95354f 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1372,6 +1372,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::OpaqueValueExprClass:
case Stmt::AsTypeExprClass:
case Stmt::ConceptSpecializationExprClass:
+ case Stmt::CXXRewrittenBinaryOperatorClass:
// Fall through.
// Cases we intentionally don't evaluate, since they don't need
diff --git a/tools/libclang/CXCursor.cpp b/tools/libclang/CXCursor.cpp
index b292511e04..d4210dee24 100644
--- a/tools/libclang/CXCursor.cpp
+++ b/tools/libclang/CXCursor.cpp
@@ -263,6 +263,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
case Stmt::CXXDefaultArgExprClass:
case Stmt::CXXDefaultInitExprClass:
case Stmt::CXXFoldExprClass:
+ case Stmt::CXXRewrittenBinaryOperatorClass:
case Stmt::CXXStdInitializerListExprClass:
case Stmt::CXXScalarValueInitExprClass:
case Stmt::CXXUuidofExprClass: