[clang] 762a478 - Revert "[Clang][C++26] Implement "Ordering of constraints involving fold expressions" (#99007)

via cfe-commits cfe-commits at lists.llvm.org
Tue Jul 16 02:00:31 PDT 2024


Author: cor3ntin
Date: 2024-07-16T11:00:27+02:00
New Revision: 762a47828ef6c19426e37e1ecae6768035a4ccac

URL: https://github.com/llvm/llvm-project/commit/762a47828ef6c19426e37e1ecae6768035a4ccac
DIFF: https://github.com/llvm/llvm-project/commit/762a47828ef6c19426e37e1ecae6768035a4ccac.diff

LOG: Revert "[Clang][C++26] Implement "Ordering of constraints involving fold expressions" (#99007)

Reverts llvm/llvm-project#98160

Breaks CI on some architectures

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/Sema/Sema.h
    clang/include/clang/Sema/SemaConcept.h
    clang/lib/Sema/SemaConcept.cpp
    clang/lib/Sema/SemaTemplateVariadic.cpp
    clang/www/cxx_status.html

Removed: 
    clang/test/SemaCXX/cxx2c-fold-exprs.cpp


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index cb35825b71e3e..969856a8f978c 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -278,9 +278,6 @@ C++2c Feature Support
 
 - Implemented `P3144R2 Deleting a Pointer to an Incomplete Type Should be Ill-formed <https://wg21.link/P3144R2>`_.
 
-- Implemented `P2963R3 Ordering of constraints involving fold expressions <https://wg21.link/P2963R3>`_.
-
-
 Resolutions to C++ Defect Reports
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 - Substitute template parameter pack, when it is not explicitly specified

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 3cb1aa935fe46..48dff1b76cc57 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -14078,11 +14078,6 @@ class Sema final : public SemaBase {
       const DeclarationNameInfo &NameInfo,
       SmallVectorImpl<UnexpandedParameterPack> &Unexpanded);
 
-  /// Collect the set of unexpanded parameter packs within the given
-  /// expression.
-  static void collectUnexpandedParameterPacks(
-      Expr *E, SmallVectorImpl<UnexpandedParameterPack> &Unexpanded);
-
   /// Invoked when parsing a template argument followed by an
   /// ellipsis, which creates a pack expansion.
   ///

diff  --git a/clang/include/clang/Sema/SemaConcept.h b/clang/include/clang/Sema/SemaConcept.h
index 8fb7dd6838e57..711443505174f 100644
--- a/clang/include/clang/Sema/SemaConcept.h
+++ b/clang/include/clang/Sema/SemaConcept.h
@@ -75,26 +75,6 @@ struct AtomicConstraint {
   }
 };
 
-struct FoldExpandedConstraint;
-
-using NormalFormConstraint =
-    llvm::PointerUnion<AtomicConstraint *, FoldExpandedConstraint *>;
-struct NormalizedConstraint;
-using NormalForm =
-    llvm::SmallVector<llvm::SmallVector<NormalFormConstraint, 2>, 4>;
-
-// A constraint is in conjunctive normal form when it is a conjunction of
-// clauses where each clause is a disjunction of atomic constraints. For atomic
-// constraints A, B, and C, the constraint A  ∧ (B  ∨ C) is in conjunctive
-// normal form.
-NormalForm makeCNF(const NormalizedConstraint &Normalized);
-
-// A constraint is in disjunctive normal form when it is a disjunction of
-// clauses where each clause is a conjunction of atomic constraints. For atomic
-// constraints A, B, and C, the disjunctive normal form of the constraint A
-//  ∧ (B  ∨ C) is (A  ∧ B)  ∨ (A  ∧ C).
-NormalForm makeDNF(const NormalizedConstraint &Normalized);
-
 /// \brief A normalized constraint, as defined in C++ [temp.constr.normal], is
 /// either an atomic constraint, a conjunction of normalized constraints or a
 /// disjunction of normalized constraints.
@@ -107,17 +87,26 @@ struct NormalizedConstraint {
       std::pair<NormalizedConstraint, NormalizedConstraint> *, 1,
       CompoundConstraintKind>;
 
-  llvm::PointerUnion<AtomicConstraint *, FoldExpandedConstraint *,
-                     CompoundConstraint>
-      Constraint;
+  llvm::PointerUnion<AtomicConstraint *, CompoundConstraint> Constraint;
 
   NormalizedConstraint(AtomicConstraint *C): Constraint{C} { };
-  NormalizedConstraint(FoldExpandedConstraint *C) : Constraint{C} {};
-
   NormalizedConstraint(ASTContext &C, NormalizedConstraint LHS,
-                       NormalizedConstraint RHS, CompoundConstraintKind Kind);
-
-  NormalizedConstraint(ASTContext &C, const NormalizedConstraint &Other);
+                       NormalizedConstraint RHS, CompoundConstraintKind Kind)
+      : Constraint{CompoundConstraint{
+            new (C) std::pair<NormalizedConstraint, NormalizedConstraint>{
+                std::move(LHS), std::move(RHS)}, Kind}} { };
+
+  NormalizedConstraint(ASTContext &C, const NormalizedConstraint &Other) {
+    if (Other.isAtomic()) {
+      Constraint = new (C) AtomicConstraint(*Other.getAtomicConstraint());
+    } else {
+      Constraint = CompoundConstraint(
+          new (C) std::pair<NormalizedConstraint, NormalizedConstraint>{
+              NormalizedConstraint(C, Other.getLHS()),
+              NormalizedConstraint(C, Other.getRHS())},
+              Other.getCompoundKind());
+    }
+  }
   NormalizedConstraint(NormalizedConstraint &&Other):
       Constraint(Other.Constraint) {
     Other.Constraint = nullptr;
@@ -131,24 +120,20 @@ struct NormalizedConstraint {
     return *this;
   }
 
-  bool isAtomic() const { return Constraint.is<AtomicConstraint *>(); }
-  bool isFoldExpanded() const {
-    return Constraint.is<FoldExpandedConstraint *>();
-  }
-  bool isCompound() const { return Constraint.is<CompoundConstraint>(); }
-
   CompoundConstraintKind getCompoundKind() const {
-    assert(isCompound() && "getCompoundKind on a non-compound constraint..");
+    assert(!isAtomic() && "getCompoundKind called on atomic constraint.");
     return Constraint.get<CompoundConstraint>().getInt();
   }
 
+  bool isAtomic() const { return Constraint.is<AtomicConstraint *>(); }
+
   NormalizedConstraint &getLHS() const {
-    assert(isCompound() && "getLHS called on a non-compound constraint.");
+    assert(!isAtomic() && "getLHS called on atomic constraint.");
     return Constraint.get<CompoundConstraint>().getPointer()->first;
   }
 
   NormalizedConstraint &getRHS() const {
-    assert(isCompound() && "getRHS called on a non-compound constraint.");
+    assert(!isAtomic() && "getRHS called on atomic constraint.");
     return Constraint.get<CompoundConstraint>().getPointer()->second;
   }
 
@@ -158,12 +143,6 @@ struct NormalizedConstraint {
     return Constraint.get<AtomicConstraint *>();
   }
 
-  FoldExpandedConstraint *getFoldExpandedConstraint() const {
-    assert(isFoldExpanded() &&
-           "getFoldExpandedConstraint called on non-fold-expanded constraint.");
-    return Constraint.get<FoldExpandedConstraint *>();
-  }
-
 private:
   static std::optional<NormalizedConstraint>
   fromConstraintExprs(Sema &S, NamedDecl *D, ArrayRef<const Expr *> E);
@@ -171,112 +150,6 @@ struct NormalizedConstraint {
   fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E);
 };
 
-struct FoldExpandedConstraint {
-  enum class FoldOperatorKind { And, Or } Kind;
-  NormalizedConstraint Constraint;
-  const Expr *Pattern;
-
-  FoldExpandedConstraint(FoldOperatorKind K, NormalizedConstraint C,
-                         const Expr *Pattern)
-      : Kind(K), Constraint(std::move(C)), Pattern(Pattern) {};
-
-  template <typename AtomicSubsumptionEvaluator>
-  bool subsumes(const FoldExpandedConstraint &Other,
-                const AtomicSubsumptionEvaluator &E) const;
-
-  static bool AreCompatibleForSubsumption(const FoldExpandedConstraint &A,
-                                          const FoldExpandedConstraint &B);
-};
-
-const NormalizedConstraint *getNormalizedAssociatedConstraints(
-    Sema &S, NamedDecl *ConstrainedDecl,
-    ArrayRef<const Expr *> AssociatedConstraints);
-
-template <typename AtomicSubsumptionEvaluator>
-bool subsumes(const NormalForm &PDNF, const NormalForm &QCNF,
-              const AtomicSubsumptionEvaluator &E) {
-  // C++ [temp.constr.order] p2
-  //   Then, P subsumes Q if and only if, for every disjunctive clause Pi in the
-  //   disjunctive normal form of P, Pi subsumes every conjunctive clause Qj in
-  //   the conjuctive normal form of Q, where [...]
-  for (const auto &Pi : PDNF) {
-    for (const auto &Qj : QCNF) {
-      // C++ [temp.constr.order] p2
-      //   - [...] a disjunctive clause Pi subsumes a conjunctive clause Qj if
-      //     and only if there exists an atomic constraint Pia in Pi for which
-      //     there exists an atomic constraint, Qjb, in Qj such that Pia
-      //     subsumes Qjb.
-      bool Found = false;
-      for (NormalFormConstraint Pia : Pi) {
-        for (NormalFormConstraint Qjb : Qj) {
-          if (Pia.is<FoldExpandedConstraint *>() &&
-              Qjb.is<FoldExpandedConstraint *>()) {
-            if (Pia.get<FoldExpandedConstraint *>()->subsumes(
-                    *Qjb.get<FoldExpandedConstraint *>(), E)) {
-              Found = true;
-              break;
-            }
-          } else if (Pia.is<AtomicConstraint *>() &&
-                     Qjb.is<AtomicConstraint *>()) {
-            if (E(*Pia.get<AtomicConstraint *>(),
-                  *Qjb.get<AtomicConstraint *>())) {
-              Found = true;
-              break;
-            }
-          }
-        }
-        if (Found)
-          break;
-      }
-      if (!Found)
-        return false;
-    }
-  }
-  return true;
-}
-
-template <typename AtomicSubsumptionEvaluator>
-bool subsumes(Sema &S, NamedDecl *DP, ArrayRef<const Expr *> P, NamedDecl *DQ,
-              ArrayRef<const Expr *> Q, bool &Subsumes,
-              const AtomicSubsumptionEvaluator &E) {
-  // C++ [temp.constr.order] p2
-  //   In order to determine if a constraint P subsumes a constraint Q, P is
-  //   transformed into disjunctive normal form, and Q is transformed into
-  //   conjunctive normal form. [...]
-  const NormalizedConstraint *PNormalized =
-      getNormalizedAssociatedConstraints(S, DP, P);
-  if (!PNormalized)
-    return true;
-  NormalForm PDNF = makeDNF(*PNormalized);
-
-  const NormalizedConstraint *QNormalized =
-      getNormalizedAssociatedConstraints(S, DQ, Q);
-  if (!QNormalized)
-    return true;
-  NormalForm QCNF = makeCNF(*QNormalized);
-
-  Subsumes = subsumes(PDNF, QCNF, E);
-  return false;
-}
-
-template <typename AtomicSubsumptionEvaluator>
-bool FoldExpandedConstraint::subsumes(
-    const FoldExpandedConstraint &Other,
-    const AtomicSubsumptionEvaluator &E) const {
-
-  // [C++26] [temp.constr.order]
-  // a fold expanded constraint A subsumes another fold expanded constraint B if
-  // they are compatible for subsumption, have the same fold-operator, and the
-  // constraint of A subsumes that of B
-
-  if (Kind != Other.Kind || !AreCompatibleForSubsumption(*this, Other))
-    return false;
-
-  NormalForm PDNF = makeDNF(this->Constraint);
-  NormalForm QCNF = makeCNF(Other.Constraint);
-  return clang::subsumes(PDNF, QCNF, E);
-}
-
 } // clang
 
 #endif // LLVM_CLANG_SEMA_SEMACONCEPT_H

