https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/122423
>From 56bacf47c53aca276ae4fa6aa2972b7eda152ddd Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Fri, 10 Jan 2025 09:46:24 +0800 Subject: [PATCH 01/10] 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<ArrayRef<TemplateArgument>> 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<TemplateArgument> TemplateArgs, - ConstraintSatisfaction &Satisfaction); + bool CheckFunctionTemplateConstraints(SourceLocation PointOfInstantiation, + FunctionDecl *Decl, + ArrayRef<TemplateArgument> 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<Decl *, DeclArgumentPack *> * 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<Decl *, DeclArgumentPack *> * + 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<TemplateArgument> TemplateArgs, + ConstraintSatisfaction &Satisfaction) { + SmallVector<const Expr *, 3> 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<Sema::CXXThisScopeRAII> ThisScope; + if (auto *Method = dyn_cast<CXXMethodDecl>(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<TemplateArgument> 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<AssociatedConstraint, 3> 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<ArrayRef<TemplateArgument>> 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<TemplateInstantiator> inherited; @@ -1430,12 +1456,19 @@ namespace { ArrayRef<UnexpandedParameterPack> 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<NamedDecl *>(); + if (!isa_and_present<ParmVarDecl>(VD)) + continue; + if (maybeInstantiateFunctionParameterToScope(cast<ParmVarDecl>(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<ParmVarDecl>(D) && + maybeInstantiateFunctionParameterToScope(cast<ParmVarDecl>(D))) + return nullptr; + } + return SemaRef.FindInstantiatedDecl(Loc, cast<NamedDecl>(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<UnexpandedParameterPack, 2> Unexpanded; + + // Find the parameter packs that could be expanded. + TypeLoc TL = OldParm->getTypeSourceInfo()->getTypeLoc(); + PackExpansionTypeLoc ExpansionTL = TL.castAs<PackExpansionTypeLoc>(); + TypeLoc Pattern = ExpansionTL.getPatternLoc(); + SemaRef.collectUnexpandedParameterPacks(Pattern, Unexpanded); + assert(!Unexpanded.empty() && "Pack expansion without parameter packs?"); + + bool ShouldExpand = false; + bool RetainExpansion = false; + std::optional<unsigned> OrigNumExpansions = + ExpansionTL.getTypePtr()->getNumExpansions(); + std::optional<unsigned> 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<Decl *, LocalInstantiationScope::DeclArgumentPack *> * -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<Decl *, LocalInstantiationScope::DeclArgumentPack *> * +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<NonTypeTemplateParmDecl>(D) || isa<TemplateTypeParmDecl>(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 <class T> struct Z { + typedef typename T::x xx; +}; + +template <class T> +concept C = requires { typename T::A; }; +template <C T> typename Z<T>::xx f(void *, T); // #1 +template <class T> void f(int, T); // #2 + +struct A { +} a; + +struct ZZ { + template <class T, class = typename Z<T>::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<int> 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<int>' required here}} -// since-cxx20-note@#cwg2672-f-0 {{in instantiation of function template specialization 'cwg2672::f<int>' requested here}} +// since-cxx20-note@#cwg2672-f-0 {{while substituting deduced template arguments into function template 'f' [with T = int]}} void f(...); template <class T> 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<typename T> +struct B { + static_assert(sizeof(T) == 1); + using type = int; +}; + +template<typename T> +int f(T t, typename B<T>::type u) requires (sizeof(t) == 1); + +template<typename T> +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 <typename T = int> 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<int>(); // expected-note@-1 {{while checking constraint satisfaction for template 'bar<int>' 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<typename T> struct S { // expected-error@+3{{atomic constraint must be of type 'bool' (found 'S<int>')}} // 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<typename T> requires (S<T>{}) 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<int>')}} // 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<typename T> requires (S<T>{} && true) void f2(T); void f2(long); @@ -32,7 +32,7 @@ template<typename T> 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<bool, bool>'}} // expected-note@#2 {{substituting template arguments into constraint expression here}} // expected-note@#5 {{checking constraint satisfaction for template 'compare<Object *, Object *>'}} -// expected-note@#5 {{in instantiation of function template specialization 'compare<Object *, Object *>' 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 <int K> using Bar = Foo<double, K>; // 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 <int K> requires __is_deducible(test14::Bar, Foo<double, K>) Bar(Foo<double, K>) -> Foo<double, K>'}} // expected-note@-3 {{implicit deduction guide declared as 'template <int K> requires __is_deducible(test14::Bar, Foo<double, K>) Bar(const double (&)[K]) -> Foo<double, K>'}} 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<T>) { return 2; } // expected-note 4 {{while che static_assert(f5<int>() == 1); static_assert(f5<D>() == 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<double>() == 2); -static_assert(f5<E>() == 1); // expected-note {{while checking constraint satisfaction}} expected-note {{in instantiation of}} -static_assert(f5<F>() == 2); // expected-note {{while checking constraint satisfaction}} expected-note {{in instantiation of}} +static_assert(f5<E>() == 1); // expected-note {{while checking constraint satisfaction}} expected-note {{while substituting deduced template arguments}} +static_assert(f5<F>() == 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<Thingy, Thingy> *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<int>' required here}} - // expected-note@-1 {{in instantiation of function template specialization 'lambda_in_constraints::func<int>'}} + // expected-note@-1 {{while substituting deduced template arguments into function template 'func' [with T = int]}} static_assert(__is_same(decltype(func(WithFoo())), int*)); template <class T> @@ -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<const char *>' 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<DirectRecursiveCheck::my_range>' 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<DirectRecursiveCheck::my_range>' 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<GH50891::Deferred>' 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<int>(), 0); // expected-note@-1 {{while checking}} expected-error@-1{{no matching function}} \ - expected-note@-1 {{in instantiation}} + expected-note@-1 {{while substituting}} template<typename T> void bar() requires requires { requires f<int[2]>; } { }; // 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<typename A, typename T> concept True = true; template<typename T> -concept False = false; +concept False = false; // #False template<class X> struct concepts { template<class Y> struct B { @@ -68,7 +68,7 @@ template<typename X> struct nested_init_list { Y y; }; - template<False F> + template<False F> // #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<int>::B<int>; // expected-error@+1 {{no viable constructor or deduction guide for deduction of template arguments of 'nested_init_list<int>::concept_fail'}} nested_init_list<int>::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 <False F> concept_fail(int, F) -> concept_fail<F>'}} // 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 <False F> concept_fail(concept_fail<F>) -> concept_fail<F>'}} 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 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> <td><a href="https://cplusplus.github.io/CWG/issues/2369.html">2369</a></td> <td>CD6</td> <td>Ordering between constraints and substitution</td> - <td class="unknown" align="center">Unknown</td> + <td class="partial" align="center">Partial</td> </tr> <tr id="2370"> <td><a href="https://cplusplus.github.io/CWG/issues/2370.html">2370</a></td> @@ -16468,7 +16468,11 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> <td><a href="https://cplusplus.github.io/CWG/issues/2770.html">2770</a></td> <td>open</td> <td>Trailing <I>requires-clause</I> can refer to function parameters before they are substituted into</td> - <td align="center">Not resolved</td> + <td align="center"> + <details> + <summary>Not resolved</summary> + Clang 20 implements 2023-07-14 resolution + </details></td> </tr> <tr id="2771"> <td><a href="https://cplusplus.github.io/CWG/issues/2771.html">2771</a></td> >From df7500578957061d4f05d9d71fec07ff5e75e354 Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Fri, 10 Jan 2025 13:32:11 +0800 Subject: [PATCH 02/10] 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<void(llvm::raw_ostream &)>; /// \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<UnexpandedParameterPack> 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<NamedDecl *>(); if (!isa_and_present<ParmVarDecl>(VD)) @@ -1953,7 +1955,7 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) { } if (SemaRef.CurrentInstantiationScope) { - if (isSubstitutingConstraints() && isa<ParmVarDecl>(D) && + if (SemaRef.inConstraintSubstitution() && isa<ParmVarDecl>(D) && maybeInstantiateFunctionParameterToScope(cast<ParmVarDecl>(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 <class, class Up> +using compare_three_way_result_t = Up::type; + +struct sfinae_assign_base {}; + +template <class Tp> +concept is_derived_from_optional = + requires(Tp param) { []<class Up>(Up) {}(param); }; + +template <class Tp, class Up> + requires(is_derived_from_optional<Up>) +compare_three_way_result_t<Tp, Up> 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 <zyn7...@gmail.com> Date: Sat, 11 Jan 2025 16:14:24 +0800 Subject: [PATCH 03/10] 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 <class, class Up> 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 <class _Tp> +constexpr _Tp * __to_address(_Tp *) { + return nullptr; +} + +template <class _Ip> +concept contiguous_iterator = requires(_Ip __i) { __to_address(__i); }; + +struct basic_string_view { + template <contiguous_iterator _It> + 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 <zyn7...@gmail.com> Date: Sat, 11 Jan 2025 20:59:25 +0800 Subject: [PATCH 04/10] 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<EnterExpressionEvaluationContext> 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) { []<class Up>(Up) {}(param); }; template <class Tp, class Up> - requires(is_derived_from_optional<Up>) + requires(is_derived_from_optional<Up> && []<class W>(W) { return true; }(Up())) compare_three_way_result_t<Tp, Up> operator<=>(Tp, Up); struct RuntimeModeArgs { >From ec143c18731e1bc885c452443adae19fd81facc4 Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Sat, 11 Jan 2025 22:14:38 +0800 Subject: [PATCH 05/10] 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<CXXRecordDecl>(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 <zyn7...@gmail.com> Date: Sat, 11 Jan 2025 22:27:45 +0800 Subject: [PATCH 06/10] 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 <zyn7...@gmail.com> Date: Sat, 11 Jan 2025 23:10:38 +0800 Subject: [PATCH 07/10] 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 <zyn7...@gmail.com> Date: Wed, 23 Apr 2025 16:40:21 +0800 Subject: [PATCH 08/10] 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<TemplateArgument> TemplateArgs, ConstraintSatisfaction &Satisfaction) { - SmallVector<const Expr *, 3> TemplateAC; + SmallVector<AssociatedConstraint, 3> 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<unsigned> OrigNumExpansions = + UnsignedOrNone OrigNumExpansions = ExpansionTL.getTypePtr()->getNumExpansions(); - std::optional<unsigned> 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<auto ...> struct X {}; template<template<typename X, X> typename> struct Y {}; @@ -234,7 +234,7 @@ F s(0); // CHECK: | `-CXXBoolLiteralExpr {{.*}} 'bool' false // CHECK: |-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for F> 'auto (U) -> F<value-parameter-0-0>' // CHECK: | `-ParmVarDecl {{.*}} 'U' -// CHECK: `-CXXDeductionGuideDecl {{.*}} implicit <deduction guide for F> 'auto (int) -> F<>' +// CHECK: `-CXXDeductionGuideDecl {{.*}} implicit used <deduction guide for F> '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 <zyn7...@gmail.com> Date: Fri, 24 Jan 2025 13:52:37 +0800 Subject: [PATCH 09/10] 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<QualType> ParamTypes, ArrayRef<Expr *> 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<OriginalCallArg> const *OriginalCallArgs, bool PartialOverloading, bool PartialOrdering, - llvm::function_ref<bool()> CheckNonDependent = [] { return false; }); + llvm::function_ref<bool(bool)> 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<bool(ArrayRef<QualType>)> CheckNonDependent); + llvm::function_ref<bool(ArrayRef<QualType>, 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<QualType> ParamTypes) { + [&](ArrayRef<QualType> 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<QualType> ParamTypes) { + [&](ArrayRef<QualType> 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<QualType> ParamTypes, ArrayRef<Expr *> 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<RecordType>()) { + if (auto *RD = dyn_cast<CXXRecordDecl>(RT->getDecl())) { + if (auto *ClassTemplateSpec = + dyn_cast<ClassTemplateSpecializationDecl>(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<RecordType>()) + if (auto *RD = dyn_cast<CXXRecordDecl>(RT->getDecl()); + RD && RD->hasDefinition() && !RD->isAggregate()) + return false; + + if (auto *RT = ArgType->getAs<RecordType>()) + if (auto *RD = dyn_cast<CXXRecordDecl>(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<OriginalCallArg> const *OriginalCallArgs, bool PartialOverloading, bool PartialOrdering, - llvm::function_ref<bool()> CheckNonDependent) { + llvm::function_ref<bool(bool)> 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<bool(ArrayRef<QualType>)> CheckNonDependent) { + llvm::function_ref<bool(ArrayRef<QualType>, 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<typename _type, typename _stream> +concept streamable = requires(_stream &s, _type &&v) { + s << static_cast<_type &&>(v); +}; + +struct type_a { + template<typename _arg> + 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<typename _arg> + 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<class> +struct iterator_traits +{}; + +template<class T> + requires requires(T __t, T __u) { __t == __u; } +struct iterator_traits<T> +{}; + +template<class T> +concept C = requires { typename iterator_traits<T>::A; }; + +struct unreachable_sentinel_t +{ + template<C _Iter> + friend constexpr bool operator==(unreachable_sentinel_t, const _Iter&) noexcept; +}; + +template<class T> +struct S +{}; + +static_assert(!C<S<unreachable_sentinel_t>>); + +} + +// 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<typename... T> +auto f(tag_foo, T... x) +{ + return (x + ...); +} + +template<typename... T> +concept fooable = requires (T... x) { f(foo, x...); }; + +template<typename... T> requires (fooable<T...>) +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 <class T> +concept fooable = requires(T it) { + invoke_tag(foo_tag{}, it); // <-- here +}; + +template <class T> auto invoke_tag(foo_tag, T in) { return in; } + +template <fooable T> 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<typename T> class indirect { +public: + template<typename U> 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<int> 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 <sstream> +#include <string> + +template <class T> +concept StreamCanReceiveString = requires(T& t, std::string s) { + { t << s }; +}; + +struct NotAStream {}; +struct UnrelatedType {}; + +template <StreamCanReceiveString S> +S& operator<<(S& s, UnrelatedType) { + return s; +} + +static_assert(!StreamCanReceiveString<NotAStream>); + +static_assert(StreamCanReceiveString<std::stringstream>); +#endif +} +} >From b32afd2383dccd4cb501f4dc51e17e8bdb9e7ebe Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Fri, 25 Apr 2025 19:59:01 +0800 Subject: [PATCH 10/10] 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<QualType> ParamTypes, ArrayRef<Expr *> 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<QualType> ParamTypes, ArrayRef<Expr *> 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<CXXConstructorDecl>(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<RecordType>()) { - if (auto *RD = dyn_cast<CXXRecordDecl>(RT->getDecl())) { - if (auto *ClassTemplateSpec = - dyn_cast<ClassTemplateSpecializationDecl>(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<RecordType>()) if (auto *RD = dyn_cast<CXXRecordDecl>(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<RecordType>()) if (auto *RD = dyn_cast<CXXRecordDecl>(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, _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits