summaryrefslogtreecommitdiffstats
path: root/clang/lib
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/AST/ExprConstant.cpp30
-rw-r--r--clang/lib/Sema/SemaDeclCXX.cpp341
-rw-r--r--clang/lib/Sema/SemaExpr.cpp9
-rw-r--r--clang/lib/Sema/SemaOverload.cpp126
-rw-r--r--clang/lib/Sema/SemaTemplateInstantiate.cpp20
5 files changed, 499 insertions, 27 deletions
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 3151ec044cb..5aa151984e5 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -6830,6 +6830,36 @@ public:
return StmtVisitorTy::Visit(Source);
}
+ bool VisitPseudoObjectExpr(const PseudoObjectExpr *E) {
+ for (const Expr *SemE : E->semantics()) {
+ if (auto *OVE = dyn_cast<OpaqueValueExpr>(SemE)) {
+ // FIXME: We can't handle the case where an OpaqueValueExpr is also the
+ // result expression: there could be two different LValues that would
+ // refer to the same object in that case, and we can't model that.
+ if (SemE == E->getResultExpr())
+ return Error(E);
+
+ // Unique OVEs get evaluated if and when we encounter them when
+ // emitting the rest of the semantic form, rather than eagerly.
+ if (OVE->isUnique())
+ continue;
+
+ LValue LV;
+ if (!Evaluate(Info.CurrentCall->createTemporary(
+ OVE, getStorageType(Info.Ctx, OVE), false, LV),
+ Info, OVE->getSourceExpr()))
+ return false;
+ } else if (SemE == E->getResultExpr()) {
+ if (!StmtVisitorTy::Visit(SemE))
+ return false;
+ } else {
+ if (!EvaluateIgnoredValue(Info, SemE))
+ return false;
+ }
+ }
+ return true;
+ }
+
bool VisitCallExpr(const CallExpr *E) {
APValue Result;
if (!handleCallExpr(E, Result, nullptr))
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index ba516b66608..c8b95983f03 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -7085,7 +7085,8 @@ namespace {
///
/// This is accomplished by performing two visitation steps over the eventual
/// body of the function.
-template<typename Derived, typename Result, typename Subobject>
+template<typename Derived, typename ResultList, typename Result,
+ typename Subobject>
class DefaultedComparisonVisitor {
public:
using DefaultedComparisonKind = Sema::DefaultedComparisonKind;
@@ -7094,45 +7095,54 @@ public:
DefaultedComparisonKind DCK)
: S(S), RD(RD), FD(FD), DCK(DCK) {}
- Result visit() {
+ ResultList visit() {
// The type of an lvalue naming a parameter of this function.
QualType ParamLvalType =
FD->getParamDecl(0)->getType().getNonReferenceType();
+ ResultList Results;
+
switch (DCK) {
case DefaultedComparisonKind::None:
llvm_unreachable("not a defaulted comparison");
case DefaultedComparisonKind::Equal:
case DefaultedComparisonKind::ThreeWay:
- return getDerived().visitSubobjects(RD, ParamLvalType.getQualifiers());
+ getDerived().visitSubobjects(Results, RD, ParamLvalType.getQualifiers());
+ return Results;
case DefaultedComparisonKind::NotEqual:
case DefaultedComparisonKind::Relational:
- return getDerived().visitExpandedSubobject(
- ParamLvalType, getDerived().getCompleteObject());
+ Results.add(getDerived().visitExpandedSubobject(
+ ParamLvalType, getDerived().getCompleteObject()));
+ return Results;
}
}
protected:
Derived &getDerived() { return static_cast<Derived&>(*this); }
- Result visitSubobjects(CXXRecordDecl *Record, Qualifiers Quals) {
- Result R;
- // C++ [class.compare.default]p5:
- // The direct base class subobjects of C [...]
+ /// Visit the expanded list of subobjects of the given type, as specified in
+ /// C++2a [class.compare.default].
+ ///
+ /// \return \c true if the ResultList object said we're done, \c false if not.
+ bool visitSubobjects(ResultList &Results, CXXRecordDecl *Record,
+ Qualifiers Quals) {
+ // C++2a [class.compare.default]p4:
+ // The direct base class subobjects of C
for (CXXBaseSpecifier &Base : Record->bases())
- if (R.add(getDerived().visitSubobject(
+ if (Results.add(getDerived().visitSubobject(
S.Context.getQualifiedType(Base.getType(), Quals),
getDerived().getBase(&Base))))
- return R;
- // followed by the non-static data members of C [...]
+ return true;
+
+ // followed by the non-static data members of C
for (FieldDecl *Field : Record->fields()) {
// Recursively expand anonymous structs.
if (Field->isAnonymousStructOrUnion()) {
- if (R.add(
- visitSubobjects(Field->getType()->getAsCXXRecordDecl(), Quals)))
- return R;
+ if (visitSubobjects(Results, Field->getType()->getAsCXXRecordDecl(),
+ Quals))
+ return true;
continue;
}
@@ -7143,12 +7153,13 @@ protected:
QualType FieldType =
S.Context.getQualifiedType(Field->getType(), FieldQuals);
- if (R.add(getDerived().visitSubobject(FieldType,
- getDerived().getField(Field))))
- return R;
+ if (Results.add(getDerived().visitSubobject(
+ FieldType, getDerived().getField(Field))))
+ return true;
}
+
// form a list of subobjects.
- return R;
+ return false;
}
Result visitSubobject(QualType Type, Subobject Subobj) {
@@ -7200,6 +7211,7 @@ struct DefaultedComparisonSubobject {
class DefaultedComparisonAnalyzer
: public DefaultedComparisonVisitor<DefaultedComparisonAnalyzer,
DefaultedComparisonInfo,
+ DefaultedComparisonInfo,
DefaultedComparisonSubobject> {
public:
enum DiagnosticKind { NoDiagnostics, ExplainDeleted, ExplainConstexpr };
@@ -7407,6 +7419,260 @@ private:
return R;
}
};
+
+/// A list of statements.
+struct StmtListResult {
+ bool IsInvalid = false;
+ llvm::SmallVector<Stmt*, 16> Stmts;
+
+ bool add(const StmtResult &S) {
+ IsInvalid |= S.isInvalid();
+ if (IsInvalid)
+ return true;
+ Stmts.push_back(S.get());
+ return false;
+ }
+};
+
+/// A visitor over the notional body of a defaulted comparison that synthesizes
+/// the actual body.
+class DefaultedComparisonSynthesizer
+ : public DefaultedComparisonVisitor<DefaultedComparisonSynthesizer,
+ StmtListResult, StmtResult,
+ std::pair<ExprResult, ExprResult>> {
+ SourceLocation Loc;
+
+public:
+ using Base = DefaultedComparisonVisitor;
+ using ExprPair = std::pair<ExprResult, ExprResult>;
+
+ friend Base;
+
+ DefaultedComparisonSynthesizer(Sema &S, CXXRecordDecl *RD, FunctionDecl *FD,
+ DefaultedComparisonKind DCK,
+ SourceLocation BodyLoc)
+ : Base(S, RD, FD, DCK), Loc(BodyLoc) {}
+
+ /// Build a suitable function body for this defaulted comparison operator.
+ StmtResult build() {
+ Sema::CompoundScopeRAII CompoundScope(S);
+
+ StmtListResult Stmts = visit();
+ if (Stmts.IsInvalid)
+ return StmtError();
+
+ ExprResult RetVal;
+ switch (DCK) {
+ case DefaultedComparisonKind::None:
+ llvm_unreachable("not a defaulted comparison");
+
+ 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;
+ continue;
+ }
+ RetVal = S.CreateBuiltinBinOp(Loc, BO_LAnd, E, RetVal.get());
+ if (RetVal.isInvalid())
+ return StmtError();
+ }
+ // 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:
+ // return static_cast<R>(std::strong_ordering::equal);
+ QualType StrongOrdering = S.CheckComparisonCategoryType(
+ ComparisonCategoryType::StrongOrdering, Loc);
+ if (StrongOrdering.isNull())
+ return StmtError();
+ VarDecl *EqualVD = S.Context.CompCategories.getInfoForType(StrongOrdering)
+ .getValueInfo(ComparisonCategoryResult::Equal)
+ ->VD;
+ RetVal = S.BuildDeclarationNameExpr(
+ CXXScopeSpec(), DeclarationNameInfo(), EqualVD);
+ if (RetVal.isInvalid())
+ return StmtError();
+ RetVal = buildStaticCastToR(RetVal.get());
+ break;
+ }
+
+ case DefaultedComparisonKind::NotEqual:
+ case DefaultedComparisonKind::Relational:
+ RetVal = cast<Expr>(Stmts.Stmts.pop_back_val());
+ break;
+ }
+
+ // Build the final return statement.
+ if (RetVal.isInvalid())
+ return StmtError();
+ StmtResult ReturnStmt = S.BuildReturnStmt(Loc, RetVal.get());
+ if (ReturnStmt.isInvalid())
+ return StmtError();
+ Stmts.Stmts.push_back(ReturnStmt.get());
+
+ return S.ActOnCompoundStmt(Loc, Loc, Stmts.Stmts, /*IsStmtExpr=*/false);
+ }
+
+private:
+ ExprResult getParam(unsigned I) {
+ ParmVarDecl *PD = FD->getParamDecl(I);
+ return S.BuildDeclarationNameExpr(
+ CXXScopeSpec(), DeclarationNameInfo(PD->getDeclName(), Loc), PD);
+ }
+
+ ExprPair getCompleteObject() {
+ unsigned Param = 0;
+ ExprResult LHS;
+ if (isa<CXXMethodDecl>(FD)) {
+ // LHS is '*this'.
+ LHS = S.ActOnCXXThis(Loc);
+ if (!LHS.isInvalid())
+ LHS = S.CreateBuiltinUnaryOp(Loc, UO_Deref, LHS.get());
+ } else {
+ LHS = getParam(Param++);
+ }
+ ExprResult RHS = getParam(Param++);
+ assert(Param == FD->getNumParams());
+ return {LHS, RHS};
+ }
+
+ ExprPair getBase(CXXBaseSpecifier *Base) {
+ ExprPair Obj = getCompleteObject();
+ if (Obj.first.isInvalid() || Obj.second.isInvalid())
+ return {ExprError(), ExprError()};
+ CXXCastPath Path = {Base};
+ return {S.ImpCastExprToType(Obj.first.get(), Base->getType(),
+ CK_DerivedToBase, VK_LValue, &Path),
+ S.ImpCastExprToType(Obj.second.get(), Base->getType(),
+ CK_DerivedToBase, VK_LValue, &Path)};
+ }
+
+ ExprPair getField(FieldDecl *Field) {
+ ExprPair Obj = getCompleteObject();
+ if (Obj.first.isInvalid() || Obj.second.isInvalid())
+ return {ExprError(), ExprError()};
+
+ DeclAccessPair Found = DeclAccessPair::make(Field, Field->getAccess());
+ DeclarationNameInfo NameInfo(Field->getDeclName(), Loc);
+ return {S.BuildFieldReferenceExpr(Obj.first.get(), /*IsArrow=*/false, Loc,
+ CXXScopeSpec(), Field, Found, NameInfo),
+ S.BuildFieldReferenceExpr(Obj.second.get(), /*IsArrow=*/false, Loc,
+ CXXScopeSpec(), Field, Found, NameInfo)};
+ }
+
+ // 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 visitExpandedSubobject(QualType Type, ExprPair Obj) {
+ UnresolvedSet<4> Fns; // FIXME: Track this.
+
+ if (Obj.first.isInvalid() || Obj.second.isInvalid())
+ return StmtError();
+
+ OverloadedOperatorKind OO = FD->getOverloadedOperator();
+ ExprResult Op = S.CreateOverloadedBinOp(
+ Loc, BinaryOperator::getOverloadedOpcode(OO), Fns,
+ Obj.first.get(), Obj.second.get(), /*PerformADL=*/true,
+ /*AllowRewrittenCandidates=*/true, FD);
+ if (Op.isInvalid())
+ return StmtError();
+
+ switch (DCK) {
+ case DefaultedComparisonKind::None:
+ llvm_unreachable("not a defaulted comparison");
+
+ case DefaultedComparisonKind::Equal:
+ // Per C++2a [class.eq]p2, each comparison is individually contextually
+ // converted to bool.
+ Op = S.PerformContextuallyConvertToBool(Op.get());
+ if (Op.isInvalid())
+ return StmtError();
+ return Op.get();
+
+ case DefaultedComparisonKind::ThreeWay: {
+ // Per C++2a [class.spaceship]p3, form:
+ // if (R cmp = static_cast<R>(op); cmp != 0)
+ // return cmp;
+ QualType R = FD->getReturnType();
+ Op = buildStaticCastToR(Op.get());
+ if (Op.isInvalid())
+ return StmtError();
+
+ // R cmp = ...;
+ IdentifierInfo *Name = &S.Context.Idents.get("cmp");
+ VarDecl *VD =
+ VarDecl::Create(S.Context, S.CurContext, Loc, Loc, Name, R,
+ S.Context.getTrivialTypeSourceInfo(R, Loc), SC_None);
+ S.AddInitializerToDecl(VD, Op.get(), /*DirectInit=*/false);
+ Stmt *InitStmt = new (S.Context) DeclStmt(DeclGroupRef(VD), Loc, Loc);
+
+ // cmp != 0
+ ExprResult VDRef = S.BuildDeclarationNameExpr(
+ CXXScopeSpec(), DeclarationNameInfo(Name, Loc), VD);
+ if (VDRef.isInvalid())
+ return StmtError();
+ llvm::APInt ZeroVal(S.Context.getIntWidth(S.Context.IntTy), 0);
+ Expr *Zero =
+ IntegerLiteral::Create(S.Context, ZeroVal, S.Context.IntTy, Loc);
+ ExprResult Comp = S.CreateOverloadedBinOp(Loc, BO_NE, Fns, VDRef.get(),
+ Zero, true, true, FD);
+ if (Comp.isInvalid())
+ return StmtError();
+ Sema::ConditionResult Cond = S.ActOnCondition(
+ nullptr, Loc, Comp.get(), Sema::ConditionKind::Boolean);
+ if (Cond.isInvalid())
+ return StmtError();
+
+ // return cmp;
+ VDRef = S.BuildDeclarationNameExpr(
+ CXXScopeSpec(), DeclarationNameInfo(Name, Loc), VD);
+ if (VDRef.isInvalid())
+ return StmtError();
+ StmtResult ReturnStmt = S.BuildReturnStmt(Loc, VDRef.get());
+ if (ReturnStmt.isInvalid())
+ return StmtError();
+
+ // if (...)
+ return S.ActOnIfStmt(Loc, /*IsConstexpr=*/false, InitStmt, Cond,
+ ReturnStmt.get(), /*ElseLoc=*/SourceLocation(),
+ /*Else=*/nullptr);
+ }
+
+ case DefaultedComparisonKind::NotEqual:
+ case DefaultedComparisonKind::Relational:
+ // C++2a [class.compare.secondary]p2:
+ // Otherwise, the operator function yields x @ y.
+ return Op.get();
+ }
+ }
+
+ /// Build "static_cast<R>(E)".
+ ExprResult buildStaticCastToR(Expr *E) {
+ QualType R = FD->getReturnType();
+ assert(!R->isUndeducedType() && "type should have been deduced already");
+
+ // Don't bother forming a no-op cast in the common case.
+ if (E->isRValue() && S.Context.hasSameType(E->getType(), R))
+ return E;
+ return S.BuildCXXNamedCast(Loc, tok::kw_static_cast,
+ S.Context.getTrivialTypeSourceInfo(R, Loc), E,
+ SourceRange(Loc, Loc), SourceRange(Loc, Loc));
+ }
+};
}
bool Sema::CheckExplicitlyDefaultedComparison(FunctionDecl *FD,
@@ -7540,6 +7806,43 @@ bool Sema::CheckExplicitlyDefaultedComparison(FunctionDecl *FD,
return false;
}
+void Sema::DefineDefaultedComparison(SourceLocation UseLoc, FunctionDecl *FD,
+ DefaultedComparisonKind DCK) {
+ assert(FD->isDefaulted() && !FD->isDeleted() &&
+ !FD->doesThisDeclarationHaveABody());
+ if (FD->willHaveBody() || FD->isInvalidDecl())
+ return;
+
+ SynthesizedFunctionScope Scope(*this, FD);
+
+ // The exception specification is needed because we are defining the
+ // function.
+ // FIXME: Handle this better. Computing the exception specification will
+ // eventually need the function body.
+ ResolveExceptionSpec(UseLoc, FD->getType()->castAs<FunctionProtoType>());
+
+ // Add a context note for diagnostics produced after this point.
+ Scope.addContextNote(UseLoc);
+
+ // Build and set up the function body.
+ {
+ CXXRecordDecl *RD = cast<CXXRecordDecl>(FD->getLexicalParent());
+ SourceLocation BodyLoc =
+ FD->getEndLoc().isValid() ? FD->getEndLoc() : FD->getLocation();
+ StmtResult Body =
+ DefaultedComparisonSynthesizer(*this, RD, FD, DCK, BodyLoc).build();
+ if (Body.isInvalid()) {
+ FD->setInvalidDecl();
+ return;
+ }
+ FD->setBody(Body.get());
+ FD->markUsed(Context);
+ }
+
+ if (ASTMutationListener *L = getASTMutationListener())
+ L->CompletedImplicitDefinition(FD);
+}
+
void Sema::CheckDelayedMemberExceptionSpecs() {
decltype(DelayedOverridingExceptionSpecChecks) Overriding;
decltype(DelayedEquivalentExceptionSpecChecks) Equivalent;
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 5eeeba3c2d1..b97352e27e1 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -15416,9 +15416,8 @@ static OdrUseContext isOdrUseContext(Sema &SemaRef) {
}
static bool isImplicitlyDefinableConstexprFunction(FunctionDecl *Func) {
- CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Func);
return Func->isConstexpr() &&
- (Func->isImplicitlyInstantiable() || (MD && !MD->isUserProvided()));
+ (Func->isImplicitlyInstantiable() || !Func->isUserProvided());
}
/// Mark a function referenced, and check whether it is odr-used
@@ -15566,6 +15565,12 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,
MarkVTableUsed(Loc, MethodDecl->getParent());
}
+ if (Func->isDefaulted() && !Func->isDeleted()) {
+ DefaultedComparisonKind DCK = getDefaultedComparisonKind(Func);
+ if (DCK != DefaultedComparisonKind::None)
+ DefineDefaultedComparison(Loc, Func, DCK);
+ }
+
// Implicit instantiation of function templates and member functions of
// class templates.
if (Func->isImplicitlyInstantiable()) {
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 27e1101b482..344e54b7f3f 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -12835,11 +12835,19 @@ void Sema::LookupOverloadedBinOp(OverloadCandidateSet &CandidateSet,
///
/// \param LHS Left-hand argument.
/// \param RHS Right-hand argument.
+/// \param PerformADL Whether to consider operator candidates found by ADL.
+/// \param AllowRewrittenCandidates Whether to consider candidates found by
+/// C++20 operator rewrites.
+/// \param DefaultedFn If we are synthesizing a defaulted operator function,
+/// the function in question. Such a function is never a candidate in
+/// our overload resolution. This also enables synthesizing a three-way
+/// comparison from < and == as described in C++20 [class.spaceship]p1.
ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
BinaryOperatorKind Opc,
const UnresolvedSetImpl &Fns, Expr *LHS,
Expr *RHS, bool PerformADL,
- bool AllowRewrittenCandidates) {
+ bool AllowRewrittenCandidates,
+ FunctionDecl *DefaultedFn) {
Expr *Args[2] = { LHS, RHS };
LHS=RHS=nullptr; // Please use only Args instead of LHS/RHS couple
@@ -12906,6 +12914,8 @@ ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
OverloadCandidateSet CandidateSet(
OpLoc, OverloadCandidateSet::CSK_Operator,
OverloadCandidateSet::OperatorRewriteInfo(Op, AllowRewrittenCandidates));
+ if (DefaultedFn)
+ CandidateSet.exclude(DefaultedFn);
LookupOverloadedBinOp(CandidateSet, Op, Fns, Args, PerformADL);
bool HadMultipleCandidates = (CandidateSet.size() > 1);
@@ -13113,6 +13123,15 @@ ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
if (Opc == BO_Comma)
break;
+ // When defaulting an 'operator<=>', we can try to synthesize a three-way
+ // compare result using '==' and '<'.
+ if (DefaultedFn && Opc == BO_Cmp) {
+ ExprResult E = BuildSynthesizedThreeWayComparison(OpLoc, Fns, Args[0],
+ Args[1], DefaultedFn);
+ if (E.isInvalid() || E.isUsable())
+ return E;
+ }
+
// For class as left operand for assignment or compound assignment
// operator do not fall through to handling in built-in, but report that
// no overloaded assignment operator found
@@ -13194,6 +13213,111 @@ ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
return CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]);
}
+ExprResult Sema::BuildSynthesizedThreeWayComparison(
+ SourceLocation OpLoc, const UnresolvedSetImpl &Fns, Expr *LHS, Expr *RHS,
+ FunctionDecl *DefaultedFn) {
+ const ComparisonCategoryInfo *Info =
+ Context.CompCategories.lookupInfoForType(DefaultedFn->getReturnType());
+ // If we're not producing a known comparison category type, we can't
+ // synthesize a three-way comparison. Let the caller diagnose this.
+ if (!Info)
+ return ExprResult((Expr*)nullptr);
+
+ // If we ever want to perform this synthesis more generally, we will need to
+ // apply the temporary materialization conversion to the operands.
+ assert(LHS->isGLValue() && RHS->isGLValue() &&
+ "cannot use prvalue expressions more than once");
+ Expr *OrigLHS = LHS;
+ Expr *OrigRHS = RHS;
+
+ // Replace the LHS and RHS with OpaqueValueExprs; we're going to refer to
+ // each of them multiple times below.
+ LHS = new (Context)
+ OpaqueValueExpr(LHS->getExprLoc(), LHS->getType(), LHS->getValueKind(),
+ LHS->getObjectKind(), LHS);
+ RHS = new (Context)
+ OpaqueValueExpr(RHS->getExprLoc(), RHS->getType(), RHS->getValueKind(),
+ RHS->getObjectKind(), RHS);
+
+ ExprResult Eq = CreateOverloadedBinOp(OpLoc, BO_EQ, Fns, LHS, RHS, true, true,
+ DefaultedFn);
+ if (Eq.isInvalid())
+ return ExprError();
+
+ ExprResult Less;
+ if (Info->isOrdered()) {
+ Less = CreateOverloadedBinOp(OpLoc, BO_LT, Fns, LHS, RHS, true, true,
+ DefaultedFn);
+ if (Less.isInvalid())
+ return ExprError();
+ }
+
+ ExprResult Greater;
+ if (Info->isOrdered()) {
+ Greater = CreateOverloadedBinOp(OpLoc, BO_LT, Fns, RHS, LHS, true, true,
+ DefaultedFn);
+ if (Greater.isInvalid())
+ return ExprError();
+ }
+
+ // Form the list of comparisons we're going to perform.
+ struct Comparison {
+ ExprResult Cmp;
+ ComparisonCategoryResult Result;
+ } Comparisons[4] =
+ { {Eq, Info->isStrong() ? ComparisonCategoryResult::Equal
+ : ComparisonCategoryResult::Equivalent},
+ {Less, ComparisonCategoryResult::Less},
+ {Greater, ComparisonCategoryResult::Greater},
+ {ExprResult(), ComparisonCategoryResult::Unordered},
+ };
+
+ int I;
+ if (Info->isEquality()) {
+ Comparisons[1].Result = Info->isStrong()
+ ? ComparisonCategoryResult::Nonequal
+ : ComparisonCategoryResult::Nonequivalent;
+ I = 1;
+ } else if (!Info->isPartial()) {
+ I = 2;
+ } else {
+ I = 3;
+ }
+
+ // Combine the comparisons with suitable conditional expressions.
+ ExprResult Result;
+ for (; I >= 0; --I) {
+ // Build a reference to the comparison category constant.
+ auto *VI = Info->lookupValueInfo(Comparisons[I].Result);
+ // FIXME: Missing a constant for a comparison category. Diagnose this?
+ if (!VI)
+ return ExprResult((Expr*)nullptr);
+ ExprResult ThisResult =
+ BuildDeclarationNameExpr(CXXScopeSpec(), DeclarationNameInfo(), VI->VD);
+ if (ThisResult.isInvalid())
+ return ExprError();
+
+ // Build a conditional unless this is the final case.
+ if (Result.get()) {
+ Result = ActOnConditionalOp(OpLoc, OpLoc, Comparisons[I].Cmp.get(),
+ ThisResult.get(), Result.get());
+ if (Result.isInvalid())
+ return ExprError();
+ } else {
+ Result = ThisResult;
+ }
+ }
+
+ // Build a PseudoObjectExpr to model the rewriting of an <=> operator, and to
+ // bind the OpaqueValueExprs before they're (repeatedly) used.
+ Expr *SyntacticForm = new (Context)
+ BinaryOperator(OrigLHS, OrigRHS, BO_Cmp, Result.get()->getType(),
+ Result.get()->getValueKind(),
+ Result.get()->getObjectKind(), OpLoc, FPFeatures);
+ Expr *SemanticForm[] = {LHS, RHS, Result.get()};
+ return PseudoObjectExpr::Create(Context, SyntacticForm, SemanticForm, 2);
+}
+
ExprResult
Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
SourceLocation RLoc,
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 2496c919311..4d54ec17b99 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -672,13 +672,23 @@ void Sema::PrintInstantiationStack() {
break;
case CodeSynthesisContext::DefiningSynthesizedFunction: {
- // FIXME: For synthesized members other than special members, produce a note.
- auto *MD = dyn_cast<CXXMethodDecl>(Active->Entity);
- auto CSM = MD ? getSpecialMember(MD) : CXXInvalid;
- if (CSM != CXXInvalid) {
+ // FIXME: For synthesized functions that are not defaulted,
+ // produce a note.
+ auto *FD = dyn_cast<FunctionDecl>(Active->Entity);
+ DefaultedFunctionKind DFK =
+ FD ? getDefaultedFunctionKind(FD) : DefaultedFunctionKind();
+ if (DFK.isSpecialMember()) {
+ auto *MD = cast<CXXMethodDecl>(FD);
Diags.Report(Active->PointOfInstantiation,
diag::note_member_synthesized_at)
- << CSM << Context.getTagDeclType(MD->getParent());
+ << MD->isExplicitlyDefaulted() << DFK.asSpecialMember()
+ << Context.getTagDeclType(MD->getParent());
+ } else if (DFK.isComparison()) {
+ Diags.Report(Active->PointOfInstantiation,
+ diag::note_comparison_synthesized_at)
+ << (int)DFK.asComparison()
+ << Context.getTagDeclType(
+ cast<CXXRecordDecl>(FD->getLexicalDeclContext()));
}
break;
}
OpenPOWER on IntegriCloud