diff  --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 70562a327dcaa..54891150da20f 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -65,7 +65,6 @@ class LogicalBinOp {
 
   const Expr *getLHS() const { return LHS; }
   const Expr *getRHS() const { return RHS; }
-  OverloadedOperatorKind getOp() const { return Op; }
 
   ExprResult recreateBinOp(Sema &SemaRef, ExprResult LHS) const {
     return recreateBinOp(SemaRef, LHS, const_cast<Expr *>(getRHS()));
@@ -178,177 +177,77 @@ struct SatisfactionStackRAII {
 };
 } // namespace
 
-template <typename ConstraintEvaluator>
+template <typename AtomicEvaluator>
 static ExprResult
 calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
                                 ConstraintSatisfaction &Satisfaction,
-                                const ConstraintEvaluator &Evaluator);
-
-template <typename ConstraintEvaluator>
-static ExprResult
-calculateConstraintSatisfaction(Sema &S, const Expr *LHS,
-                                OverloadedOperatorKind Op, const Expr *RHS,
-                                ConstraintSatisfaction &Satisfaction,
-                                const ConstraintEvaluator &Evaluator) {
-  size_t EffectiveDetailEndIndex = Satisfaction.Details.size();
-
-  ExprResult LHSRes =
-      calculateConstraintSatisfaction(S, LHS, Satisfaction, Evaluator);
+                                AtomicEvaluator &&Evaluator) {
+  ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts();
 
-  if (LHSRes.isInvalid())
-    return ExprError();
+  if (LogicalBinOp BO = ConstraintExpr) {
+    size_t EffectiveDetailEndIndex = Satisfaction.Details.size();
+    ExprResult LHSRes = calculateConstraintSatisfaction(
+        S, BO.getLHS(), Satisfaction, Evaluator);
 
-  bool IsLHSSatisfied = Satisfaction.IsSatisfied;
-
-  if (Op == clang::OO_PipePipe && IsLHSSatisfied)
-    // [temp.constr.op] p3
-    //    A disjunction is a constraint taking two operands. To determine if
-    //    a disjunction is satisfied, the satisfaction of the first operand
-    //    is checked. If that is satisfied, the disjunction is satisfied.
-    //    Otherwise, the disjunction is satisfied if and only if the second
-    //    operand is satisfied.
-    // LHS is instantiated while RHS is not. Skip creating invalid BinaryOp.
-    return LHSRes;
-
-  if (Op == clang::OO_AmpAmp && !IsLHSSatisfied)
-    // [temp.constr.op] p2
-    //    A conjunction is a constraint taking two operands. To determine if
-    //    a conjunction is satisfied, the satisfaction of the first operand
-    //    is checked. If that is not satisfied, the conjunction is not
-    //    satisfied. Otherwise, the conjunction is satisfied if and only if
-    //    the second operand is satisfied.
-    // LHS is instantiated while RHS is not. Skip creating invalid BinaryOp.
-    return LHSRes;
-
-  ExprResult RHSRes =
-      calculateConstraintSatisfaction(S, RHS, Satisfaction, Evaluator);
-  if (RHSRes.isInvalid())
-    return ExprError();
-
-  bool IsRHSSatisfied = Satisfaction.IsSatisfied;
-  // Current implementation adds diagnostic information about the falsity
-  // of each false atomic constraint expression when it evaluates them.
-  // When the evaluation results to `false || true`, the information
-  // generated during the evaluation of left-hand side is meaningless
-  // because the whole expression evaluates to true.
-  // The following code removes the irrelevant diagnostic information.
-  // FIXME: We should probably delay the addition of diagnostic information
-  // until we know the entire expression is false.
-  if (Op == clang::OO_PipePipe && IsRHSSatisfied) {
-    auto EffectiveDetailEnd = Satisfaction.Details.begin();
-    std::advance(EffectiveDetailEnd, EffectiveDetailEndIndex);
-    Satisfaction.Details.erase(EffectiveDetailEnd, Satisfaction.Details.end());
-  }
+    if (LHSRes.isInvalid())
+      return ExprError();
 
-  if (!LHSRes.isUsable() || !RHSRes.isUsable())
-    return ExprEmpty();
+    bool IsLHSSatisfied = Satisfaction.IsSatisfied;
 
-  return BinaryOperator::Create(S.Context, LHSRes.get(), RHSRes.get(),
-                                BinaryOperator::getOverloadedOpcode(Op),
-                                S.Context.BoolTy, VK_PRValue, OK_Ordinary,
-                                LHS->getBeginLoc(), FPOptionsOverride{});
-}
+    if (BO.isOr() && IsLHSSatisfied)
+      // [temp.constr.op] p3
+      //    A disjunction is a constraint taking two operands. To determine if
+      //    a disjunction is satisfied, the satisfaction of the first operand
+      //    is checked. If that is satisfied, the disjunction is satisfied.
+      //    Otherwise, the disjunction is satisfied if and only if the second
+      //    operand is satisfied.
+      // LHS is instantiated while RHS is not. Skip creating invalid BinaryOp.
+      return LHSRes;
 
-template <typename ConstraintEvaluator>
-static ExprResult
-calculateConstraintSatisfaction(Sema &S, const CXXFoldExpr *FE,
-                                ConstraintSatisfaction &Satisfaction,
-                                const ConstraintEvaluator &Evaluator) {
-  bool Conjunction = FE->getOperator() == BinaryOperatorKind::BO_LAnd;
-  size_t EffectiveDetailEndIndex = Satisfaction.Details.size();
-
-  ExprResult Out;
-  if (FE->isLeftFold() && FE->getInit()) {
-    Out = calculateConstraintSatisfaction(S, FE->getInit(), Satisfaction,
-                                          Evaluator);
-    if (Out.isInvalid())
+    if (BO.isAnd() && !IsLHSSatisfied)
+      // [temp.constr.op] p2
+      //    A conjunction is a constraint taking two operands. To determine if
+      //    a conjunction is satisfied, the satisfaction of the first operand
+      //    is checked. If that is not satisfied, the conjunction is not
+      //    satisfied. Otherwise, the conjunction is satisfied if and only if
+      //    the second operand is satisfied.
+      // LHS is instantiated while RHS is not. Skip creating invalid BinaryOp.
+      return LHSRes;
+
+    ExprResult RHSRes = calculateConstraintSatisfaction(
+        S, BO.getRHS(), Satisfaction, std::forward<AtomicEvaluator>(Evaluator));
+    if (RHSRes.isInvalid())
       return ExprError();
 
-    // If the first clause of a conjunction is not satisfied,
-    // or if the first clause of a disjection is satisfied,
-    // we have established satisfaction of the whole constraint
-    // and we should not continue further.
-    if (Conjunction != Satisfaction.IsSatisfied)
-      return Out;
-  }
-  std::optional<unsigned> NumExpansions =
-      Evaluator.EvaluateFoldExpandedConstraintSize(FE);
-  if (!NumExpansions)
-    return ExprError();
-  for (unsigned I = 0; I < *NumExpansions; I++) {
-    Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(S, I);
-    ExprResult Res = calculateConstraintSatisfaction(S, FE->getPattern(),
-                                                     Satisfaction, Evaluator);
-    if (Res.isInvalid())
-      return ExprError();
     bool IsRHSSatisfied = Satisfaction.IsSatisfied;
-    if (!Conjunction && IsRHSSatisfied) {
+    // Current implementation adds diagnostic information about the falsity
+    // of each false atomic constraint expression when it evaluates them.
+    // When the evaluation results to `false || true`, the information
+    // generated during the evaluation of left-hand side is meaningless
+    // because the whole expression evaluates to true.
+    // The following code removes the irrelevant diagnostic information.
+    // FIXME: We should probably delay the addition of diagnostic information
+    // until we know the entire expression is false.
+    if (BO.isOr() && IsRHSSatisfied) {
       auto EffectiveDetailEnd = Satisfaction.Details.begin();
       std::advance(EffectiveDetailEnd, EffectiveDetailEndIndex);
       Satisfaction.Details.erase(EffectiveDetailEnd,
                                  Satisfaction.Details.end());
     }
-    if (Out.isUnset())
-      Out = Res;
-    else if (!Res.isUnset()) {
-      Out = BinaryOperator::Create(
-          S.Context, Out.get(), Res.get(), FE->getOperator(), S.Context.BoolTy,
-          VK_PRValue, OK_Ordinary, FE->getBeginLoc(), FPOptionsOverride{});
-    }
-    if (Conjunction != IsRHSSatisfied)
-      return Out;
-  }
-
-  if (FE->isRightFold() && FE->getInit()) {
-    ExprResult Res = calculateConstraintSatisfaction(S, FE->getInit(),
-                                                     Satisfaction, Evaluator);
-    if (Out.isInvalid())
-      return ExprError();
-
-    if (Out.isUnset())
-      Out = Res;
-    else if (!Res.isUnset()) {
-      Out = BinaryOperator::Create(
-          S.Context, Out.get(), Res.get(), FE->getOperator(), S.Context.BoolTy,
-          VK_PRValue, OK_Ordinary, FE->getBeginLoc(), FPOptionsOverride{});
-    }
-  }
 
-  if (Out.isUnset()) {
-    Satisfaction.IsSatisfied = Conjunction;
-    Out = S.BuildEmptyCXXFoldExpr(FE->getBeginLoc(), FE->getOperator());
+    return BO.recreateBinOp(S, LHSRes, RHSRes);
   }
-  return Out;
-}
-
-template <typename ConstraintEvaluator>
-static ExprResult
-calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr,
-                                ConstraintSatisfaction &Satisfaction,
-                                const ConstraintEvaluator &Evaluator) {
-  ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts();
-
-  if (LogicalBinOp BO = ConstraintExpr)
-    return calculateConstraintSatisfaction(
-        S, BO.getLHS(), BO.getOp(), BO.getRHS(), Satisfaction, Evaluator);
 
   if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpr)) {
     // These aren't evaluated, so we don't care about cleanups, so we can just
     // evaluate these as if the cleanups didn't exist.
-    return calculateConstraintSatisfaction(S, C->getSubExpr(), Satisfaction,
-                                           Evaluator);
-  }
-
-  if (auto *FE = dyn_cast<CXXFoldExpr>(ConstraintExpr);
-      FE && S.getLangOpts().CPlusPlus26 &&
-      (FE->getOperator() == BinaryOperatorKind::BO_LAnd ||
-       FE->getOperator() == BinaryOperatorKind::BO_LOr)) {
-    return calculateConstraintSatisfaction(S, FE, Satisfaction, Evaluator);
+    return calculateConstraintSatisfaction(
+        S, C->getSubExpr(), Satisfaction,
+        std::forward<AtomicEvaluator>(Evaluator));
   }
 
   // An atomic constraint expression
