diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index e647ac267ab395..5c72270ff15047 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -2803,7 +2803,207 @@ getRHSTemplateDeclAndArgs(Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate) { return {Template, AliasRhsTemplateArgs}; } -// Build deduction guides for a type alias template. +// Build deduction guides for a type alias template from the given underlying +// deduction guide F. +FunctionTemplateDecl * +BuildDeductionGuideForTypeAlias(Sema &SemaRef, + TypeAliasTemplateDecl *AliasTemplate, + FunctionTemplateDecl *F, SourceLocation Loc) { + LocalInstantiationScope Scope(SemaRef); + Sema::InstantiatingTemplate BuildingDeductionGuides( + SemaRef, AliasTemplate->getLocation(), F, + Sema::InstantiatingTemplate::BuildingDeductionGuidesTag{}); + if (BuildingDeductionGuides.isInvalid()) + return nullptr; + + auto &Context = SemaRef.Context; + auto [Template, AliasRhsTemplateArgs] = + getRHSTemplateDeclAndArgs(SemaRef, AliasTemplate); + + auto RType = F->getTemplatedDecl()->getReturnType(); + // The (trailing) return type of the deduction guide. + const TemplateSpecializationType *FReturnType = + RType->getAs(); + if (const auto *InjectedCNT = RType->getAs()) + // implicitly-generated deduction guide. + FReturnType = InjectedCNT->getInjectedTST(); + else if (const auto *ET = RType->getAs()) + // explicit deduction guide. + FReturnType = ET->getNamedType()->getAs(); + assert(FReturnType && "expected to see a return type"); + // Deduce template arguments of the deduction guide f from the RHS of + // the alias. + // + // C++ [over.match.class.deduct]p3: ...For each function or function + // template f in the guides of the template named by the + // simple-template-id of the defining-type-id, the template arguments + // of the return type of f are deduced from the defining-type-id of A + // according to the process in [temp.deduct.type] with the exception + // that deduction does not fail if not all template arguments are + // deduced. + // + // + // template + // f(X, Y) -> f; + // + // template + // using alias = f; + // + // The RHS of alias is f, we deduced the template arguments of + // the return type of the deduction guide from it: Y->int, X->U + sema::TemplateDeductionInfo TDeduceInfo(Loc); + // Must initialize n elements, this is required by DeduceTemplateArguments. + SmallVector DeduceResults( + F->getTemplateParameters()->size()); + + // FIXME: DeduceTemplateArguments stops immediately at the first + // non-deducible template argument. However, this doesn't seem to casue + // issues for practice cases, we probably need to extend it to continue + // performing deduction for rest of arguments to align with the C++ + // standard. + SemaRef.DeduceTemplateArguments( + F->getTemplateParameters(), FReturnType->template_arguments(), + AliasRhsTemplateArgs, TDeduceInfo, DeduceResults, + /*NumberOfArgumentsMustMatch=*/false); + + SmallVector DeducedArgs; + SmallVector NonDeducedTemplateParamsInFIndex; + // !!NOTE: DeduceResults respects the sequence of template parameters of + // the deduction guide f. + for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) { + if (const auto &D = DeduceResults[Index]; !D.isNull()) // Deduced + DeducedArgs.push_back(D); + else + NonDeducedTemplateParamsInFIndex.push_back(Index); + } + auto DeducedAliasTemplateParams = + TemplateParamsReferencedInTemplateArgumentList( + AliasTemplate->getTemplateParameters()->asArray(), DeducedArgs); + // All template arguments null by default. + SmallVector TemplateArgsForBuildingFPrime( + F->getTemplateParameters()->size()); + + // Create a template parameter list for the synthesized deduction guide f'. + // + // C++ [over.match.class.deduct]p3.2: + // If f is a function template, f' is a function template whose template + // parameter list consists of all the template parameters of A + // (including their default template arguments) that appear in the above + // deductions or (recursively) in their default template arguments + SmallVector FPrimeTemplateParams; + // Store template arguments that refer to the newly-created template + // parameters, used for building `TemplateArgsForBuildingFPrime`. + SmallVector TransformedDeducedAliasArgs( + AliasTemplate->getTemplateParameters()->size()); + + for (unsigned AliasTemplateParamIdx : DeducedAliasTemplateParams) { + auto *TP = + AliasTemplate->getTemplateParameters()->getParam(AliasTemplateParamIdx); + // Rebuild any internal references to earlier parameters and reindex as + // we go. + MultiLevelTemplateArgumentList Args; + Args.setKind(TemplateSubstitutionKind::Rewrite); + Args.addOuterTemplateArguments(TransformedDeducedAliasArgs); + NamedDecl *NewParam = transformTemplateParameter( + SemaRef, AliasTemplate->getDeclContext(), TP, Args, + /*NewIndex=*/FPrimeTemplateParams.size()); + FPrimeTemplateParams.push_back(NewParam); + + auto NewTemplateArgument = Context.getCanonicalTemplateArgument( + Context.getInjectedTemplateArg(NewParam)); + TransformedDeducedAliasArgs[AliasTemplateParamIdx] = NewTemplateArgument; + } + // ...followed by the template parameters of f that were not deduced + // (including their default template arguments) + for (unsigned FTemplateParamIdx : NonDeducedTemplateParamsInFIndex) { + auto *TP = F->getTemplateParameters()->getParam(FTemplateParamIdx); + MultiLevelTemplateArgumentList Args; + Args.setKind(TemplateSubstitutionKind::Rewrite); + // We take a shortcut here, it is ok to reuse the + // TemplateArgsForBuildingFPrime. + Args.addOuterTemplateArguments(TemplateArgsForBuildingFPrime); + NamedDecl *NewParam = transformTemplateParameter( + SemaRef, F->getDeclContext(), TP, Args, FPrimeTemplateParams.size()); + FPrimeTemplateParams.push_back(NewParam); + + assert(TemplateArgsForBuildingFPrime[FTemplateParamIdx].isNull() && + "The argument must be null before setting"); + TemplateArgsForBuildingFPrime[FTemplateParamIdx] = + Context.getCanonicalTemplateArgument( + Context.getInjectedTemplateArg(NewParam)); + } + + // To form a deduction guide f' from f, we leverage clang's instantiation + // mechanism, we construct a template argument list where the template + // arguments refer to the newly-created template parameters of f', and + // then apply instantiation on this template argument list to instantiate + // f, this ensures all template parameter occurrences are updated + // correctly. + // + // The template argument list is formed from the `DeducedArgs`, two parts: + // 1) appeared template parameters of alias: transfrom the deduced + // template argument; + // 2) non-deduced template parameters of f: rebuild a + // template argument; + // + // 2) has been built already (when rebuilding the new template + // parameters), we now perform 1). + MultiLevelTemplateArgumentList Args; + Args.setKind(TemplateSubstitutionKind::Rewrite); + Args.addOuterTemplateArguments(TransformedDeducedAliasArgs); + for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) { + const auto &D = DeduceResults[Index]; + if (D.isNull()) { + // 2): Non-deduced template parameter has been built already. + assert(!TemplateArgsForBuildingFPrime[Index].isNull() && + "template arguments for non-deduced template parameters should " + "be been set!"); + continue; + } + TemplateArgumentLoc Input = + SemaRef.getTrivialTemplateArgumentLoc(D, QualType(), SourceLocation{}); + TemplateArgumentLoc Output; + if (!SemaRef.SubstTemplateArgument(Input, Args, Output)) { + assert(TemplateArgsForBuildingFPrime[Index].isNull() && + "InstantiatedArgs must be null before setting"); + TemplateArgsForBuildingFPrime[Index] = Output.getArgument(); + } + } + + auto *TemplateArgListForBuildingFPrime = + TemplateArgumentList::CreateCopy(Context, TemplateArgsForBuildingFPrime); + // Form the f' by substituting the template arguments into f. + if (auto *FPrime = SemaRef.InstantiateFunctionDeclaration( + F, TemplateArgListForBuildingFPrime, AliasTemplate->getLocation(), + Sema::CodeSynthesisContext::BuildingDeductionGuides)) { + auto *GG = cast(FPrime); + + Expr *RequiresClause = + transformRequireClause(SemaRef, F, TemplateArgsForBuildingFPrime); + + // FIXME: implement the is_deducible constraint per C++ + // [over.match.class.deduct]p3.3: + // ... and a constraint that is satisfied if and only if the arguments + // of A are deducible (see below) from the return type. + auto *FPrimeTemplateParamList = TemplateParameterList::Create( + Context, AliasTemplate->getTemplateParameters()->getTemplateLoc(), + AliasTemplate->getTemplateParameters()->getLAngleLoc(), + FPrimeTemplateParams, + AliasTemplate->getTemplateParameters()->getRAngleLoc(), + /*RequiresClause=*/RequiresClause); + FunctionTemplateDecl *Result = buildDeductionGuide( + SemaRef, AliasTemplate, FPrimeTemplateParamList, + GG->getCorrespondingConstructor(), GG->getExplicitSpecifier(), + GG->getTypeSourceInfo(), AliasTemplate->getBeginLoc(), + AliasTemplate->getLocation(), AliasTemplate->getEndLoc(), + F->isImplicit()); + cast(Result->getTemplatedDecl()) + ->setDeductionCandidateKind(GG->getDeductionCandidateKind()); + return Result; + } + return nullptr; +} + void DeclareImplicitDeductionGuidesForTypeAlias( Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate, SourceLocation Loc) { if (AliasTemplate->isInvalidDecl()) @@ -2831,197 +3031,13 @@ void DeclareImplicitDeductionGuidesForTypeAlias( if (!F) continue; // The **aggregate** deduction guides are handled in a different code path - // (DeclareImplicitDeductionGuideFromInitList), which involves the tricky + // (DeclareAggregateDeductionGuideFromInitList), which involves the tricky // cache. if (cast(F->getTemplatedDecl()) ->getDeductionCandidateKind() == DeductionCandidate::Aggregate) continue; - auto RType = F->getTemplatedDecl()->getReturnType(); - // The (trailing) return type of the deduction guide. - const TemplateSpecializationType *FReturnType = - RType->getAs(); - if (const auto *InjectedCNT = RType->getAs()) - // implicitly-generated deduction guide. - FReturnType = InjectedCNT->getInjectedTST(); - else if (const auto *ET = RType->getAs()) - // explicit deduction guide. - FReturnType = ET->getNamedType()->getAs(); - assert(FReturnType && "expected to see a return type"); - // Deduce template arguments of the deduction guide f from the RHS of - // the alias. - // - // C++ [over.match.class.deduct]p3: ...For each function or function - // template f in the guides of the template named by the - // simple-template-id of the defining-type-id, the template arguments - // of the return type of f are deduced from the defining-type-id of A - // according to the process in [temp.deduct.type] with the exception - // that deduction does not fail if not all template arguments are - // deduced. - // - // - // template - // f(X, Y) -> f; - // - // template - // using alias = f; - // - // The RHS of alias is f, we deduced the template arguments of - // the return type of the deduction guide from it: Y->int, X->U - sema::TemplateDeductionInfo TDeduceInfo(Loc); - // Must initialize n elements, this is required by DeduceTemplateArguments. - SmallVector DeduceResults( - F->getTemplateParameters()->size()); - - // FIXME: DeduceTemplateArguments stops immediately at the first - // non-deducible template argument. However, this doesn't seem to casue - // issues for practice cases, we probably need to extend it to continue - // performing deduction for rest of arguments to align with the C++ - // standard. - SemaRef.DeduceTemplateArguments( - F->getTemplateParameters(), FReturnType->template_arguments(), - AliasRhsTemplateArgs, TDeduceInfo, DeduceResults, - /*NumberOfArgumentsMustMatch=*/false); - - SmallVector DeducedArgs; - SmallVector NonDeducedTemplateParamsInFIndex; - // !!NOTE: DeduceResults respects the sequence of template parameters of - // the deduction guide f. - for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) { - if (const auto &D = DeduceResults[Index]; !D.isNull()) // Deduced - DeducedArgs.push_back(D); - else - NonDeducedTemplateParamsInFIndex.push_back(Index); - } - auto DeducedAliasTemplateParams = - TemplateParamsReferencedInTemplateArgumentList( - AliasTemplate->getTemplateParameters()->asArray(), DeducedArgs); - // All template arguments null by default. - SmallVector TemplateArgsForBuildingFPrime( - F->getTemplateParameters()->size()); - - Sema::InstantiatingTemplate BuildingDeductionGuides( - SemaRef, AliasTemplate->getLocation(), F, - Sema::InstantiatingTemplate::BuildingDeductionGuidesTag{}); - if (BuildingDeductionGuides.isInvalid()) - return; - LocalInstantiationScope Scope(SemaRef); - - // Create a template parameter list for the synthesized deduction guide f'. - // - // C++ [over.match.class.deduct]p3.2: - // If f is a function template, f' is a function template whose template - // parameter list consists of all the template parameters of A - // (including their default template arguments) that appear in the above - // deductions or (recursively) in their default template arguments - SmallVector FPrimeTemplateParams; - // Store template arguments that refer to the newly-created template - // parameters, used for building `TemplateArgsForBuildingFPrime`. - SmallVector TransformedDeducedAliasArgs( - AliasTemplate->getTemplateParameters()->size()); - - for (unsigned AliasTemplateParamIdx : DeducedAliasTemplateParams) { - auto *TP = AliasTemplate->getTemplateParameters()->getParam( - AliasTemplateParamIdx); - // Rebuild any internal references to earlier parameters and reindex as - // we go. - MultiLevelTemplateArgumentList Args; - Args.setKind(TemplateSubstitutionKind::Rewrite); - Args.addOuterTemplateArguments(TransformedDeducedAliasArgs); - NamedDecl *NewParam = transformTemplateParameter( - SemaRef, AliasTemplate->getDeclContext(), TP, Args, - /*NewIndex*/ FPrimeTemplateParams.size()); - FPrimeTemplateParams.push_back(NewParam); - - auto NewTemplateArgument = Context.getCanonicalTemplateArgument( - Context.getInjectedTemplateArg(NewParam)); - TransformedDeducedAliasArgs[AliasTemplateParamIdx] = NewTemplateArgument; - } - // ...followed by the template parameters of f that were not deduced - // (including their default template arguments) - for (unsigned FTemplateParamIdx : NonDeducedTemplateParamsInFIndex) { - auto *TP = F->getTemplateParameters()->getParam(FTemplateParamIdx); - MultiLevelTemplateArgumentList Args; - Args.setKind(TemplateSubstitutionKind::Rewrite); - // We take a shortcut here, it is ok to reuse the - // TemplateArgsForBuildingFPrime. - Args.addOuterTemplateArguments(TemplateArgsForBuildingFPrime); - NamedDecl *NewParam = transformTemplateParameter( - SemaRef, F->getDeclContext(), TP, Args, FPrimeTemplateParams.size()); - FPrimeTemplateParams.push_back(NewParam); - - assert(TemplateArgsForBuildingFPrime[FTemplateParamIdx].isNull() && - "The argument must be null before setting"); - TemplateArgsForBuildingFPrime[FTemplateParamIdx] = - Context.getCanonicalTemplateArgument( - Context.getInjectedTemplateArg(NewParam)); - } - - // To form a deduction guide f' from f, we leverage clang's instantiation - // mechanism, we construct a template argument list where the template - // arguments refer to the newly-created template parameters of f', and - // then apply instantiation on this template argument list to instantiate - // f, this ensures all template parameter occurrences are updated - // correctly. - // - // The template argument list is formed from the `DeducedArgs`, two parts: - // 1) appeared template parameters of alias: transfrom the deduced - // template argument; - // 2) non-deduced template parameters of f: rebuild a - // template argument; - // - // 2) has been built already (when rebuilding the new template - // parameters), we now perform 1). - MultiLevelTemplateArgumentList Args; - Args.setKind(TemplateSubstitutionKind::Rewrite); - Args.addOuterTemplateArguments(TransformedDeducedAliasArgs); - for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) { - const auto &D = DeduceResults[Index]; - if (D.isNull()) { - // 2): Non-deduced template parameter has been built already. - assert(!TemplateArgsForBuildingFPrime[Index].isNull() && - "template arguments for non-deduced template parameters should " - "be been set!"); - continue; - } - TemplateArgumentLoc Input = SemaRef.getTrivialTemplateArgumentLoc( - D, QualType(), SourceLocation{}); - TemplateArgumentLoc Output; - if (!SemaRef.SubstTemplateArgument(Input, Args, Output)) { - assert(TemplateArgsForBuildingFPrime[Index].isNull() && - "InstantiatedArgs must be null before setting"); - TemplateArgsForBuildingFPrime[Index] = (Output.getArgument()); - } - } - - auto *TemplateArgListForBuildingFPrime = TemplateArgumentList::CreateCopy( - Context, TemplateArgsForBuildingFPrime); - // Form the f' by substituting the template arguments into f. - if (auto *FPrime = SemaRef.InstantiateFunctionDeclaration( - F, TemplateArgListForBuildingFPrime, AliasTemplate->getLocation(), - Sema::CodeSynthesisContext::BuildingDeductionGuides)) { - auto *GG = cast(FPrime); - // Substitute new template parameters into requires-clause if present. - Expr *RequiresClause = - transformRequireClause(SemaRef, F, TemplateArgsForBuildingFPrime); - // FIXME: implement the is_deducible constraint per C++ - // [over.match.class.deduct]p3.3: - // ... and a constraint that is satisfied if and only if the arguments - // of A are deducible (see below) from the return type. - auto *FPrimeTemplateParamList = TemplateParameterList::Create( - Context, AliasTemplate->getTemplateParameters()->getTemplateLoc(), - AliasTemplate->getTemplateParameters()->getLAngleLoc(), - FPrimeTemplateParams, - AliasTemplate->getTemplateParameters()->getRAngleLoc(), - /*RequiresClause=*/RequiresClause); - - buildDeductionGuide(SemaRef, AliasTemplate, FPrimeTemplateParamList, - GG->getCorrespondingConstructor(), - GG->getExplicitSpecifier(), GG->getTypeSourceInfo(), - AliasTemplate->getBeginLoc(), - AliasTemplate->getLocation(), - AliasTemplate->getEndLoc(), F->isImplicit()); - } + BuildDeductionGuideForTypeAlias(SemaRef, AliasTemplate, F, Loc); } } @@ -3037,66 +3053,8 @@ FunctionTemplateDecl *DeclareAggregateDeductionGuideForTypeAlias( RHSTemplate, ParamTypes, Loc); if (!RHSDeductionGuide) return nullptr; - - LocalInstantiationScope Scope(SemaRef); - Sema::InstantiatingTemplate BuildingDeductionGuides( - SemaRef, AliasTemplate->getLocation(), RHSDeductionGuide, - Sema::InstantiatingTemplate::BuildingDeductionGuidesTag{}); - if (BuildingDeductionGuides.isInvalid()) - return nullptr; - - // Build a new template parameter list for the synthesized aggregate deduction - // guide by transforming the one from RHSDeductionGuide. - SmallVector TransformedTemplateParams; - // Template args that refer to the rebuilt template parameters. - // All template arguments must be initialized in advance. - SmallVector TransformedTemplateArgs( - RHSDeductionGuide->getTemplateParameters()->size()); - for (auto *TP : *RHSDeductionGuide->getTemplateParameters()) { - // Rebuild any internal references to earlier parameters and reindex as - // we go. - MultiLevelTemplateArgumentList Args; - Args.setKind(TemplateSubstitutionKind::Rewrite); - Args.addOuterTemplateArguments(TransformedTemplateArgs); - NamedDecl *NewParam = transformTemplateParameter( - SemaRef, AliasTemplate->getDeclContext(), TP, Args, - /*NewIndex=*/TransformedTemplateParams.size()); - - TransformedTemplateArgs[TransformedTemplateParams.size()] = - SemaRef.Context.getCanonicalTemplateArgument( - SemaRef.Context.getInjectedTemplateArg(NewParam)); - TransformedTemplateParams.push_back(NewParam); - } - // FIXME: implement the is_deducible constraint per C++ - // [over.match.class.deduct]p3.3. - Expr *TransformedRequiresClause = transformRequireClause( - SemaRef, RHSDeductionGuide, TransformedTemplateArgs); - auto *TransformedTemplateParameterList = TemplateParameterList::Create( - SemaRef.Context, AliasTemplate->getTemplateParameters()->getTemplateLoc(), - AliasTemplate->getTemplateParameters()->getLAngleLoc(), - TransformedTemplateParams, - AliasTemplate->getTemplateParameters()->getRAngleLoc(), - TransformedRequiresClause); - auto *TransformedTemplateArgList = TemplateArgumentList::CreateCopy( - SemaRef.Context, TransformedTemplateArgs); - - if (auto *TransformedDeductionGuide = SemaRef.InstantiateFunctionDeclaration( - RHSDeductionGuide, TransformedTemplateArgList, - AliasTemplate->getLocation(), - Sema::CodeSynthesisContext::BuildingDeductionGuides)) { - auto *GD = - llvm::dyn_cast(TransformedDeductionGuide); - FunctionTemplateDecl *Result = buildDeductionGuide( - SemaRef, AliasTemplate, TransformedTemplateParameterList, - GD->getCorrespondingConstructor(), GD->getExplicitSpecifier(), - GD->getTypeSourceInfo(), AliasTemplate->getBeginLoc(), - AliasTemplate->getLocation(), AliasTemplate->getEndLoc(), - GD->isImplicit()); - cast(Result->getTemplatedDecl()) - ->setDeductionCandidateKind(DeductionCandidate::Aggregate); - return Result; - } - return nullptr; + return BuildDeductionGuideForTypeAlias(SemaRef, AliasTemplate, + RHSDeductionGuide, Loc); } } // namespace diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp index 508a3a5da76a91..e8b4383f53c5ea 100644 --- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp +++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp @@ -307,3 +307,17 @@ using AFoo = Foo>; AFoo a(Derived{}); } // namespace test22 + +namespace test23 { +// We have an aggregate deduction guide "G(T) -> G". +template +struct G { T t1; }; + +template +using AG = G; + +AG ag(1.0); +// Verify that the aggregate deduction guide "AG(int) -> AG" is built and +// choosen. +static_assert(__is_same(decltype(ag.t1), int)); +} // namespace test23 diff --git a/clang/test/SemaTemplate/deduction-guide.cpp b/clang/test/SemaTemplate/deduction-guide.cpp index ff5e39216762fa..51e1eb49c5de75 100644 --- a/clang/test/SemaTemplate/deduction-guide.cpp +++ b/clang/test/SemaTemplate/deduction-guide.cpp @@ -261,6 +261,13 @@ AG ag = {1}; // CHECK: | `-BuiltinType {{.*}} 'int' // CHECK: `-ParmVarDecl {{.*}} 'int' +template +using BG = G; +BG bg(1.0); +// CHECK-LABEL: Dumping +// CHECK: FunctionTemplateDecl {{.*}} implicit +// CHECK: |-CXXDeductionGuideDecl {{.*}} 'auto (int) -> G' aggregate + template requires (sizeof(D) == 4) struct Foo {