From 56bacf47c53aca276ae4fa6aa2972b7eda152ddd Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Fri, 10 Jan 2025 09:46:24 +0800 Subject: [PATCH 01/20] Reapply "[Clang] Implement CWG2369 "Ordering between constraints and substitution"" (#122130) This reverts commit 3972ed57088f6515b787d7d38dec03dc74e51827. --- clang/include/clang/Sema/Sema.h | 22 +++- clang/include/clang/Sema/Template.h | 6 + clang/lib/Sema/SemaConcept.cpp | 47 ++++++- clang/lib/Sema/SemaTemplateDeduction.cpp | 49 +++++--- clang/lib/Sema/SemaTemplateDeductionGuide.cpp | 8 +- clang/lib/Sema/SemaTemplateInstantiate.cpp | 115 ++++++++++++++++-- clang/lib/Sema/TreeTransform.h | 2 +- clang/test/CXX/drs/cwg23xx.cpp | 29 +++++ clang/test/CXX/drs/cwg26xx.cpp | 2 +- clang/test/CXX/drs/cwg27xx.cpp | 20 +++ .../expr.prim.req/nested-requirement.cpp | 2 +- .../constrant-satisfaction-conversions.cpp | 6 +- .../SemaCXX/concept-crash-on-diagnostic.cpp | 2 +- clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 2 +- clang/test/SemaCXX/cxx23-assume.cpp | 6 +- clang/test/SemaCXX/cxx2c-fold-exprs.cpp | 2 +- clang/test/SemaCXX/lambda-unevaluated.cpp | 4 +- .../SemaTemplate/concepts-recursive-inst.cpp | 4 +- .../SemaTemplate/cxx2a-constraint-exprs.cpp | 2 +- .../nested-implicit-deduction-guides.cpp | 8 +- clang/www/cxx_dr_status.html | 8 +- 21 files changed, 289 insertions(+), 57 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 96d81e618494a..3b1f4d3234ea9 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -13303,6 +13303,7 @@ class Sema final : public SemaBase { /// /// \param SkipForSpecialization when specified, any template specializations /// in a traversal would be ignored. + /// /// \param ForDefaultArgumentSubstitution indicates we should continue looking /// when encountering a specialized member function template, rather than /// returning immediately. @@ -13314,6 +13315,17 @@ class Sema final : public SemaBase { bool SkipForSpecialization = false, bool ForDefaultArgumentSubstitution = false); + /// Apart from storing the result to \p Result, this behaves the same as + /// another overload. + void getTemplateInstantiationArgs( + MultiLevelTemplateArgumentList &Result, const NamedDecl *D, + const DeclContext *DC = nullptr, bool Final = false, + std::optional> Innermost = std::nullopt, + bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr, + bool ForConstraintInstantiation = false, + bool SkipForSpecialization = false, + bool ForDefaultArgumentSubstitution = false); + /// RAII object to handle the state changes required to synthesize /// a function body. class SynthesizedFunctionScope { @@ -13590,7 +13602,7 @@ class Sema final : public SemaBase { ExprResult SubstConstraintExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs); - // Unlike the above, this does not evaluates constraints. + // Unlike the above, this does not evaluate constraints. ExprResult SubstConstraintExprWithoutSatisfaction( Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs); @@ -14732,10 +14744,10 @@ class Sema final : public SemaBase { const MultiLevelTemplateArgumentList &TemplateArgs, SourceRange TemplateIDRange); - bool CheckInstantiatedFunctionTemplateConstraints( - SourceLocation PointOfInstantiation, FunctionDecl *Decl, - ArrayRef TemplateArgs, - ConstraintSatisfaction &Satisfaction); + bool CheckFunctionTemplateConstraints(SourceLocation PointOfInstantiation, + FunctionDecl *Decl, + ArrayRef TemplateArgs, + ConstraintSatisfaction &Satisfaction); /// \brief Emit diagnostics explaining why a constraint expression was deemed /// unsatisfied. diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h index f9a10cfafb1f7..39f0cf225e673 100644 --- a/clang/include/clang/Sema/Template.h +++ b/clang/include/clang/Sema/Template.h @@ -522,6 +522,12 @@ enum class TemplateSubstitutionKind : char { llvm::PointerUnion * findInstantiationOf(const Decl *D); + /// Similar to \p findInstantiationOf(), but it wouldn't assert if the + /// instantiation was not found within the current instantiation scope. This + /// is helpful for on-demand declaration instantiation. + llvm::PointerUnion * + findInstantiationUnsafe(const Decl *D); + void InstantiatedLocal(const Decl *D, Decl *Inst); void InstantiatedLocalPackArg(const Decl *D, VarDecl *Inst); void MakeInstantiatedLocalArgPack(const Decl *D); diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 15b9c97489e7f..520fe4aac97be 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -792,7 +792,7 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD, bool ForOverloadResolution) { // Don't check constraints if the function is dependent. Also don't check if // this is a function template specialization, as the call to - // CheckinstantiatedFunctionTemplateConstraints after this will check it + // CheckFunctionTemplateConstraints after this will check it // better. if (FD->isDependentContext() || FD->getTemplatedKind() == @@ -1060,12 +1060,55 @@ bool Sema::EnsureTemplateArgumentListConstraints( return false; } -bool Sema::CheckInstantiatedFunctionTemplateConstraints( +static bool CheckFunctionConstraintsWithoutInstantiation( + Sema &SemaRef, SourceLocation PointOfInstantiation, + FunctionTemplateDecl *Template, ArrayRef TemplateArgs, + ConstraintSatisfaction &Satisfaction) { + SmallVector TemplateAC; + Template->getAssociatedConstraints(TemplateAC); + if (TemplateAC.empty()) { + Satisfaction.IsSatisfied = true; + return false; + } + + LocalInstantiationScope Scope(SemaRef); + + FunctionDecl *FD = Template->getTemplatedDecl(); + // Collect the list of template arguments relative to the 'primary' + // template. We need the entire list, since the constraint is completely + // uninstantiated at this point. + + // FIXME: Add TemplateArgs through the 'Innermost' parameter once + // the refactoring of getTemplateInstantiationArgs() relands. + MultiLevelTemplateArgumentList MLTAL; + MLTAL.addOuterTemplateArguments(Template, std::nullopt, /*Final=*/false); + SemaRef.getTemplateInstantiationArgs( + MLTAL, /*D=*/FD, FD, + /*Final=*/false, /*Innermost=*/std::nullopt, /*RelativeToPrimary=*/true, + /*Pattern=*/nullptr, /*ForConstraintInstantiation=*/true); + MLTAL.replaceInnermostTemplateArguments(Template, TemplateArgs); + + Sema::ContextRAII SavedContext(SemaRef, FD); + std::optional ThisScope; + if (auto *Method = dyn_cast(FD)) + ThisScope.emplace(SemaRef, /*Record=*/Method->getParent(), + /*ThisQuals=*/Method->getMethodQualifiers()); + return SemaRef.CheckConstraintSatisfaction( + Template, TemplateAC, MLTAL, PointOfInstantiation, Satisfaction); +} + +bool Sema::CheckFunctionTemplateConstraints( SourceLocation PointOfInstantiation, FunctionDecl *Decl, ArrayRef TemplateArgs, ConstraintSatisfaction &Satisfaction) { // In most cases we're not going to have constraints, so check for that first. FunctionTemplateDecl *Template = Decl->getPrimaryTemplate(); + + if (!Template) + return ::CheckFunctionConstraintsWithoutInstantiation( + *this, PointOfInstantiation, Decl->getDescribedFunctionTemplate(), + TemplateArgs, Satisfaction); + // Note - code synthesis context for the constraints check is created // inside CheckConstraintsSatisfaction. SmallVector TemplateAC; diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 0ecdbb3ffb89f..4a58fdf014d43 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -3991,18 +3991,6 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( Result != TemplateDeductionResult::Success) return Result; - // C++ [temp.deduct.call]p10: [DR1391] - // If deduction succeeds for all parameters that contain - // template-parameters that participate in template argument deduction, - // and all template arguments are explicitly specified, deduced, or - // obtained from default template arguments, remaining parameters are then - // compared with the corresponding arguments. For each remaining parameter - // P with a type that was non-dependent before substitution of any - // explicitly-specified template arguments, if the corresponding argument - // A cannot be implicitly converted to P, deduction fails. - if (CheckNonDependent()) - return TemplateDeductionResult::NonDependentConversionFailure; - // Form the template argument list from the deduced template arguments. TemplateArgumentList *SugaredDeducedArgumentList = TemplateArgumentList::CreateCopy(Context, CTAI.SugaredConverted); @@ -4017,6 +4005,39 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( Owner = FunctionTemplate->getLexicalDeclContext(); FunctionDecl *FD = FunctionTemplate->getTemplatedDecl(); + // C++20 [temp.deduct.general]p5: [CWG2369] + // If the function template has associated constraints, those constraints + // are checked for satisfaction. If the constraints are not satisfied, type + // deduction fails. + // + // FIXME: We haven't implemented CWG2369 for lambdas yet, because we need + // to figure out how to instantiate lambda captures to the scope without + // first instantiating the lambda. + bool IsLambda = isLambdaCallOperator(FD) || isLambdaConversionOperator(FD); + if (!IsLambda && !IsIncomplete) { + if (CheckFunctionTemplateConstraints( + Info.getLocation(), + FunctionTemplate->getCanonicalDecl()->getTemplatedDecl(), + CTAI.CanonicalConverted, Info.AssociatedConstraintsSatisfaction)) + return TemplateDeductionResult::MiscellaneousDeductionFailure; + if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) { + Info.reset(Info.takeSugared(), TemplateArgumentList::CreateCopy( + Context, CTAI.CanonicalConverted)); + return TemplateDeductionResult::ConstraintsNotSatisfied; + } + } + // C++ [temp.deduct.call]p10: [CWG1391] + // If deduction succeeds for all parameters that contain + // template-parameters that participate in template argument deduction, + // and all template arguments are explicitly specified, deduced, or + // obtained from default template arguments, remaining parameters are then + // compared with the corresponding arguments. For each remaining parameter + // P with a type that was non-dependent before substitution of any + // explicitly-specified template arguments, if the corresponding argument + // A cannot be implicitly converted to P, deduction fails. + if (CheckNonDependent()) + return TemplateDeductionResult::NonDependentConversionFailure; + MultiLevelTemplateArgumentList SubstArgs( FunctionTemplate, CanonicalDeducedArgumentList->asArray(), /*Final=*/false); @@ -4051,8 +4072,8 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( // ([temp.constr.decl]), those constraints are checked for satisfaction // ([temp.constr.constr]). If the constraints are not satisfied, type // deduction fails. - if (!IsIncomplete) { - if (CheckInstantiatedFunctionTemplateConstraints( + if (IsLambda && !IsIncomplete) { + if (CheckFunctionTemplateConstraints( Info.getLocation(), Specialization, CTAI.CanonicalConverted, Info.AssociatedConstraintsSatisfaction)) return TemplateDeductionResult::MiscellaneousDeductionFailure; diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp index 29c5736a9bf9e..0f3dd1ee9b071 100644 --- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp +++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp @@ -955,10 +955,12 @@ Expr *buildIsDeducibleConstraint(Sema &SemaRef, Context.getTrivialTypeSourceInfo( Context.getDeducedTemplateSpecializationType( TemplateName(AliasTemplate), /*DeducedType=*/QualType(), - /*IsDependent=*/true)), // template specialization type whose - // arguments will be deduced. + /*IsDependent=*/true), + AliasTemplate->getLocation()), // template specialization type whose + // arguments will be deduced. Context.getTrivialTypeSourceInfo( - ReturnType), // type from which template arguments are deduced. + ReturnType, AliasTemplate->getLocation()), // type from which template + // arguments are deduced. }; return TypeTraitExpr::Create( Context, Context.getLogicalOperationType(), AliasTemplate->getLocation(), diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 0e81804f8c1e7..49b1ac32692ef 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -475,6 +475,21 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( assert((ND || DC) && "Can't find arguments for a decl if one isn't provided"); // Accumulate the set of template argument lists in this structure. MultiLevelTemplateArgumentList Result; + getTemplateInstantiationArgs( + Result, ND, DC, Final, Innermost, RelativeToPrimary, Pattern, + ForConstraintInstantiation, SkipForSpecialization, + ForDefaultArgumentSubstitution); + return Result; +} + +void Sema::getTemplateInstantiationArgs( + MultiLevelTemplateArgumentList &Result, const NamedDecl *ND, + const DeclContext *DC, bool Final, + std::optional> Innermost, bool RelativeToPrimary, + const FunctionDecl *Pattern, bool ForConstraintInstantiation, + bool SkipForSpecialization, bool ForDefaultArgumentSubstitution) { + assert((ND || DC) && "Can't find arguments for a decl if one isn't provided"); + // Accumulate the set of template argument lists in this structure. using namespace TemplateInstArgsHelpers; const Decl *CurDecl = ND; @@ -534,14 +549,12 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( } if (R.IsDone) - return Result; + return; if (R.ClearRelativeToPrimary) RelativeToPrimary = false; assert(R.NextDecl); CurDecl = R.NextDecl; } - - return Result; } bool Sema::CodeSynthesisContext::isInstantiationRecord() const { @@ -1374,6 +1387,19 @@ namespace { // Whether an incomplete substituion should be treated as an error. bool BailOutOnIncomplete; + private: + bool isSubstitutingConstraints() const { + return llvm::any_of(SemaRef.CodeSynthesisContexts, [](auto &Context) { + return Context.Kind == + Sema::CodeSynthesisContext::ConstraintSubstitution; + }); + } + + // CWG2770: Function parameters should be instantiated when they are + // needed by a satisfaction check of an atomic constraint or + // (recursively) by another function parameter. + bool maybeInstantiateFunctionParameterToScope(ParmVarDecl *OldParm); + public: typedef TreeTransform inherited; @@ -1430,12 +1456,19 @@ namespace { ArrayRef Unexpanded, bool &ShouldExpand, bool &RetainExpansion, UnsignedOrNone &NumExpansions) { - return getSema().CheckParameterPacksForExpansion(EllipsisLoc, - PatternRange, Unexpanded, - TemplateArgs, - ShouldExpand, - RetainExpansion, - NumExpansions); + if (SemaRef.CurrentInstantiationScope && isSubstitutingConstraints()) { + for (UnexpandedParameterPack ParmPack : Unexpanded) { + NamedDecl *VD = ParmPack.first.dyn_cast(); + if (!isa_and_present(VD)) + continue; + if (maybeInstantiateFunctionParameterToScope(cast(VD))) + return true; + } + } + + return getSema().CheckParameterPacksForExpansion( + EllipsisLoc, PatternRange, Unexpanded, TemplateArgs, ShouldExpand, + RetainExpansion, NumExpansions); } void ExpandingFunctionParameterPack(ParmVarDecl *Pack) { @@ -1919,9 +1952,62 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) { // template parameter. } + if (SemaRef.CurrentInstantiationScope) { + if (isSubstitutingConstraints() && isa(D) && + maybeInstantiateFunctionParameterToScope(cast(D))) + return nullptr; + } + return SemaRef.FindInstantiatedDecl(Loc, cast(D), TemplateArgs); } +bool TemplateInstantiator::maybeInstantiateFunctionParameterToScope( + ParmVarDecl *OldParm) { + if (SemaRef.CurrentInstantiationScope->findInstantiationUnsafe(OldParm)) + return false; + // We're instantiating a function parameter whose associated function template + // has not been instantiated at this point for constraint evaluation, so make + // sure the instantiated parameters are owned by a function declaration such + // that they can be correctly 'captured' in tryCaptureVariable(). + Sema::ContextRAII Context(SemaRef, OldParm->getDeclContext()); + + if (!OldParm->isParameterPack()) + return !TransformFunctionTypeParam(OldParm, /*indexAdjustment=*/0, + /*NumExpansions=*/std::nullopt, + /*ExpectParameterPack=*/false); + + SmallVector Unexpanded; + + // Find the parameter packs that could be expanded. + TypeLoc TL = OldParm->getTypeSourceInfo()->getTypeLoc(); + PackExpansionTypeLoc ExpansionTL = TL.castAs(); + TypeLoc Pattern = ExpansionTL.getPatternLoc(); + SemaRef.collectUnexpandedParameterPacks(Pattern, Unexpanded); + assert(!Unexpanded.empty() && "Pack expansion without parameter packs?"); + + bool ShouldExpand = false; + bool RetainExpansion = false; + std::optional OrigNumExpansions = + ExpansionTL.getTypePtr()->getNumExpansions(); + std::optional NumExpansions = OrigNumExpansions; + if (TryExpandParameterPacks(ExpansionTL.getEllipsisLoc(), + Pattern.getSourceRange(), Unexpanded, + ShouldExpand, RetainExpansion, NumExpansions)) + return true; + + assert(ShouldExpand && !RetainExpansion && + "Shouldn't preserve pack expansion when evaluating constraints"); + ExpandingFunctionParameterPack(OldParm); + for (unsigned I = 0; I != *NumExpansions; ++I) { + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I); + if (!TransformFunctionTypeParam(OldParm, /*indexAdjustment=*/0, + /*NumExpansions=*/OrigNumExpansions, + /*ExpectParameterPack=*/false)) + return true; + } + return false; +} + Decl *TemplateInstantiator::TransformDefinition(SourceLocation Loc, Decl *D) { Decl *Inst = getSema().SubstDecl(D, getSema().CurContext, TemplateArgs); if (!Inst) @@ -4485,9 +4571,8 @@ static const Decl *getCanonicalParmVarDecl(const Decl *D) { return D; } - llvm::PointerUnion * -LocalInstantiationScope::findInstantiationOf(const Decl *D) { +LocalInstantiationScope::findInstantiationUnsafe(const Decl *D) { D = getCanonicalParmVarDecl(D); for (LocalInstantiationScope *Current = this; Current; Current = Current->Outer) { @@ -4512,6 +4597,14 @@ LocalInstantiationScope::findInstantiationOf(const Decl *D) { break; } + return nullptr; +} + +llvm::PointerUnion * +LocalInstantiationScope::findInstantiationOf(const Decl *D) { + auto *Result = findInstantiationUnsafe(D); + if (Result) + return Result; // If we're performing a partial substitution during template argument // deduction, we may not have values for template parameters yet. if (isa(D) || isa(D) || diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 071389543ab68..4b1752105bafc 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -714,7 +714,7 @@ class TreeTransform { /// variables vector are acceptable. /// /// LastParamTransformed, if non-null, will be set to the index of the last - /// parameter on which transfromation was started. In the event of an error, + /// parameter on which transformation was started. In the event of an error, /// this will contain the parameter which failed to instantiate. /// /// Return true on error. diff --git a/clang/test/CXX/drs/cwg23xx.cpp b/clang/test/CXX/drs/cwg23xx.cpp index 74e72f2371e2a..128566537b577 100644 --- a/clang/test/CXX/drs/cwg23xx.cpp +++ b/clang/test/CXX/drs/cwg23xx.cpp @@ -365,6 +365,35 @@ struct A { #endif } // namespace cwg2363 +namespace cwg2369 { // cwg2369: partial +#if __cplusplus >= 202002L +template struct Z { + typedef typename T::x xx; +}; + +template +concept C = requires { typename T::A; }; +template typename Z::xx f(void *, T); // #1 +template void f(int, T); // #2 + +struct A { +} a; + +struct ZZ { + template ::xx> operator T *(); + operator int(); +}; + +void foo() { + ZZ zz; + f(1, a); // OK, deduction fails for #1 because there is no conversion from int + // to void* + f(zz, 42); // OK, deduction fails for #1 because C is not satisfied +} + +#endif +} // namespace cwg2369 + namespace cwg2370 { // cwg2370: no namespace N { typedef int type; diff --git a/clang/test/CXX/drs/cwg26xx.cpp b/clang/test/CXX/drs/cwg26xx.cpp index 3eb70583b6026..ab4d3695b6e22 100644 --- a/clang/test/CXX/drs/cwg26xx.cpp +++ b/clang/test/CXX/drs/cwg26xx.cpp @@ -310,7 +310,7 @@ void f(T) requires requires { []() { T::invalid; } (); }; // since-cxx20-note@-3 {{in instantiation of requirement here}} // since-cxx20-note@-4 {{while substituting template arguments into constraint expression here}} // since-cxx20-note@#cwg2672-f-0 {{while checking constraint satisfaction for template 'f' required here}} -// since-cxx20-note@#cwg2672-f-0 {{in instantiation of function template specialization 'cwg2672::f' requested here}} +// since-cxx20-note@#cwg2672-f-0 {{while substituting deduced template arguments into function template 'f' [with T = int]}} void f(...); template diff --git a/clang/test/CXX/drs/cwg27xx.cpp b/clang/test/CXX/drs/cwg27xx.cpp index a87d26dfc9acf..7caf36a9f23b2 100644 --- a/clang/test/CXX/drs/cwg27xx.cpp +++ b/clang/test/CXX/drs/cwg27xx.cpp @@ -174,6 +174,26 @@ static_assert(!__is_layout_compatible(StructWithAnonUnion, StructWithAnonUnion3) #endif } // namespace cwg2759 +namespace cwg2770 { // cwg2770: 20 open 2023-07-14 +#if __cplusplus >= 202002L +template +struct B { + static_assert(sizeof(T) == 1); + using type = int; +}; + +template +int f(T t, typename B::type u) requires (sizeof(t) == 1); + +template +int f(T t, long); + +int i = f(1, 2); +int j = f('a', 2); + +#endif +} // namespace cwg2770 + namespace cwg2789 { // cwg2789: 18 #if __cplusplus >= 202302L template diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp index 651cca927d513..19145f68b2f75 100644 --- a/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp @@ -154,7 +154,7 @@ void func() { bar(); // expected-note@-1 {{while checking constraint satisfaction for template 'bar' required here}} \ - // expected-note@-1 {{in instantiation of function template specialization}} + // expected-note@-1 {{while substituting deduced template arguments into function template 'bar' [with T = int]}} // expected-note@#bar {{in instantiation of static data member}} // expected-note@#bar {{in instantiation of requirement here}} // expected-note@#bar {{while checking the satisfaction of nested requirement requested here}} diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp index 083e743818121..59e6a48e48878 100644 --- a/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp +++ b/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp @@ -11,7 +11,7 @@ template struct S { // expected-error@+3{{atomic constraint must be of type 'bool' (found 'S')}} // expected-note@#FINST{{while checking constraint satisfaction}} -// expected-note@#FINST{{in instantiation of function template specialization}} +// expected-note@#FINST{{while substituting deduced template arguments into function template 'f' [with T = int]}} template requires (S{}) void f(T); void f(long); @@ -19,7 +19,7 @@ void f(long); // Ensure this applies to operator && as well. // expected-error@+3{{atomic constraint must be of type 'bool' (found 'S')}} // expected-note@#F2INST{{while checking constraint satisfaction}} -// expected-note@#F2INST{{in instantiation of function template specialization}} +// expected-note@#F2INST{{while substituting deduced template arguments into function template 'f2' [with T = int]}} template requires (S{} && true) void f2(T); void f2(long); @@ -32,7 +32,7 @@ template requires requires { // expected-note@-4{{while checking the satisfaction}} // expected-note@-6{{while substituting template arguments}} // expected-note@#F3INST{{while checking constraint satisfaction}} - // expected-note@#F3INST{{in instantiation of function template specialization}} + // expected-note@#F3INST{{while substituting deduced template arguments into function template 'f3' [with T = int]}} // } void f3(T); diff --git a/clang/test/SemaCXX/concept-crash-on-diagnostic.cpp b/clang/test/SemaCXX/concept-crash-on-diagnostic.cpp index c38f8888075de..00d6f8b62a458 100644 --- a/clang/test/SemaCXX/concept-crash-on-diagnostic.cpp +++ b/clang/test/SemaCXX/concept-crash-on-diagnostic.cpp @@ -31,7 +31,7 @@ void function() { // expected-note@#3 {{checking the satisfaction of concept 'convertible_to'}} // expected-note@#2 {{substituting template arguments into constraint expression here}} // expected-note@#5 {{checking constraint satisfaction for template 'compare'}} -// expected-note@#5 {{in instantiation of function template specialization 'compare' requested here}} +// expected-note@#5 {{while substituting deduced template arguments into function template 'compare' [with IteratorL = Object *, IteratorR = Object *]}} // expected-note@#4 {{candidate template ignored: constraints not satisfied [with IteratorL = Object *, IteratorR = Object *]}} // We don't know exactly the substituted type for `lhs == rhs`, thus a placeholder 'expr-type' is emitted. diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp index c863cc841af42..aeb02c9e4898e 100644 --- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp +++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp @@ -196,7 +196,7 @@ struct Foo { template using Bar = Foo; // expected-note {{constraints not satisfied for class template 'Foo'}} -// expected-note@-1 {{candidate template ignored: could not match}} +// expected-note@-1 {{candidate template ignored: could not match}} expected-note@-1 {{candidate template ignored: constraints not satisfied}} // expected-note@-2 {{implicit deduction guide declared as 'template requires __is_deducible(test14::Bar, Foo) Bar(Foo) -> Foo'}} // expected-note@-3 {{implicit deduction guide declared as 'template requires __is_deducible(test14::Bar, Foo) Bar(const double (&)[K]) -> Foo'}} double abc[3]; diff --git a/clang/test/SemaCXX/cxx23-assume.cpp b/clang/test/SemaCXX/cxx23-assume.cpp index 7f80cdfe7d452..726cb3bff652e 100644 --- a/clang/test/SemaCXX/cxx23-assume.cpp +++ b/clang/test/SemaCXX/cxx23-assume.cpp @@ -129,12 +129,12 @@ constexpr int f5() requires (!C) { return 2; } // expected-note 4 {{while che static_assert(f5() == 1); static_assert(f5() == 1); // expected-note 3 {{while checking constraint satisfaction}} - // expected-note@-1 3 {{in instantiation of}} + // expected-note@-1 3 {{while substituting deduced template arguments}} // expected-error@-2 {{no matching function for call}} static_assert(f5() == 2); -static_assert(f5() == 1); // expected-note {{while checking constraint satisfaction}} expected-note {{in instantiation of}} -static_assert(f5() == 2); // expected-note {{while checking constraint satisfaction}} expected-note {{in instantiation of}} +static_assert(f5() == 1); // expected-note {{while checking constraint satisfaction}} expected-note {{while substituting deduced template arguments}} +static_assert(f5() == 2); // expected-note {{while checking constraint satisfaction}} expected-note {{while substituting deduced template arguments}} // Do not validate assumptions whose evaluation would have side-effects. constexpr int foo() { diff --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp index 48061439941f2..4220486d3aed3 100644 --- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp +++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp @@ -233,7 +233,7 @@ void g() { A *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}} + // expected-note {{while substituting deduced template arguments}} } } diff --git a/clang/test/SemaCXX/lambda-unevaluated.cpp b/clang/test/SemaCXX/lambda-unevaluated.cpp index a9bcab58464e2..d3f937281f201 100644 --- a/clang/test/SemaCXX/lambda-unevaluated.cpp +++ b/clang/test/SemaCXX/lambda-unevaluated.cpp @@ -174,7 +174,7 @@ int* func(T) requires requires { []() { T::foo(); }; }; // expected-error{{type double* func(...); static_assert(__is_same(decltype(func(0)), double*)); // expected-note {{while checking constraint satisfaction for template 'func' required here}} - // expected-note@-1 {{in instantiation of function template specialization 'lambda_in_constraints::func'}} + // expected-note@-1 {{while substituting deduced template arguments into function template 'func' [with T = int]}} static_assert(__is_same(decltype(func(WithFoo())), int*)); template @@ -252,7 +252,7 @@ S s("a"); // #use // expected-note@#S-requires {{substituting template arguments into constraint expression here}} // expected-note@#S-requires {{in instantiation of requirement here}} // expected-note@#use {{checking constraint satisfaction for template 'S' required here}} -// expected-note@#use {{requested here}} +// expected-note@#use {{while substituting deduced template arguments into function template 'S' [with value:auto = const char *]}} // expected-note-re@#S 2{{candidate constructor {{.*}} not viable}} // expected-note@#S-ctor {{constraints not satisfied}} // expected-note-re@#S-requires {{because {{.*}} would be invalid}} diff --git a/clang/test/SemaTemplate/concepts-recursive-inst.cpp b/clang/test/SemaTemplate/concepts-recursive-inst.cpp index 9330df8cdd039..30a410cef91ee 100644 --- a/clang/test/SemaTemplate/concepts-recursive-inst.cpp +++ b/clang/test/SemaTemplate/concepts-recursive-inst.cpp @@ -76,7 +76,7 @@ auto it = begin(rng); // #BEGIN_CALL // expected-note@#INF_BEGIN {{while checking the satisfaction of concept 'Inf' requested here}} // expected-note@#INF_BEGIN {{while substituting template arguments into constraint expression here}} // expected-note@#BEGIN_CALL {{while checking constraint satisfaction for template 'begin' required here}} -// expected-note@#BEGIN_CALL {{in instantiation of function template specialization}} +// expected-note@#BEGIN_CALL {{while substituting deduced template arguments into function template}} // Fallout of the failure is failed lookup, which is necessary to stop odd // cascading errors. @@ -103,7 +103,7 @@ namespace GH50891 { // expected-note@#OP_TO {{while checking the satisfaction of concept 'Numeric' requested here}} // expected-note@#OP_TO {{while substituting template arguments into constraint expression here}} // expected-note@#FOO_CALL {{while checking constraint satisfaction for template}} - // expected-note@#FOO_CALL {{in instantiation of function template specialization}} + // expected-note@#FOO_CALL {{while substituting deduced template arguments into function template}} // expected-note@#FOO_CALL {{in instantiation of requirement here}} // expected-note@#NUMERIC {{while substituting template arguments into constraint expression here}} diff --git a/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp b/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp index f4403587a6259..5809ef684bbf3 100644 --- a/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp +++ b/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp @@ -34,7 +34,7 @@ namespace constant_evaluated { expected-note@-1{{candidate template ignored}} int a = (foo(), 0); // expected-note@-1 {{while checking}} expected-error@-1{{no matching function}} \ - expected-note@-1 {{in instantiation}} + expected-note@-1 {{while substituting}} template void bar() requires requires { requires f; } { }; // expected-note@-1{{in instantiation}} \ expected-note@-1{{while substituting}} \ diff --git a/clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp b/clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp index af3e3358f6138..5c7a90273d0e0 100644 --- a/clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp +++ b/clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp @@ -38,7 +38,7 @@ template concept True = true; template -concept False = false; +concept False = false; // #False template struct concepts { template struct B { @@ -68,7 +68,7 @@ template struct nested_init_list { Y y; }; - template + template // #INIT_LIST_INNER_INVALID_HEADER struct concept_fail { // #INIT_LIST_INNER_INVALID X x; F f; @@ -81,7 +81,9 @@ using NIL = nested_init_list::B; // expected-error@+1 {{no viable constructor or deduction guide for deduction of template arguments of 'nested_init_list::concept_fail'}} nested_init_list::concept_fail nil_invalid{1, ""}; -// expected-note@#INIT_LIST_INNER_INVALID {{candidate template ignored: substitution failure [with F = const char *]: constraints not satisfied for class template 'concept_fail' [with F = const char *]}} +// expected-note@#INIT_LIST_INNER_INVALID {{candidate template ignored: constraints not satisfied [with F = const char *]}} +// expected-note@#INIT_LIST_INNER_INVALID_HEADER {{because 'const char *' does not satisfy 'False'}} +// expected-note@#False {{because 'false' evaluated to false}} // expected-note@#INIT_LIST_INNER_INVALID {{implicit deduction guide declared as 'template concept_fail(int, F) -> concept_fail'}} // expected-note@#INIT_LIST_INNER_INVALID {{candidate function template not viable: requires 1 argument, but 2 were provided}} // expected-note@#INIT_LIST_INNER_INVALID {{implicit deduction guide declared as 'template concept_fail(concept_fail) -> concept_fail'}} diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index 8fe53ad46aca9..969356df960b7 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -14049,7 +14049,7 @@

C++ defect report implementation status

2369 CD6 Ordering between constraints and substitution - Unknown + Partial 2370 @@ -16468,7 +16468,11 @@

C++ defect report implementation status

2770 open Trailing requires-clause can refer to function parameters before they are substituted into - Not resolved + +
+ Not resolved + Clang 20 implements 2023-07-14 resolution +
2771 From df7500578957061d4f05d9d71fec07ff5e75e354 Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Fri, 10 Jan 2025 13:32:11 +0800 Subject: [PATCH 02/20] Prevent dependent code generation when substituting constraints --- clang/include/clang/Sema/Sema.h | 9 ++++++ clang/lib/Sema/Sema.cpp | 3 +- clang/lib/Sema/SemaTemplateInstantiate.cpp | 20 +++++++------ .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 10 +++++-- clang/test/CodeGenCXX/ms-mangle-requires.cpp | 29 +++++++++++++++++++ 5 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 clang/test/CodeGenCXX/ms-mangle-requires.cpp diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 3b1f4d3234ea9..6419410ec181e 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -13424,6 +13424,10 @@ class Sema final : public SemaBase { // FIXME: Should we have a similar limit for other forms of synthesis? unsigned NonInstantiationEntries; + /// The number of \p CodeSynthesisContexts that are not constraint + /// substitution. + unsigned NonConstraintSubstitutionEntries; + /// The depth of the context stack at the point when the most recent /// error or warning was produced. /// @@ -13752,6 +13756,11 @@ class Sema final : public SemaBase { return CodeSynthesisContexts.size() > NonInstantiationEntries; } + /// Determine whether we are currently performing constraint substitution. + bool inConstraintSubstitution() const { + return CodeSynthesisContexts.size() > NonConstraintSubstitutionEntries; + } + using EntityPrinter = llvm::function_ref; /// \brief create a Requirement::SubstitutionDiagnostic with only a diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index d2da9cd1201c2..7ee839676c9d9 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -266,7 +266,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, TyposCorrected(0), IsBuildingRecoveryCallExpr(false), NumSFINAEErrors(0), AccessCheckingSFINAE(false), CurrentInstantiationScope(nullptr), InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0), - ArgPackSubstIndex(std::nullopt), SatisfactionCache(Context) { + NonConstraintSubstitutionEntries(0), ArgPackSubstIndex(std::nullopt), + SatisfactionCache(Context) { assert(pp.TUKind == TUKind); TUScope = nullptr; diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 49b1ac32692ef..df2b07b402702 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -833,6 +833,9 @@ void Sema::pushCodeSynthesisContext(CodeSynthesisContext Ctx) { if (!Ctx.isInstantiationRecord()) ++NonInstantiationEntries; + if (Ctx.Kind != CodeSynthesisContext::ConstraintSubstitution) + ++NonConstraintSubstitutionEntries; + // Check to see if we're low on stack space. We can't do anything about this // from here, but we can at least warn the user. StackHandler.warnOnStackNearlyExhausted(Ctx.PointOfInstantiation); @@ -845,6 +848,11 @@ void Sema::popCodeSynthesisContext() { --NonInstantiationEntries; } + if (Active.Kind != CodeSynthesisContext::ConstraintSubstitution) { + assert(NonConstraintSubstitutionEntries > 0); + --NonConstraintSubstitutionEntries; + } + InNonInstantiationSFINAEContext = Active.SavedInNonInstantiationSFINAEContext; // Name lookup no longer looks in this template's defining module. @@ -1388,13 +1396,6 @@ namespace { bool BailOutOnIncomplete; private: - bool isSubstitutingConstraints() const { - return llvm::any_of(SemaRef.CodeSynthesisContexts, [](auto &Context) { - return Context.Kind == - Sema::CodeSynthesisContext::ConstraintSubstitution; - }); - } - // CWG2770: Function parameters should be instantiated when they are // needed by a satisfaction check of an atomic constraint or // (recursively) by another function parameter. @@ -1456,7 +1457,8 @@ namespace { ArrayRef Unexpanded, bool &ShouldExpand, bool &RetainExpansion, UnsignedOrNone &NumExpansions) { - if (SemaRef.CurrentInstantiationScope && isSubstitutingConstraints()) { + if (SemaRef.CurrentInstantiationScope && + SemaRef.inConstraintSubstitution()) { for (UnexpandedParameterPack ParmPack : Unexpanded) { NamedDecl *VD = ParmPack.first.dyn_cast(); if (!isa_and_present(VD)) @@ -1953,7 +1955,7 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) { } if (SemaRef.CurrentInstantiationScope) { - if (isSubstitutingConstraints() && isa(D) && + if (SemaRef.inConstraintSubstitution() && isa(D) && maybeInstantiateFunctionParameterToScope(cast(D))) return nullptr; } diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 76c055d28f091..9d6cfb5860049 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -5833,8 +5833,14 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, savedContext.pop(); } - DeclGroupRef DG(Function); - Consumer.HandleTopLevelDecl(DG); + // With CWG2369, we substitute constraints before instantiating the associated + // function template. This helps prevent potential code generation for + // dependent types, particularly under the MS ABI. + if (!inConstraintSubstitution() || + !getLambdaAwareParentOfDeclContext(Function)->isDependentContext()) { + DeclGroupRef DG(Function); + Consumer.HandleTopLevelDecl(DG); + } // This class may have local implicit instantiations that need to be // instantiation within this scope. diff --git a/clang/test/CodeGenCXX/ms-mangle-requires.cpp b/clang/test/CodeGenCXX/ms-mangle-requires.cpp new file mode 100644 index 0000000000000..1d873a3914fad --- /dev/null +++ b/clang/test/CodeGenCXX/ms-mangle-requires.cpp @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -std=c++20 -triple=x86_64-windows-msvc -Wno-defaulted-function-deleted -fms-compatibility -fms-extensions -emit-llvm %s -o - | FileCheck %s + +namespace CWG2369_Regression { + +template +using compare_three_way_result_t = Up::type; + +struct sfinae_assign_base {}; + +template +concept is_derived_from_optional = + requires(Tp param) { [](Up) {}(param); }; + +template + requires(is_derived_from_optional) +compare_three_way_result_t operator<=>(Tp, Up); + +struct RuntimeModeArgs { + auto operator<=>(const RuntimeModeArgs &) const = default; + sfinae_assign_base needs_admin; +}; + +RuntimeModeArgs foo() { + return {}; +} + +// CHECK: ?foo@CWG2369_Regression@@YA?AURuntimeModeArgs@1@XZ + +} From ea9dd82737e47d78d751d1ff75891c42db9ed615 Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Sat, 11 Jan 2025 16:14:24 +0800 Subject: [PATCH 03/20] Add a regression test --- clang/test/CodeGenCXX/ms-mangle-requires.cpp | 34 ++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/clang/test/CodeGenCXX/ms-mangle-requires.cpp b/clang/test/CodeGenCXX/ms-mangle-requires.cpp index 1d873a3914fad..1160d81d726d1 100644 --- a/clang/test/CodeGenCXX/ms-mangle-requires.cpp +++ b/clang/test/CodeGenCXX/ms-mangle-requires.cpp @@ -1,6 +1,8 @@ // RUN: %clang_cc1 -std=c++20 -triple=x86_64-windows-msvc -Wno-defaulted-function-deleted -fms-compatibility -fms-extensions -emit-llvm %s -o - | FileCheck %s -namespace CWG2369_Regression { +namespace CWG2369 { + +namespace Regression1 { template using compare_three_way_result_t = Up::type; @@ -24,6 +26,34 @@ RuntimeModeArgs foo() { return {}; } -// CHECK: ?foo@CWG2369_Regression@@YA?AURuntimeModeArgs@1@XZ +// CHECK: ?foo@Regression1@CWG2369@@YA?AURuntimeModeArgs@12@XZ + +} // namespace Regression1 + +namespace Regression2 { + +template +constexpr _Tp * __to_address(_Tp *) { + return nullptr; +} + +template +concept contiguous_iterator = requires(_Ip __i) { __to_address(__i); }; + +struct basic_string_view { + template + basic_string_view(_It, _It); +}; + +const char *str; +void sv() { basic_string_view(str, str); } + +void m_fn2() { + const char __trans_tmp_1 = *__to_address(&__trans_tmp_1); +} + +// CHECK: define {{.*}} @"??$__to_address@$$CBD@Regression2@CWG2369@@YAPEBDPEBD@Z" + +} // namespace Regression2 } From 4943c262df8620eb3a36ba4e8848259b83916620 Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Sat, 11 Jan 2025 20:59:25 +0800 Subject: [PATCH 04/20] Switch to checking EvaluationContext --- clang/lib/Sema/SemaTemplateDeduction.cpp | 7 ++++--- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 7 +++++-- clang/test/CodeGenCXX/ms-mangle-requires.cpp | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 4a58fdf014d43..42b9e3e9d7e80 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -4829,8 +4829,8 @@ TemplateDeductionResult Sema::DeduceTemplateArguments( /*AdjustExceptionSpec*/false); // Unevaluated SFINAE context. - EnterExpressionEvaluationContext Unevaluated( - *this, Sema::ExpressionEvaluationContext::Unevaluated); + std::optional Unevaluated( + std::in_place, *this, Sema::ExpressionEvaluationContext::Unevaluated); SFINAETrap Trap(*this); Deduced.resize(TemplateParams->size()); @@ -4873,13 +4873,14 @@ TemplateDeductionResult Sema::DeduceTemplateArguments( DeduceReturnType(Specialization, Info.getLocation(), false)) return TemplateDeductionResult::MiscellaneousDeductionFailure; + Unevaluated = std::nullopt; // [C++26][expr.const]/p17 // An expression or conversion is immediate-escalating if it is not initially // in an immediate function context and it is [...] // a potentially-evaluated id-expression that denotes an immediate function. if (IsAddressOfFunction && getLangOpts().CPlusPlus20 && Specialization->isImmediateEscalating() && - parentEvaluationContext().isPotentiallyEvaluated() && + currentEvaluationContext().isPotentiallyEvaluated() && CheckIfFunctionSpecializationIsImmediate(Specialization, Info.getLocation())) return TemplateDeductionResult::MiscellaneousDeductionFailure; diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 9d6cfb5860049..c090d0e6f6798 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -5836,8 +5836,11 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, // With CWG2369, we substitute constraints before instantiating the associated // function template. This helps prevent potential code generation for // dependent types, particularly under the MS ABI. - if (!inConstraintSubstitution() || - !getLambdaAwareParentOfDeclContext(Function)->isDependentContext()) { + bool InUnevaluatedOrImmediateContext = + llvm::any_of(ExprEvalContexts, [](auto &Context) { + return Context.isUnevaluated() || Context.isImmediateFunctionContext(); + }); + if (!InUnevaluatedOrImmediateContext) { DeclGroupRef DG(Function); Consumer.HandleTopLevelDecl(DG); } diff --git a/clang/test/CodeGenCXX/ms-mangle-requires.cpp b/clang/test/CodeGenCXX/ms-mangle-requires.cpp index 1160d81d726d1..15318ecffaf01 100644 --- a/clang/test/CodeGenCXX/ms-mangle-requires.cpp +++ b/clang/test/CodeGenCXX/ms-mangle-requires.cpp @@ -14,7 +14,7 @@ concept is_derived_from_optional = requires(Tp param) { [](Up) {}(param); }; template - requires(is_derived_from_optional) + requires(is_derived_from_optional && [](W) { return true; }(Up())) compare_three_way_result_t operator<=>(Tp, Up); struct RuntimeModeArgs { From ec143c18731e1bc885c452443adae19fd81facc4 Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Sat, 11 Jan 2025 22:14:38 +0800 Subject: [PATCH 05/20] fixup! Switch to checking EvaluationContext --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index c090d0e6f6798..4a71bc3ac85bb 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -5836,11 +5836,16 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, // With CWG2369, we substitute constraints before instantiating the associated // function template. This helps prevent potential code generation for // dependent types, particularly under the MS ABI. - bool InUnevaluatedOrImmediateContext = - llvm::any_of(ExprEvalContexts, [](auto &Context) { - return Context.isUnevaluated() || Context.isImmediateFunctionContext(); - }); - if (!InUnevaluatedOrImmediateContext) { + bool ShouldSkipCG = [&] { + auto *RD = dyn_cast(Function->getParent()); + if (!RD || !RD->isLambda()) + return false; + + return llvm::any_of(ExprEvalContexts, [](auto &Context) { + return Context.isUnevaluated() || Context.isImmediateFunctionContext(); + }); + }(); + if (ShouldSkipCG) { DeclGroupRef DG(Function); Consumer.HandleTopLevelDecl(DG); } From 5134b1b5b73989d4db31936cbef077b274c16c8d Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Sat, 11 Jan 2025 22:27:45 +0800 Subject: [PATCH 06/20] negative, sorry --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 4a71bc3ac85bb..5b6a9156f88a8 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -5845,7 +5845,7 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, return Context.isUnevaluated() || Context.isImmediateFunctionContext(); }); }(); - if (ShouldSkipCG) { + if (!ShouldSkipCG) { DeclGroupRef DG(Function); Consumer.HandleTopLevelDecl(DG); } From 7270bacd47529dcde3f23db27b3fd2acdcee5f12 Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Sat, 11 Jan 2025 23:10:38 +0800 Subject: [PATCH 07/20] Remove unused header --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 5b6a9156f88a8..732b350f2a16c 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -12,7 +12,6 @@ #include "TreeTransform.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" -#include "clang/AST/ASTLambda.h" #include "clang/AST/ASTMutationListener.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/DependentDiagnostic.h" From 671976d2423fec7b852d2188ae30699a5360e9b5 Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Wed, 23 Apr 2025 16:40:21 +0800 Subject: [PATCH 08/20] Use UnsignedOrNone --- clang/lib/Sema/SemaConcept.cpp | 2 +- clang/lib/Sema/SemaTemplateInstantiate.cpp | 6 +++--- .../test/SemaCXX/overload-resolution-deferred-templates.cpp | 2 +- clang/test/SemaTemplate/deduction-guide.cpp | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 520fe4aac97be..4791d324ad30d 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -1064,7 +1064,7 @@ static bool CheckFunctionConstraintsWithoutInstantiation( Sema &SemaRef, SourceLocation PointOfInstantiation, FunctionTemplateDecl *Template, ArrayRef TemplateArgs, ConstraintSatisfaction &Satisfaction) { - SmallVector TemplateAC; + SmallVector TemplateAC; Template->getAssociatedConstraints(TemplateAC); if (TemplateAC.empty()) { Satisfaction.IsSatisfied = true; diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index df2b07b402702..c6e6c9e01b8b5 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1989,9 +1989,9 @@ bool TemplateInstantiator::maybeInstantiateFunctionParameterToScope( bool ShouldExpand = false; bool RetainExpansion = false; - std::optional OrigNumExpansions = + UnsignedOrNone OrigNumExpansions = ExpansionTL.getTypePtr()->getNumExpansions(); - std::optional NumExpansions = OrigNumExpansions; + UnsignedOrNone NumExpansions = OrigNumExpansions; if (TryExpandParameterPacks(ExpansionTL.getEllipsisLoc(), Pattern.getSourceRange(), Unexpanded, ShouldExpand, RetainExpansion, NumExpansions)) @@ -2001,7 +2001,7 @@ bool TemplateInstantiator::maybeInstantiateFunctionParameterToScope( "Shouldn't preserve pack expansion when evaluating constraints"); ExpandingFunctionParameterPack(OldParm); for (unsigned I = 0; I != *NumExpansions; ++I) { - Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I); + Sema::ArgPackSubstIndexRAII SubstIndex(getSema(), I); if (!TransformFunctionTypeParam(OldParm, /*indexAdjustment=*/0, /*NumExpansions=*/OrigNumExpansions, /*ExpectParameterPack=*/false)) diff --git a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp index d68a942f64050..9736550f38e30 100644 --- a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp +++ b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp @@ -173,7 +173,7 @@ void f(int); void g(int n) { f(n); } // OK void h(short n) { f(n); } // expected-error@#GH62096-err {{static assertion failed due to requirement 'sizeof(short) == 0'}} \ -// expected-note@-1{{in instantiation of function template specialization}} \ +// expected-note@-1{{while substituting deduced template arguments}} \ // expected-note@-1{{while checking constraint satisfaction for template}} // expected-note@#GH62096-note1{{in instantiation}} // expected-note@#GH62096-note1{{while substituting template arguments into constraint expression here}} diff --git a/clang/test/SemaTemplate/deduction-guide.cpp b/clang/test/SemaTemplate/deduction-guide.cpp index dabd0cf12f77e..fac6d0496f15f 100644 --- a/clang/test/SemaTemplate/deduction-guide.cpp +++ b/clang/test/SemaTemplate/deduction-guide.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++2a -verify -ast-dump -ast-dump-decl-types -ast-dump-filter "deduction guide" %s | FileCheck %s --strict-whitespace -dump-input=always +// RUN: %clang_cc1 -std=c++2a -verify -ast-dump -ast-dump-decl-types -ast-dump-filter "deduction guide" %s | FileCheck %s --strict-whitespace template struct X {}; template typename> struct Y {}; @@ -234,7 +234,7 @@ F s(0); // CHECK: | `-CXXBoolLiteralExpr {{.*}} 'bool' false // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit 'auto (U) -> F' // CHECK: | `-ParmVarDecl {{.*}} 'U' -// CHECK: `-CXXDeductionGuideDecl {{.*}} implicit 'auto (int) -> F<>' +// CHECK: `-CXXDeductionGuideDecl {{.*}} implicit used 'auto (int) -> F<>' // CHECK: |-TemplateArgument integral ''x'' // CHECK: |-TemplateArgument type 'int' // CHECK: | `-BuiltinType {{.*}} 'int' From 0a517fd8743dcdb46f1d9f147268e7c881aeeafa Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Fri, 24 Jan 2025 13:52:37 +0800 Subject: [PATCH 09/20] Implement GCC's CWG 2369 heuristic --- clang/include/clang/Sema/Sema.h | 9 +- clang/lib/Sema/SemaOverload.cpp | 62 ++++++- clang/lib/Sema/SemaTemplateDeduction.cpp | 13 +- .../SemaTemplate/concepts-recursive-inst.cpp | 169 ++++++++++++++++++ 4 files changed, 240 insertions(+), 13 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 6419410ec181e..5611bad6fb2d6 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -10452,7 +10452,8 @@ class Sema final : public SemaBase { FunctionTemplateDecl *FunctionTemplate, ArrayRef ParamTypes, ArrayRef Args, OverloadCandidateSet &CandidateSet, ConversionSequenceList &Conversions, bool SuppressUserConversions, - CXXRecordDecl *ActingContext = nullptr, QualType ObjectType = QualType(), + bool NonInstOnly, CXXRecordDecl *ActingContext = nullptr, + QualType ObjectType = QualType(), Expr::Classification ObjectClassification = {}, OverloadCandidateParamOrder PO = {}); @@ -12497,7 +12498,9 @@ class Sema final : public SemaBase { sema::TemplateDeductionInfo &Info, SmallVectorImpl const *OriginalCallArgs, bool PartialOverloading, bool PartialOrdering, - llvm::function_ref CheckNonDependent = [] { return false; }); + llvm::function_ref CheckNonDependent = [](bool) { + return false; + }); /// Perform template argument deduction from a function call /// (C++ [temp.deduct.call]). @@ -12532,7 +12535,7 @@ class Sema final : public SemaBase { bool PartialOverloading, bool AggregateDeductionCandidate, bool PartialOrdering, QualType ObjectType, Expr::Classification ObjectClassification, - llvm::function_ref)> CheckNonDependent); + llvm::function_ref, bool)> CheckNonDependent); /// Deduce template arguments when taking the address of a function /// template (C++ [temp.deduct.funcaddr]) or matching a specialization to diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 5b224b6c08fef..eaaff56ca336c 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -7829,10 +7829,10 @@ static void AddMethodTemplateCandidateImmediately( MethodTmpl, ExplicitTemplateArgs, Args, Specialization, Info, PartialOverloading, /*AggregateDeductionCandidate=*/false, /*PartialOrdering=*/false, ObjectType, ObjectClassification, - [&](ArrayRef ParamTypes) { + [&](ArrayRef ParamTypes, bool NonInstOnly) { return S.CheckNonDependentConversions( MethodTmpl, ParamTypes, Args, CandidateSet, Conversions, - SuppressUserConversions, ActingContext, ObjectType, + SuppressUserConversions, NonInstOnly, ActingContext, ObjectType, ObjectClassification, PO); }); Result != TemplateDeductionResult::Success) { @@ -7943,10 +7943,11 @@ static void AddTemplateOverloadCandidateImmediately( /*PartialOrdering=*/false, /*ObjectType=*/QualType(), /*ObjectClassification=*/Expr::Classification(), - [&](ArrayRef ParamTypes) { + [&](ArrayRef ParamTypes, bool NonInstOnly) { return S.CheckNonDependentConversions( FunctionTemplate, ParamTypes, Args, CandidateSet, Conversions, - SuppressUserConversions, nullptr, QualType(), {}, PO); + SuppressUserConversions, NonInstOnly, nullptr, QualType(), {}, + PO); }); Result != TemplateDeductionResult::Success) { OverloadCandidate &Candidate = @@ -8021,7 +8022,7 @@ bool Sema::CheckNonDependentConversions( FunctionTemplateDecl *FunctionTemplate, ArrayRef ParamTypes, ArrayRef Args, OverloadCandidateSet &CandidateSet, ConversionSequenceList &Conversions, bool SuppressUserConversions, - CXXRecordDecl *ActingContext, QualType ObjectType, + bool NonInstOnly, CXXRecordDecl *ActingContext, QualType ObjectType, Expr::Classification ObjectClassification, OverloadCandidateParamOrder PO) { // FIXME: The cases in which we allow explicit conversions for constructor // arguments never consider calling a constructor template. It's not clear @@ -8058,6 +8059,54 @@ bool Sema::CheckNonDependentConversions( } } + // A heuristic & speculative workaround for bug + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99599 that manifests after + // CWG2369. + auto ConversionMightInduceInstantiation = [&](QualType ParmType, + QualType ArgType) { + ParmType = ParmType.getNonReferenceType(); + ArgType = ArgType.getNonReferenceType(); + bool PointerConv = ParmType->isPointerType() && ArgType->isPointerType(); + if (PointerConv) { + ParmType = ParmType->getPointeeType(); + ArgType = ArgType->getPointeeType(); + } + + auto IsInstantiation = [&](QualType T) { + if (auto *RT = T->getAs()) { + if (auto *RD = dyn_cast(RT->getDecl())) { + if (auto *ClassTemplateSpec = + dyn_cast(RD)) + return ClassTemplateSpec->getSpecializationKind() == TSK_Undeclared; + if (RD->getInstantiatedFromMemberClass()) + return RD->getMemberSpecializationInfo() + ->getTemplateSpecializationKind() != + TemplateSpecializationKind::TSK_ExplicitSpecialization; + } + } + return false; + }; + if (IsInstantiation(ParmType) || IsInstantiation(ArgType)) + return true; + + if (PointerConv || CompareReferenceRelationship(SourceLocation(), ParmType, + ArgType) == Ref_Related) + return false; + + if (auto *RT = ParmType->getAs()) + if (auto *RD = dyn_cast(RT->getDecl()); + RD && RD->hasDefinition() && !RD->isAggregate()) + return false; + + if (auto *RT = ArgType->getAs()) + if (auto *RD = dyn_cast(RT->getDecl()); + RD && RD->hasDefinition() && + !RD->getVisibleConversionFunctions().empty()) + return true; + + return false; + }; + unsigned Offset = Method && Method->hasCXXExplicitFunctionObjectParameter() ? 1 : 0; @@ -8078,6 +8127,9 @@ bool Sema::CheckNonDependentConversions( // For members, 'this' got ConvIdx = 0 previously. ConvIdx = ThisConversions + I; } + if (NonInstOnly && + ConversionMightInduceInstantiation(ParamType, Args[I]->getType())) + continue; Conversions[ConvIdx] = TryCopyInitialization(*this, Args[I], ParamType, SuppressUserConversions, diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 42b9e3e9d7e80..d3b3ef8cbef5f 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -3962,7 +3962,7 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( TemplateDeductionInfo &Info, SmallVectorImpl const *OriginalCallArgs, bool PartialOverloading, bool PartialOrdering, - llvm::function_ref CheckNonDependent) { + llvm::function_ref CheckNonDependent) { // Unevaluated SFINAE context. EnterExpressionEvaluationContext Unevaluated( *this, Sema::ExpressionEvaluationContext::Unevaluated); @@ -4005,6 +4005,9 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( Owner = FunctionTemplate->getLexicalDeclContext(); FunctionDecl *FD = FunctionTemplate->getTemplatedDecl(); + if (CheckNonDependent(/*NonInstOnly=*/true)) + return TemplateDeductionResult::NonDependentConversionFailure; + // C++20 [temp.deduct.general]p5: [CWG2369] // If the function template has associated constraints, those constraints // are checked for satisfaction. If the constraints are not satisfied, type @@ -4035,7 +4038,7 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( // P with a type that was non-dependent before substitution of any // explicitly-specified template arguments, if the corresponding argument // A cannot be implicitly converted to P, deduction fails. - if (CheckNonDependent()) + if (CheckNonDependent(/*NonInstOnly=*/false)) return TemplateDeductionResult::NonDependentConversionFailure; MultiLevelTemplateArgumentList SubstArgs( @@ -4532,7 +4535,7 @@ TemplateDeductionResult Sema::DeduceTemplateArguments( bool PartialOverloading, bool AggregateDeductionCandidate, bool PartialOrdering, QualType ObjectType, Expr::Classification ObjectClassification, - llvm::function_ref)> CheckNonDependent) { + llvm::function_ref, bool)> CheckNonDependent) { if (FunctionTemplate->isInvalidDecl()) return TemplateDeductionResult::Invalid; @@ -4746,9 +4749,9 @@ TemplateDeductionResult Sema::DeduceTemplateArguments( Result = FinishTemplateArgumentDeduction( FunctionTemplate, Deduced, NumExplicitlySpecified, Specialization, Info, &OriginalCallArgs, PartialOverloading, PartialOrdering, - [&, CallingCtx]() { + [&, CallingCtx](bool NonInstOnly) { ContextRAII SavedContext(*this, CallingCtx); - return CheckNonDependent(ParamTypesForArgChecking); + return CheckNonDependent(ParamTypesForArgChecking, NonInstOnly); }); }); return Result; diff --git a/clang/test/SemaTemplate/concepts-recursive-inst.cpp b/clang/test/SemaTemplate/concepts-recursive-inst.cpp index 30a410cef91ee..f251ba6dc5b09 100644 --- a/clang/test/SemaTemplate/concepts-recursive-inst.cpp +++ b/clang/test/SemaTemplate/concepts-recursive-inst.cpp @@ -143,3 +143,172 @@ namespace GH60323 { Size().sizeparens(i); } } + +namespace CWG2369_Regressions { + +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109397 +namespace GCC_103997 { + +template +concept streamable = requires(_stream &s, _type &&v) { + s << static_cast<_type &&>(v); +}; + +struct type_a { + template + type_a &operator<<(_arg &&) { + // std::clog << "type_a" << std::endl; + return *this; + } +}; + +struct type_b { + type_b &operator<<(type_a const &) { + // std::clog << "type_b" << std::endl; + return *this; + } +}; + +struct type_c { + type_b b; + template + requires streamable<_arg, type_b> + friend type_c &operator<<(type_c &c, _arg &&a) { + // std::clog << "type_c" << std::endl; + c.b << static_cast<_arg &&>(a); + return c; + } +}; + +void foo() { + type_a a; + type_c c; + a << c; // "type_a\n" (gcc gives error here) + c << a; // "type_c\ntype_b\n" +} + +} + +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108393 +namespace GCC_108393 { + +template +struct iterator_traits +{}; + +template + requires requires(T __t, T __u) { __t == __u; } +struct iterator_traits +{}; + +template +concept C = requires { typename iterator_traits::A; }; + +struct unreachable_sentinel_t +{ + template + friend constexpr bool operator==(unreachable_sentinel_t, const _Iter&) noexcept; +}; + +template +struct S +{}; + +static_assert(!C>); + +} + +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107429 +namespace GCC_107429 { + +struct tag_foo { } inline constexpr foo; +struct tag_bar { } inline constexpr bar; + +template +auto f(tag_foo, T... x) +{ + return (x + ...); +} + +template +concept fooable = requires (T... x) { f(foo, x...); }; + +template requires (fooable) +auto f(tag_bar, T... x) +{ + return f(foo, x...); +} + +auto test() +{ + return f(bar, 1, 2, 3); +} + +} + +namespace GCC_99599 { + +struct foo_tag {}; +struct bar_tag {}; + +template +concept fooable = requires(T it) { + invoke_tag(foo_tag{}, it); // <-- here +}; + +template auto invoke_tag(foo_tag, T in) { return in; } + +template auto invoke_tag(bar_tag, T it) { return it; } + +int main() { + // Neither line below compiles in GCC 11, independently of the other + return invoke_tag(foo_tag{}, 2) + invoke_tag(bar_tag{}, 2); +} + +} + +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99599#c22 +namespace GCC_99599_2 { + +template class indirect { +public: + template requires + requires (const T& t, const U& u) { t == u; } + friend constexpr bool operator==(const indirect&, const U&) { return false; } + +private: + T* _M_ptr{}; +}; + +indirect i; +bool b = i == 1; + +} + +namespace FAILED_GCC_110160 { +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110160 +// Current heuristic FAILED; GCC trunk also failed +// https://godbolt.org/z/r3Pz9Tehz +#if 0 +#include +#include + +template +concept StreamCanReceiveString = requires(T& t, std::string s) { + { t << s }; +}; + +struct NotAStream {}; +struct UnrelatedType {}; + +template +S& operator<<(S& s, UnrelatedType) { + return s; +} + +static_assert(!StreamCanReceiveString); + +static_assert(StreamCanReceiveString); +#endif +} +} From b32afd2383dccd4cb501f4dc51e17e8bdb9e7ebe Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Fri, 25 Apr 2025 19:59:01 +0800 Subject: [PATCH 10/20] Check the non-user-defined-non-dependent conversions earlier --- clang/include/clang/Sema/Sema.h | 2 +- clang/lib/Sema/SemaOverload.cpp | 66 ++++++++++++++++----------------- 2 files changed, 32 insertions(+), 36 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 5611bad6fb2d6..baccc6949680c 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -10452,7 +10452,7 @@ class Sema final : public SemaBase { FunctionTemplateDecl *FunctionTemplate, ArrayRef ParamTypes, ArrayRef Args, OverloadCandidateSet &CandidateSet, ConversionSequenceList &Conversions, bool SuppressUserConversions, - bool NonInstOnly, CXXRecordDecl *ActingContext = nullptr, + bool SkipUserDefinedConversions, CXXRecordDecl *ActingContext = nullptr, QualType ObjectType = QualType(), Expr::Classification ObjectClassification = {}, OverloadCandidateParamOrder PO = {}); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index eaaff56ca336c..405aefb7e9105 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -8022,8 +8022,9 @@ bool Sema::CheckNonDependentConversions( FunctionTemplateDecl *FunctionTemplate, ArrayRef ParamTypes, ArrayRef Args, OverloadCandidateSet &CandidateSet, ConversionSequenceList &Conversions, bool SuppressUserConversions, - bool NonInstOnly, CXXRecordDecl *ActingContext, QualType ObjectType, - Expr::Classification ObjectClassification, OverloadCandidateParamOrder PO) { + bool SkipUserDefinedConversions, CXXRecordDecl *ActingContext, + QualType ObjectType, Expr::Classification ObjectClassification, + OverloadCandidateParamOrder PO) { // FIXME: The cases in which we allow explicit conversions for constructor // arguments never consider calling a constructor template. It's not clear // that is correct. @@ -8034,8 +8035,9 @@ bool Sema::CheckNonDependentConversions( bool HasThisConversion = Method && !isa(Method); unsigned ThisConversions = HasThisConversion ? 1 : 0; - Conversions = - CandidateSet.allocateConversionSequences(ThisConversions + Args.size()); + Conversions = Conversions.empty() ? CandidateSet.allocateConversionSequences( + ThisConversions + Args.size()) + : Conversions; // Overload resolution is always an unevaluated context. EnterExpressionEvaluationContext Unevaluated( @@ -8059,11 +8061,11 @@ bool Sema::CheckNonDependentConversions( } } - // A heuristic & speculative workaround for bug - // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99599 that manifests after - // CWG2369. - auto ConversionMightInduceInstantiation = [&](QualType ParmType, - QualType ArgType) { + // A speculative workaround for self-dependent constraint bugs that manifest + // after CWG2369. + // FIXME: Add references to the standard once P3606 is adopted. + auto MaybeInvolveUserDefinedConversion = [&](QualType ParmType, + QualType ArgType) { ParmType = ParmType.getNonReferenceType(); ArgType = ArgType.getNonReferenceType(); bool PointerConv = ParmType->isPointerType() && ArgType->isPointerType(); @@ -8072,37 +8074,29 @@ bool Sema::CheckNonDependentConversions( ArgType = ArgType->getPointeeType(); } - auto IsInstantiation = [&](QualType T) { - if (auto *RT = T->getAs()) { - if (auto *RD = dyn_cast(RT->getDecl())) { - if (auto *ClassTemplateSpec = - dyn_cast(RD)) - return ClassTemplateSpec->getSpecializationKind() == TSK_Undeclared; - if (RD->getInstantiatedFromMemberClass()) - return RD->getMemberSpecializationInfo() - ->getTemplateSpecializationKind() != - TemplateSpecializationKind::TSK_ExplicitSpecialization; - } - } - return false; - }; - if (IsInstantiation(ParmType) || IsInstantiation(ArgType)) - return true; - - if (PointerConv || CompareReferenceRelationship(SourceLocation(), ParmType, - ArgType) == Ref_Related) - return false; - if (auto *RT = ParmType->getAs()) if (auto *RD = dyn_cast(RT->getDecl()); - RD && RD->hasDefinition() && !RD->isAggregate()) - return false; + RD && RD->hasDefinition()) { + if (llvm::any_of(LookupConstructors(RD), [](NamedDecl *ND) { + auto Info = getConstructorInfo(ND); + if (!Info) + return false; + CXXConstructorDecl *Ctor = Info.Constructor; + /// isConvertingConstructor takes copy/move constructors into + /// account! + return !Ctor->isCopyOrMoveConstructor() && + Ctor->isConvertingConstructor( + /*AllowExplicit=*/true); + })) + return true; + } if (auto *RT = ArgType->getAs()) if (auto *RD = dyn_cast(RT->getDecl()); RD && RD->hasDefinition() && - !RD->getVisibleConversionFunctions().empty()) + !RD->getVisibleConversionFunctions().empty()) { return true; + } return false; }; @@ -8127,8 +8121,10 @@ bool Sema::CheckNonDependentConversions( // For members, 'this' got ConvIdx = 0 previously. ConvIdx = ThisConversions + I; } - if (NonInstOnly && - ConversionMightInduceInstantiation(ParamType, Args[I]->getType())) + if (Conversions[ConvIdx].isInitialized()) + continue; + if (SkipUserDefinedConversions && + MaybeInvolveUserDefinedConversion(ParamType, Args[I]->getType())) continue; Conversions[ConvIdx] = TryCopyInitialization(*this, Args[I], ParamType, From 8423cefd2e4d123f5bd05b93c17eb46f186469e4 Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Wed, 7 May 2025 19:42:18 +0800 Subject: [PATCH 11/20] Check only the aggregates, matching GCC's behavior --- clang/lib/Sema/SemaOverload.cpp | 26 +++++-------------- .../SemaTemplate/concepts-recursive-inst.cpp | 9 ++++--- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 405aefb7e9105..b41f6f89f7fca 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -8064,32 +8064,20 @@ bool Sema::CheckNonDependentConversions( // A speculative workaround for self-dependent constraint bugs that manifest // after CWG2369. // FIXME: Add references to the standard once P3606 is adopted. - auto MaybeInvolveUserDefinedConversion = [&](QualType ParmType, + auto MaybeInvolveUserDefinedConversion = [&](QualType ParamType, QualType ArgType) { - ParmType = ParmType.getNonReferenceType(); + ParamType = ParamType.getNonReferenceType(); ArgType = ArgType.getNonReferenceType(); - bool PointerConv = ParmType->isPointerType() && ArgType->isPointerType(); + bool PointerConv = ParamType->isPointerType() && ArgType->isPointerType(); if (PointerConv) { - ParmType = ParmType->getPointeeType(); + ParamType = ParamType->getPointeeType(); ArgType = ArgType->getPointeeType(); } - if (auto *RT = ParmType->getAs()) + if (auto *RT = ParamType->getAs()) if (auto *RD = dyn_cast(RT->getDecl()); - RD && RD->hasDefinition()) { - if (llvm::any_of(LookupConstructors(RD), [](NamedDecl *ND) { - auto Info = getConstructorInfo(ND); - if (!Info) - return false; - CXXConstructorDecl *Ctor = Info.Constructor; - /// isConvertingConstructor takes copy/move constructors into - /// account! - return !Ctor->isCopyOrMoveConstructor() && - Ctor->isConvertingConstructor( - /*AllowExplicit=*/true); - })) - return true; - } + RD && RD->hasDefinition() && !RD->isAggregate()) + return true; if (auto *RT = ArgType->getAs()) if (auto *RD = dyn_cast(RT->getDecl()); diff --git a/clang/test/SemaTemplate/concepts-recursive-inst.cpp b/clang/test/SemaTemplate/concepts-recursive-inst.cpp index f251ba6dc5b09..573aaf1e62817 100644 --- a/clang/test/SemaTemplate/concepts-recursive-inst.cpp +++ b/clang/test/SemaTemplate/concepts-recursive-inst.cpp @@ -268,7 +268,9 @@ int main() { } // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99599#c22 -namespace GCC_99599_2 { +// FIXME: Is this valid?? +#if 0 +namespace FAILED_GCC_99599_2 { template class indirect { public: @@ -284,12 +286,13 @@ indirect i; bool b = i == 1; } +#endif +#if 0 namespace FAILED_GCC_110160 { // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110160 // Current heuristic FAILED; GCC trunk also failed // https://godbolt.org/z/r3Pz9Tehz -#if 0 #include #include @@ -309,6 +312,6 @@ S& operator<<(S& s, UnrelatedType) { static_assert(!StreamCanReceiveString); static_assert(StreamCanReceiveString); -#endif } +#endif } From 04be7cc31fe72bcdc45ea31f1815ffd021f6e6f4 Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Wed, 7 May 2025 19:52:18 +0800 Subject: [PATCH 12/20] Fix comments --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 732b350f2a16c..86079ca27250e 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -5832,9 +5832,11 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, savedContext.pop(); } - // With CWG2369, we substitute constraints before instantiating the associated - // function template. This helps prevent potential code generation for - // dependent types, particularly under the MS ABI. + // We never need to emit the code for a lambda in unevaluated context. + // We also can't mangle a lambda in the require clause of a function template + // during constraint checking as the MSI ABI would need to mangle the (not yet + // specialized) enclosing declaration + // FIXME: Should we try to skip this for non-lambda functions too? bool ShouldSkipCG = [&] { auto *RD = dyn_cast(Function->getParent()); if (!RD || !RD->isLambda()) From da25c55bd9d062f93a2023f7d85545f78dc29426 Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Wed, 14 May 2025 17:41:50 +0800 Subject: [PATCH 13/20] Revert "Check only the aggregates, matching GCC's behavior" This reverts commit 8423cefd2e4d123f5bd05b93c17eb46f186469e4. --- clang/lib/Sema/SemaOverload.cpp | 26 ++++++++++++++----- .../SemaTemplate/concepts-recursive-inst.cpp | 9 +++---- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index b41f6f89f7fca..405aefb7e9105 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -8064,20 +8064,32 @@ bool Sema::CheckNonDependentConversions( // A speculative workaround for self-dependent constraint bugs that manifest // after CWG2369. // FIXME: Add references to the standard once P3606 is adopted. - auto MaybeInvolveUserDefinedConversion = [&](QualType ParamType, + auto MaybeInvolveUserDefinedConversion = [&](QualType ParmType, QualType ArgType) { - ParamType = ParamType.getNonReferenceType(); + ParmType = ParmType.getNonReferenceType(); ArgType = ArgType.getNonReferenceType(); - bool PointerConv = ParamType->isPointerType() && ArgType->isPointerType(); + bool PointerConv = ParmType->isPointerType() && ArgType->isPointerType(); if (PointerConv) { - ParamType = ParamType->getPointeeType(); + ParmType = ParmType->getPointeeType(); ArgType = ArgType->getPointeeType(); } - if (auto *RT = ParamType->getAs()) + if (auto *RT = ParmType->getAs()) if (auto *RD = dyn_cast(RT->getDecl()); - RD && RD->hasDefinition() && !RD->isAggregate()) - return true; + RD && RD->hasDefinition()) { + if (llvm::any_of(LookupConstructors(RD), [](NamedDecl *ND) { + auto Info = getConstructorInfo(ND); + if (!Info) + return false; + CXXConstructorDecl *Ctor = Info.Constructor; + /// isConvertingConstructor takes copy/move constructors into + /// account! + return !Ctor->isCopyOrMoveConstructor() && + Ctor->isConvertingConstructor( + /*AllowExplicit=*/true); + })) + return true; + } if (auto *RT = ArgType->getAs()) if (auto *RD = dyn_cast(RT->getDecl()); diff --git a/clang/test/SemaTemplate/concepts-recursive-inst.cpp b/clang/test/SemaTemplate/concepts-recursive-inst.cpp index 573aaf1e62817..f251ba6dc5b09 100644 --- a/clang/test/SemaTemplate/concepts-recursive-inst.cpp +++ b/clang/test/SemaTemplate/concepts-recursive-inst.cpp @@ -268,9 +268,7 @@ int main() { } // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99599#c22 -// FIXME: Is this valid?? -#if 0 -namespace FAILED_GCC_99599_2 { +namespace GCC_99599_2 { template class indirect { public: @@ -286,13 +284,12 @@ indirect i; bool b = i == 1; } -#endif -#if 0 namespace FAILED_GCC_110160 { // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110160 // Current heuristic FAILED; GCC trunk also failed // https://godbolt.org/z/r3Pz9Tehz +#if 0 #include #include @@ -312,6 +309,6 @@ S& operator<<(S& s, UnrelatedType) { static_assert(!StreamCanReceiveString); static_assert(StreamCanReceiveString); -} #endif } +} From cbf10c2b92d5a4fc8d806af35425e67df1553984 Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Wed, 14 May 2025 17:51:07 +0800 Subject: [PATCH 14/20] More tests! --- clang/lib/Sema/SemaOverload.cpp | 10 ++-- .../SemaTemplate/concepts-recursive-inst.cpp | 50 +++++++++++++++++++ 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 405aefb7e9105..f7b45c2ad99a2 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -8064,17 +8064,17 @@ bool Sema::CheckNonDependentConversions( // A speculative workaround for self-dependent constraint bugs that manifest // after CWG2369. // FIXME: Add references to the standard once P3606 is adopted. - auto MaybeInvolveUserDefinedConversion = [&](QualType ParmType, + auto MaybeInvolveUserDefinedConversion = [&](QualType ParamType, QualType ArgType) { - ParmType = ParmType.getNonReferenceType(); + ParamType = ParamType.getNonReferenceType(); ArgType = ArgType.getNonReferenceType(); - bool PointerConv = ParmType->isPointerType() && ArgType->isPointerType(); + bool PointerConv = ParamType->isPointerType() && ArgType->isPointerType(); if (PointerConv) { - ParmType = ParmType->getPointeeType(); + ParamType = ParamType->getPointeeType(); ArgType = ArgType->getPointeeType(); } - if (auto *RT = ParmType->getAs()) + if (auto *RT = ParamType->getAs()) if (auto *RD = dyn_cast(RT->getDecl()); RD && RD->hasDefinition()) { if (llvm::any_of(LookupConstructors(RD), [](NamedDecl *ND) { diff --git a/clang/test/SemaTemplate/concepts-recursive-inst.cpp b/clang/test/SemaTemplate/concepts-recursive-inst.cpp index f251ba6dc5b09..097cad1a64179 100644 --- a/clang/test/SemaTemplate/concepts-recursive-inst.cpp +++ b/clang/test/SemaTemplate/concepts-recursive-inst.cpp @@ -285,6 +285,56 @@ bool b = i == 1; } +namespace GCC_99599_3 { + +template +struct S { T t; }; + +template +concept C = sizeof(S) > 0; + +struct I; + +struct from_range_t { + explicit from_range_t() = default; +}; +inline constexpr from_range_t from_range; + +template +concept FromRange = __is_same_as (T, from_range_t); + +//#define WORKAROUND +#ifdef WORKAROUND +template +void f(U, T*); +#else +template +void f(from_range_t, T*); +#endif + +void f(...); + +void g(I* p) { + f(0, p); +} + +} + +namespace GCC_99599_4 { + +struct A { + A(...); +}; + +template void f(A, T) { } + +int main() +{ + f(42, 24); +} + +} + namespace FAILED_GCC_110160 { // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110160 // Current heuristic FAILED; GCC trunk also failed From debef50c5cc472ded7e705d029dd351df5759ca7 Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Thu, 15 May 2025 16:51:51 +0800 Subject: [PATCH 15/20] [Clang] Profile singly-resolved UnresolvedLookupExpr with the declaration For a dependent variable template specialization, we don't build a dependent Decl node or a DeclRefExpr to represent it. Instead, we preserve the UnresolvedLookupExpr until instantiation. However, this approach isn't ideal for constraint normalization. We consider the qualifier during profiling, but since that's based on the written code, it can introduce confusing differences, even when the expressions resolve to the same declaration. This change ensures that, if possible, we profile the resolved declaration instead of its qualifier. For expressions that resolve to more than one declarations, we still profile its qualifier, as otherwise it would make us depend on the order of lookup results. --- clang/docs/ReleaseNotes.rst | 1 + clang/lib/AST/StmtProfile.cpp | 5 ++++- .../SemaTemplate/concepts-out-of-line-def.cpp | 15 +++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 5ccd346a93b4f..36977ed1ddb99 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -545,6 +545,7 @@ Bug Fixes to C++ Support - Clang no longer crashes when establishing subsumption between some constraint expressions. (#GH122581) - Clang now issues an error when placement new is used to modify a const-qualified variable in a ``constexpr`` function. (#GH131432) +- Fixed a function declaration mismatch that caused inconsistencies between concepts and variable template declarations. (#GH139476) - Clang now emits a warning when class template argument deduction for alias templates is used in C++17. (#GH133806) Bug Fixes to AST Handling diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 83d54da9be7e5..261e73fe9e303 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -2189,7 +2189,10 @@ StmtProfiler::VisitCXXPseudoDestructorExpr(const CXXPseudoDestructorExpr *S) { void StmtProfiler::VisitOverloadExpr(const OverloadExpr *S) { VisitExpr(S); - VisitNestedNameSpecifier(S->getQualifier()); + if (S->getNumDecls() == 1) + VisitDecl(*S->decls_begin()); + else + VisitNestedNameSpecifier(S->getQualifier()); VisitName(S->getName(), /*TreatAsDecl*/ true); ID.AddBoolean(S->hasExplicitTemplateArgs()); if (S->hasExplicitTemplateArgs()) diff --git a/clang/test/SemaTemplate/concepts-out-of-line-def.cpp b/clang/test/SemaTemplate/concepts-out-of-line-def.cpp index 5af4ec75cae90..e1e38e472370c 100644 --- a/clang/test/SemaTemplate/concepts-out-of-line-def.cpp +++ b/clang/test/SemaTemplate/concepts-out-of-line-def.cpp @@ -779,3 +779,18 @@ template consteval void S::mfn() requires (bool(&fn)) {} } + +namespace GH139476 { + +namespace moo { + template + constexpr bool baa = true; + + template requires baa + void caw(); +} + +template requires moo::baa +void moo::caw() {} + +} From 70acf05971934484c2727c72e62f6cae4d8aced3 Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Mon, 26 May 2025 14:27:04 +0800 Subject: [PATCH 16/20] Revert "[Clang] Profile singly-resolved UnresolvedLookupExpr with the declaration" This reverts commit debef50c5cc472ded7e705d029dd351df5759ca7. --- clang/lib/AST/StmtProfile.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 19db338f760ba..f7d1655f67ed1 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -2189,10 +2189,7 @@ StmtProfiler::VisitCXXPseudoDestructorExpr(const CXXPseudoDestructorExpr *S) { void StmtProfiler::VisitOverloadExpr(const OverloadExpr *S) { VisitExpr(S); - if (S->getNumDecls() == 1) - VisitDecl(*S->decls_begin()); - else - VisitNestedNameSpecifier(S->getQualifier()); + VisitNestedNameSpecifier(S->getQualifier()); VisitName(S->getName(), /*TreatAsDecl*/ true); ID.AddBoolean(S->hasExplicitTemplateArgs()); if (S->hasExplicitTemplateArgs()) From 778d2a98e0f78d0ac13c1a5971c2f1d64e1de86b Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Mon, 26 May 2025 14:28:58 +0800 Subject: [PATCH 17/20] Fix param names --- clang/lib/Sema/SemaTemplateDeduction.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 3a56b9071a880..b58868dfdc351 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -3926,7 +3926,7 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( Owner = FunctionTemplate->getLexicalDeclContext(); FunctionDecl *FD = FunctionTemplate->getTemplatedDecl(); - if (CheckNonDependent(/*NonInstOnly=*/true)) + if (CheckNonDependent(/*SkipUserDefinedConversions=*/true)) return TemplateDeductionResult::NonDependentConversionFailure; // C++20 [temp.deduct.general]p5: [CWG2369] @@ -3959,7 +3959,7 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( // P with a type that was non-dependent before substitution of any // explicitly-specified template arguments, if the corresponding argument // A cannot be implicitly converted to P, deduction fails. - if (CheckNonDependent(/*NonInstOnly=*/false)) + if (CheckNonDependent(/*SkipUserDefinedConversions=*/false)) return TemplateDeductionResult::NonDependentConversionFailure; MultiLevelTemplateArgumentList SubstArgs( From a74e14a04a97f434c2dd29a4a0f8cb67c2199be4 Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Mon, 26 May 2025 17:57:46 +0800 Subject: [PATCH 18/20] Fix a regression involving injected class types --- clang/lib/Sema/SemaTemplateInstantiate.cpp | 5 ----- clang/test/SemaTemplate/concepts.cpp | 23 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index f7d7b63578176..8902439695daf 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1969,11 +1969,6 @@ bool TemplateInstantiator::maybeInstantiateFunctionParameterToScope( ParmVarDecl *OldParm) { if (SemaRef.CurrentInstantiationScope->findInstantiationUnsafe(OldParm)) return false; - // We're instantiating a function parameter whose associated function template - // has not been instantiated at this point for constraint evaluation, so make - // sure the instantiated parameters are owned by a function declaration such - // that they can be correctly 'captured' in tryCaptureVariable(). - Sema::ContextRAII Context(SemaRef, OldParm->getDeclContext()); if (!OldParm->isParameterPack()) return !TransformFunctionTypeParam(OldParm, /*indexAdjustment=*/0, diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp index f335ca3bd22bc..3e50a8bce24af 100644 --- a/clang/test/SemaTemplate/concepts.cpp +++ b/clang/test/SemaTemplate/concepts.cpp @@ -1177,3 +1177,26 @@ template struct incrementable_traits; // expected-error {{not more specialized than the primary}} } + +namespace InjectedClassNameType { + +template class expected { +public: + template + expected(...); + + template + friend bool operator==(expected x, expected<_T2, _E2>) + requires requires { + { x }; + } + { + return true; + } +}; + +bool test_val_types() { + return expected() == 1; +} + +} From e3eab962b62e12b6dcf6cfcb9b0856d493502a34 Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Wed, 28 May 2025 16:39:09 +0800 Subject: [PATCH 19/20] checkpoint --- clang/include/clang/Sema/Sema.h | 40 ++++++++++++---------- clang/include/clang/Sema/Template.h | 2 +- clang/lib/Sema/Sema.cpp | 3 +- clang/lib/Sema/SemaOverload.cpp | 6 ++-- clang/lib/Sema/SemaTemplateInstantiate.cpp | 35 +++++++++---------- 5 files changed, 43 insertions(+), 43 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 758cbc2bd687b..6e9a05fd0302a 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -12548,6 +12548,10 @@ class Sema final : public SemaBase { /// /// \param OriginalCallArgs If non-NULL, the original call arguments against /// which the deduced argument types should be compared. + /// \param CheckNonDependent Callback before substituting into the declaration + /// with the deduced template arguments. \param SkipUserDefinedConversion is + /// used as a workaround for some breakages introduced by CWG2369, where + /// non-user-defined conversions are checked before constraints. TemplateDeductionResult FinishTemplateArgumentDeduction( FunctionTemplateDecl *FunctionTemplate, SmallVectorImpl &Deduced, @@ -12555,9 +12559,8 @@ class Sema final : public SemaBase { sema::TemplateDeductionInfo &Info, SmallVectorImpl const *OriginalCallArgs, bool PartialOverloading, bool PartialOrdering, - llvm::function_ref CheckNonDependent = [](bool) { - return false; - }); + llvm::function_ref CheckNonDependent = + [](bool /*SkipUserDefinedConversion*/) { return false; }); /// Perform template argument deduction from a function call /// (C++ [temp.deduct.call]). @@ -13069,6 +13072,9 @@ class Sema final : public SemaBase { /// Was the enclosing context a non-instantiation SFINAE context? bool SavedInNonInstantiationSFINAEContext; + /// Whether we're substituting into constraints. + bool InConstraintSubstitution; + /// The point of instantiation or synthesis within the source code. SourceLocation PointOfInstantiation; @@ -13118,9 +13124,9 @@ class Sema final : public SemaBase { CodeSynthesisContext() : Kind(TemplateInstantiation), - SavedInNonInstantiationSFINAEContext(false), Entity(nullptr), - Template(nullptr), TemplateArgs(nullptr), NumTemplateArgs(0), - DeductionInfo(nullptr) {} + SavedInNonInstantiationSFINAEContext(false), + InConstraintSubstitution(false), Entity(nullptr), Template(nullptr), + TemplateArgs(nullptr), NumTemplateArgs(0), DeductionInfo(nullptr) {} /// Determines whether this template is an actual instantiation /// that should be counted toward the maximum instantiation depth. @@ -13368,19 +13374,20 @@ class Sema final : public SemaBase { /// \param ForDefaultArgumentSubstitution indicates we should continue looking /// when encountering a specialized member function template, rather than /// returning immediately. - MultiLevelTemplateArgumentList getTemplateInstantiationArgs( - const NamedDecl *D, const DeclContext *DC = nullptr, bool Final = false, + void getTemplateInstantiationArgs( + MultiLevelTemplateArgumentList &Result, const NamedDecl *D, + const DeclContext *DC = nullptr, bool Final = false, std::optional> Innermost = std::nullopt, bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr, bool ForConstraintInstantiation = false, bool SkipForSpecialization = false, bool ForDefaultArgumentSubstitution = false); - /// Apart from storing the result to \p Result, this behaves the same as - /// another overload. - void getTemplateInstantiationArgs( - MultiLevelTemplateArgumentList &Result, const NamedDecl *D, - const DeclContext *DC = nullptr, bool Final = false, + /// This creates a new \p MultiLevelTemplateArgumentList and invokes the other + /// overload with it as the first parameter. Prefer this overload in most + /// situations. + MultiLevelTemplateArgumentList getTemplateInstantiationArgs( + const NamedDecl *D, const DeclContext *DC = nullptr, bool Final = false, std::optional> Innermost = std::nullopt, bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr, bool ForConstraintInstantiation = false, @@ -13473,10 +13480,6 @@ class Sema final : public SemaBase { // FIXME: Should we have a similar limit for other forms of synthesis? unsigned NonInstantiationEntries; - /// The number of \p CodeSynthesisContexts that are not constraint - /// substitution. - unsigned NonConstraintSubstitutionEntries; - /// The depth of the context stack at the point when the most recent /// error or warning was produced. /// @@ -13807,7 +13810,8 @@ class Sema final : public SemaBase { /// Determine whether we are currently performing constraint substitution. bool inConstraintSubstitution() const { - return CodeSynthesisContexts.size() > NonConstraintSubstitutionEntries; + return !CodeSynthesisContexts.empty() && + CodeSynthesisContexts.back().InConstraintSubstitution; } using EntityPrinter = llvm::function_ref; diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h index 39f0cf225e673..af0c1e8506cf3 100644 --- a/clang/include/clang/Sema/Template.h +++ b/clang/include/clang/Sema/Template.h @@ -526,7 +526,7 @@ enum class TemplateSubstitutionKind : char { /// instantiation was not found within the current instantiation scope. This /// is helpful for on-demand declaration instantiation. llvm::PointerUnion * - findInstantiationUnsafe(const Decl *D); + getInstantiationOfIfExists(const Decl *D); void InstantiatedLocal(const Decl *D, Decl *Inst); void InstantiatedLocalPackArg(const Decl *D, VarDecl *Inst); diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 7bc49088fa2b1..1901d19b14dfc 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -303,8 +303,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, TyposCorrected(0), IsBuildingRecoveryCallExpr(false), NumSFINAEErrors(0), AccessCheckingSFINAE(false), CurrentInstantiationScope(nullptr), InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0), - NonConstraintSubstitutionEntries(0), ArgPackSubstIndex(std::nullopt), - SatisfactionCache(Context) { + ArgPackSubstIndex(std::nullopt), SatisfactionCache(Context) { assert(pp.TUKind == TUKind); TUScope = nullptr; diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 1876bb31abc5b..4aec0f9e241d1 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -8056,9 +8056,9 @@ bool Sema::CheckNonDependentConversions( bool HasThisConversion = Method && !isa(Method); unsigned ThisConversions = HasThisConversion ? 1 : 0; - Conversions = Conversions.empty() ? CandidateSet.allocateConversionSequences( - ThisConversions + Args.size()) - : Conversions; + if (Conversions.empty()) + Conversions = + CandidateSet.allocateConversionSequences(ThisConversions + Args.size()); // Overload resolution is always an unevaluated context. EnterExpressionEvaluationContext Unevaluated( diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 8902439695daf..c5c696f649820 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -623,6 +623,12 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( Inst.NumTemplateArgs = TemplateArgs.size(); Inst.DeductionInfo = DeductionInfo; Inst.InstantiationRange = InstantiationRange; + Inst.InConstraintSubstitution = + Inst.Kind == CodeSynthesisContext::ConstraintSubstitution; + if (!SemaRef.CodeSynthesisContexts.empty()) + Inst.InConstraintSubstitution |= + SemaRef.CodeSynthesisContexts.back().InConstraintSubstitution; + SemaRef.pushCodeSynthesisContext(Inst); AlreadyInstantiating = !Inst.Entity ? false : @@ -833,9 +839,6 @@ void Sema::pushCodeSynthesisContext(CodeSynthesisContext Ctx) { if (!Ctx.isInstantiationRecord()) ++NonInstantiationEntries; - if (Ctx.Kind != CodeSynthesisContext::ConstraintSubstitution) - ++NonConstraintSubstitutionEntries; - // Check to see if we're low on stack space. We can't do anything about this // from here, but we can at least warn the user. StackHandler.warnOnStackNearlyExhausted(Ctx.PointOfInstantiation); @@ -848,11 +851,6 @@ void Sema::popCodeSynthesisContext() { --NonInstantiationEntries; } - if (Active.Kind != CodeSynthesisContext::ConstraintSubstitution) { - assert(NonConstraintSubstitutionEntries > 0); - --NonConstraintSubstitutionEntries; - } - InNonInstantiationSFINAEContext = Active.SavedInNonInstantiationSFINAEContext; // Name lookup no longer looks in this template's defining module. @@ -1463,9 +1461,8 @@ namespace { SemaRef.inConstraintSubstitution()) { for (UnexpandedParameterPack ParmPack : Unexpanded) { NamedDecl *VD = ParmPack.first.dyn_cast(); - if (!isa_and_present(VD)) - continue; - if (maybeInstantiateFunctionParameterToScope(cast(VD))) + if (auto *PVD = dyn_cast_if_present(VD); + PVD && maybeInstantiateFunctionParameterToScope(PVD)) return true; } } @@ -1956,18 +1953,18 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) { // template parameter. } - if (SemaRef.CurrentInstantiationScope) { - if (SemaRef.inConstraintSubstitution() && isa(D) && - maybeInstantiateFunctionParameterToScope(cast(D))) - return nullptr; - } + if (ParmVarDecl *PVD = dyn_cast(D); + PVD && SemaRef.CurrentInstantiationScope && + SemaRef.inConstraintSubstitution() && + maybeInstantiateFunctionParameterToScope(PVD)) + return nullptr; return SemaRef.FindInstantiatedDecl(Loc, cast(D), TemplateArgs); } bool TemplateInstantiator::maybeInstantiateFunctionParameterToScope( ParmVarDecl *OldParm) { - if (SemaRef.CurrentInstantiationScope->findInstantiationUnsafe(OldParm)) + if (SemaRef.CurrentInstantiationScope->getInstantiationOfIfExists(OldParm)) return false; if (!OldParm->isParameterPack()) @@ -4587,7 +4584,7 @@ static const Decl *getCanonicalParmVarDecl(const Decl *D) { } llvm::PointerUnion * -LocalInstantiationScope::findInstantiationUnsafe(const Decl *D) { +LocalInstantiationScope::getInstantiationOfIfExists(const Decl *D) { D = getCanonicalParmVarDecl(D); for (LocalInstantiationScope *Current = this; Current; Current = Current->Outer) { @@ -4617,7 +4614,7 @@ LocalInstantiationScope::findInstantiationUnsafe(const Decl *D) { llvm::PointerUnion * LocalInstantiationScope::findInstantiationOf(const Decl *D) { - auto *Result = findInstantiationUnsafe(D); + auto *Result = getInstantiationOfIfExists(D); if (Result) return Result; // If we're performing a partial substitution during template argument From 58af4e89276e30fca721e3474cc3cc354e11be41 Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Wed, 28 May 2025 17:35:22 +0800 Subject: [PATCH 20/20] Pack the two flags --- clang/include/clang/Sema/Sema.h | 37 ++++++++++++++++++----- clang/lib/Sema/SemaOverload.cpp | 38 +++++++++++++----------- clang/lib/Sema/SemaTemplateDeduction.cpp | 9 +++--- 3 files changed, 56 insertions(+), 28 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 6e9a05fd0302a..cfe687b1d8786 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -10499,15 +10499,35 @@ class Sema final : public SemaBase { OverloadCandidateParamOrder PO = {}, bool AggregateCandidateDeduction = false); + struct CheckNonDependentConversionsFlag { + /// Do not consider any user-defined conversions when constructing the + /// initializing sequence. + bool SuppressUserConversions; + + /// Before constructing the initializing sequence, we check whether the + /// parameter type and argument type contain any user defined conversions. + /// If so, do not initialize them. This effectively bypasses some undesired + /// instantiation before checking constaints, which might otherwise result + /// in non-SFINAE errors e.g. recursive constraints. + bool OnlyInitializeNonUserDefinedConversions; + + CheckNonDependentConversionsFlag( + bool SuppressUserConversions, + bool OnlyInitializeNonUserDefinedConversions) + : SuppressUserConversions(SuppressUserConversions), + OnlyInitializeNonUserDefinedConversions( + OnlyInitializeNonUserDefinedConversions) {} + }; + /// Check that implicit conversion sequences can be formed for each argument /// whose corresponding parameter has a non-dependent type, per DR1391's /// [temp.deduct.call]p10. bool CheckNonDependentConversions( FunctionTemplateDecl *FunctionTemplate, ArrayRef ParamTypes, ArrayRef Args, OverloadCandidateSet &CandidateSet, - ConversionSequenceList &Conversions, bool SuppressUserConversions, - bool SkipUserDefinedConversions, CXXRecordDecl *ActingContext = nullptr, - QualType ObjectType = QualType(), + ConversionSequenceList &Conversions, + CheckNonDependentConversionsFlag UserConversionFlag, + CXXRecordDecl *ActingContext = nullptr, QualType ObjectType = QualType(), Expr::Classification ObjectClassification = {}, OverloadCandidateParamOrder PO = {}); @@ -12549,9 +12569,10 @@ class Sema final : public SemaBase { /// \param OriginalCallArgs If non-NULL, the original call arguments against /// which the deduced argument types should be compared. /// \param CheckNonDependent Callback before substituting into the declaration - /// with the deduced template arguments. \param SkipUserDefinedConversion is - /// used as a workaround for some breakages introduced by CWG2369, where - /// non-user-defined conversions are checked before constraints. + /// with the deduced template arguments. + /// \param OnlyInitializeNonUserDefinedConversions is used as a workaround for + /// some breakages introduced by CWG2369, where non-user-defined conversions + /// are checked first before the constraints. TemplateDeductionResult FinishTemplateArgumentDeduction( FunctionTemplateDecl *FunctionTemplate, SmallVectorImpl &Deduced, @@ -12560,7 +12581,9 @@ class Sema final : public SemaBase { SmallVectorImpl const *OriginalCallArgs, bool PartialOverloading, bool PartialOrdering, llvm::function_ref CheckNonDependent = - [](bool /*SkipUserDefinedConversion*/) { return false; }); + [](bool /*OnlyInitializeNonUserDefinedConversions*/) { + return false; + }); /// Perform template argument deduction from a function call /// (C++ [temp.deduct.call]). diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 4aec0f9e241d1..17087fee2bff9 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -7848,10 +7848,13 @@ static void AddMethodTemplateCandidateImmediately( /*PartialOrdering=*/false, ObjectType, ObjectClassification, CandidateSet.getKind() == clang::OverloadCandidateSet::CSK_AddressOfOverloadSet, - [&](ArrayRef ParamTypes, bool SkipUserDefinedConversions) { + [&](ArrayRef ParamTypes, + bool OnlyInitializeNonUserDefinedConversions) { return S.CheckNonDependentConversions( MethodTmpl, ParamTypes, Args, CandidateSet, Conversions, - SuppressUserConversions, SkipUserDefinedConversions, + Sema::CheckNonDependentConversionsFlag( + SuppressUserConversions, + OnlyInitializeNonUserDefinedConversions), ActingContext, ObjectType, ObjectClassification, PO); }); Result != TemplateDeductionResult::Success) { @@ -7964,11 +7967,14 @@ static void AddTemplateOverloadCandidateImmediately( /*ObjectClassification=*/Expr::Classification(), CandidateSet.getKind() == OverloadCandidateSet::CSK_AddressOfOverloadSet, - [&](ArrayRef ParamTypes, bool SkipUserDefinedConversions) { + [&](ArrayRef ParamTypes, + bool OnlyInitializeNonUserDefinedConversions) { return S.CheckNonDependentConversions( FunctionTemplate, ParamTypes, Args, CandidateSet, Conversions, - SuppressUserConversions, SkipUserDefinedConversions, nullptr, - QualType(), {}, PO); + Sema::CheckNonDependentConversionsFlag( + SuppressUserConversions, + OnlyInitializeNonUserDefinedConversions), + nullptr, QualType(), {}, PO); }); Result != TemplateDeductionResult::Success) { OverloadCandidate &Candidate = @@ -8042,10 +8048,10 @@ void Sema::AddTemplateOverloadCandidate( bool Sema::CheckNonDependentConversions( FunctionTemplateDecl *FunctionTemplate, ArrayRef ParamTypes, ArrayRef Args, OverloadCandidateSet &CandidateSet, - ConversionSequenceList &Conversions, bool SuppressUserConversions, - bool SkipUserDefinedConversions, CXXRecordDecl *ActingContext, - QualType ObjectType, Expr::Classification ObjectClassification, - OverloadCandidateParamOrder PO) { + ConversionSequenceList &Conversions, + CheckNonDependentConversionsFlag UserConversionFlag, + CXXRecordDecl *ActingContext, QualType ObjectType, + Expr::Classification ObjectClassification, OverloadCandidateParamOrder PO) { // FIXME: The cases in which we allow explicit conversions for constructor // arguments never consider calling a constructor template. It's not clear // that is correct. @@ -8144,16 +8150,14 @@ bool Sema::CheckNonDependentConversions( } if (Conversions[ConvIdx].isInitialized()) continue; - if (SkipUserDefinedConversions && + if (UserConversionFlag.OnlyInitializeNonUserDefinedConversions && MaybeInvolveUserDefinedConversion(ParamType, Args[I]->getType())) continue; - Conversions[ConvIdx] - = TryCopyInitialization(*this, Args[I], ParamType, - SuppressUserConversions, - /*InOverloadResolution=*/true, - /*AllowObjCWritebackConversion=*/ - getLangOpts().ObjCAutoRefCount, - AllowExplicit); + Conversions[ConvIdx] = TryCopyInitialization( + *this, Args[I], ParamType, UserConversionFlag.SuppressUserConversions, + /*InOverloadResolution=*/true, + /*AllowObjCWritebackConversion=*/ + getLangOpts().ObjCAutoRefCount, AllowExplicit); if (Conversions[ConvIdx].isBad()) return true; } diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index b58868dfdc351..d8a2f56ec8546 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -3926,7 +3926,7 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( Owner = FunctionTemplate->getLexicalDeclContext(); FunctionDecl *FD = FunctionTemplate->getTemplatedDecl(); - if (CheckNonDependent(/*SkipUserDefinedConversions=*/true)) + if (CheckNonDependent(/*OnlyInitializeNonUserDefinedConversions=*/true)) return TemplateDeductionResult::NonDependentConversionFailure; // C++20 [temp.deduct.general]p5: [CWG2369] @@ -3959,7 +3959,7 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( // P with a type that was non-dependent before substitution of any // explicitly-specified template arguments, if the corresponding argument // A cannot be implicitly converted to P, deduction fails. - if (CheckNonDependent(/*SkipUserDefinedConversions=*/false)) + if (CheckNonDependent(/*OnlyInitializeNonUserDefinedConversions=*/false)) return TemplateDeductionResult::NonDependentConversionFailure; MultiLevelTemplateArgumentList SubstArgs( @@ -4679,9 +4679,10 @@ TemplateDeductionResult Sema::DeduceTemplateArguments( Result = FinishTemplateArgumentDeduction( FunctionTemplate, Deduced, NumExplicitlySpecified, Specialization, Info, &OriginalCallArgs, PartialOverloading, PartialOrdering, - [&, CallingCtx](bool NonInstOnly) { + [&, CallingCtx](bool OnlyInitializeNonUserDefinedConversions) { ContextRAII SavedContext(*this, CallingCtx); - return CheckNonDependent(ParamTypesForArgChecking, NonInstOnly); + return CheckNonDependent(ParamTypesForArgChecking, + OnlyInitializeNonUserDefinedConversions); }); }); return Result;