-  ExprResult SubstitutedAtomicExpr =
-      Evaluator.EvaluateAtomicConstraint(ConstraintExpr);
+  ExprResult SubstitutedAtomicExpr = Evaluator(ConstraintExpr);
 
   if (SubstitutedAtomicExpr.isInvalid())
     return ExprError();
@@ -435,132 +334,91 @@ static ExprResult calculateConstraintSatisfaction(
     Sema &S, const NamedDecl *Template, SourceLocation TemplateNameLoc,
     const MultiLevelTemplateArgumentList &MLTAL, const Expr *ConstraintExpr,
     ConstraintSatisfaction &Satisfaction) {
-
-  struct ConstraintEvaluator {
-    Sema &S;
-    const NamedDecl *Template;
-    SourceLocation TemplateNameLoc;
-    const MultiLevelTemplateArgumentList &MLTAL;
-    ConstraintSatisfaction &Satisfaction;
-
-    ExprResult EvaluateAtomicConstraint(const Expr *AtomicExpr) const {
-      EnterExpressionEvaluationContext ConstantEvaluated(
-          S, Sema::ExpressionEvaluationContext::ConstantEvaluated,
-          Sema::ReuseLambdaContextDecl);
-
-      // Atomic constraint - substitute arguments and check satisfaction.
-      ExprResult SubstitutedExpression;
-      {
-        TemplateDeductionInfo Info(TemplateNameLoc);
-        Sema::InstantiatingTemplate Inst(
-            S, AtomicExpr->getBeginLoc(),
-            Sema::InstantiatingTemplate::ConstraintSubstitution{},
-            const_cast<NamedDecl *>(Template), Info,
-            AtomicExpr->getSourceRange());
-        if (Inst.isInvalid())
-          return ExprError();
-
-        llvm::FoldingSetNodeID ID;
-        if (Template &&
-            DiagRecursiveConstraintEval(S, ID, Template, AtomicExpr, MLTAL)) {
-          Satisfaction.IsSatisfied = false;
-          Satisfaction.ContainsErrors = true;
-          return ExprEmpty();
-        }
-
-        SatisfactionStackRAII StackRAII(S, Template, ID);
-
-        // We do not want error diagnostics escaping here.
-        Sema::SFINAETrap Trap(S);
-        SubstitutedExpression =
-            S.SubstConstraintExpr(const_cast<Expr *>(AtomicExpr), MLTAL);
-
-        if (SubstitutedExpression.isInvalid() || Trap.hasErrorOccurred()) {
-          // C++2a [temp.constr.atomic]p1
-          //   ...If substitution results in an invalid type or expression, the
-          //   constraint is not satisfied.
-          if (!Trap.hasErrorOccurred())
-            // A non-SFINAE error has occurred as a result of this
-            // substitution.
+  return calculateConstraintSatisfaction(
+      S, ConstraintExpr, Satisfaction, [&](const Expr *AtomicExpr) {
+        EnterExpressionEvaluationContext ConstantEvaluated(
+            S, Sema::ExpressionEvaluationContext::ConstantEvaluated,
+            Sema::ReuseLambdaContextDecl);
+
+        // Atomic constraint - substitute arguments and check satisfaction.
+        ExprResult SubstitutedExpression;
+        {
+          TemplateDeductionInfo Info(TemplateNameLoc);
+          Sema::InstantiatingTemplate Inst(S, AtomicExpr->getBeginLoc(),
+              Sema::InstantiatingTemplate::ConstraintSubstitution{},
+              const_cast<NamedDecl *>(Template), Info,
+              AtomicExpr->getSourceRange());
+          if (Inst.isInvalid())
             return ExprError();
 
-          PartialDiagnosticAt SubstDiag{SourceLocation(),
-                                        PartialDiagnostic::NullDiagnostic()};
-          Info.takeSFINAEDiagnostic(SubstDiag);
-          // FIXME: Concepts: This is an unfortunate consequence of there
-          //  being no serialization code for PartialDiagnostics and the fact
-          //  that serializing them would likely take a lot more storage than
-          //  just storing them as strings. We would still like, in the
-          //  future, to serialize the proper PartialDiagnostic as serializing
-          //  it as a string defeats the purpose of the diagnostic mechanism.
-          SmallString<128> DiagString;
-          DiagString = ": ";
-          SubstDiag.second.EmitToString(S.getDiagnostics(), DiagString);
-          unsigned MessageSize = DiagString.size();
-          char *Mem = new (S.Context) char[MessageSize];
-          memcpy(Mem, DiagString.c_str(), MessageSize);
-          Satisfaction.Details.emplace_back(
-              new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{
-                  SubstDiag.first, StringRef(Mem, MessageSize)});
-          Satisfaction.IsSatisfied = false;
-          return ExprEmpty();
+          llvm::FoldingSetNodeID ID;
+          if (Template &&
+              DiagRecursiveConstraintEval(S, ID, Template, AtomicExpr, MLTAL)) {
+            Satisfaction.IsSatisfied = false;
+            Satisfaction.ContainsErrors = true;
+            return ExprEmpty();
+          }
+
+          SatisfactionStackRAII StackRAII(S, Template, ID);
+
+          // We do not want error diagnostics escaping here.
+          Sema::SFINAETrap Trap(S);
+          SubstitutedExpression =
+              S.SubstConstraintExpr(const_cast<Expr *>(AtomicExpr), MLTAL);
+
+          if (SubstitutedExpression.isInvalid() || Trap.hasErrorOccurred()) {
+            // C++2a [temp.constr.atomic]p1
+            //   ...If substitution results in an invalid type or expression, the
+            //   constraint is not satisfied.
+            if (!Trap.hasErrorOccurred())
+              // A non-SFINAE error has occurred as a result of this
+              // substitution.
+              return ExprError();
+
+            PartialDiagnosticAt SubstDiag{SourceLocation(),
+                                          PartialDiagnostic::NullDiagnostic()};
+            Info.takeSFINAEDiagnostic(SubstDiag);
+            // FIXME: Concepts: This is an unfortunate consequence of there
+            //  being no serialization code for PartialDiagnostics and the fact
+            //  that serializing them would likely take a lot more storage than
+            //  just storing them as strings. We would still like, in the
+            //  future, to serialize the proper PartialDiagnostic as serializing
+            //  it as a string defeats the purpose of the diagnostic mechanism.
+            SmallString<128> DiagString;
+            DiagString = ": ";
+            SubstDiag.second.EmitToString(S.getDiagnostics(), DiagString);
+            unsigned MessageSize = DiagString.size();
+            char *Mem = new (S.Context) char[MessageSize];
+            memcpy(Mem, DiagString.c_str(), MessageSize);
+            Satisfaction.Details.emplace_back(
+                new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{
+                    SubstDiag.first, StringRef(Mem, MessageSize)});
+            Satisfaction.IsSatisfied = false;
+            return ExprEmpty();
+          }
         }
-      }
 
-      if (!S.CheckConstraintExpression(SubstitutedExpression.get()))
-        return ExprError();
-
-      // [temp.constr.atomic]p3: To determine if an atomic constraint is
-      // satisfied, the parameter mapping and template arguments are first
-      // substituted into its expression.  If substitution results in an
-      // invalid type or expression, the constraint is not satisfied.
-      // Otherwise, the lvalue-to-rvalue conversion is performed if necessary,
-      // and E shall be a constant expression of type bool.
-      //
-      // Perform the L to R Value conversion if necessary. We do so for all
-      // non-PRValue categories, else we fail to extend the lifetime of
-      // temporaries, and that fails the constant expression check.
-      if (!SubstitutedExpression.get()->isPRValue())
-        SubstitutedExpression = ImplicitCastExpr::Create(
-            S.Context, SubstitutedExpression.get()->getType(),
-            CK_LValueToRValue, SubstitutedExpression.get(),
-            /*BasePath=*/nullptr, VK_PRValue, FPOptionsOverride());
-
-      return SubstitutedExpression;
-    }
-
-    std::optional<unsigned>
-    EvaluateFoldExpandedConstraintSize(const CXXFoldExpr *FE) const {
-      Expr *Pattern = FE->getPattern();
-
-      SmallVector<UnexpandedParameterPack, 2> Unexpanded;
-      S.collectUnexpandedParameterPacks(Pattern, Unexpanded);
-      assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
-      bool Expand = true;
-      bool RetainExpansion = false;
-      std::optional<unsigned> OrigNumExpansions = FE->getNumExpansions(),
-                              NumExpansions = OrigNumExpansions;
-      if (S.CheckParameterPacksForExpansion(
-              FE->getEllipsisLoc(), Pattern->getSourceRange(), Unexpanded,
-              MLTAL, Expand, RetainExpansion, NumExpansions) ||
-          !Expand || RetainExpansion)
-        return std::nullopt;
-
-      if (NumExpansions && S.getLangOpts().BracketDepth < NumExpansions) {
-        S.Diag(FE->getEllipsisLoc(),
-               clang::diag::err_fold_expression_limit_exceeded)
-            << *NumExpansions << S.getLangOpts().BracketDepth
-            << FE->getSourceRange();
-        S.Diag(FE->getEllipsisLoc(), diag::note_bracket_depth);
-        return std::nullopt;
-      }
-      return NumExpansions;
-    }
-  };
+        if (!S.CheckConstraintExpression(SubstitutedExpression.get()))
+          return ExprError();
 
-  return calculateConstraintSatisfaction(
-      S, ConstraintExpr, Satisfaction,
-      ConstraintEvaluator{S, Template, TemplateNameLoc, MLTAL, Satisfaction});
+        // [temp.constr.atomic]p3: To determine if an atomic constraint is
+        // satisfied, the parameter mapping and template arguments are first
+        // substituted into its expression.  If substitution results in an
+        // invalid type or expression, the constraint is not satisfied.
+        // Otherwise, the lvalue-to-rvalue conversion is performed if necessary,
+        // and E shall be a constant expression of type bool.
+        //
+        // Perform the L to R Value conversion if necessary. We do so for all
+        // non-PRValue categories, else we fail to extend the lifetime of
+        // temporaries, and that fails the constant expression check.
+        if (!SubstitutedExpression.get()->isPRValue())
+          SubstitutedExpression = ImplicitCastExpr::Create(
+              S.Context, SubstitutedExpression.get()->getType(),
+              CK_LValueToRValue, SubstitutedExpression.get(),
+              /*BasePath=*/nullptr, VK_PRValue, FPOptionsOverride());
+
+        return SubstitutedExpression;
+      });
 }
 
 static bool CheckConstraintSatisfaction(
@@ -676,21 +534,13 @@ bool Sema::CheckConstraintSatisfaction(
 
 bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr,
                                        ConstraintSatisfaction &Satisfaction) {
-
-  struct ConstraintEvaluator {
-    Sema &S;
-    ExprResult EvaluateAtomicConstraint(const Expr *AtomicExpr) const {
-      return S.PerformContextuallyConvertToBool(const_cast<Expr *>(AtomicExpr));
-    }
-
-    std::optional<unsigned>
-    EvaluateFoldExpandedConstraintSize(const CXXFoldExpr *FE) const {
-      return 0;
-    }
-  };
-
-  return calculateConstraintSatisfaction(*this, ConstraintExpr, Satisfaction,
-                                         ConstraintEvaluator{*this})
+  return calculateConstraintSatisfaction(
+             *this, ConstraintExpr, Satisfaction,
+             [this](const Expr *AtomicExpr) -> ExprResult {
+               // We only do this to immitate lvalue-to-rvalue conversion.
+               return PerformContextuallyConvertToBool(
+                   const_cast<Expr *>(AtomicExpr));
+             })
       .isInvalid();
 }
 
@@ -1385,34 +1235,18 @@ Sema::getNormalizedAssociatedConstraints(
   return CacheEntry->second;
 }
 
-const NormalizedConstraint *clang::getNormalizedAssociatedConstraints(
-    Sema &S, NamedDecl *ConstrainedDecl,
-    ArrayRef<const Expr *> AssociatedConstraints) {
-  return S.getNormalizedAssociatedConstraints(ConstrainedDecl,
-                                              AssociatedConstraints);
-}
-
 static bool
 substituteParameterMappings(Sema &S, NormalizedConstraint &N,
                             ConceptDecl *Concept,
                             const MultiLevelTemplateArgumentList &MLTAL,
                             const ASTTemplateArgumentListInfo *ArgsAsWritten) {
-
-  if (N.isCompound()) {
+  if (!N.isAtomic()) {
     if (substituteParameterMappings(S, N.getLHS(), Concept, MLTAL,
                                     ArgsAsWritten))
       return true;
     return substituteParameterMappings(S, N.getRHS(), Concept, MLTAL,
                                        ArgsAsWritten);
   }
-
-  if (N.isFoldExpanded()) {
-    Sema::ArgumentPackSubstitutionIndexRAII _(S, -1);
-    return substituteParameterMappings(
-        S, N.getFoldExpandedConstraint()->Constraint, Concept, MLTAL,
-        ArgsAsWritten);
-  }
-
   TemplateParameterList *TemplateParams = Concept->getTemplateParameters();
 
   AtomicConstraint &Atomic = *N.getAtomicConstraint();
@@ -1479,33 +1313,6 @@ static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
                                      CSE->getTemplateArgsAsWritten());
 }
 
-NormalizedConstraint::NormalizedConstraint(ASTContext &C,
-                                           NormalizedConstraint LHS,
-                                           NormalizedConstraint RHS,
-                                           CompoundConstraintKind Kind)
-    : Constraint{CompoundConstraint{
-          new(C) std::pair<NormalizedConstraint, NormalizedConstraint>{
-              std::move(LHS), std::move(RHS)},
-          Kind}} {}
-
-NormalizedConstraint::NormalizedConstraint(ASTContext &C,
-                                           const NormalizedConstraint &Other) {
-  if (Other.isAtomic()) {
-    Constraint = new (C) AtomicConstraint(*Other.getAtomicConstraint());
-  } else if (Other.isFoldExpanded()) {
-    Constraint = new (C) FoldExpandedConstraint(
-        Other.getFoldExpandedConstraint()->Kind,
-        NormalizedConstraint(C, Other.getFoldExpandedConstraint()->Constraint),
-        Other.getFoldExpandedConstraint()->Pattern);
-  } else {
-    Constraint = CompoundConstraint(
-        new (C) std::pair<NormalizedConstraint, NormalizedConstraint>{
-            NormalizedConstraint(C, Other.getLHS()),
-            NormalizedConstraint(C, Other.getRHS())},
-        Other.getCompoundKind());
-  }
-}
-
 std::optional<NormalizedConstraint>
 NormalizedConstraint::fromConstraintExprs(Sema &S, NamedDecl *D,
                                           ArrayRef<const Expr *> E) {
@@ -1580,75 +1387,17 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E) {
       return std::nullopt;
 
     return New;
-  } else if (auto *FE = dyn_cast<const CXXFoldExpr>(E);
-             FE && S.getLangOpts().CPlusPlus26 &&
-             (FE->getOperator() == BinaryOperatorKind::BO_LAnd ||
-              FE->getOperator() == BinaryOperatorKind::BO_LOr)) {
-
-    // Normalize fold expressions in C++26.
-
-    FoldExpandedConstraint::FoldOperatorKind Kind =
-        FE->getOperator() == BinaryOperatorKind::BO_LAnd
-            ? FoldExpandedConstraint::FoldOperatorKind::And
-            : FoldExpandedConstraint::FoldOperatorKind::Or;
-
-    if (FE->getInit()) {
-      auto LHS = fromConstraintExpr(S, D, FE->getLHS());
-      auto RHS = fromConstraintExpr(S, D, FE->getRHS());
-      if (!LHS || !RHS)
-        return std::nullopt;
-
-      if (FE->isRightFold())
-        RHS = NormalizedConstraint{new (S.Context) FoldExpandedConstraint{
-            Kind, std::move(*RHS), FE->getPattern()}};
-      else
-        LHS = NormalizedConstraint{new (S.Context) FoldExpandedConstraint{
-            Kind, std::move(*LHS), FE->getPattern()}};
-
-      return NormalizedConstraint(
-          S.Context, std::move(*LHS), std::move(*RHS),
-          FE->getOperator() == BinaryOperatorKind::BO_LAnd ? CCK_Conjunction
-                                                           : CCK_Disjunction);
-    }
-    auto Sub = fromConstraintExpr(S, D, FE->getPattern());
-    if (!Sub)
-      return std::nullopt;
-    return NormalizedConstraint{new (S.Context) FoldExpandedConstraint{
-        Kind, std::move(*Sub), FE->getPattern()}};
   }
-
   return NormalizedConstraint{new (S.Context) AtomicConstraint(S, E)};
 }
 
-bool FoldExpandedConstraint::AreCompatibleForSubsumption(
-    const FoldExpandedConstraint &A, const FoldExpandedConstraint &B) {
-
-  // [C++26] [temp.constr.fold]
-  // Two fold expanded constraints are compatible for subsumption
-  // if their respective constraints both contain an equivalent unexpanded pack.
-
-  llvm::SmallVector<UnexpandedParameterPack> APacks, BPacks;
-  Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(A.Pattern), APacks);
-  Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(B.Pattern), BPacks);
+using NormalForm =
+    llvm::SmallVector<llvm::SmallVector<AtomicConstraint *, 2>, 4>;
 
