================ @@ -2744,31 +2744,155 @@ bool hasDeclaredDeductionGuides(DeclarationName Name, DeclContext *DC) { return false; } +unsigned getTemplateDepth(NamedDecl *TemplateParam) { + if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(TemplateParam)) + return TTP->getDepth(); + if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(TemplateParam)) + return TTP->getDepth(); + if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(TemplateParam)) + return NTTP->getDepth(); + llvm_unreachable("Unhandled template parameter types"); +} + NamedDecl *transformTemplateParameter(Sema &SemaRef, DeclContext *DC, NamedDecl *TemplateParam, MultiLevelTemplateArgumentList &Args, - unsigned NewIndex) { + unsigned NewIndex, unsigned NewDepth) { if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(TemplateParam)) - return transformTemplateTypeParam(SemaRef, DC, TTP, Args, TTP->getDepth(), + return transformTemplateTypeParam(SemaRef, DC, TTP, Args, NewDepth, NewIndex); if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(TemplateParam)) return transformTemplateParam(SemaRef, DC, TTP, Args, NewIndex, - TTP->getDepth()); + NewDepth); if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(TemplateParam)) return transformTemplateParam(SemaRef, DC, NTTP, Args, NewIndex, - NTTP->getDepth()); + NewDepth); llvm_unreachable("Unhandled template parameter types"); } -Expr *transformRequireClause(Sema &SemaRef, FunctionTemplateDecl *FTD, - llvm::ArrayRef<TemplateArgument> TransformedArgs) { - Expr *RC = FTD->getTemplateParameters()->getRequiresClause(); +// Transform the require-clause of F if any. +// The return result is expected to be the require-clause for the synthesized +// alias deduction guide. +Expr *transformRequireClause( + Sema &SemaRef, FunctionTemplateDecl *F, + TypeAliasTemplateDecl *AliasTemplate, + ArrayRef<DeducedTemplateArgument> DeduceResults) { + Expr *RC = F->getTemplateParameters()->getRequiresClause(); if (!RC) return nullptr; + + auto &Context = SemaRef.Context; + LocalInstantiationScope Scope(SemaRef); + + // In the clang AST, constraint nodes are not instantiated at all unless they + // are being evaluated. This means that occurrences of template parameters + // in the require-clause expr have subtle differences compared to occurrences + // in other places, such as function parameters. When transforming the + // require-clause, we must respect these differences, particularly regarding + // the 'depth' information: + // 1) In the transformed require-clause, occurrences of template parameters + // must use the "uninstantiated" depth; + // 2) When substituting on the require-clause expr of the underlying + // deduction guide, we must use the entire set of template argument lists. + // + // It's important to note that we're performing this transformation on an + // *instantiated* AliasTemplate. + + // For 1), if the alias template is nested within a class template, we + // calcualte the 'uninstantiated' depth by adding the substitution level back. + unsigned AdjustDepth = 0; + if (auto *PrimaryTemplate = AliasTemplate->getInstantiatedFromMemberTemplate()) + AdjustDepth = PrimaryTemplate->getTemplateDepth(); + + // We rebuild all template parameters with the uninstantiated depth, and + // build template arguments refer to them. + SmallVector<TemplateArgument> AdjustedAliasTemplateArgs; + + for (auto *TP : *AliasTemplate->getTemplateParameters()) { + // Rebuild any internal references to earlier parameters and reindex + // as we go. + MultiLevelTemplateArgumentList Args; + Args.setKind(TemplateSubstitutionKind::Rewrite); + Args.addOuterTemplateArguments(AdjustedAliasTemplateArgs); + NamedDecl *NewParam = transformTemplateParameter( + SemaRef, AliasTemplate->getDeclContext(), TP, Args, + /*NewIndex=*/AdjustedAliasTemplateArgs.size(), + getTemplateDepth(TP) + AdjustDepth); + + auto NewTemplateArgument = Context.getCanonicalTemplateArgument( + Context.getInjectedTemplateArg(NewParam)); + AdjustedAliasTemplateArgs.push_back(NewTemplateArgument); + } + // Template arguments used to transform the template arguments in + // DeducedResults. + SmallVector<TemplateArgument> TemplateArgsForBuildingRC( + F->getTemplateParameters()->size()); + // Transform the transformed template args MultiLevelTemplateArgumentList Args; Args.setKind(TemplateSubstitutionKind::Rewrite); - Args.addOuterTemplateArguments(TransformedArgs); - ExprResult E = SemaRef.SubstExpr(RC, Args); + Args.addOuterTemplateArguments(AdjustedAliasTemplateArgs); + + for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) { + const auto &D = DeduceResults[Index]; + if (D.isNull()) + continue; + TemplateArgumentLoc Input = + SemaRef.getTrivialTemplateArgumentLoc(D, QualType(), SourceLocation{}); + TemplateArgumentLoc Output; + if (!SemaRef.SubstTemplateArgument(Input, Args, Output)) { + assert(TemplateArgsForBuildingRC[Index].isNull() && + "InstantiatedArgs must be null before setting"); + TemplateArgsForBuildingRC[Index] = Output.getArgument(); + } + } + + // A list of template arguments for transforming the require-clause of F. + // It must contain the entire set of template argument lists. + MultiLevelTemplateArgumentList ArgsForBuildingRC; + ArgsForBuildingRC.setKind(clang::TemplateSubstitutionKind::Rewrite); + ArgsForBuildingRC.addOuterTemplateArguments(TemplateArgsForBuildingRC); + // For 2), if the underlying F is instantiated from a member template, we need + // the entire template argument list, as the constraint AST in the + // require-clause of F remains completely uninstantiated. + // + // For example: + // template <typename T> // depth 0 + // struct Outer { + // template <typename U> + // struct Foo { Foo(U); }; + // + // template <typename U> // depth 1 + // requires C<U> + // Foo(U) -> Foo<int>; + // }; + // template <typename U> + // using AFoo = Outer<int>::Foo<U>; + // + // In this scenario, the deduction guide for `Foo` inside `Outer<int>`: + // - The occurrence of U in the require-expression is [depth:1, index:0] + // - The occurrence of U in the function parameter is [depth:0, index:0] + // - The template parameter of U is [depth:0, index:0] + // Particularly, for the `Foo` deduction guide inside the `Outer<int>`: + // + // - occurrence of U in the require-expression is [depth:1, index:0] + // - occurrence of U in the function parameter is [depth:0, index:0] + // - the template parameter of U is [depth:0, index:0] + // + // We add the outer template arguments which is [int] to the multi-level arg + // list to ensure that the occurrence U in `C<U>` will be replaced with int + // during the substitution. + if (F->getInstantiatedFromMemberTemplate()) { ---------------- zyn0217 wrote:
Ditto. Do we want to use the “primary” template after possibly multiple transformations? https://github.com/llvm/llvm-project/pull/90961 _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits