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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits