summaryrefslogtreecommitdiffstats
path: root/clang/lib
diff options
context:
space:
mode:
authorRichard Smith <richard@metafoo.co.uk>2019-12-05 18:51:19 -0800
committerRichard Smith <richard@metafoo.co.uk>2019-12-09 14:54:06 -0800
commite6e6e34b95cfe03275943fde0db259cc7d57f4ad (patch)
treee646ed63dcfd9eaa1cd158b0e9220dbb6da9cac0 /clang/lib
parent295db41ce230d9b0f1e0d24829abf0fb772b31ff (diff)
downloadbcm5719-llvm-e6e6e34b95cfe03275943fde0db259cc7d57f4ad.tar.gz
bcm5719-llvm-e6e6e34b95cfe03275943fde0db259cc7d57f4ad.zip
[c++20] Defaulted comparison support for array members.
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/Sema/SemaDeclCXX.cpp141
1 files changed, 131 insertions, 10 deletions
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 28159b9c081..1ee6ee5dcf1 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -7442,6 +7442,7 @@ class DefaultedComparisonSynthesizer
StmtListResult, StmtResult,
std::pair<ExprResult, ExprResult>> {
SourceLocation Loc;
+ unsigned ArrayDepth = 0;
public:
using Base = DefaultedComparisonVisitor;
@@ -7467,29 +7468,56 @@ public:
case DefaultedComparisonKind::None:
llvm_unreachable("not a defaulted comparison");
- case DefaultedComparisonKind::Equal:
+ case DefaultedComparisonKind::Equal: {
// C++2a [class.eq]p3:
// [...] compar[e] the corresponding elements [...] until the first
// index i where xi == yi yields [...] false. If no such index exists,
// V is true. Otherwise, V is false.
//
// Join the comparisons with '&&'s and return the result. Use a right
- // fold because that short-circuits more naturally.
- for (Stmt *EAsStmt : llvm::reverse(Stmts.Stmts)) {
- Expr *E = cast<Expr>(EAsStmt);
- if (RetVal.isUnset()) {
- RetVal = E;
+ // fold (traversing the conditions right-to-left), because that
+ // short-circuits more naturally.
+ auto OldStmts = std::move(Stmts.Stmts);
+ Stmts.Stmts.clear();
+ ExprResult CmpSoFar;
+ // Finish a particular comparison chain.
+ auto FinishCmp = [&] {
+ if (Expr *Prior = CmpSoFar.get()) {
+ // Convert the last expression to 'return ...;'
+ if (RetVal.isUnset() && Stmts.Stmts.empty())
+ RetVal = CmpSoFar;
+ // Convert any prior comparison to 'if (!(...)) return false;'
+ else if (Stmts.add(buildIfNotCondReturnFalse(Prior)))
+ return true;
+ CmpSoFar = ExprResult();
+ }
+ return false;
+ };
+ for (Stmt *EAsStmt : llvm::reverse(OldStmts)) {
+ Expr *E = dyn_cast<Expr>(EAsStmt);
+ if (!E) {
+ // Found an array comparison.
+ if (FinishCmp() || Stmts.add(EAsStmt))
+ return StmtError();
+ continue;
+ }
+
+ if (CmpSoFar.isUnset()) {
+ CmpSoFar = E;
continue;
}
- RetVal = S.CreateBuiltinBinOp(Loc, BO_LAnd, E, RetVal.get());
- if (RetVal.isInvalid())
+ CmpSoFar = S.CreateBuiltinBinOp(Loc, BO_LAnd, E, CmpSoFar.get());
+ if (CmpSoFar.isInvalid())
return StmtError();
}
+ if (FinishCmp())
+ return StmtError();
+ std::reverse(Stmts.Stmts.begin(), Stmts.Stmts.end());
// If no such index exists, V is true.
if (RetVal.isUnset())
RetVal = S.ActOnCXXBoolLiteral(Loc, tok::kw_true);
- Stmts.Stmts.clear();
break;
+ }
case DefaultedComparisonKind::ThreeWay: {
// Per C++2a [class.spaceship]p3, as a fallback add:
@@ -7579,7 +7607,100 @@ private:
// FIXME: When expanding a subobject, register a note in the code synthesis
// stack to say which subobject we're comparing.
- // FIXME: Build a loop for an array subobject.
+ StmtResult buildIfNotCondReturnFalse(ExprResult Cond) {
+ if (Cond.isInvalid())
+ return StmtError();
+
+ ExprResult NotCond = S.CreateBuiltinUnaryOp(Loc, UO_LNot, Cond.get());
+ if (NotCond.isInvalid())
+ return StmtError();
+
+ ExprResult False = S.ActOnCXXBoolLiteral(Loc, tok::kw_false);
+ assert(!False.isInvalid() && "should never fail");
+ StmtResult ReturnFalse = S.BuildReturnStmt(Loc, False.get());
+ if (ReturnFalse.isInvalid())
+ return StmtError();
+
+ return S.ActOnIfStmt(Loc, false, nullptr,
+ S.ActOnCondition(nullptr, Loc, NotCond.get(),
+ Sema::ConditionKind::Boolean),
+ ReturnFalse.get(), SourceLocation(), nullptr);
+ }
+
+ StmtResult visitSubobjectArray(QualType Type, llvm::APInt Size,
+ ExprPair Subobj) {
+ QualType SizeType = S.Context.getSizeType();
+ Size = Size.zextOrTrunc(S.Context.getTypeSize(SizeType));
+
+ // Build 'size_t i$n = 0'.
+ IdentifierInfo *IterationVarName = nullptr;
+ {
+ SmallString<8> Str;
+ llvm::raw_svector_ostream OS(Str);
+ OS << "i" << ArrayDepth;
+ IterationVarName = &S.Context.Idents.get(OS.str());
+ }
+ VarDecl *IterationVar = VarDecl::Create(
+ S.Context, S.CurContext, Loc, Loc, IterationVarName, SizeType,
+ S.Context.getTrivialTypeSourceInfo(SizeType, Loc), SC_None);
+ llvm::APInt Zero(S.Context.getTypeSize(SizeType), 0);
+ IterationVar->setInit(
+ IntegerLiteral::Create(S.Context, Zero, SizeType, Loc));
+ Stmt *Init = new (S.Context) DeclStmt(DeclGroupRef(IterationVar), Loc, Loc);
+
+ auto IterRef = [&] {
+ ExprResult Ref = S.BuildDeclarationNameExpr(
+ CXXScopeSpec(), DeclarationNameInfo(IterationVarName, Loc),
+ IterationVar);
+ assert(!Ref.isInvalid() && "can't reference our own variable?");
+ return Ref.get();
+ };
+
+ // Build 'i$n != Size'.
+ ExprResult Cond = S.CreateBuiltinBinOp(
+ Loc, BO_NE, IterRef(),
+ IntegerLiteral::Create(S.Context, Size, SizeType, Loc));
+ assert(!Cond.isInvalid() && "should never fail");
+
+ // Build '++i$n'.
+ ExprResult Inc = S.CreateBuiltinUnaryOp(Loc, UO_PreInc, IterRef());
+ assert(!Inc.isInvalid() && "should never fail");
+
+ // Build 'a[i$n]' and 'b[i$n]'.
+ auto Index = [&](ExprResult E) {
+ if (E.isInvalid())
+ return ExprError();
+ return S.CreateBuiltinArraySubscriptExpr(E.get(), Loc, IterRef(), Loc);
+ };
+ Subobj.first = Index(Subobj.first);
+ Subobj.second = Index(Subobj.second);
+
+ // Compare the array elements.
+ ++ArrayDepth;
+ StmtResult Substmt = visitSubobject(Type, Subobj);
+ --ArrayDepth;
+
+ if (Substmt.isInvalid())
+ return StmtError();
+
+ // For the inner level of an 'operator==', build 'if (!cmp) return false;'.
+ // For outer levels or for an 'operator<=>' we already have a suitable
+ // statement that returns as necessary.
+ if (Expr *ElemCmp = dyn_cast<Expr>(Substmt.get())) {
+ assert(DCK == DefaultedComparisonKind::Equal &&
+ "should have non-expression statement");
+ Substmt = buildIfNotCondReturnFalse(ElemCmp);
+ if (Substmt.isInvalid())
+ return StmtError();
+ }
+
+ // Build 'for (...) ...'
+ return S.ActOnForStmt(Loc, Loc, Init,
+ S.ActOnCondition(nullptr, Loc, Cond.get(),
+ Sema::ConditionKind::Boolean),
+ S.MakeFullDiscardedValueExpr(Inc.get()), Loc,
+ Substmt.get());
+ }
StmtResult visitExpandedSubobject(QualType Type, ExprPair Obj) {
UnresolvedSet<4> Fns; // FIXME: Track this.
OpenPOWER on IntegriCloud