================
@@ -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,
+                                                    PostfixOperatorKind Kind,
+                                                    SourceLocation Loc) {
+  Sema::CompoundScopeRAII CompoundScope(S);
+
+  // Build an expression denoting 'c'.
+  auto BuildObject = [&]() -> ExprResult {
+    if (const auto *MD = dyn_cast<CXXMethodDecl>(FD);
+        MD && MD->isImplicitObjectMemberFunction()) {
+      ExprResult This = S.ActOnCXXThis(Loc);
+      if (This.isInvalid())
+        return ExprError();
+      return S.CreateBuiltinUnaryOp(Loc, UO_Deref, This.get());
+    }
+    ParmVarDecl *Param = FD->getParamDecl(0);
+    return S.BuildDeclarationNameExpr(
+        CXXScopeSpec(), DeclarationNameInfo(Param->getDeclName(), Loc), Param);
+  };
+
+  // C tmp(c);
+  ExprResult Init = BuildObject();
+  if (Init.isInvalid())
+    return StmtError();
+  QualType C = FD->getReturnType();
+  IdentifierInfo *TmpName = &S.Context.Idents.get("tmp");
+  VarDecl *Tmp =
+      VarDecl::Create(S.Context, S.CurContext, Loc, Loc, TmpName, C,
+                      S.Context.getTrivialTypeSourceInfo(C, Loc), SC_None);
+  Expr *InitArgs[] = {Init.get()};
+  S.AddInitializerToDecl(Tmp,
+                         ParenListExpr::Create(S.Context, Loc, InitArgs, Loc),
+                         /*DirectInit=*/true);
+  if (Tmp->isInvalidDecl())
+    return StmtError();
+  StmtResult TmpStmt = S.ActOnDeclStmt(S.ConvertDeclToDeclGroup(Tmp), Loc, 
Loc);
+  if (TmpStmt.isInvalid())
+    return StmtError();
+
+  // ++c; or --c;
+  ExprResult Obj = BuildObject();
+  if (Obj.isInvalid())
+    return StmtError();
+  UnaryOperatorKind Opc =
+      Kind == PostfixOperatorKind::Increment ? UO_PreInc : UO_PreDec;
+  ExprResult Prefix;
+  if (Obj.get()->getType()->isOverloadableType()) {
+    UnresolvedSet<16> Fns;
+    if (auto *DFI = FD->getDefaultedOrDeletedInfo())
+      Fns.assign(DFI->getUnqualifiedLookups().begin(),
+                 DFI->getUnqualifiedLookups().end());
+    Prefix = S.CreateOverloadedUnaryOp(Loc, Opc, Fns, Obj.get(),
+                                       /*RequiresADL=*/true);
+  } else {
+    Prefix = S.CreateBuiltinUnaryOp(Loc, Opc, Obj.get());
+  }
+  if (Prefix.isInvalid())
+    return StmtError();
+  StmtResult PrefixStmt = S.ActOnExprStmt(Prefix, /*DiscardedValue=*/true);
+  if (PrefixStmt.isInvalid())
+    return StmtError();
+
+  // return tmp;
+  ExprResult TmpRef = S.BuildDeclarationNameExpr(
+      CXXScopeSpec(), DeclarationNameInfo(TmpName, Loc), Tmp);
+  if (TmpRef.isInvalid())
+    return StmtError();
+  StmtResult Return = S.BuildReturnStmt(Loc, TmpRef.get());
+  if (Return.isInvalid())
+    return StmtError();
+  // 'tmp' is the only variable returned by this function, so it qualifies for
+  // the named return value optimization.
+  if (cast<ReturnStmt>(Return.get())->getNRVOCandidate() == Tmp)
+    Tmp->setNRVOVariable(true);
+
+  Stmt *Stmts[] = {TmpStmt.get(), PrefixStmt.get(), Return.get()};
+  return S.ActOnCompoundStmt(Loc, Loc, Stmts, /*IsStmtExpr=*/false);
+}
+
+bool Sema::CheckExplicitlyDefaultedPostfixOperator(Scope *S, FunctionDecl *FD,
+                                                   PostfixOperatorKind Kind) {
+  assert(Kind != PostfixOperatorKind::None &&
+         "not a defaulted postfix operator");
+  bool IsIncrement = Kind == PostfixOperatorKind::Increment;
+
+  // Perform the unqualified lookup of the prefix operator that the implicit
+  // definition needs. 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.
+  if (S) {
+    UnresolvedSet<16> Operators;
+    LookupOverloadedOperatorName(IsIncrement ? OO_PlusPlus : OO_MinusMinus, S,
+                                 Operators);
+    FD->setDefaultedOrDeletedInfo(
+        FunctionDecl::DefaultedOrDeletedFunctionInfo::Create(
+            Context, Operators.pairs(), CurFPFeatureOverrides()));
+  }
+
+  // C++2d [over.inc.default]p1:
+  //   A defaulted postfix increment or decrement operator function for a type
+  //   C shall be a non-template function that
+  //    -- has a first parameter of type "reference to C" or a first parameter
+  //       of type "reference to volatile C", where the implicit object
+  //       parameter (if any) is considered to be the first parameter,
+  //    -- is defined as defaulted in C or in a context where C is complete,
+  //       and
+  //    -- has a declared return type of C.
+  auto *MD = dyn_cast<CXXMethodDecl>(FD);
+  ParmVarDecl *FirstParam = FD->getParamDecl(0);
+  QualType FirstParamType = getPostfixOperatorFirstParamType(FD);
+  QualType ReturnType = FD->getDeclaredReturnType();
+
+  // Determine the type C. For a member function, this is the enclosing class.
+  // For a non-member function, infer it from the first parameter, falling back
+  // to the return type if the first parameter is not a reference.
+  QualType C;
+  if (MD)
+    C = Context.getCanonicalTagType(MD->getParent());
+  else if (FirstParamType->isReferenceType())
+    C = FirstParamType.getNonReferenceType().getUnqualifiedType();
+  else if (!ReturnType->isReferenceType())
+    C = ReturnType.getUnqualifiedType();
+  if (C.isNull() || (!MD && !C->isDependentType() && !C->isRecordType() &&
+                     !C->isEnumeralType())) {
+    Diag(FirstParam->getBeginLoc(),
+         diag::err_defaulted_postfix_operator_param_unknown)
+        << (int)Kind << FirstParamType << FirstParam->getSourceRange();
+    return true;
+  }
+
+  // Check the type of the first parameter.
+  if (!FirstParamType->isDependentType() && !C->isDependentType()) {
+    QualType Referent = FirstParamType->isReferenceType()
+                            ? FirstParamType.getNonReferenceType()
+                            : QualType();
+    if (Referent.isNull() || Referent.isConstQualified() ||
+        !Context.hasSameType(Referent.getUnqualifiedType(), C)) {
+      if (MD && MD->isImplicitObjectMemberFunction()) {
+        // The implicit object parameter is always a reference to the class,
+        // so the only possible problem is a const qualifier.
+        Diag(MD->getLocation(), diag::err_defaulted_postfix_operator_const)
+            << (int)Kind;
+      } else {
+        Diag(FirstParam->getBeginLoc(),
+             diag::err_defaulted_postfix_operator_param)
+            << (int)Kind << !MD << FirstParamType
+            << Context.getLValueReferenceType(C)
+            << Context.getLValueReferenceType(C.withVolatile())
+            << FirstParam->getSourceRange();
+      }
+      return true;
+    }
+  }
+
+  // Check the declared return type. A placeholder type is not permitted; this
+  // would need P2952R2 (auto& operator=(X&&) = default), which is not yet
+  // part of the standard.
+  if (ReturnType->getContainedDeducedType() ||
+      (!ReturnType->isDependentType() && !C->isDependentType() &&
+       !Context.hasSameType(ReturnType, C))) {
+    Diag(FD->getLocation(), diag::err_defaulted_postfix_operator_return_type)
+        << (int)Kind << C << ReturnType << FD->getReturnTypeSourceRange();
+    return true;
+  }
+
+  // For a defaulted function in a dependent context, defer all remaining
+  // checks until instantiation.
+  if (C->isDependentType() || FD->isDependentContext())
+    return false;
+
+  //    -- is defined as defaulted in C or in a context where C is complete
+  if (RequireCompleteType(FD->getLocation(), C,
+                          diag::err_defaulted_postfix_operator_incomplete,
+                          (int)Kind))
+    return true;
+
+  // Determine whether the function should be defined as deleted.
+  DefaultedPostfixOperatorInfo Info = analyzeDefaultedPostfixOperator(
+      *this, FD, Kind, C, PostfixOperatorDiagnoseKind::NoDiagnostics);
+
+  bool First = FD == FD->getCanonicalDecl();
+
+  if (!First) {
+    if (Info.Deleted) {
+      // C++11 [dcl.fct.def.default]p4:
+      //   [For a] user-provided explicitly-defaulted function [...] if such a
+      //   function is implicitly defined as deleted, the program is 
ill-formed.
+      //
+      // This is really just a consequence of the general rule that you can
+      // only delete a function on its first declaration.
+      Diag(FD->getLocation(),
+           diag::err_non_first_default_postfix_operator_deletes)
+          << (int)Kind;
+      analyzeDefaultedPostfixOperator(
+          *this, FD, Kind, C, PostfixOperatorDiagnoseKind::ExplainDeleted);
+      return true;
+    }
+    if (isa<CXXRecordDecl>(FD->getLexicalDeclContext())) {
+      // C++2d [over.inc.default]p1:
+      //   A definition of a postfix increment or decrement operator as
+      //   defaulted that appears in a class shall be the first declaration of
+      //   that function.
+      Diag(FD->getLocation(),
+           diag::err_non_first_default_postfix_operator_in_class)
+          << (int)Kind;
+      Diag(FD->getCanonicalDecl()->getLocation(),
+           diag::note_previous_declaration);
+      return true;
+    }
+  }
+
+  // If we want to delete the function, then do so; there's nothing else to
+  // check in that case.
+  if (Info.Deleted) {
+    SetDeclDeleted(FD, FD->getLocation());
+    if (!inTemplateInstantiation() && !FD->isImplicit()) {
+      Diag(FD->getLocation(), diag::warn_defaulted_postfix_operator_deleted)
+          << (int)Kind;
+      analyzeDefaultedPostfixOperator(
+          *this, FD, Kind, C, PostfixOperatorDiagnoseKind::ExplainDeleted);
+      if (FD->getDefaultLoc().isValid())
+        Diag(FD->getDefaultLoc(), diag::note_replace_equals_default_to_delete)
+            << FixItHint::CreateReplacement(FD->getDefaultLoc(), "delete");
+    }
+    return false;
+  }
+
+  // C++2a [dcl.fct.def.default]p3 [P2002R0]:
+  //   An explicitly-defaulted function that is not defined as deleted may be
+  //   declared constexpr or consteval only if it is constexpr-compatible.
+  // By analogy with C++2a [class.compare.default]p3, we consider a defaulted
+  // postfix operator to be constexpr-compatible if its parameter and return
+  // types are literal types and no overload resolution performed when
+  // determining whether to delete it results in a non-constexpr function.
+  //
+  // C++23 [dcl.fct.def.default]p3 [P2448R2] removed this restriction: a
+  // defaulted function may always be declared constexpr.
+  if (FD->isConstexpr() && !getLangOpts().CPlusPlus23 &&
+      !FD->isTemplateInstantiation() &&
+      CheckConstexprReturnType(*this, FD, CheckConstexprKind::Diagnose) &&
+      CheckConstexprParameterTypes(*this, FD, CheckConstexprKind::Diagnose) &&
+      !Info.Constexpr) {
+    Diag(FD->getBeginLoc(),
+         diag::err_defaulted_postfix_operator_constexpr_mismatch)
+        << (int)Kind << FD->isConsteval();
+    analyzeDefaultedPostfixOperator(
+        *this, FD, Kind, C, PostfixOperatorDiagnoseKind::ExplainConstexpr);
+    return true;
+  }
+
+  if (First) {
+    // C++11 [dcl.fct.def.default]p5:
+    //   A function explicitly defaulted on its first declaration is implicitly
+    //   inline.
+    FD->setImplicitlyInline();
+
+    // C++2a [dcl.fct.def.default]p3 [P2002R0]:
+    //   If a constexpr-compatible function is explicitly defaulted on its
+    //   first declaration, it is implicitly considered to be constexpr.
+    // C++23 [dcl.fct.def.default]p3 [P2448R2]:
+    //   A function explicitly defaulted on its first declaration is [...]
+    //   implicitly constexpr if it is constexpr-suitable.
+    // A postfix operator function is always constexpr-suitable. The implicit
+    // definition declares a variable, so it can only be constexpr in C++14
+    // and later, and a virtual function can only be constexpr in C++20 and
+    // later.
+    if (!FD->isConstexpr() && getLangOpts().CPlusPlus14 &&
+        (getLangOpts().CPlusPlus20 || !MD || !MD->isVirtual()) &&
+        (getLangOpts().CPlusPlus23 ||
+         (Info.Constexpr &&
+          CheckConstexprReturnType(*this, FD, CheckConstexprKind::CheckValid) 
&&
+          CheckConstexprParameterTypes(*this, FD,
+                                       CheckConstexprKind::CheckValid))))
+      FD->setConstexprKind(ConstexprSpecKind::Constexpr);
+
+    // C++2d [except.spec]p10:
+    //   The exception specification for [...] a postfix increment or decrement
+    //   operator function without a noexcept-specifier that is defaulted on
+    //   its first declaration is potentially-throwing if and only if any
+    //   expression in the implicit definition is potentially-throwing.
+    if (FD->getExceptionSpecType() == EST_None) {
+      auto *FPT = FD->getType()->castAs<FunctionProtoType>();
+      FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo();
+      EPI.ExceptionSpec.Type = EST_Unevaluated;
+      EPI.ExceptionSpec.SourceDecl = FD;
+      FD->setType(Context.getFunctionType(FPT->getReturnType(),
+                                          FPT->getParamTypes(), EPI));
+    }
+  }
+
+  return false;
+}
+
+void Sema::DefineDefaultedPostfixOperator(SourceLocation UseLoc,
+                                          FunctionDecl *FD,
+                                          PostfixOperatorKind Kind) {
+  assert(FD->isDefaulted() && !FD->isDeleted() &&
+         !FD->doesThisDeclarationHaveABody());
+  if (FD->willHaveBody() || FD->isInvalidDecl())
+    return;
+
+  SynthesizedFunctionScope Scope(*this, FD);
+
+  // Add a context note for diagnostics produced after this point.
+  Scope.addContextNote(UseLoc);
+
+  DefaultedFunctionFPFeaturesRAII RestoreFP(*this, FD);
+
+  {
+    // Build and set up the function body.
+    SourceLocation BodyLoc =
+        FD->getEndLoc().isValid() ? FD->getEndLoc() : FD->getLocation();
+    StmtResult Body =
+        buildDefaultedPostfixOperatorBody(*this, FD, Kind, BodyLoc);
+    if (Body.isInvalid()) {
+      FD->setInvalidDecl();
+      return;
+    }
+    FD->setBody(Body.get());
+    FD->markUsed(Context);
+  }
+
+  // The exception specification is needed because we are defining the
+  // function. Note that this will reuse the body we just built.
+  ResolveExceptionSpec(UseLoc, FD->getType()->castAs<FunctionProtoType>());
+
+  if (ASTMutationListener *L = getASTMutationListener())
+    L->CompletedImplicitDefinition(FD);
+}
+
+static Sema::ImplicitExceptionSpecification
+ComputeDefaultedPostfixOperatorExceptionSpec(Sema &S, SourceLocation Loc,
+                                             FunctionDecl *FD,
+                                             PostfixOperatorKind Kind) {
+  ComputingExceptionSpec CES(S, FD, Loc);
+  Sema::ImplicitExceptionSpecification ExceptSpec(S);
+
+  if (FD->isInvalidDecl())
+    return ExceptSpec;
+
+  // The common case is that we just defined the function. In that case, just
+  // look at whether the body can throw.
+  if (Stmt *FunctionBody = FD->getBody()) {
+    ExceptSpec.CalledStmt(FunctionBody);
+  } else {
+    // Otherwise, build a body so we can check it. Pretend to synthesize the
+    // function body in an unevaluated context: we are not permitted to mark
+    // its callees as referenced.
+    Sema::SynthesizedFunctionScope Scope(S, FD);
+    EnterExpressionEvaluationContext Context(
+        S, Sema::ExpressionEvaluationContext::Unevaluated);
+
+    SourceLocation BodyLoc =
+        FD->getEndLoc().isValid() ? FD->getEndLoc() : FD->getLocation();
+    StmtResult Body = buildDefaultedPostfixOperatorBody(S, FD, Kind, BodyLoc);
+    if (!Body.isInvalid())
+      ExceptSpec.CalledStmt(Body.get());
+  }
+
+  // The implicit definition also destroys the temporary copy.
+  if (CXXRecordDecl *RD = FD->getReturnType()->getAsCXXRecordDecl())
+    if (CXXDestructorDecl *Dtor = S.LookupDestructor(RD))
+      ExceptSpec.CalledDecl(Loc, Dtor);
----------------
zwuis wrote:

Is it necessary? I'm surprised that this is not computed as part of function 
body.

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