diff options
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/AST/ExprConstant.cpp | 30 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 341 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 9 | ||||
-rw-r--r-- | clang/lib/Sema/SemaOverload.cpp | 126 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiate.cpp | 20 |
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; } |