================
@@ -9481,6 +9499,620 @@ ComputeDefaultedComparisonExceptionSpec(Sema &S,
SourceLocation Loc,
return ExceptSpec;
}
+//===----------------------------------------------------------------------===//
+// C++2d [over.inc.default]: defaulted postfix increment and decrement operator
+// functions (P3668R4).
+//===----------------------------------------------------------------------===//
+
+namespace {
+/// The operations performed by the implicit definition of a defaulted postfix
+/// increment or decrement operator function. The values correspond to %select
+/// indices in the diagnostics explaining why such a function is deleted or is
+/// not constexpr.
+enum class PostfixOperatorStep {
+ Constructor,
+ Destructor,
+ PrefixIncrement,
+ PrefixDecrement,
+ ConversionFunction
+};
+
+/// Which diagnostics to produce while analyzing a defaulted postfix increment
+/// or decrement operator function.
+enum class PostfixOperatorDiagnoseKind {
+ NoDiagnostics,
+ ExplainDeleted,
+ ExplainConstexpr
+};
+
+/// Information about a defaulted postfix increment or decrement operator
+/// function, as determined by analyzeDefaultedPostfixOperator.
+struct DefaultedPostfixOperatorInfo {
+ /// Whether the function should be defined as deleted.
+ bool Deleted = false;
+ /// Whether every function invoked by the implicit definition is constexpr.
+ bool Constexpr = true;
+};
+} // namespace
+
+/// Get the type of the first parameter of a postfix increment or decrement
+/// operator function, where the implicit object parameter (if any) is
+/// considered to be the first parameter.
+static QualType getPostfixOperatorFirstParamType(const FunctionDecl *FD) {
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FD))
+ return MD->getFunctionObjectParameterReferenceType();
+ return FD->getParamDecl(0)->getType();
+}
+
+/// Get the type C for which a well-formed defaulted postfix increment or
+/// decrement operator function is defaulted.
+static QualType getDefaultedPostfixOperatorType(const FunctionDecl *FD) {
+ return getPostfixOperatorFirstParamType(FD)
+ .getNonReferenceType()
+ .getUnqualifiedType();
+}
+
+/// Determine whether a defaulted postfix increment or decrement operator
+/// function for type \p C should be defined as deleted, as specified in C++2d
+/// [over.inc.default]p2, and whether every function it would invoke is
+/// constexpr.
+static DefaultedPostfixOperatorInfo
+analyzeDefaultedPostfixOperator(Sema &S, FunctionDecl *FD,
+ PostfixOperatorKind Kind, QualType C,
+ PostfixOperatorDiagnoseKind Diagnose) {
+ using DiagnoseKind = PostfixOperatorDiagnoseKind;
+ DefaultedPostfixOperatorInfo Info;
+ SourceLocation Loc = FD->getLocation();
+
+ // C++2d [over.inc.default]p1:
+ // Name lookups and access checks in the implicit definition of a defaulted
+ // postfix increment or decrement operator function are performed from a
+ // context equivalent to its function-body.
+ Sema::ContextRAII SavedContext(S, FD);
+
+ // Let c be an lvalue of type C.
+ OpaqueValueExpr Obj(Loc, C, VK_LValue);
+ Expr *Args[] = {&Obj};
+
+ auto Deleted = [&] {
+ Info.Deleted = true;
+ return Info;
+ };
+ auto InaccessibleDiag = [&](PostfixOperatorStep Step) {
+ return Diagnose == DiagnoseKind::ExplainDeleted
+ ? S.PDiag(diag::note_defaulted_postfix_operator_inaccessible)
+ << FD << (int)Step
+ : S.PDiag();
+ };
+ auto NoteCallsDeleted = [&](PostfixOperatorStep Step, FunctionDecl *Callee) {
+ if (Diagnose != DiagnoseKind::ExplainDeleted)
+ return;
+ S.Diag(Loc, diag::note_defaulted_postfix_operator_calls_deleted)
+ << FD << (int)Step;
+ if (Callee)
+ S.NoteDeletedFunction(Callee);
+ };
+ auto CheckConstexpr = [&](const FunctionDecl *Callee,
+ PostfixOperatorStep Step) {
+ if (Callee->isConstexpr())
+ return;
+ Info.Constexpr = false;
+ if (Diagnose == DiagnoseKind::ExplainConstexpr)
+ S.Diag(Callee->getLocation(),
+ diag::note_defaulted_postfix_operator_not_constexpr_here)
+ << (int)Step;
+ };
+
+ // C++2d [over.inc.default]p2:
+ // A defaulted postfix increment or decrement operator function for a type
+ // C is defined as deleted if
+ CXXRecordDecl *RD = C->getAsCXXRecordDecl();
+ if (RD) {
+ // -- C is a class type for which overload resolution, as applied to
+ // direct-initialization of a variable of type C from an lvalue of
+ // type C, does not result in a usable candidate,
+ InitializedEntity Entity = InitializedEntity::InitializeTemporary(C);
+ InitializationKind InitKind =
+ InitializationKind::CreateDirect(Loc, Loc, Loc);
+ InitializationSequence Seq(S, Entity, InitKind, Args);
+ if (Seq.Failed()) {
+ if (Diagnose == DiagnoseKind::ExplainDeleted) {
+ OverloadCandidateSet &Cands = Seq.getFailedCandidateSet();
+ OverloadingResult OR =
+ Seq.getFailureKind() ==
+ InitializationSequence::FK_ConstructorOverloadFailed
+ ? Seq.getFailedOverloadResult()
+ : OR_No_Viable_Function;
+ OverloadCandidateSet::iterator Best;
+ switch (OR) {
+ case OR_Deleted:
+ NoteCallsDeleted(PostfixOperatorStep::Constructor,
+ Cands.BestViableFunction(S, Loc, Best) == OR_Deleted
+ ? Best->Function
+ : nullptr);
+ break;
+ case OR_Ambiguous:
+ Cands.NoteCandidates(
+ PartialDiagnosticAt(
+ Loc, S.PDiag(diag::note_defaulted_postfix_operator_ambiguous)
+ << FD << 0 << C),
+ S, OCD_AmbiguousCandidates, Args, /*Opc=*/"", Loc);
+ break;
+ case OR_Success:
+ case OR_No_Viable_Function:
+ Cands.NoteCandidates(
+ PartialDiagnosticAt(
+ Loc, S.PDiag(diag::note_defaulted_postfix_operator_no_viable)
+ << FD << 0 << C),
+ S, OCD_AllCandidates, Args, /*Opc=*/"", Loc);
+ break;
+ }
+ }
+ return Deleted();
+ }
+ for (const InitializationSequence::Step &Step : Seq.steps()) {
+ if (Step.Kind != InitializationSequence::SK_ConstructorInitialization)
+ continue;
+ if (!S.isMemberAccessibleForDeletion(
+ RD, Step.Function.FoundDecl, C, Loc,
+ InaccessibleDiag(PostfixOperatorStep::Constructor)))
+ return Deleted();
+ CheckConstexpr(Step.Function.Function, PostfixOperatorStep::Constructor);
+ }
+
+ // -- C has a destructor that is deleted or inaccessible from the context
+ // of the function-body, or
+ if (CXXDestructorDecl *Dtor = S.LookupDestructor(RD)) {
+ if (Dtor->isDeleted()) {
+ NoteCallsDeleted(PostfixOperatorStep::Destructor, Dtor);
+ return Deleted();
+ }
+ if (!S.isMemberAccessibleForDeletion(
+ RD, DeclAccessPair::make(Dtor, Dtor->getAccess()), C, Loc,
+ InaccessibleDiag(PostfixOperatorStep::Destructor)))
+ return Deleted();
+ }
+ }
+
+ // -- for an lvalue c of type C, overload resolution as applied to ++c for a
+ // postfix increment operator function or --c for a postfix decrement
+ // operator function does not result in a usable candidate.
+ bool IsIncrement = Kind == PostfixOperatorKind::Increment;
+ PostfixOperatorStep PrefixStep = IsIncrement
+ ? PostfixOperatorStep::PrefixIncrement
+ : PostfixOperatorStep::PrefixDecrement;
+ UnresolvedSet<16> Fns;
+ if (auto *DFI = FD->getDefaultedOrDeletedInfo())
+ Fns.assign(DFI->getUnqualifiedLookups().begin(),
+ DFI->getUnqualifiedLookups().end());
+ OverloadCandidateSet CandidateSet(Loc, OverloadCandidateSet::CSK_Operator);
+ S.LookupOverloadedUnaryOp(
+ CandidateSet, IsIncrement ? OO_PlusPlus : OO_MinusMinus, Fns, Args);
+
+ OverloadCandidateSet::iterator Best;
+ switch (CandidateSet.BestViableFunction(S, Loc, Best)) {
+ case OR_Success:
+ if (FunctionDecl *Prefix = Best->Function) {
+ // Only a member of C can be inaccessible here; the non-member
+ // candidates were found by unqualified lookup and ADL.
+ if (RD && Best->FoundDecl.getDecl() &&
+ Best->FoundDecl.getDecl()->isCXXClassMember() &&
+ !S.isMemberAccessibleForDeletion(RD, Best->FoundDecl, C, Loc,
+ InaccessibleDiag(PrefixStep)))
+ return Deleted();
+ CheckConstexpr(Prefix, PrefixStep);
+ } else if (!Best->Conversions.empty() &&
+ Best->Conversions[0].isUserDefined()) {
+ // A built-in candidate, reached through a conversion function.
+ CheckConstexpr(Best->Conversions[0].UserDefined.ConversionFunction,
+ PostfixOperatorStep::ConversionFunction);
+ }
+ break;
+
+ case OR_Ambiguous:
+ if (Diagnose == DiagnoseKind::ExplainDeleted)
+ CandidateSet.NoteCandidates(
+ PartialDiagnosticAt(
+ Loc, S.PDiag(diag::note_defaulted_postfix_operator_ambiguous)
+ << FD << (int)Kind << C),
+ S, OCD_AmbiguousCandidates, Args, IsIncrement ? "++" : "--", Loc);
+ return Deleted();
+
+ case OR_Deleted:
+ NoteCallsDeleted(PrefixStep, Best->Function);
+ return Deleted();
+
+ case OR_No_Viable_Function:
+ // Don't list the non-viable candidates: like for any other unary operator,
+ // the candidate notes can't sensibly describe an arity mismatch for a
+ // member postfix operator (whose object argument is not a parameter).
+ if (Diagnose == DiagnoseKind::ExplainDeleted)
+ S.Diag(Loc, diag::note_defaulted_postfix_operator_no_viable)
+ << FD << (int)Kind << C;
+ return Deleted();
+ }
+
+ return Info;
+}
+
+/// Build the body of a defaulted postfix increment or decrement operator
+/// function. C++2d [over.inc.default]p3:
+/// The implicit definition of a defaulted postfix increment or decrement
+/// operator function F that is not defined as deleted for a type C is
+/// equivalent to:
+/// C tmp(c);
+/// ++c; // or --c
+/// return tmp;
+/// where tmp is a variable defined for exposition only, and c is an lvalue
+/// that denotes *this if F is an implicit object member function, or the
+/// first parameter of F otherwise.
+static StmtResult buildDefaultedPostfixOperatorBody(Sema &S, FunctionDecl *FD,
----------------
zwuis wrote:
Each
```cpp
if (...)
return XXXError();
```
in this function needs tests.
https://github.com/llvm/llvm-project/pull/229560
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits