Skip to content
This repository has been archived by the owner on Apr 23, 2020. It is now read-only.

Commit

Permalink
[c++20] Add CXXRewrittenBinaryOperator to represent a comparison
Browse files Browse the repository at this point in the history
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
  • Loading branch information
zygoloid committed Oct 19, 2019
1 parent 3b03365 commit ea521aa
Show file tree
Hide file tree
Showing 23 changed files with 295 additions and 0 deletions.
90 changes: 90 additions & 0 deletions include/clang/AST/ExprCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,96 @@ class CXXMemberCallExpr final : public CallExpr {
}
};

/// 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;
Expand Down
9 changes: 9 additions & 0 deletions include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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, {})
Expand Down
10 changes: 10 additions & 0 deletions include/clang/AST/Stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,15 @@ class alignas(void *) Stmt {
unsigned FPFeatures : 3;
};

class CXXRewrittenBinaryOperatorBitfields {
friend class ASTStmtReader;
friend class CXXRewrittenBinaryOperator;

unsigned : NumCallExprBits;

unsigned IsReversed : 1;
};

class CXXBoolLiteralExprBitfields {
friend class CXXBoolLiteralExpr;

Expand Down Expand Up @@ -978,6 +987,7 @@ class alignas(void *) Stmt {

// C++ Expressions
CXXOperatorCallExprBitfields CXXOperatorCallExprBits;
CXXRewrittenBinaryOperatorBitfields CXXRewrittenBinaryOperatorBits;
CXXBoolLiteralExprBitfields CXXBoolLiteralExprBits;
CXXNullPtrLiteralExprBitfields CXXNullPtrLiteralExprBits;
CXXThisExprBitfields CXXThisExprBits;
Expand Down
1 change: 1 addition & 0 deletions include/clang/Basic/StmtNodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down
3 changes: 3 additions & 0 deletions include/clang/Serialization/ASTBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand Down
1 change: 1 addition & 0 deletions lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3473,6 +3473,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
case ArrayInitLoopExprClass:
case ParenListExprClass:
case CXXPseudoDestructorExprClass:
case CXXRewrittenBinaryOperatorClass:
case CXXStdInitializerListExprClass:
case SubstNonTypeTemplateParmExprClass:
case MaterializeTemporaryExprClass:
Expand Down
70 changes: 70 additions & 0 deletions lib/AST/ExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions lib/AST/ExprClassification.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
7 changes: 7 additions & 0 deletions lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6765,6 +6765,10 @@ class ExprEvaluatorBase
}
}

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.
Expand Down Expand Up @@ -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();
Expand Down
11 changes: 11 additions & 0 deletions lib/AST/ItaniumMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4090,6 +4090,17 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) {
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);
Expand Down
9 changes: 9 additions & 0 deletions lib/AST/StmtPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
10 changes: 10 additions & 0 deletions lib/AST/StmtProfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions lib/CodeGen/CGExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
3 changes: 3 additions & 0 deletions lib/CodeGen/CGExprAgg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
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) {
Expand Down
4 changes: 4 additions & 0 deletions lib/CodeGen/CGExprComplex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,10 @@ class ComplexExprEmitter
return EmitBinDiv(EmitBinOps(E));
}

ComplexPairTy VisitCXXRewrittenBinaryOperator(CXXRewrittenBinaryOperator *E) {
return Visit(E->getSemanticForm());
}

// Compound assignments.
ComplexPairTy VisitBinAddAssign(const CompoundAssignOperator *E) {
return EmitCompoundAssign(E, &ComplexExprEmitter::EmitBinAdd);
Expand Down
4 changes: 4 additions & 0 deletions lib/CodeGen/CGExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,10 @@ class ScalarExprEmitter
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 *);
Expand Down
1 change: 1 addition & 0 deletions lib/Sema/SemaExceptionSpec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Loading

0 comments on commit ea521aa

Please sign in to comment.