diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -474,6 +474,9 @@ bool isInStdNamespace() const; + // Return true if this is a FileContext Decl. + bool isFileContextDecl() const; + ASTContext &getASTContext() const LLVM_READONLY; /// Helper to get the language options from the ASTContext. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9053,7 +9053,7 @@ MultiLevelTemplateArgumentList getTemplateInstantiationArgs( const NamedDecl *D, const TemplateArgumentList *Innermost = nullptr, bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr, - bool LookBeyondLambda = false, bool IncludeContainingStruct = false); + bool ForConstraintInstantiation = false); /// A context in which code is being synthesized (where a source location /// alone is not sufficient to identify the context). This covers template diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -397,6 +397,11 @@ return DC && DC->isStdNamespace(); } +bool Decl::isFileContextDecl() const { + const auto *DC = dyn_cast(this); + return DC && DC->isFileContext(); +} + TranslationUnitDecl *Decl::getTranslationUnitDecl() { if (auto *TUD = dyn_cast(this)) return TUD; diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -505,9 +505,10 @@ // 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. - MLTAL = getTemplateInstantiationArgs(FD, nullptr, /*RelativeToPrimary*/ true, - /*Pattern*/ nullptr, - /*LookBeyondLambda*/ true); + MLTAL = getTemplateInstantiationArgs(FD, /*Innermost=*/nullptr, + /*RelativeToPrimary=*/true, + /*Pattern=*/nullptr, + /*ForConstraintInstantiation=*/true); if (SetupConstraintScope(FD, TemplateArgs, MLTAL, Scope)) return {}; @@ -581,9 +582,9 @@ static unsigned CalculateTemplateDepthForConstraints(Sema &S, const NamedDecl *ND) { MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs( - ND, nullptr, /*RelativeToPrimary*/ true, - /*Pattern*/ nullptr, - /*LookBeyondLambda*/ true, /*IncludeContainingStruct*/ true); + ND, /*Innermost=*/nullptr, /*RelativeToPrimary=*/true, + /*Pattern=*/nullptr, + /*ForConstraintInstantiation=*/true); return MLTAL.getNumSubstitutedLevels(); } @@ -724,6 +725,12 @@ Record = Method->getParent(); } CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr); + FunctionScopeRAII FuncScope(*this); + if (isLambdaCallOperator(Decl)) + PushLambdaScope(); + else + FuncScope.disable(); + llvm::SmallVector Converted; return CheckConstraintSatisfaction(Template, TemplateAC, Converted, *MLTAL, PointOfInstantiation, Satisfaction); @@ -1062,9 +1069,9 @@ CSE->getTemplateArguments()}; MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(CSE->getNamedConcept(), &TAL, - /*RelativeToPrimary*/ true, - /*Pattern*/ nullptr, - /*LookBeyondLambda*/ true); + /*RelativeToPrimary=*/true, + /*Pattern=*/nullptr, + /*ForConstraintInstantiation=*/true); return substituteParameterMappings(S, N, CSE->getNamedConcept(), MLTAL, CSE->getTemplateArgsAsWritten()); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -6103,9 +6103,9 @@ CXXThisScopeRAII(*this, RD, ThisQuals, RD != nullptr); MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs( - Template, &StackTemplateArgs, /*RelativeToPrimary*/ true, - /*Pattern*/ nullptr, - /*LookBeyondLambda*/ true, /*IncludeContainingStruct*/ true); + Template, &StackTemplateArgs, /*RelativeToPrimary=*/true, + /*Pattern=*/nullptr, + /*ForConceptInstantiation=*/true); if (EnsureTemplateArgumentListConstraints( Template, MLTAL, SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc()))) { diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2876,9 +2876,9 @@ TemplateArgumentList DeducedTAL{TemplateArgumentList::OnStack, DeducedArgs}; MLTAL = S.getTemplateInstantiationArgs( - Template, /*InnerMost*/ NeedsReplacement ? nullptr : &DeducedTAL, - /*RelativeToPrimary*/ true, /*Pattern*/ - nullptr, /*LookBeyondLambda*/ true); + Template, /*InnerMost=*/NeedsReplacement ? nullptr : &DeducedTAL, + /*RelativeToPrimary=*/true, /*Pattern=*/ + nullptr, /*ForConstraintInstantiation=*/true); // getTemplateInstantiationArgs picks up the non-deduced version of the // template args when this is a variable template partial specialization and diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -40,13 +40,207 @@ // Template Instantiation Support //===----------------------------------------------------------------------===/ +namespace { +namespace TemplateInstArgsHelpers { +struct Response { + const Decl *NextDecl = nullptr; + bool IsDone = false; + bool ClearRelativeToPrimary = true; + static Response Done() { + Response R; + R.IsDone = true; + return R; + } + static Response ChangeDecl(const Decl *ND) { + Response R; + R.NextDecl = ND; + return R; + } + static Response ChangeDecl(const DeclContext *Ctx) { + Response R; + R.NextDecl = Decl::castFromDeclContext(Ctx); + return R; + } + + static Response UseNextDecl(const Decl *CurDecl) { + return ChangeDecl(CurDecl->getDeclContext()); + } + + static Response DontClearRelativeToPrimaryNextDecl(const Decl *CurDecl) { + Response R = Response::UseNextDecl(CurDecl); + R.ClearRelativeToPrimary = false; + return R; + } +}; +// Add template arguments from a variable template instantiation. +Response +HandleVarTemplateSpec(const VarTemplateSpecializationDecl *VarTemplSpec, + MultiLevelTemplateArgumentList &Result) { + // For a class-scope explicit specialization, there are no template arguments + // at this level, but there may be enclosing template arguments. + if (VarTemplSpec->isClassScopeExplicitSpecialization()) + return Response::DontClearRelativeToPrimaryNextDecl(VarTemplSpec); + + // We're done when we hit an explicit specialization. + if (VarTemplSpec->getSpecializationKind() == TSK_ExplicitSpecialization && + !isa(VarTemplSpec)) + return Response::Done(); + + Result.addOuterTemplateArguments( + &VarTemplSpec->getTemplateInstantiationArgs()); + + // If this variable template specialization was instantiated from a + // specialized member that is a variable template, we're done. + assert(VarTemplSpec->getSpecializedTemplate() && "No variable template?"); + llvm::PointerUnion + Specialized = VarTemplSpec->getSpecializedTemplateOrPartial(); + if (VarTemplatePartialSpecializationDecl *Partial = + Specialized.dyn_cast()) { + if (Partial->isMemberSpecialization()) + return Response::Done(); + } else { + VarTemplateDecl *Tmpl = Specialized.get(); + if (Tmpl->isMemberSpecialization()) + return Response::Done(); + } + return Response::DontClearRelativeToPrimaryNextDecl(VarTemplSpec); +} + +// If we have a template template parameter with translation unit context, +// then we're performing substitution into a default template argument of +// this template template parameter before we've constructed the template +// that will own this template template parameter. In this case, we +// use empty template parameter lists for all of the outer templates +// to avoid performing any substitutions. +Response +HandleDefaultTempArgIntoTempTempParam(const TemplateTemplateParmDecl *TTP, + MultiLevelTemplateArgumentList &Result) { + for (unsigned I = 0, N = TTP->getDepth() + 1; I != N; ++I) + Result.addOuterTemplateArguments(None); + return Response::Done(); +} + +// Add template arguments from a class template instantiation. +Response +HandleClassTemplateSpec(const ClassTemplateSpecializationDecl *ClassTemplSpec, + MultiLevelTemplateArgumentList &Result) { + if (!ClassTemplSpec->isClassScopeExplicitSpecialization()) { + // We're done when we hit an explicit specialization. + if (ClassTemplSpec->getSpecializationKind() == TSK_ExplicitSpecialization && + !isa(ClassTemplSpec)) + return Response::Done(); + + Result.addOuterTemplateArguments( + &ClassTemplSpec->getTemplateInstantiationArgs()); + + // If this class template specialization was instantiated from a + // specialized member that is a class template, we're done. + assert(ClassTemplSpec->getSpecializedTemplate() && "No class template?"); + if (ClassTemplSpec->getSpecializedTemplate()->isMemberSpecialization()) + return Response::Done(); + } + return Response::UseNextDecl(ClassTemplSpec); +} + +Response HandleFunction(const FunctionDecl *Function, + MultiLevelTemplateArgumentList &Result, + const FunctionDecl *Pattern, bool RelativeToPrimary, + bool ForConstraintInstantiation) { + // Add template arguments from a function template specialization. + if (!RelativeToPrimary && + Function->getTemplateSpecializationKindForInstantiation() == + TSK_ExplicitSpecialization) + return Response::Done(); + + if (!RelativeToPrimary && + Function->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) { + // This is an implicit instantiation of an explicit specialization. We + // don't get any template arguments from this function but might get + // some from an enclosing template. + return Response::UseNextDecl(Function); + } else if (const TemplateArgumentList *TemplateArgs = + Function->getTemplateSpecializationArgs()) { + // Add the template arguments for this specialization. + Result.addOuterTemplateArguments(TemplateArgs); + + // If this function was instantiated from a specialized member that is + // a function template, we're done. + assert(Function->getPrimaryTemplate() && "No function template?"); + if (Function->getPrimaryTemplate()->isMemberSpecialization()) + return Response::Done(); + + // If this function is a generic lambda specialization, we are done. + if (!ForConstraintInstantiation && + isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) + return Response::Done(); + + } else if (Function->getDescribedFunctionTemplate()) { + assert( + (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && + "Outer template not instantiated?"); + } + // If this is a friend declaration and it declares an entity at + // namespace scope, take arguments from its lexical parent + // instead of its semantic parent, unless of course the pattern we're + // instantiating actually comes from the file's context! + if (Function->getFriendObjectKind() && + Function->getNonTransparentDeclContext()->isFileContext() && + (!Pattern || !Pattern->getLexicalDeclContext()->isFileContext())) { + return Response::ChangeDecl(Function->getLexicalDeclContext()); + } + return Response::UseNextDecl(Function); +} + +Response HandleRecordDecl(const CXXRecordDecl *Rec, + MultiLevelTemplateArgumentList &Result, + ASTContext &Context, + bool ForConstraintInstantiation) { + if (ClassTemplateDecl *ClassTemplate = Rec->getDescribedClassTemplate()) { + assert( + (ForConstraintInstantiation || Result.getNumSubstitutedLevels() == 0) && + "Outer template not instantiated?"); + if (ClassTemplate->isMemberSpecialization()) + return Response::Done(); + if (ForConstraintInstantiation) { + QualType RecordType = Context.getTypeDeclType(Rec); + QualType Injected = cast(RecordType) + ->getInjectedSpecializationType(); + const auto *InjectedType = cast(Injected); + Result.addOuterTemplateArguments(InjectedType->template_arguments()); + } + } + + bool IsFriend = Rec->getFriendObjectKind() || + (Rec->getDescribedClassTemplate() && + Rec->getDescribedClassTemplate()->getFriendObjectKind()); + if (ForConstraintInstantiation && IsFriend && + Rec->getNonTransparentDeclContext()->isFileContext()) { + return Response::ChangeDecl(Rec->getLexicalDeclContext()); + } + + // This is to make sure we pick up the VarTemplateSpecializationDecl that this + // lambda is defined inside of. + if (Rec->isLambda()) + if (const Decl *LCD = Rec->getLambdaContextDecl()) + return Response::ChangeDecl(LCD); + + return Response::UseNextDecl(Rec); +} + +Response HandleGenericDeclContext(const Decl *CurDecl) { + return Response::UseNextDecl(CurDecl); +} +} // namespace TemplateInstArgsHelpers +} // namespace + /// Retrieve the template argument list(s) that should be used to /// instantiate the definition of the given declaration. /// -/// \param D the declaration for which we are computing template instantiation +/// \param ND the declaration for which we are computing template instantiation /// arguments. /// -/// \param Innermost if non-NULL, the innermost template argument list. +/// \param Innermost if non-NULL, specifies a template argument list for the +/// template declaration passed as ND. /// /// \param RelativeToPrimary true if we should get the template /// arguments relative to the primary template, even when we're @@ -54,166 +248,59 @@ /// template specializations. /// /// \param Pattern If non-NULL, indicates the pattern from which we will be -/// instantiating the definition of the given declaration, \p D. This is +/// instantiating the definition of the given declaration, \p ND. This is /// used to determine the proper set of template instantiation arguments for /// friend function template specializations. /// -/// \param LookBeyondLambda Indicates that this collection of arguments should -/// continue looking when it encounters a lambda generic call operator. -/// -/// \param IncludeContainingStructArgs Indicates that this collection of -/// arguments should include arguments for any class template that this -/// declaration is included inside of. +/// \param ForConstraintInstantiation when collecting arguments, +/// ForConstraintInstantiation indicates we should continue looking when +/// encountering a lambda generic call operator, and continue looking for +/// arguments on an enclosing class template. MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( - const NamedDecl *D, const TemplateArgumentList *Innermost, - bool RelativeToPrimary, const FunctionDecl *Pattern, bool LookBeyondLambda, - bool IncludeContainingStructArgs) { + const NamedDecl *ND, const TemplateArgumentList *Innermost, + bool RelativeToPrimary, const FunctionDecl *Pattern, + bool ForConstraintInstantiation) { + assert(ND && "Can't find arguments for a decl if one isn't provided"); // Accumulate the set of template argument lists in this structure. MultiLevelTemplateArgumentList Result; if (Innermost) Result.addOuterTemplateArguments(Innermost); - const auto *Ctx = dyn_cast(D); - if (!Ctx) { - Ctx = D->getDeclContext(); - - // Add template arguments from a variable template instantiation. For a - // class-scope explicit specialization, there are no template arguments - // at this level, but there may be enclosing template arguments. - const auto *Spec = dyn_cast(D); - if (Spec && !Spec->isClassScopeExplicitSpecialization()) { - // We're done when we hit an explicit specialization. - if (Spec->getSpecializationKind() == TSK_ExplicitSpecialization && - !isa(Spec)) - return Result; - - Result.addOuterTemplateArguments(&Spec->getTemplateInstantiationArgs()); - - // If this variable template specialization was instantiated from a - // specialized member that is a variable template, we're done. - assert(Spec->getSpecializedTemplate() && "No variable template?"); - llvm::PointerUnion Specialized - = Spec->getSpecializedTemplateOrPartial(); - if (VarTemplatePartialSpecializationDecl *Partial = - Specialized.dyn_cast()) { - if (Partial->isMemberSpecialization()) - return Result; - } else { - VarTemplateDecl *Tmpl = Specialized.get(); - if (Tmpl->isMemberSpecialization()) - return Result; - } - } - - // If we have a template template parameter with translation unit context, - // then we're performing substitution into a default template argument of - // this template template parameter before we've constructed the template - // that will own this template template parameter. In this case, we - // use empty template parameter lists for all of the outer templates - // to avoid performing any substitutions. - if (Ctx->isTranslationUnit()) { - if (const auto *TTP = dyn_cast(D)) { - for (unsigned I = 0, N = TTP->getDepth() + 1; I != N; ++I) - Result.addOuterTemplateArguments(None); - return Result; - } - } - } - - while (!Ctx->isFileContext()) { - // Add template arguments from a class template instantiation. - const auto *Spec = dyn_cast(Ctx); - if (Spec && !Spec->isClassScopeExplicitSpecialization()) { - // We're done when we hit an explicit specialization. - if (Spec->getSpecializationKind() == TSK_ExplicitSpecialization && - !isa(Spec)) - break; - - Result.addOuterTemplateArguments(&Spec->getTemplateInstantiationArgs()); - - // If this class template specialization was instantiated from a - // specialized member that is a class template, we're done. - assert(Spec->getSpecializedTemplate() && "No class template?"); - if (Spec->getSpecializedTemplate()->isMemberSpecialization()) - break; - } - // Add template arguments from a function template specialization. - else if (const auto *Function = dyn_cast(Ctx)) { - if (!RelativeToPrimary && - Function->getTemplateSpecializationKindForInstantiation() == - TSK_ExplicitSpecialization) - break; - - if (!RelativeToPrimary && Function->getTemplateSpecializationKind() == - TSK_ExplicitSpecialization) { - // This is an implicit instantiation of an explicit specialization. We - // don't get any template arguments from this function but might get - // some from an enclosing template. - } else if (const TemplateArgumentList *TemplateArgs - = Function->getTemplateSpecializationArgs()) { - // Add the template arguments for this specialization. - Result.addOuterTemplateArguments(TemplateArgs); - - // If this function was instantiated from a specialized member that is - // a function template, we're done. - assert(Function->getPrimaryTemplate() && "No function template?"); - if (Function->getPrimaryTemplate()->isMemberSpecialization()) - break; - - // If this function is a generic lambda specialization, we are done. - if (!LookBeyondLambda && - isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) - break; - - } else if (Function->getDescribedFunctionTemplate()) { - assert((IncludeContainingStructArgs || - Result.getNumSubstitutedLevels() == 0) && - "Outer template not instantiated?"); - } - - // If this is a friend declaration and it declares an entity at - // namespace scope, take arguments from its lexical parent - // instead of its semantic parent, unless of course the pattern we're - // instantiating actually comes from the file's context! - if (Function->getFriendObjectKind() && - Function->getNonTransparentDeclContext()->isFileContext() && - (!Pattern || !Pattern->getLexicalDeclContext()->isFileContext())) { - Ctx = Function->getLexicalDeclContext(); - RelativeToPrimary = false; - continue; - } - } else if (const auto *Rec = dyn_cast(Ctx)) { - if (ClassTemplateDecl *ClassTemplate = Rec->getDescribedClassTemplate()) { - assert((IncludeContainingStructArgs || - Result.getNumSubstitutedLevels() == 0) && - "Outer template not instantiated?"); - if (ClassTemplate->isMemberSpecialization()) - break; - if (IncludeContainingStructArgs) { - QualType RecordType = Context.getTypeDeclType(Rec); - QualType Injected = cast(RecordType) - ->getInjectedSpecializationType(); - const auto *InjectedType = cast(Injected); - Result.addOuterTemplateArguments(InjectedType->template_arguments()); + const Decl *CurDecl = ND; + + while (!CurDecl->isFileContextDecl()) { + using namespace TemplateInstArgsHelpers; + Response R; + if (const auto *VarTemplSpec = + dyn_cast(CurDecl)) { + R = HandleVarTemplateSpec(VarTemplSpec, Result); + } else if (const auto *ClassTemplSpec = + dyn_cast(CurDecl)) { + R = HandleClassTemplateSpec(ClassTemplSpec, Result); + } else if (const auto *Function = dyn_cast(CurDecl)) { + R = HandleFunction(Function, Result, Pattern, RelativeToPrimary, + ForConstraintInstantiation); + } else if (const auto *Rec = dyn_cast(CurDecl)) { + R = HandleRecordDecl(Rec, Result, Context, ForConstraintInstantiation); + } else if (!isa(CurDecl)) { + R = Response::DontClearRelativeToPrimaryNextDecl(CurDecl); + if (CurDecl->getDeclContext()->isTranslationUnit()) { + if (const auto *TTP = dyn_cast(CurDecl)) { + R = HandleDefaultTempArgIntoTempTempParam(TTP, Result); } } - bool IsFriend = Rec->getFriendObjectKind() || - (Rec->getDescribedClassTemplate() && - Rec->getDescribedClassTemplate()->getFriendObjectKind()); - if (IncludeContainingStructArgs && IsFriend && - Rec->getNonTransparentDeclContext()->isFileContext() && - (!Pattern || !Pattern->getLexicalDeclContext()->isFileContext())) { - Ctx = Rec->getLexicalDeclContext(); - RelativeToPrimary = false; - continue; - } + } else { + R = HandleGenericDeclContext(CurDecl); } - Ctx = Ctx->getParent(); - RelativeToPrimary = false; + if (R.IsDone) + return Result; + if (R.ClearRelativeToPrimary) + RelativeToPrimary = false; + assert(R.NextDecl); + CurDecl = R.NextDecl; } return Result; diff --git a/clang/test/SemaTemplate/concepts-lambda.cpp b/clang/test/SemaTemplate/concepts-lambda.cpp --- a/clang/test/SemaTemplate/concepts-lambda.cpp +++ b/clang/test/SemaTemplate/concepts-lambda.cpp @@ -13,3 +13,45 @@ f(); }; } + +namespace GH57945_2 { + template + concept c = true; + + template + auto f = [](auto... args) requires c { + }; + + template + auto f2 = [](auto... args) + requires (sizeof...(args) > 0) + {}; + + void g() { + f(); + f2(5.0); + } +} + +namespace GH57958 { + template concept C = true; + template constexpr bool v = [](C auto) { return true; }(0); + int _ = v<0>; +} +namespace GH57958_2 { + template concept C = true; + template constexpr bool v = [](C auto...) { return true; }(0); + int _ = v<0>; +} + +namespace GH57971 { + template + concept any = true; + + template + auto f = [](any auto) { + }; + + using function_ptr = void(*)(int); + function_ptr ptr = f; +}