From efc31fd3fb460934d5fafa0c56232b3f9da5293e Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Sat, 11 Oct 2025 12:12:33 +0800 Subject: [PATCH 1/2] [Clang] Fix a bug when checking non-dependent constraints We check the non-dependent constraints early with empty template arguments when we build a nested requirement. Therefore we cannot assume a non-empty MLTAL within the Checker. No release note because this is a regression on trunk. --- clang/lib/Sema/SemaConcept.cpp | 25 +++++++++++++++++-------- clang/test/SemaTemplate/concepts.cpp | 12 ++++++++++++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index f4df63c1d2243..13e7960dafdcf 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -604,6 +604,10 @@ ConstraintSatisfactionChecker::SubstitutionInTemplateArguments( return std::nullopt; const NormalizedConstraint::OccurenceList &Used = Constraint.mappingOccurenceList(); + // The empty MLTAL situation should only occur when evaluating non-dependent + // constraints. + if (!MLTAL.getNumSubstitutedLevels()) + MLTAL.addOuterTemplateArguments(TD, {}, /*Final=*/false); SubstitutedOuterMost = llvm::to_vector_of(MLTAL.getOutermost()); unsigned Offset = 0; @@ -623,9 +627,7 @@ ConstraintSatisfactionChecker::SubstitutionInTemplateArguments( if (Offset < SubstitutedOuterMost.size()) SubstitutedOuterMost.erase(SubstitutedOuterMost.begin() + Offset); - MLTAL.replaceOutermostTemplateArguments( - const_cast(Constraint.getConstraintDecl()), - SubstitutedOuterMost); + MLTAL.replaceOutermostTemplateArguments(TD, SubstitutedOuterMost); return std::move(MLTAL); } @@ -956,11 +958,18 @@ ExprResult ConstraintSatisfactionChecker::Evaluate( ? Constraint.getPackSubstitutionIndex() : PackSubstitutionIndex; - Sema::InstantiatingTemplate _(S, ConceptId->getBeginLoc(), - Sema::InstantiatingTemplate::ConstraintsCheck{}, - ConceptId->getNamedConcept(), - MLTAL.getInnermost(), - Constraint.getSourceRange()); + Sema::InstantiatingTemplate _( + S, ConceptId->getBeginLoc(), + Sema::InstantiatingTemplate::ConstraintsCheck{}, + ConceptId->getNamedConcept(), + // We may have empty template arguments when checking non-dependent + // nested constraint expressions. + // In such cases, non-SFINAE errors would have already been diagnosed + // during parameter mapping substitution, so the instantiating template + // arguments are less useful here. + MLTAL.getNumSubstitutedLevels() ? MLTAL.getInnermost() + : ArrayRef{}, + Constraint.getSourceRange()); unsigned Size = Satisfaction.Details.size(); diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp index 768af09afe9e2..a046fc47d1d18 100644 --- a/clang/test/SemaTemplate/concepts.cpp +++ b/clang/test/SemaTemplate/concepts.cpp @@ -1404,6 +1404,18 @@ static_assert(!std::is_constructible_v, array>); } +namespace case7 { + +template +concept __same_as_impl = __is_same(_Tp, _Up); +template +concept same_as = __same_as_impl<_Tp, _Up>; +template +concept IsEntitySpec = + requires { requires same_as; }; + +} + } namespace GH162125 { From b9d2c1a4c45d37985e0de31f46d8690def18eb3c Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Sat, 11 Oct 2025 12:46:31 +0800 Subject: [PATCH 2/2] Add the missed isInvalid() check --- clang/lib/Sema/SemaConcept.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 13e7960dafdcf..9cbd1bd772f6f 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -958,7 +958,7 @@ ExprResult ConstraintSatisfactionChecker::Evaluate( ? Constraint.getPackSubstitutionIndex() : PackSubstitutionIndex; - Sema::InstantiatingTemplate _( + Sema::InstantiatingTemplate InstTemplate( S, ConceptId->getBeginLoc(), Sema::InstantiatingTemplate::ConstraintsCheck{}, ConceptId->getNamedConcept(), @@ -970,6 +970,8 @@ ExprResult ConstraintSatisfactionChecker::Evaluate( MLTAL.getNumSubstitutedLevels() ? MLTAL.getInnermost() : ArrayRef{}, Constraint.getSourceRange()); + if (InstTemplate.isInvalid()) + return ExprError(); unsigned Size = Satisfaction.Details.size();