-  for (const UnexpandedParameterPack &APack : APacks) {
-    std::pair<unsigned, unsigned> DepthAndIndex = getDepthAndIndex(APack);
-    auto it = llvm::find_if(BPacks, [&](const UnexpandedParameterPack &BPack) {
-      return getDepthAndIndex(BPack) == DepthAndIndex;
-    });
-    if (it != BPacks.end())
-      return true;
-  }
-  return false;
-}
-
-NormalForm clang::makeCNF(const NormalizedConstraint &Normalized) {
+static NormalForm makeCNF(const NormalizedConstraint &Normalized) {
   if (Normalized.isAtomic())
     return {{Normalized.getAtomicConstraint()}};
 
-  else if (Normalized.isFoldExpanded())
-    return {{Normalized.getFoldExpandedConstraint()}};
-
   NormalForm LCNF = makeCNF(Normalized.getLHS());
   NormalForm RCNF = makeCNF(Normalized.getRHS());
   if (Normalized.getCompoundKind() == NormalizedConstraint::CCK_Conjunction) {
@@ -1674,13 +1423,10 @@ NormalForm clang::makeCNF(const NormalizedConstraint &Normalized) {
   return Res;
 }
 
-NormalForm clang::makeDNF(const NormalizedConstraint &Normalized) {
+static NormalForm makeDNF(const NormalizedConstraint &Normalized) {
   if (Normalized.isAtomic())
     return {{Normalized.getAtomicConstraint()}};
 
-  else if (Normalized.isFoldExpanded())
-    return {{Normalized.getFoldExpandedConstraint()}};
-
   NormalForm LDNF = makeDNF(Normalized.getLHS());
   NormalForm RDNF = makeDNF(Normalized.getRHS());
   if (Normalized.getCompoundKind() == NormalizedConstraint::CCK_Disjunction) {
@@ -1707,6 +1453,60 @@ NormalForm clang::makeDNF(const NormalizedConstraint &Normalized) {
   return Res;
 }
 
+template<typename AtomicSubsumptionEvaluator>
+static bool subsumes(const NormalForm &PDNF, const NormalForm &QCNF,
+                     AtomicSubsumptionEvaluator E) {
+  // C++ [temp.constr.order] p2
+  //   Then, P subsumes Q if and only if, for every disjunctive clause Pi in the
+  //   disjunctive normal form of P, Pi subsumes every conjunctive clause Qj in
+  //   the conjuctive normal form of Q, where [...]
+  for (const auto &Pi : PDNF) {
+    for (const auto &Qj : QCNF) {
+      // C++ [temp.constr.order] p2
+      //   - [...] a disjunctive clause Pi subsumes a conjunctive clause Qj if
+      //     and only if there exists an atomic constraint Pia in Pi for which
+      //     there exists an atomic constraint, Qjb, in Qj such that Pia
+      //     subsumes Qjb.
+      bool Found = false;
+      for (const AtomicConstraint *Pia : Pi) {
+        for (const AtomicConstraint *Qjb : Qj) {
+          if (E(*Pia, *Qjb)) {
+            Found = true;
+            break;
+          }
+        }
+        if (Found)
+          break;
+      }
+      if (!Found)
+        return false;
+    }
+  }
+  return true;
+}
+
+template<typename AtomicSubsumptionEvaluator>
+static bool subsumes(Sema &S, NamedDecl *DP, ArrayRef<const Expr *> P,
+                     NamedDecl *DQ, ArrayRef<const Expr *> Q, bool &Subsumes,
+                     AtomicSubsumptionEvaluator E) {
+  // C++ [temp.constr.order] p2
+  //   In order to determine if a constraint P subsumes a constraint Q, P is
+  //   transformed into disjunctive normal form, and Q is transformed into
+  //   conjunctive normal form. [...]
+  auto *PNormalized = S.getNormalizedAssociatedConstraints(DP, P);
+  if (!PNormalized)
+    return true;
+  const NormalForm PDNF = makeDNF(*PNormalized);
+
+  auto *QNormalized = S.getNormalizedAssociatedConstraints(DQ, Q);
+  if (!QNormalized)
+    return true;
+  const NormalForm QCNF = makeCNF(*QNormalized);
+
+  Subsumes = subsumes(PDNF, QCNF, E);
+  return false;
+}
+
 bool Sema::IsAtLeastAsConstrained(NamedDecl *D1,
                                   MutableArrayRef<const Expr *> AC1,
                                   NamedDecl *D2,
@@ -1759,11 +1559,10 @@ bool Sema::IsAtLeastAsConstrained(NamedDecl *D1,
     }
   }
 
-  if (clang::subsumes(
-          *this, D1, AC1, D2, AC2, Result,
-          [this](const AtomicConstraint &A, const AtomicConstraint &B) {
-            return A.subsumes(Context, B);
-          }))
+  if (subsumes(*this, D1, AC1, D2, AC2, Result,
+        [this] (const AtomicConstraint &A, const AtomicConstraint &B) {
+          return A.subsumes(Context, B);
+        }))
     return true;
   SubsumptionCache.try_emplace(Key, Result);
   return false;
@@ -1820,12 +1619,10 @@ bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(NamedDecl *D1,
     const NormalForm DNF2 = makeDNF(*Normalized2);
     const NormalForm CNF2 = makeCNF(*Normalized2);
 
-    bool Is1AtLeastAs2Normally =
-        clang::subsumes(DNF1, CNF2, NormalExprEvaluator);
-    bool Is2AtLeastAs1Normally =
-        clang::subsumes(DNF2, CNF1, NormalExprEvaluator);
-    bool Is1AtLeastAs2 = clang::subsumes(DNF1, CNF2, IdenticalExprEvaluator);
-    bool Is2AtLeastAs1 = clang::subsumes(DNF2, CNF1, IdenticalExprEvaluator);
+    bool Is1AtLeastAs2Normally = subsumes(DNF1, CNF2, NormalExprEvaluator);
+    bool Is2AtLeastAs1Normally = subsumes(DNF2, CNF1, NormalExprEvaluator);
+    bool Is1AtLeastAs2 = subsumes(DNF1, CNF2, IdenticalExprEvaluator);
+    bool Is2AtLeastAs1 = subsumes(DNF2, CNF1, IdenticalExprEvaluator);
     if (Is1AtLeastAs2 == Is1AtLeastAs2Normally &&
         Is2AtLeastAs1 == Is2AtLeastAs1Normally)
       // Same result - no ambiguity was caused by identical atomic expressions.

diff  --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 3d4ccaf68c700..6df7f2223d267 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -566,10 +566,6 @@ void Sema::collectUnexpandedParameterPacks(
     .TraverseDeclarationNameInfo(NameInfo);
 }
 
-void Sema::collectUnexpandedParameterPacks(
-    Expr *E, SmallVectorImpl<UnexpandedParameterPack> &Unexpanded) {
-  CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseStmt(E);
-}
 
 ParsedTemplateArgument
 Sema::ActOnPackExpansion(const ParsedTemplateArgument &Arg,

diff  --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
deleted file mode 100644
index 1e0bc7bcfb4e7..0000000000000
--- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
+++ /dev/null
@@ -1,277 +0,0 @@
-// RUN: %clang_cc1 -std=c++2c -verify %s
-
-template <class T> concept A = true;
-template <class T> concept C = A<T> && true;
-template <class T> concept D = A<T> && __is_same(T, int);
-
-
-template <class T> requires (A<T>)
-constexpr int f(T) { return 0; };
-template <class... T> requires (C<T> && ...)
-constexpr int f(T...) { return 1; };
-
-static_assert(f(0) == 0);
-static_assert(f(1) == 0);
-
-
-template <class... T> requires (A<T> && ...)
-constexpr int g(T...) { return 0; };
-template <class... T> requires (C<T> && ...)
-constexpr int g(T...) { return 1; };
-
-static_assert(g(0) == 1);
-static_assert(g() == 1);
-static_assert(g(1, 2) == 1);
-
-
-
-template <class... T> requires (A<T> && ...)
-constexpr int h(T...) { return 0; }; // expected-note {{candidate}}
-template <class... T> requires (C<T> || ...)
-constexpr int h(T...) { return 1; }; // expected-note {{candidate}}
-
-static_assert(h(0) == 1); // expected-error {{call to 'h' is ambiguous}}
-
-template <class... T> requires (A<T> || ...)
-constexpr int i(T...) { return 0; }; // expected-note {{candidate}}
-template <class... T> requires (C<T> && ...)
-constexpr int i(T...) { return 1; }; // expected-note {{candidate}}
-
-static_assert(i(0) == 1); // expected-error {{call to 'i' is ambiguous}}
-
-
-template <class... T> requires (A<T> || ... || true)
-constexpr int j(T...) { return 0; };
-template <class... T> requires (C<T> && ... && true)
-constexpr int j(T...) { return 1; };
-
-static_assert(j(0) == 1);
-static_assert(j() == 1);
-
-
-
-template <class... T> requires (A<T> || ...)
-constexpr int k(T...) { return 0; }; // expected-note {{candidate template ignored: constraints not satisfied [with T = <>]}}
-template <class... T> requires (C<T> || ...)
-constexpr int k(T...) { return 1; }; // expected-note {{candidate template ignored: constraints not satisfied [with T = <>]}}
-
-static_assert(k(0) == 1);
-static_assert(k() == 0); // expected-error {{no matching function for call to 'k'}}
-static_assert(k(1, 2) == 1);
-
-
-consteval int terse(A auto...) {return 1;}
-consteval int terse(D auto...) {return 2;}
-
-static_assert(terse() == 2);
-static_assert(terse(0, 0) == 2);
-static_assert(terse(0L, 0) == 1);
-
-template <A... T>
-consteval int tpl_head(A auto...) {return 1;}
-template <D... T>
-consteval int tpl_head(D auto...) {return 2;}
-
-static_assert(tpl_head() == 2);
-static_assert(tpl_head(0, 0) == 2);
-static_assert(tpl_head(0L, 0) == 1);
-
-
-namespace equivalence {
-
-template <typename... T>
-struct S {
-    template <typename... U>
-    void f() requires (A<U> && ...);
-    template <typename... U>
-    void f() requires (C<U> && ...);
-
-    template <typename... U>
-    void g() requires (A<T> && ...);
-    template <typename... U>
-    void g() requires (C<T> && ...);
-
-    template <typename... U>
-    void h() requires (A<U> && ...); // expected-note {{candidate}}
-    template <typename... U>
-    void h() requires (C<T> && ...); // expected-note {{candidate}}
-};
-
-void test() {
-    S<int>{}.f<int>();
-    S<int>{}.g<int>();
-    S<int>{}.h<int>(); // expected-error {{call to member function 'h' is ambiguous}}
-}
-
-
-}
-
-namespace substitution {
-    struct S {
-    using type = int;
-};
-
-template <typename... T>
-consteval int And1() requires (C<typename T::type> && ...) { // #and1
-    return 1;
-}
-
-template <typename T, typename... U>
-consteval int And2() requires (C<typename U::type> && ... && C<typename T::type>) { // #and2
-    return 2;
-}
-
-template <typename T, typename... U>
-consteval int And3() requires (C<typename T::type> && ... && C<typename U::type>) { // #and3
-    return 3;
-}
-
-template <typename... T>
-consteval int Or1() requires (C<typename T::type> || ...) { // #or1
-    return 1;
-}
-
-template <typename T, typename... U>
-consteval int Or2() requires (C<typename U::type> || ... || C<typename T::type>) {  // #or2
-    return 2;
-}
-
-template <typename T, typename... U>
-consteval int Or3() requires (C<typename T::type> || ... || C<typename U::type>) {  // #or3
-    return 3;
-}
-
-static_assert(And1<>() == 1);
-static_assert(And1<S>() == 1);
-static_assert(And1<S, S>() == 1);
-static_assert(And1<int>() == 1); // expected-error {{no matching function for call to 'And1'}}
-                                 // expected-note@#and1 {{candidate template ignored: constraints not satisfied}}
-                                 // expected-note@#and1 {{because substituted constraint expression is ill-formed}}
-
-static_assert(And1<S, int>() == 1); // expected-error {{no matching function for call to 'And1'}}
-                                   // expected-note@#and1 {{candidate template ignored: constraints not satisfied}}
-                                   // expected-note@#and1 {{because substituted constraint expression is ill-formed}}
-
-static_assert(And1<int, S>() == 1); // expected-error {{no matching function for call to 'And1'}}
-                                   // expected-note@#and1 {{candidate template ignored: constraints not satisfied}}
-                                   // expected-note@#and1 {{because substituted constraint expression is ill-formed}}
-
-static_assert(And2<S>() == 2);
-static_assert(And2<S, S>() == 2);
-static_assert(And2<int>() == 2);
-
-static_assert(And2<int, int>() == 2);  // expected-error {{no matching function for call to 'And2'}}
-                                      // expected-note@#and2 {{candidate template ignored: constraints not satisfied}}
-                                     // expected-note@#and2 {{because substituted constraint expression is ill-formed}}
-
-static_assert(And2<S, int>() == 2); // expected-error {{no matching function for call to 'And2'}}
-                                   // expected-note@#and2 {{candidate template ignored: constraints not satisfied}}
-                                   // expected-note@#and2 {{because substituted constraint expression is ill-formed}}
-
-static_assert(And2<int, S>() == 2); // expected-error {{no matching function for call to 'And2'}}
-                                   // expected-note@#and2 {{candidate template ignored: constraints not satisfied}}
-                                   // expected-note@#and2 {{because substituted constraint expression is ill-formed}}
-
-static_assert(And3<S>() == 3);
-static_assert(And3<S, S>() == 3);
-static_assert(And3<int>() == 3);   // expected-error {{no matching function for call to 'And3'}}
-                                   // expected-note@#and3 {{candidate template ignored: constraints not satisfied}}
-                                   // expected-note@#and3 {{because substituted constraint expression is ill-formed}}
-
-static_assert(And3<int, int>() == 3);  // expected-error {{no matching function for call to 'And3'}}
-                                      // expected-note@#and3 {{candidate template ignored: constraints not satisfied}}
-                                     // expected-note@#and3 {{because substituted constraint expression is ill-formed}}
-
-static_assert(And3<S, int>() == 3); // expected-error {{no matching function for call to 'And3'}}
-                                   // expected-note@#and3 {{candidate template ignored: constraints not satisfied}}
-                                   // expected-note@#and3 {{because substituted constraint expression is ill-formed}}
-
-static_assert(And3<int, S>() == 3); // expected-error {{no matching function for call to 'And3'}}
-                                   // expected-note@#and3 {{candidate template ignored: constraints not satisfied}}
-                                   // expected-note@#and3 {{because substituted constraint expression is ill-formed}}
-
-
-static_assert(Or1<>() == 1); // expected-error {{no matching function for call to 'Or1'}}
-                             // expected-note@#or1 {{candidate template ignored: constraints not satisfied}}
-static_assert(Or1<S>() == 1);
-static_assert(Or1<int, S>() == 1);
-static_assert(Or1<S, int>() == 1);
-static_assert(Or1<S, S>() == 1);
-static_assert(Or1<int>() == 1); // expected-error {{no matching function for call to 'Or1'}}
-                                // expected-note@#or1 {{candidate template ignored: constraints not satisfied}} \
-                                // expected-note@#or1 {{because substituted constraint expression is ill-formed}}
-
-
-static_assert(Or2<S>() == 2);
-static_assert(Or2<int, S>() == 2);
-static_assert(Or2<S, int>() == 2);
-static_assert(Or2<S, S>() == 2);
-static_assert(Or2<int>() == 2); // expected-error {{no matching function for call to 'Or2'}}
-                                // expected-note@#or2 {{candidate template ignored: constraints not satisfied}} \
-                                // expected-note@#or2 {{because substituted constraint expression is ill-formed}}
-
-static_assert(Or3<S>() == 3);
-static_assert(Or3<int, S>() == 3);
-static_assert(Or3<S, int>() == 3);
-static_assert(Or3<S, S>() == 3);
-static_assert(Or3<int>() == 3); // expected-error {{no matching function for call to 'Or3'}}
-                                // expected-note@#or3 {{candidate template ignored: constraints not satisfied}} \
-                                // expected-note@#or3 {{because substituted constraint expression is ill-formed}}
-}
-
-namespace bool_conversion_break {
-
-template <typename ...V> struct A;
-struct Thingy {
-    static constexpr int compare(const Thingy&) {return 1;}
-};
-template <typename ...T, typename ...U>
-void f(A<T ...> *, A<U ...> *) // expected-note {{candidate template ignored: failed template argument deduction}}
-requires (T::compare(U{}) && ...); // expected-error {{atomic constraint must be of type 'bool' (found 'int')}}
-
-void g() {
-    A<Thingy, Thingy> *ap;
-    f(ap, ap); // expected-error{{no matching function for call to 'f'}} \
-               // expected-note {{while checking constraint satisfaction}} \
-               // expected-note {{in instantiation of function template specialization}}
-}
-
-}
-
-namespace nested {
-
-template <typename... T>
-struct S {
-    template <typename... U>
-    consteval static int f()
-        requires ((A<T> && ...) && ... && A<U> ) {
-            return 1;
-    }
-
-    template <typename... U>
-    consteval static int f()
-        requires ((C<T> && ...) && ... && C<U> ) {
-            return 2;
-    }
-
-    template <typename... U>
-    consteval static int g() // #nested-ambiguous-g1
-        requires ((A<T> && ...) && ... && A<U> ) {
-            return 1;
-    }
-
-    template <typename... U>
-    consteval static int g() // #nested-ambiguous-g2
-        requires ((C<U> && ...) && ... && C<T> ) {
-            return 2;
-    }
-};
-
-static_assert(S<int>::f<int>() == 2);
-
-static_assert(S<int>::g<int>() == 2); // expected-error {{call to 'g' is ambiguous}}
-                                      // expected-note@#nested-ambiguous-g1 {{candidate}}
-                                      // expected-note@#nested-ambiguous-g2 {{candidate}}
-
-
-}

diff  --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index a6ded8be3ae9e..27e2213e54caa 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -218,7 +218,7 @@ <h2 id="cxx26">C++2c implementation status</h2>
  <tr>
   <td>Ordering of constraints involving fold expressions</td>
   <td><a href="https://wg21.link/P2963R3">P2963R3</a></td>
-  <td class="unreleased" align="center">Clang 19</td>
+  <td class="none" align="center">No</td>
  </tr>
  <tr>
   <td>Structured binding declaration as a condition</td>


        


More information about the cfe-commits mailing list