From ea521aa6024431138f7890d486a6e3500822b3d5 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Sat, 19 Oct 2019 00:04:38 +0000 Subject: [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 --- include/clang/AST/ExprCXX.h | 90 +++++++++++++++++++++++++++++++ include/clang/AST/RecursiveASTVisitor.h | 9 ++++ include/clang/AST/Stmt.h | 10 ++++ include/clang/Basic/StmtNodes.td | 1 + include/clang/Serialization/ASTBitCodes.h | 3 ++ lib/AST/Expr.cpp | 1 + lib/AST/ExprCXX.cpp | 70 ++++++++++++++++++++++++ lib/AST/ExprClassification.cpp | 4 ++ lib/AST/ExprConstant.cpp | 7 +++ lib/AST/ItaniumMangle.cpp | 11 ++++ lib/AST/StmtPrinter.cpp | 9 ++++ lib/AST/StmtProfile.cpp | 10 ++++ lib/CodeGen/CGExpr.cpp | 2 + lib/CodeGen/CGExprAgg.cpp | 3 ++ lib/CodeGen/CGExprComplex.cpp | 4 ++ lib/CodeGen/CGExprScalar.cpp | 4 ++ lib/Sema/SemaExceptionSpec.cpp | 1 + lib/Sema/TreeTransform.h | 34 ++++++++++++ lib/Serialization/ASTReaderStmt.cpp | 11 ++++ lib/Serialization/ASTWriter.cpp | 1 + lib/Serialization/ASTWriterStmt.cpp | 8 +++ lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 + tools/libclang/CXCursor.cpp | 1 + 23 files changed, 295 insertions(+) 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: +/// - a == b -> b == a +/// - a != b -> !(a == b) +/// - a != b -> !(b == a) +/// - For \c \@ in \c <, \c <=, \c >, \c >=, \c <=>: +/// - a @ b -> (a <=> b) @ 0 +/// - a @ b -> 0 @ (b <=> a) +/// +/// 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(SemanticForm); } + const Expr *getSemanticForm() const { return cast(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(Decomposed.LHS))); + TRY_TO(TraverseStmt(const_cast(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; // C++ Expressions. def CXXOperatorCallExpr : DStmt; def CXXMemberCallExpr : DStmt; +def CXXRewrittenBinaryOperator : DStmt; def CXXNamedCastExpr : DStmt; def CXXStaticCastExpr : DStmt; def CXXDynamicCastExpr : DStmt; 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(E)) { + assert(NotEq->getOpcode() == UO_LNot); + E = NotEq->getSubExpr()->IgnoreImplicit(); + SkippedNot = true; + } + + // Decompose the outer binary operator. + if (auto *BO = dyn_cast(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(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(E)) { + assert(BO->getOpcode() == BO_Cmp); + Result.LHS = BO->getLHS(); + Result.RHS = BO->getRHS(); + Result.InnerBinOp = BO; + } else if (auto *BO = dyn_cast(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(E)->getCallReturnType(Ctx)); + case Expr::CXXRewrittenBinaryOperatorClass: + return ClassifyInternal( + Ctx, cast(E)->getSemanticForm()); + // __builtin_choose_expr is equivalent to the chosen expression. case Expr::ChooseExprClass: return ClassifyInternal(Ctx, cast(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(E)->getSemanticForm(), + Ctx); case Expr::DeclRefExprClass: { if (isa(cast(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(E)->getDecomposedForm(); + mangleOperatorName(BinaryOperator::getOverloadedOperator(Decomposed.Opcode), + /*Arity=*/2); + mangleExpression(Decomposed.LHS); + mangleExpression(Decomposed.RHS); + break; + } + case Expr::ConditionalOperatorClass: { const ConditionalOperator *CO = cast(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(Decomposed.LHS)); + OS << ' ' << BinaryOperator::getOpcodeStr(Decomposed.Opcode) << ' '; + PrintExpr(const_cast(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(E)); + case Expr::CXXRewrittenBinaryOperatorClass: + return EmitLValue(cast(E)->getSemanticForm()); case Expr::VAArgExprClass: return EmitVAArgExprLValue(cast(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::TransformBinaryOperator(BinaryOperator *E) { LHS.get(), RHS.get()); } +template +ExprResult TreeTransform::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 ExprResult TreeTransform::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: -- cgit v1.2.1