llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Younan Zhang (zyn0217) <details> <summary>Changes</summary> We used to drop SFINAE errors that occurred during parameter mapping instantiation on the floor, making our diagnostics worse for some cases. This patch corrects that behavior. Moreover it fixes some clients where the errors were not properly handled for invalid expressions. --- Patch is 25.95 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/218618.diff 9 Files Affected: - (modified) clang/lib/Sema/SemaConcept.cpp (+38-45) - (modified) clang/lib/Sema/SemaTemplateDeduction.cpp (+4-7) - (modified) clang/lib/Sema/TreeTransform.h (-4) - (modified) clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp (+2) - (modified) clang/test/SemaCXX/cxx23-assume.cpp (+1) - (modified) clang/test/SemaCXX/cxx2c-fold-exprs.cpp (+66-15) - (modified) clang/test/SemaTemplate/concepts.cpp (+4) - (modified) clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp (+1) - (modified) clang/test/SemaTemplate/instantiate-requires-expr.cpp (+1) ``````````diff diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index def80e274e10c..5c785d9129dda 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -269,6 +269,12 @@ class AdjustConstraints : public TreeTransform<AdjustConstraints> { return Result; } + QualType TransformPackIndexingType(TypeLocBuilder &TLB, + PackIndexingTypeLoc TL) { + llvm::SaveAndRestore _1(RemoveNonPackExpansionPacks, false); + return inherited::TransformPackIndexingType(TLB, TL); + } + bool AlreadyTransformed(QualType T) { if (T.isNull()) return true; @@ -549,6 +555,31 @@ class ConstraintSatisfactionChecker { : PackSubstitutionIndex; } + StringRef allocateStringFromConceptDiagnostic(const PartialDiagnostic &Diag) { + SmallString<128> DiagString; + DiagString = ": "; + Diag.EmitToString(S.getDiagnostics(), DiagString); + return S.getASTContext().backupStr(DiagString); + } + + void consumeSFINAEFailure(TemplateDeductionInfo &Info, + ConstraintSatisfaction &Satisfaction) { + PartialDiagnosticAt SubstDiag{SourceLocation(), + PartialDiagnostic::NullDiagnostic()}; + Info.takeSFINAEDiagnostic(SubstDiag); + // FIXME: 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. + Satisfaction.Details.emplace_back( + new (S.Context) ConstraintSubstitutionDiagnostic{ + SubstDiag.first, + allocateStringFromConceptDiagnostic(SubstDiag.second)}); + Satisfaction.IsSatisfied = false; + } + ExprResult EvaluateAtomicConstraint(const Expr *AtomicExpr, const MultiLevelTemplateArgumentList &MLTAL); @@ -601,14 +632,6 @@ class ConstraintSatisfactionChecker { const MultiLevelTemplateArgumentList &MLTAL); }; -StringRef allocateStringFromConceptDiagnostic(const Sema &S, - const PartialDiagnostic Diag) { - SmallString<128> DiagString; - DiagString = ": "; - Diag.EmitToString(S.getDiagnostics(), DiagString); - return S.getASTContext().backupStr(DiagString); -} - } // namespace ExprResult ConstraintSatisfactionChecker::EvaluateAtomicConstraint( @@ -647,21 +670,7 @@ ExprResult ConstraintSatisfactionChecker::EvaluateAtomicConstraint( // A non-SFINAE error has occurred as a result of this // substitution. return ExprError(); - - PartialDiagnosticAt SubstDiag{SourceLocation(), - PartialDiagnostic::NullDiagnostic()}; - Info.takeSFINAEDiagnostic(SubstDiag); - // FIXME: 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. - Satisfaction.Details.emplace_back( - new (S.Context) ConstraintSubstitutionDiagnostic{ - SubstDiag.first, - allocateStringFromConceptDiagnostic(S, SubstDiag.second)}); - Satisfaction.IsSatisfied = false; + consumeSFINAEFailure(Info, Satisfaction); return ExprEmpty(); } } @@ -726,7 +735,7 @@ ConstraintSatisfactionChecker::SubstitutionInTemplateArguments( if (S.SubstTemplateArgumentsInParameterMapping( Constraint.getParameterMapping(), Constraint.getBeginLoc(), MLTAL, SubstArgs)) { - Satisfaction.IsSatisfied = false; + consumeSFINAEFailure(Info, Satisfaction); return std::nullopt; } @@ -787,7 +796,7 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow( SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOutermost); if (!SubstitutedArgs) { Satisfaction.IsSatisfied = false; - return ExprEmpty(); + return ExprError(); } // Make sure that concepts are not evaluated in the context they are used, @@ -821,7 +830,7 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow( Satisfaction.Details.emplace_back( new (S.Context) ConstraintSubstitutionDiagnostic{ SubstitutedAtomicExpr.get()->getBeginLoc(), - allocateStringFromConceptDiagnostic(S, Msg)}); + allocateStringFromConceptDiagnostic(Msg)}); return SubstitutedAtomicExpr; } @@ -1027,7 +1036,6 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow( if (!SubstitutedArgs) { Satisfaction.IsSatisfied = false; - // FIXME: diagnostics? return ExprError(); } @@ -1057,20 +1065,7 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow( if (!Trap.hasErrorOccurred()) return ExprError(); - PartialDiagnosticAt SubstDiag{SourceLocation(), - PartialDiagnostic::NullDiagnostic()}; - Info.takeSFINAEDiagnostic(SubstDiag); - // FIXME: 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. - Satisfaction.Details.insert( - Satisfaction.Details.begin() + Size, - new (S.Context) ConstraintSubstitutionDiagnostic{ - SubstDiag.first, - allocateStringFromConceptDiagnostic(S, SubstDiag.second)}); + consumeSFINAEFailure(Info, Satisfaction); return ExprError(); } @@ -1720,10 +1715,8 @@ bool Sema::EnsureTemplateArgumentListConstraints( llvm::SmallVector<AssociatedConstraint, 3> AssociatedConstraints; TD->getAssociatedConstraints(AssociatedConstraints); if (CheckConstraintSatisfaction(TD, AssociatedConstraints, TemplateArgsLists, - TemplateIDRange, Satisfaction)) - return true; - - if (!Satisfaction.IsSatisfied) { + TemplateIDRange, Satisfaction) || + !Satisfaction.IsSatisfied) { SmallString<128> TemplateArgString; TemplateArgString = " "; TemplateArgString += getTemplateArgumentBindingsText( diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index f177f0aa8645e..a554feae3dfb9 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -3987,9 +3987,8 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( if (CheckFunctionTemplateConstraints( Info.getLocation(), FunctionTemplate->getCanonicalDecl()->getTemplatedDecl(), - CTAI.CanonicalConverted, Info.AssociatedConstraintsSatisfaction)) - return TemplateDeductionResult::MiscellaneousDeductionFailure; - if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) { + CTAI.CanonicalConverted, Info.AssociatedConstraintsSatisfaction) || + !Info.AssociatedConstraintsSatisfaction.IsSatisfied) { Info.reset(Info.takeSugared(), TemplateArgumentList::CreateCopy( Context, CTAI.CanonicalConverted)); return TemplateDeductionResult::ConstraintsNotSatisfied; @@ -4035,10 +4034,8 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( if (IsLambda && !IsIncomplete) { if (CheckFunctionTemplateConstraints( Info.getLocation(), Specialization, CTAI.CanonicalConverted, - Info.AssociatedConstraintsSatisfaction)) - return TemplateDeductionResult::MiscellaneousDeductionFailure; - - if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) { + Info.AssociatedConstraintsSatisfaction) || + !Info.AssociatedConstraintsSatisfaction.IsSatisfied) { Info.reset(Info.takeSugared(), TemplateArgumentList::CreateCopy( Context, CTAI.CanonicalConverted)); return TemplateDeductionResult::ConstraintsNotSatisfied; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index b0c836325a971..4799f72dd6177 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -7124,10 +7124,6 @@ TreeTransform<Derived>::TransformPackIndexingType(TypeLocBuilder &TLB, for (QualType T : Types) { if (!T->containsUnexpandedParameterPack()) { - // A pack indexing type can appear in a larger pack expansion, - // e.g. `Pack...[pack_of_indexes]...` - // so we need to temporarily disable substitution of pack elements - Sema::ArgPackSubstIndexRAII SubstIndex(getSema(), std::nullopt); QualType Transformed = getDerived().TransformType(T); if (Transformed.isNull()) return QualType(); diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp index 15e00e4481e75..97c0d2e57c004 100644 --- a/clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp +++ b/clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp @@ -89,7 +89,9 @@ template<typename T> requires (T{}) // expected-error{{atomic constraint must be struct D { }; static_assert(C<int>{}); // expected-note{{while checking constraint satisfaction for template 'C<int>' required here}} +// expected-error@-1 {{constraints not satisfied for class template 'C'}} static_assert(D<int>{}); // expected-note{{while checking constraint satisfaction for template 'D<int>' required here}} +// expected-error@-1 {{constraints not satisfied for class template 'D'}} // Test the delayed instantiation, the 'foo' implementation shouldn't cause the // constraint failure(or crash!) until the use to create 'y'. diff --git a/clang/test/SemaCXX/cxx23-assume.cpp b/clang/test/SemaCXX/cxx23-assume.cpp index a594a1a44337b..d2563c1c45107 100644 --- a/clang/test/SemaCXX/cxx23-assume.cpp +++ b/clang/test/SemaCXX/cxx23-assume.cpp @@ -129,6 +129,7 @@ struct F { template <typename T> constexpr int f5() requires C<T> { return 1; } // expected-note {{while checking the satisfaction}} // expected-note@-1 {{candidate template ignored}} + // expected-note@-2 {{because 'T' does not satisfy 'C'}} template <typename T> constexpr int f5() requires (!C<T>) { return 2; } // expected-note 3 {{while checking the satisfaction}} \ diff --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp index 79d57a6f94d58..d3e681f22ab26 100644 --- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp +++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp @@ -154,58 +154,84 @@ consteval int Or3() requires (C<typename T::type> || ... || C<typename U::type>) static_assert(And1<>() == 1); static_assert(And1<S>() == 1); static_assert(And1<S, S>() == 1); -// FIXME: The diagnostics are not so great static_assert(And1<int>() == 1); // expected-error {{no matching function for call to 'And1'}} // expected-note@#and1 {{candidate template ignored: constraints not satisfied [with T = <int>]}} - // expected-note@#and1 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}} + // expected-note@#and1 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} + // expected-note@#C {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}} static_assert(And1<S, int>() == 1); // expected-error {{no matching function for call to 'And1'}} // expected-note@#and1 {{candidate template ignored: constraints not satisfied [with T = <S, int>]}} - // expected-note@#and1 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}} + // expected-note@#and1 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} + // expected-note@#C {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}} + static_assert(And1<int, S>() == 1); // expected-error {{no matching function for call to 'And1'}} // expected-note@#and1 {{candidate template ignored: constraints not satisfied [with T = <int, S>]}} - // expected-note@#and1 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}} + // expected-note@#and1 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} + // expected-note@#C {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}} + static_assert(And2<S>() == 2); static_assert(And2<S, S>() == 2); static_assert(And2<int>() == 2); // expected-error {{no matching function for call to 'And2'}} // expected-note@#and2 {{candidate template ignored: constraints not satisfied [with T = int, U = <>]}} - // expected-note@#and2 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}} + // expected-note@#and2 {{because 'typename U::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} + // expected-note@#C {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}} + static_assert(And2<int, int>() == 2); // expected-error {{no matching function for call to 'And2'}} // expected-note@#and2 {{candidate template ignored: constraints not satisfied [with T = S, U = <int>]}} \ - // expected-note@#and2 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}} + // expected-note@#and2 {{because 'typename U::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} + // expected-note@#C {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}} + static_assert(And2<S, int>() == 2); // expected-error {{no matching function for call to 'And2'}} // expected-note@#and2 {{candidate template ignored: constraints not satisfied [with T = int, U = <S>]}} - // expected-note@#and2 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}} + // expected-note@#and2 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} + // expected-note@#C {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}} + static_assert(And2<int, S>() == 2); // expected-error {{no matching function for call to 'And2'}} // expected-note@#and2 {{candidate template ignored: constraints not satisfied [with T = int, U = <int>]}} - // expected-note@#and2 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}} + // expected-note@#and2 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} + // expected-note@#C {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}} 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 [with T = int, U = <>]}} - // expected-note@#and3 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}} + // expected-note@#and3 {{because 'typename U::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} + // expected-note@#C {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}} static_assert(And3<int, int>() == 3); // expected-error {{no matching function for call to 'And3'}} // expected-note@#and3 {{candidate template ignored: constraints not satisfied [with T = int, U = <int>]}} - // expected-note@#and3 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}} + // expected-note@#and3 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} + // expected-note@#C {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}} static_assert(And3<S, int>() == 3); // expected-error {{no matching function for call to 'And3'}} // expected-note@#and3 {{candidate template ignored: constraints not satisfied [with T = S, U = <int>]}} - // expected-note@#and3 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}} + // expected-note@#and3 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} + // expected-note@#C {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}} static_assert(And3<int, S>() == 3); // expected-error {{no matching function for call to 'And3'}} // expected-note@#and3 {{candidate template ignored: constraints not satisfied [with T = int, U = <S>]}} - // expected-note@#and3 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}} + // expected-note@#and3 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/218618 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
