================
@@ -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

Reply via email to