[clang] Revert "[Clang][C++26] Implement "Ordering of constraints involving fold expressions" (PR #99007)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jul 16 02:00:52 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: cor3ntin (cor3ntin)
<details>
<summary>Changes</summary>
Reverts llvm/llvm-project#<!-- -->98160
Breaks CI on some architectures
---
Patch is 53.87 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/99007.diff
7 Files Affected:
- (modified) clang/docs/ReleaseNotes.rst (-3)
- (modified) clang/include/clang/Sema/Sema.h (-5)
- (modified) clang/include/clang/Sema/SemaConcept.h (+22-149)
- (modified) clang/lib/Sema/SemaConcept.cpp (+200-403)
- (modified) clang/lib/Sema/SemaTemplateVariadic.cpp (-4)
- (removed) clang/test/SemaCXX/cxx2c-fold-exprs.cpp (-277)
- (modified) clang/www/cxx_status.html (+1-1)
``````````diff
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));
...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/99007
More information about the cfe-commits
mailing list