Author: Haojian Wu Date: 2024-03-08T14:24:03+01:00 New Revision: 7415524b45392651969374c067041daa82dc89e7
URL: https://github.com/llvm/llvm-project/commit/7415524b45392651969374c067041daa82dc89e7 DIFF: https://github.com/llvm/llvm-project/commit/7415524b45392651969374c067041daa82dc89e7.diff LOG: [clang] Implement CTAD for type alias template. (#77890) Fixes #54051 This patch implements the C++20 feature -- CTAD for alias templates (P1814R0, specified in https://eel.is/c++draft/over.match.class.deduct#3). It is an initial patch: - it cover major pieces, thus it works for most cases; - the big missing piece is to implement the associated constraints (over.match.class.deduct#3.3) for the synthesized deduction guides, see the FIXME in code and tests; - Some enhancements on the TreeTransform&TemplateInstantiator to allow performing instantiation on `BuildingDeductionGuides` mode; Added: clang/test/SemaCXX/cxx20-ctad-type-alias.cpp Modified: clang/docs/ReleaseNotes.rst clang/include/clang/Basic/DiagnosticSemaKinds.td clang/include/clang/Sema/Sema.h clang/lib/Sema/SemaInit.cpp clang/lib/Sema/SemaTemplate.cpp clang/lib/Sema/SemaTemplateDeduction.cpp clang/lib/Sema/SemaTemplateInstantiate.cpp clang/lib/Sema/SemaTemplateInstantiateDecl.cpp clang/lib/Sema/TreeTransform.h clang/test/SemaCXX/cxx17-compat.cpp clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp clang/www/cxx_status.html Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 73901a14631b3a..24b452bd487fb1 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -91,6 +91,10 @@ C++20 Feature Support current module units. Fixes `#84002 <https://github.com/llvm/llvm-project/issues/84002>`_. +- Initial support for class template argument deduction (CTAD) for type alias + templates (`P1814R0 <https://wg21.link/p1814r0>`_). + (#GH54051). + C++23 Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index a80c8b75e863ed..6da49facd27ec2 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2515,10 +2515,11 @@ def err_deduced_class_template_compound_type : Error< "cannot %select{form pointer to|form reference to|form array of|" "form function returning|use parentheses when declaring variable with}0 " "deduced class template specialization type">; -def err_deduced_non_class_template_specialization_type : Error< +def err_deduced_non_class_or_alias_template_specialization_type : Error< "%select{<error>|function template|variable template|alias template|" "template template parameter|concept|template}0 %1 requires template " - "arguments; argument deduction only allowed for class templates">; + "arguments; argument deduction only allowed for class templates or alias " + "templates">; def err_deduced_class_template_ctor_ambiguous : Error< "ambiguous deduction for template arguments of %0">; def err_deduced_class_template_ctor_no_viable : Error< @@ -8201,6 +8202,11 @@ let CategoryName = "Lambda Issue" in { def warn_cxx17_compat_lambda_def_ctor_assign : Warning< "%select{default construction|assignment}0 of lambda is incompatible with " "C++ standards before C++20">, InGroup<CXXPre20Compat>, DefaultIgnore; + + // C++20 class template argument deduction for alias templates. + def warn_cxx17_compat_ctad_for_alias_templates : Warning< + "class template argument deduction for alias templates is incompatible with " + "C++ standards before C++20">, InGroup<CXXPre20Compat>, DefaultIgnore; } def err_return_in_captured_stmt : Error< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 2cac7e6ca08fad..5b4d67494f2a45 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9827,6 +9827,12 @@ class Sema final { ArrayRef<TemplateArgument> TemplateArgs, sema::TemplateDeductionInfo &Info); + TemplateDeductionResult DeduceTemplateArguments( + TemplateParameterList *TemplateParams, ArrayRef<TemplateArgument> Ps, + ArrayRef<TemplateArgument> As, sema::TemplateDeductionInfo &Info, + SmallVectorImpl<DeducedTemplateArgument> &Deduced, + bool NumberOfArgumentsMustMatch); + TemplateDeductionResult SubstituteExplicitTemplateArguments( FunctionTemplateDecl *FunctionTemplate, TemplateArgumentListInfo &ExplicitTemplateArgs, @@ -10378,6 +10384,9 @@ class Sema final { InstantiatingTemplate &operator=(const InstantiatingTemplate &) = delete; }; + bool SubstTemplateArgument(const TemplateArgumentLoc &Input, + const MultiLevelTemplateArgumentList &TemplateArgs, + TemplateArgumentLoc &Output); bool SubstTemplateArguments(ArrayRef<TemplateArgumentLoc> Args, const MultiLevelTemplateArgumentList &TemplateArgs, @@ -10862,9 +10871,11 @@ class Sema final { ParmVarDecl *Param); void InstantiateExceptionSpec(SourceLocation PointOfInstantiation, FunctionDecl *Function); - FunctionDecl *InstantiateFunctionDeclaration(FunctionTemplateDecl *FTD, - const TemplateArgumentList *Args, - SourceLocation Loc); + FunctionDecl *InstantiateFunctionDeclaration( + FunctionTemplateDecl *FTD, const TemplateArgumentList *Args, + SourceLocation Loc, + CodeSynthesisContext::SynthesisKind CSC = + CodeSynthesisContext::ExplicitTemplateArgumentSubstitution); void InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, FunctionDecl *Function, bool Recursive = false, diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 011deed7a9a9f1..aa470adb30b47f 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -10720,13 +10720,40 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( if (TemplateName.isDependent()) return SubstAutoTypeDependent(TSInfo->getType()); - // We can only perform deduction for class templates. + // We can only perform deduction for class templates or alias templates. auto *Template = dyn_cast_or_null<ClassTemplateDecl>(TemplateName.getAsTemplateDecl()); + TemplateDecl *LookupTemplateDecl = Template; + if (!Template) { + if (auto *AliasTemplate = dyn_cast_or_null<TypeAliasTemplateDecl>( + TemplateName.getAsTemplateDecl())) { + Diag(Kind.getLocation(), + diag::warn_cxx17_compat_ctad_for_alias_templates); + LookupTemplateDecl = AliasTemplate; + auto UnderlyingType = AliasTemplate->getTemplatedDecl() + ->getUnderlyingType() + .getCanonicalType(); + // C++ [over.match.class.deduct#3]: ..., the defining-type-id of A must be + // of the form + // [typename] [nested-name-specifier] [template] simple-template-id + if (const auto *TST = + UnderlyingType->getAs<TemplateSpecializationType>()) { + Template = dyn_cast_or_null<ClassTemplateDecl>( + TST->getTemplateName().getAsTemplateDecl()); + } else if (const auto *RT = UnderlyingType->getAs<RecordType>()) { + // Cases where template arguments in the RHS of the alias are not + // dependent. e.g. + // using AliasFoo = Foo<bool>; + if (const auto *CTSD = llvm::dyn_cast<ClassTemplateSpecializationDecl>( + RT->getAsCXXRecordDecl())) + Template = CTSD->getSpecializedTemplate(); + } + } + } if (!Template) { Diag(Kind.getLocation(), - diag::err_deduced_non_class_template_specialization_type) - << (int)getTemplateNameKindForDiagnostics(TemplateName) << TemplateName; + diag::err_deduced_non_class_or_alias_template_specialization_type) + << (int)getTemplateNameKindForDiagnostics(TemplateName) << TemplateName; if (auto *TD = TemplateName.getAsTemplateDecl()) NoteTemplateLocation(*TD); return QualType(); @@ -10753,10 +10780,10 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( // template-name, a function template [...] // - For each deduction-guide, a function or function template [...] DeclarationNameInfo NameInfo( - Context.DeclarationNames.getCXXDeductionGuideName(Template), + Context.DeclarationNames.getCXXDeductionGuideName(LookupTemplateDecl), TSInfo->getTypeLoc().getEndLoc()); LookupResult Guides(*this, NameInfo, LookupOrdinaryName); - LookupQualifiedName(Guides, Template->getDeclContext()); + LookupQualifiedName(Guides, LookupTemplateDecl->getDeclContext()); // FIXME: Do not diagnose inaccessible deduction guides. The standard isn't // clear on this, but they're not found by name so access does not apply. diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 83eee83aa6cad8..d62095558d0ffb 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -2262,6 +2262,92 @@ class ExtractTypeForDeductionGuide } }; +// Build a deduction guide with the specified parameter types. +FunctionTemplateDecl *buildDeductionGuide( + Sema &SemaRef, TemplateDecl *OriginalTemplate, + TemplateParameterList *TemplateParams, CXXConstructorDecl *Ctor, + ExplicitSpecifier ES, TypeSourceInfo *TInfo, SourceLocation LocStart, + SourceLocation Loc, SourceLocation LocEnd, bool IsImplicit, + llvm::ArrayRef<TypedefNameDecl *> MaterializedTypedefs = {}) { + DeclContext *DC = OriginalTemplate->getDeclContext(); + auto DeductionGuideName = + SemaRef.Context.DeclarationNames.getCXXDeductionGuideName( + OriginalTemplate); + + DeclarationNameInfo Name(DeductionGuideName, Loc); + ArrayRef<ParmVarDecl *> Params = + TInfo->getTypeLoc().castAs<FunctionProtoTypeLoc>().getParams(); + + // Build the implicit deduction guide template. + auto *Guide = + CXXDeductionGuideDecl::Create(SemaRef.Context, DC, LocStart, ES, Name, + TInfo->getType(), TInfo, LocEnd, Ctor); + Guide->setImplicit(IsImplicit); + Guide->setParams(Params); + + for (auto *Param : Params) + Param->setDeclContext(Guide); + for (auto *TD : MaterializedTypedefs) + TD->setDeclContext(Guide); + + auto *GuideTemplate = FunctionTemplateDecl::Create( + SemaRef.Context, DC, Loc, DeductionGuideName, TemplateParams, Guide); + GuideTemplate->setImplicit(IsImplicit); + Guide->setDescribedFunctionTemplate(GuideTemplate); + + if (isa<CXXRecordDecl>(DC)) { + Guide->setAccess(AS_public); + GuideTemplate->setAccess(AS_public); + } + + DC->addDecl(GuideTemplate); + return GuideTemplate; +} + +// Transform a given template type parameter `TTP`. +TemplateTypeParmDecl * +transformTemplateTypeParam(Sema &SemaRef, DeclContext *DC, + TemplateTypeParmDecl *TTP, + MultiLevelTemplateArgumentList &Args, + unsigned NewDepth, unsigned NewIndex) { + // TemplateTypeParmDecl's index cannot be changed after creation, so + // substitute it directly. + auto *NewTTP = TemplateTypeParmDecl::Create( + SemaRef.Context, DC, TTP->getBeginLoc(), TTP->getLocation(), NewDepth, + NewIndex, TTP->getIdentifier(), TTP->wasDeclaredWithTypename(), + TTP->isParameterPack(), TTP->hasTypeConstraint(), + TTP->isExpandedParameterPack() + ? std::optional<unsigned>(TTP->getNumExpansionParameters()) + : std::nullopt); + if (const auto *TC = TTP->getTypeConstraint()) + SemaRef.SubstTypeConstraint(NewTTP, TC, Args, + /*EvaluateConstraint=*/true); + if (TTP->hasDefaultArgument()) { + TypeSourceInfo *InstantiatedDefaultArg = + SemaRef.SubstType(TTP->getDefaultArgumentInfo(), Args, + TTP->getDefaultArgumentLoc(), TTP->getDeclName()); + if (InstantiatedDefaultArg) + NewTTP->setDefaultArgument(InstantiatedDefaultArg); + } + SemaRef.CurrentInstantiationScope->InstantiatedLocal(TTP, NewTTP); + return NewTTP; +} +// Similar to above, but for non-type template or template template parameters. +template <typename NonTypeTemplateOrTemplateTemplateParmDecl> +NonTypeTemplateOrTemplateTemplateParmDecl * +transformTemplateParam(Sema &SemaRef, DeclContext *DC, + NonTypeTemplateOrTemplateTemplateParmDecl *OldParam, + MultiLevelTemplateArgumentList &Args, unsigned NewIndex, + unsigned NewDepth) { + // Ask the template instantiator to do the heavy lifting for us, then adjust + // the index of the parameter once it's done. + auto *NewParam = cast<NonTypeTemplateOrTemplateTemplateParmDecl>( + SemaRef.SubstDecl(OldParam, DC, Args)); + NewParam->setPosition(NewIndex); + NewParam->setDepth(NewDepth); + return NewParam; +} + /// Transform to convert portions of a constructor declaration into the /// corresponding deduction guide, per C++1z [over.match.class.deduct]p1. struct ConvertConstructorToDeductionGuideTransform { @@ -2338,7 +2424,6 @@ struct ConvertConstructorToDeductionGuideTransform { NamedDecl *NewParam = transformTemplateParameter(Param, Args); if (!NewParam) return nullptr; - // Constraints require that we substitute depth-1 arguments // to match depths when substituted for evaluation later Depth1Args.push_back(SemaRef.Context.getCanonicalTemplateArgument( @@ -2411,9 +2496,10 @@ struct ConvertConstructorToDeductionGuideTransform { return nullptr; TypeSourceInfo *NewTInfo = TLB.getTypeSourceInfo(SemaRef.Context, NewType); - return buildDeductionGuide(TemplateParams, CD, CD->getExplicitSpecifier(), - NewTInfo, CD->getBeginLoc(), CD->getLocation(), - CD->getEndLoc(), MaterializedTypedefs); + return buildDeductionGuide( + SemaRef, Template, TemplateParams, CD, CD->getExplicitSpecifier(), + NewTInfo, CD->getBeginLoc(), CD->getLocation(), CD->getEndLoc(), + /*IsImplicit=*/true, MaterializedTypedefs); } /// Build a deduction guide with the specified parameter types. @@ -2448,8 +2534,9 @@ struct ConvertConstructorToDeductionGuideTransform { Params.push_back(NewParam); } - return buildDeductionGuide(GetTemplateParameterList(Template), nullptr, - ExplicitSpecifier(), TSI, Loc, Loc, Loc); + return buildDeductionGuide( + SemaRef, Template, GetTemplateParameterList(Template), nullptr, + ExplicitSpecifier(), TSI, Loc, Loc, Loc, /*IsImplicit=*/true); } private: @@ -2458,50 +2545,18 @@ struct ConvertConstructorToDeductionGuideTransform { /// renumbering as we go. NamedDecl *transformTemplateParameter(NamedDecl *TemplateParam, MultiLevelTemplateArgumentList &Args) { - if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(TemplateParam)) { - // TemplateTypeParmDecl's index cannot be changed after creation, so - // substitute it directly. - auto *NewTTP = TemplateTypeParmDecl::Create( - SemaRef.Context, DC, TTP->getBeginLoc(), TTP->getLocation(), - TTP->getDepth() - 1, Depth1IndexAdjustment + TTP->getIndex(), - TTP->getIdentifier(), TTP->wasDeclaredWithTypename(), - TTP->isParameterPack(), TTP->hasTypeConstraint(), - TTP->isExpandedParameterPack() - ? std::optional<unsigned>(TTP->getNumExpansionParameters()) - : std::nullopt); - if (const auto *TC = TTP->getTypeConstraint()) - SemaRef.SubstTypeConstraint(NewTTP, TC, Args, - /*EvaluateConstraint*/ true); - if (TTP->hasDefaultArgument()) { - TypeSourceInfo *InstantiatedDefaultArg = - SemaRef.SubstType(TTP->getDefaultArgumentInfo(), Args, - TTP->getDefaultArgumentLoc(), TTP->getDeclName()); - if (InstantiatedDefaultArg) - NewTTP->setDefaultArgument(InstantiatedDefaultArg); - } - SemaRef.CurrentInstantiationScope->InstantiatedLocal(TemplateParam, - NewTTP); - return NewTTP; - } - + if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(TemplateParam)) + return transformTemplateTypeParam( + SemaRef, DC, TTP, Args, TTP->getDepth() - 1, + Depth1IndexAdjustment + TTP->getIndex()); if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(TemplateParam)) - return transformTemplateParameterImpl(TTP, Args); - - return transformTemplateParameterImpl( - cast<NonTypeTemplateParmDecl>(TemplateParam), Args); - } - template<typename TemplateParmDecl> - TemplateParmDecl * - transformTemplateParameterImpl(TemplateParmDecl *OldParam, - MultiLevelTemplateArgumentList &Args) { - // Ask the template instantiator to do the heavy lifting for us, then adjust - // the index of the parameter once it's done. - auto *NewParam = - cast<TemplateParmDecl>(SemaRef.SubstDecl(OldParam, DC, Args)); - assert(NewParam->getDepth() == OldParam->getDepth() - 1 && - "unexpected template param depth"); - NewParam->setPosition(NewParam->getPosition() + Depth1IndexAdjustment); - return NewParam; + return transformTemplateParam(SemaRef, DC, TTP, Args, + Depth1IndexAdjustment + TTP->getIndex(), + TTP->getDepth() - 1); + auto *NTTP = cast<NonTypeTemplateParmDecl>(TemplateParam); + return transformTemplateParam(SemaRef, DC, NTTP, Args, + Depth1IndexAdjustment + NTTP->getIndex(), + NTTP->getDepth() - 1); } QualType transformFunctionProtoType( @@ -2596,7 +2651,7 @@ struct ConvertConstructorToDeductionGuideTransform { // placeholder to indicate there is a default argument. QualType ParamTy = NewDI->getType(); NewDefArg = new (SemaRef.Context) - OpaqueValueExpr(OldParam->getDefaultArgRange().getBegin(), + OpaqueValueExpr(OldParam->getDefaultArg()->getBeginLoc(), ParamTy.getNonLValueExprType(SemaRef.Context), ParamTy->isLValueReferenceType() ? VK_LValue : ParamTy->isRValueReferenceType() ? VK_XValue @@ -2616,44 +2671,310 @@ struct ConvertConstructorToDeductionGuideTransform { SemaRef.CurrentInstantiationScope->InstantiatedLocal(OldParam, NewParam); return NewParam; } +}; + +// Find all template parameters that appear in the given DeducedArgs. +// Return the indices of the template parameters in the TemplateParams. +SmallVector<unsigned> TemplateParamsReferencedInTemplateArgumentList( + ArrayRef<NamedDecl *> TemplateParams, + ArrayRef<TemplateArgument> DeducedArgs) { + struct TemplateParamsReferencedFinder + : public RecursiveASTVisitor<TemplateParamsReferencedFinder> { + llvm::DenseSet<NamedDecl *> TemplateParams; + llvm::DenseSet<const NamedDecl *> ReferencedTemplateParams; + + TemplateParamsReferencedFinder(ArrayRef<NamedDecl *> TemplateParams) + : TemplateParams(TemplateParams.begin(), TemplateParams.end()) {} + + bool VisitTemplateTypeParmType(TemplateTypeParmType *TTP) { + TTP->getIndex(); + MarkAppeared(TTP->getDecl()); + return true; + } + bool VisitDeclRefExpr(DeclRefExpr *DRE) { + MarkAppeared(DRE->getFoundDecl()); + return true; + } + + void MarkAppeared(NamedDecl *ND) { + if (TemplateParams.contains(ND)) + ReferencedTemplateParams.insert(ND); + } + }; + TemplateParamsReferencedFinder Finder(TemplateParams); + Finder.TraverseTemplateArguments(DeducedArgs); - FunctionTemplateDecl *buildDeductionGuide( - TemplateParameterList *TemplateParams, CXXConstructorDecl *Ctor, - ExplicitSpecifier ES, TypeSourceInfo *TInfo, SourceLocation LocStart, - SourceLocation Loc, SourceLocation LocEnd, - llvm::ArrayRef<TypedefNameDecl *> MaterializedTypedefs = {}) { - DeclarationNameInfo Name(DeductionGuideName, Loc); - ArrayRef<ParmVarDecl *> Params = - TInfo->getTypeLoc().castAs<FunctionProtoTypeLoc>().getParams(); + SmallVector<unsigned> Results; + for (unsigned Index = 0; Index < TemplateParams.size(); ++Index) { + if (Finder.ReferencedTemplateParams.contains(TemplateParams[Index])) + Results.push_back(Index); + } + return Results; +} - // Build the implicit deduction guide template. - auto *Guide = - CXXDeductionGuideDecl::Create(SemaRef.Context, DC, LocStart, ES, Name, - TInfo->getType(), TInfo, LocEnd, Ctor); - Guide->setImplicit(); - Guide->setParams(Params); +bool hasDeclaredDeductionGuides(DeclarationName Name, DeclContext *DC) { + // Check whether we've already declared deduction guides for this template. + // FIXME: Consider storing a flag on the template to indicate this. + assert(Name.getNameKind() == + DeclarationName::NameKind::CXXDeductionGuideName && + "name must be a deduction guide name"); + auto Existing = DC->lookup(Name); + for (auto *D : Existing) + if (D->isImplicit()) + return true; + return false; +} - for (auto *Param : Params) - Param->setDeclContext(Guide); - for (auto *TD : MaterializedTypedefs) - TD->setDeclContext(Guide); +// Build deduction guides for a type alias template. +void DeclareImplicitDeductionGuidesForTypeAlias( + Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate, SourceLocation Loc) { + auto &Context = SemaRef.Context; + // FIXME: if there is an explicit deduction guide after the first use of the + // type alias usage, we will not cover this explicit deduction guide. fix this + // case. + if (hasDeclaredDeductionGuides( + Context.DeclarationNames.getCXXDeductionGuideName(AliasTemplate), + AliasTemplate->getDeclContext())) + return; + // Unwrap the sugared ElaboratedType. + auto RhsType = AliasTemplate->getTemplatedDecl() + ->getUnderlyingType() + .getSingleStepDesugaredType(Context); + TemplateDecl *Template = nullptr; + llvm::ArrayRef<TemplateArgument> AliasRhsTemplateArgs; + if (const auto *TST = RhsType->getAs<TemplateSpecializationType>()) { + // Cases where the RHS of the alias is dependent. e.g. + // template<typename T> + // using AliasFoo1 = Foo<T>; // a class/type alias template specialization + Template = TST->getTemplateName().getAsTemplateDecl(); + AliasRhsTemplateArgs = TST->template_arguments(); + } else if (const auto *RT = RhsType->getAs<RecordType>()) { + // Cases where template arguments in the RHS of the alias are not + // dependent. e.g. + // using AliasFoo = Foo<bool>; + if (const auto *CTSD = llvm::dyn_cast<ClassTemplateSpecializationDecl>( + RT->getAsCXXRecordDecl())) { + Template = CTSD->getSpecializedTemplate(); + AliasRhsTemplateArgs = CTSD->getTemplateArgs().asArray(); + } + } else { + assert(false && "unhandled RHS type of the alias"); + } + if (!Template) + return; + DeclarationNameInfo NameInfo( + Context.DeclarationNames.getCXXDeductionGuideName(Template), Loc); + LookupResult Guides(SemaRef, NameInfo, clang::Sema::LookupOrdinaryName); + SemaRef.LookupQualifiedName(Guides, Template->getDeclContext()); + Guides.suppressDiagnostics(); + + for (auto *G : Guides) { + FunctionTemplateDecl *F = dyn_cast<FunctionTemplateDecl>(G); + if (!F) + continue; + auto RType = F->getTemplatedDecl()->getReturnType(); + // The (trailing) return type of the deduction guide. + const TemplateSpecializationType *FReturnType = + RType->getAs<TemplateSpecializationType>(); + if (const auto *InjectedCNT = RType->getAs<InjectedClassNameType>()) + // implicitly-generated deduction guide. + FReturnType = InjectedCNT->getInjectedTST(); + else if (const auto *ET = RType->getAs<ElaboratedType>()) + // explicit deduction guide. + FReturnType = ET->getNamedType()->getAs<TemplateSpecializationType>(); + 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<typename X, typename Y> + // f(X, Y) -> f<Y, X>; + // + // template<typename U> + // using alias = f<int, U>; + // + // The RHS of alias is f<int, U>, 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<DeducedTemplateArgument> 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<TemplateArgument> DeducedArgs; + SmallVector<unsigned> 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<TemplateArgument> TemplateArgsForBuildingFPrime( + F->getTemplateParameters()->size()); + + Sema::InstantiatingTemplate BuildingDeductionGuides( + SemaRef, AliasTemplate->getLocation(), F, + Sema::InstantiatingTemplate::BuildingDeductionGuidesTag{}); + if (BuildingDeductionGuides.isInvalid()) + return; + LocalInstantiationScope Scope(SemaRef); - auto *GuideTemplate = FunctionTemplateDecl::Create( - SemaRef.Context, DC, Loc, DeductionGuideName, TemplateParams, Guide); - GuideTemplate->setImplicit(); - Guide->setDescribedFunctionTemplate(GuideTemplate); + // 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<NamedDecl *> FPrimeTemplateParams; + // Store template arguments that refer to the newly-created template + // parameters, used for building `TemplateArgsForBuildingFPrime`. + SmallVector<TemplateArgument, 16> TransformedDeducedAliasArgs( + AliasTemplate->getTemplateParameters()->size()); + auto TransformTemplateParameter = + [&SemaRef](DeclContext *DC, NamedDecl *TemplateParam, + MultiLevelTemplateArgumentList &Args, + unsigned NewIndex) -> NamedDecl * { + if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(TemplateParam)) + return transformTemplateTypeParam(SemaRef, DC, TTP, Args, + TTP->getDepth(), NewIndex); + if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(TemplateParam)) + return transformTemplateParam(SemaRef, DC, TTP, Args, NewIndex, + TTP->getDepth()); + if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(TemplateParam)) + return transformTemplateParam(SemaRef, DC, NTTP, Args, NewIndex, + NTTP->getDepth()); + return nullptr; + }; - if (isa<CXXRecordDecl>(DC)) { - Guide->setAccess(AS_public); - GuideTemplate->setAccess(AS_public); + 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(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( + 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)); + } + // FIXME: implement the associated constraint per C++ + // [over.match.class.deduct]p3.3: + // The associated constraints ([temp.constr.decl]) are the + // conjunction of the associated constraints of g 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=*/nullptr); + + // 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()); + } } - DC->addDecl(GuideTemplate); - return GuideTemplate; + 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 = dyn_cast<CXXDeductionGuideDecl>(FPrime); + buildDeductionGuide(SemaRef, AliasTemplate, FPrimeTemplateParamList, + GG->getCorrespondingConstructor(), + GG->getExplicitSpecifier(), GG->getTypeSourceInfo(), + AliasTemplate->getBeginLoc(), + AliasTemplate->getLocation(), + AliasTemplate->getEndLoc(), F->isImplicit()); + } } -}; } +} // namespace + FunctionTemplateDecl *Sema::DeclareImplicitDeductionGuideFromInitList( TemplateDecl *Template, MutableArrayRef<QualType> ParamTypes, SourceLocation Loc) { @@ -2697,6 +3018,10 @@ FunctionTemplateDecl *Sema::DeclareImplicitDeductionGuideFromInitList( void Sema::DeclareImplicitDeductionGuides(TemplateDecl *Template, SourceLocation Loc) { + if (auto *AliasTemplate = llvm::dyn_cast<TypeAliasTemplateDecl>(Template)) { + DeclareImplicitDeductionGuidesForTypeAlias(*this, AliasTemplate, Loc); + return; + } if (CXXRecordDecl *DefRecord = cast<CXXRecordDecl>(Template->getTemplatedDecl())->getDefinition()) { if (TemplateDecl *DescribedTemplate = DefRecord->getDescribedClassTemplate()) @@ -2712,12 +3037,8 @@ void Sema::DeclareImplicitDeductionGuides(TemplateDecl *Template, if (!isCompleteType(Loc, Transform.DeducedType)) return; - // Check whether we've already declared deduction guides for this template. - // FIXME: Consider storing a flag on the template to indicate this. - auto Existing = DC->lookup(Transform.DeductionGuideName); - for (auto *D : Existing) - if (D->isImplicit()) - return; + if (hasDeclaredDeductionGuides(Transform.DeductionGuideName, DC)) + return; // In case we were expanding a pack when we attempted to declare deduction // guides, turn off pack expansion for everything we're about to do. @@ -5368,6 +5689,15 @@ bool Sema::CheckTemplateTypeArgument( [[fallthrough]]; } default: { + // We allow instantiateing a template with template argument packs when + // building deduction guides. + if (Arg.getKind() == TemplateArgument::Pack && + CodeSynthesisContexts.back().Kind == + Sema::CodeSynthesisContext::BuildingDeductionGuides) { + SugaredConverted.push_back(Arg); + CanonicalConverted.push_back(Arg); + return false; + } // We have a template type parameter but the template argument // is not a type. SourceRange SR = AL.getSourceRange(); diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 65f7fa15b20dd7..97f8445bf819c8 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2531,6 +2531,15 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, return TemplateDeductionResult::Success; } +TemplateDeductionResult Sema::DeduceTemplateArguments( + TemplateParameterList *TemplateParams, ArrayRef<TemplateArgument> Ps, + ArrayRef<TemplateArgument> As, sema::TemplateDeductionInfo &Info, + SmallVectorImpl<DeducedTemplateArgument> &Deduced, + bool NumberOfArgumentsMustMatch) { + return ::DeduceTemplateArguments(*this, TemplateParams, Ps, As, Info, Deduced, + NumberOfArgumentsMustMatch); +} + /// Determine whether two template arguments are the same. static bool isSameTemplateArg(ASTContext &Context, TemplateArgument X, diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 371378485626c2..d9994d7fd37adb 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -21,6 +21,7 @@ #include "clang/AST/ExprConcepts.h" #include "clang/AST/PrettyDeclStackTrace.h" #include "clang/AST/Type.h" +#include "clang/AST/TypeLoc.h" #include "clang/AST/TypeVisitor.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/Stack.h" @@ -547,9 +548,9 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( : InstantiatingTemplate(SemaRef, Kind, PointOfInstantiation, InstantiationRange, FunctionTemplate, nullptr, TemplateArgs, &DeductionInfo) { - assert( - Kind == CodeSynthesisContext::ExplicitTemplateArgumentSubstitution || - Kind == CodeSynthesisContext::DeducedTemplateArgumentSubstitution); + assert(Kind == CodeSynthesisContext::ExplicitTemplateArgumentSubstitution || + Kind == CodeSynthesisContext::DeducedTemplateArgumentSubstitution || + Kind == CodeSynthesisContext::BuildingDeductionGuides); } Sema::InstantiatingTemplate::InstantiatingTemplate( @@ -1446,6 +1447,59 @@ namespace { return inherited::TransformFunctionProtoType(TLB, TL); } + QualType TransformInjectedClassNameType(TypeLocBuilder &TLB, + InjectedClassNameTypeLoc TL) { + auto Type = inherited::TransformInjectedClassNameType(TLB, TL); + // Special case for transforming a deduction guide, we return a + // transformed TemplateSpecializationType. + if (Type.isNull() && + SemaRef.CodeSynthesisContexts.back().Kind == + Sema::CodeSynthesisContext::BuildingDeductionGuides) { + // Return a TemplateSpecializationType for transforming a deduction + // guide. + if (auto *ICT = TL.getType()->getAs<InjectedClassNameType>()) { + auto Type = + inherited::TransformType(ICT->getInjectedSpecializationType()); + TLB.pushTrivial(SemaRef.Context, Type, TL.getNameLoc()); + return Type; + } + } + return Type; + } + // Override the default version to handle a rewrite-template-arg-pack case + // for building a deduction guide. + bool TransformTemplateArgument(const TemplateArgumentLoc &Input, + TemplateArgumentLoc &Output, + bool Uneval = false) { + const TemplateArgument &Arg = Input.getArgument(); + std::vector<TemplateArgument> TArgs; + switch (Arg.getKind()) { + case TemplateArgument::Pack: + // Literally rewrite the template argument pack, instead of unpacking + // it. + assert( + SemaRef.CodeSynthesisContexts.back().Kind == + Sema::CodeSynthesisContext::BuildingDeductionGuides && + "Transforming a template argument pack is only allowed in building " + "deduction guide"); + for (auto &pack : Arg.getPackAsArray()) { + TemplateArgumentLoc Input = SemaRef.getTrivialTemplateArgumentLoc( + pack, QualType(), SourceLocation{}); + TemplateArgumentLoc Output; + if (SemaRef.SubstTemplateArgument(Input, TemplateArgs, Output)) + return true; // fails + TArgs.push_back(Output.getArgument()); + } + Output = SemaRef.getTrivialTemplateArgumentLoc( + TemplateArgument(llvm::ArrayRef(TArgs).copy(SemaRef.Context)), + QualType(), SourceLocation{}); + return false; + default: + break; + } + return inherited::TransformTemplateArgument(Input, Output, Uneval); + } + template<typename Fn> QualType TransformFunctionProtoType(TypeLocBuilder &TLB, FunctionProtoTypeLoc TL, @@ -4138,6 +4192,15 @@ Sema::SubstStmt(Stmt *S, const MultiLevelTemplateArgumentList &TemplateArgs) { return Instantiator.TransformStmt(S); } +bool Sema::SubstTemplateArgument( + const TemplateArgumentLoc &Input, + const MultiLevelTemplateArgumentList &TemplateArgs, + TemplateArgumentLoc &Output) { + TemplateInstantiator Instantiator(*this, TemplateArgs, SourceLocation(), + DeclarationName()); + return Instantiator.TransformTemplateArgument(Input, Output); +} + bool Sema::SubstTemplateArguments( ArrayRef<TemplateArgumentLoc> Args, const MultiLevelTemplateArgumentList &TemplateArgs, diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 9c696e072ba4a7..20c2c93ac9c7b4 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2219,7 +2219,9 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl( FunctionTemplate->setInstantiatedFromMemberTemplate( D->getDescribedFunctionTemplate()); } - } else if (FunctionTemplate) { + } else if (FunctionTemplate && + SemaRef.CodeSynthesisContexts.back().Kind != + Sema::CodeSynthesisContext::BuildingDeductionGuides) { // Record this function template specialization. ArrayRef<TemplateArgument> Innermost = TemplateArgs.getInnermost(); Function->setFunctionTemplateSpecialization(FunctionTemplate, @@ -4853,16 +4855,13 @@ bool TemplateDeclInstantiator::SubstDefaultedFunction(FunctionDecl *New, /// /// Usually this should not be used, and template argument deduction should be /// used in its place. -FunctionDecl * -Sema::InstantiateFunctionDeclaration(FunctionTemplateDecl *FTD, - const TemplateArgumentList *Args, - SourceLocation Loc) { +FunctionDecl *Sema::InstantiateFunctionDeclaration( + FunctionTemplateDecl *FTD, const TemplateArgumentList *Args, + SourceLocation Loc, CodeSynthesisContext::SynthesisKind CSC) { FunctionDecl *FD = FTD->getTemplatedDecl(); sema::TemplateDeductionInfo Info(Loc); - InstantiatingTemplate Inst( - *this, Loc, FTD, Args->asArray(), - CodeSynthesisContext::ExplicitTemplateArgumentSubstitution, Info); + InstantiatingTemplate Inst(*this, Loc, FTD, Args->asArray(), CSC, Info); if (Inst.isInvalid()) return nullptr; @@ -6286,8 +6285,18 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D, QualType T = CheckTemplateIdType(TemplateName(TD), Loc, Args); if (T.isNull()) return nullptr; - auto *SubstRecord = T->getAsCXXRecordDecl(); - assert(SubstRecord && "class template id not a class type?"); + CXXRecordDecl *SubstRecord = T->getAsCXXRecordDecl(); + + if (!SubstRecord) { + // T can be a dependent TemplateSpecializationType when performing a + // substitution for building a deduction guide. + assert(CodeSynthesisContexts.back().Kind == + CodeSynthesisContext::BuildingDeductionGuides); + // Return a nullptr as a sentinel value, we handle it properly in + // the TemplateInstantiator::TransformInjectedClassNameType + // override, which we transform it to a TemplateSpecializationType. + return nullptr; + } // Check that this template-id names the primary template and not a // partial or explicit specialization. (In the latter cases, it's // meaningless to attempt to find an instantiation of D within the diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 409aee73d960eb..2d22692f3ab750 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -4785,6 +4785,14 @@ bool TreeTransform<Derived>::TransformTemplateArguments( TemplateArgumentLoc In = *First; if (In.getArgument().getKind() == TemplateArgument::Pack) { + // When building the deduction guides, we rewrite the argument packs + // instead of unpacking. + if (getSema().CodeSynthesisContexts.back().Kind == + Sema::CodeSynthesisContext::BuildingDeductionGuides) { + if (getDerived().TransformTemplateArgument(In, Out, Uneval)) + return true; + continue; + } // Unpack argument packs, which we translate them into separate // arguments. // FIXME: We could do much better if we could guarantee that the diff --git a/clang/test/SemaCXX/cxx17-compat.cpp b/clang/test/SemaCXX/cxx17-compat.cpp index d53d80f0d42ca9..54ea3384022d42 100644 --- a/clang/test/SemaCXX/cxx17-compat.cpp +++ b/clang/test/SemaCXX/cxx17-compat.cpp @@ -131,3 +131,14 @@ namespace NTTP { // expected-warning@-4 {{non-type template parameter of type 'A' is incompatible with C++ standards before C++20}} #endif } + +namespace CTADForAliasTemplate { +template<typename T> struct A { A(T); }; +template<typename T> using B = A<T>; +B b = {1}; +#if __cplusplus <= 201703L + // FIXME: diagnose as well +#else + // expected-warning@-4 {{class template argument deduction for alias templates is incompatible with C++ standards before C++20}} +#endif +} diff --git a/clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp b/clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp index 33ed4295c2e48c..2f067ea53a5029 100644 --- a/clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp +++ b/clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp @@ -101,13 +101,13 @@ namespace dependent { struct B { template<typename T> struct X { X(T); }; X(int) -> X<int>; - template<typename T> using Y = X<T>; // expected-note {{template}} + template<typename T> using Y = X<T>; }; template<typename T> void f() { typename T::X tx = 0; - typename T::Y ty = 0; // expected-error {{alias template 'Y' requires template arguments; argument deduction only allowed for class templates}} + typename T::Y ty = 0; } - template void f<B>(); // expected-note {{in instantiation of}} + template void f<B>(); template<typename T> struct C { C(T); }; template<typename T> C(T) -> C<T>; diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp new file mode 100644 index 00000000000000..794496ed418489 --- /dev/null +++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp @@ -0,0 +1,232 @@ +// RUN: %clang_cc1 -fsyntax-only -Wno-c++11-narrowing -Wno-literal-conversion -std=c++20 -verify %s + +namespace test1 { +template <typename T> +struct Foo { T t; }; +template <typename U> +using Bar = Foo<U>; + +Bar s = {1}; +} // namespace test1 + +namespace test2 { +template <typename X, typename Y> +struct XYpair { + X x; + Y y; +}; +// A tricky explicit deduction guide that swapping X and Y. +template <typename X, typename Y> +XYpair(X, Y) -> XYpair<Y, X>; +template <typename U, typename V> +using AliasXYpair = XYpair<U, V>; + +AliasXYpair xy = {1.1, 2}; // XYpair<int, double> +static_assert(__is_same(decltype(xy.x), int)); +static_assert(__is_same(decltype(xy.y), double)); +} // namespace test2 + +namespace test3 { +template <typename T, class> +struct container { + // test with default arguments. + container(T a, T b = T()); +}; + +template <class T> +using vector = container<T, int>; +vector v(0, 0); +} // namespace test3 + +namespace test4 { +// Explicit deduction guide. +template <class T> +struct X { + T t; + X(T); +}; + +template <class T> +X(T) -> X<double>; + +template <class T> +using AX = X<T>; + +AX s = {1}; +static_assert(__is_same(decltype(s.t), double)); // explicit one is picked. +} // namespace test4 + +namespace test5 { +template <int B> +struct Foo {}; +// Template parameter pack +template <int... C> +using AF = Foo<1>; +auto a = AF{}; +} // namespace test5 + +namespace test6 { +// non-type template argument. +template <typename T, bool B = false> +struct Foo { + Foo(T); +}; +template <typename T> +using AF = Foo<T, 1>; + +AF b{0}; +} // namespace test6 + +namespace test7 { +template <typename T> +struct Foo { + Foo(T); +}; +// using alias chain. +template <typename U> +using AF1 = Foo<U>; +template <typename K> +using AF2 = AF1<K>; +AF2 b = 1; +} // namespace test7 + +namespace test8 { +template <typename T, int N> +struct Foo { + Foo(T const (&)[N]); +}; + +template <typename X, int Y> +using Bar = Foo<X, Y>; + +Bar s = {{1}}; +} // namespace test8 + +namespace test9 { +template <typename T, int N> +struct Foo { + Foo(T const (&)[N]); +}; + +template <typename X, int Y> +using Bar = Foo<X, sizeof(X)>; + +// FIXME: we should reject this case? GCC rejects it, MSVC accepts it. +Bar s = {{1}}; +} // namespace test9 + +namespace test10 { +template <typename T> +struct Foo { + template <typename U> + Foo(U); +}; + +template <typename U> +Foo(U) -> Foo<U*>; + +template <typename K> +using A = Foo<K>; +A a(2); // Foo<int*> +} // namespace test10 + +namespace test11 { +struct A {}; +template<class T> struct Foo { T c; }; +template<class X, class Y=A> using AFoo = Foo<Y>; + +AFoo s = {1}; +} // namespace test11 + +namespace test12 { +// no crash on null access attribute +template<typename X> +struct Foo { + template<typename K> + struct Bar { + Bar(K); + }; + + template<typename U> + using ABar = Bar<U>; + void test() { ABar k = 2; } +}; + +void func(Foo<int> s) { + s.test(); +} +} // namespace test12 + +namespace test13 { +template <typename... Ts> +struct Foo { + Foo(Ts...); +}; + +template <typename... Ts> +using AFoo = Foo<Ts...>; + +auto b = AFoo{}; +} // namespace test13 + +namespace test14 { +template<typename T> +concept IsInt = __is_same(decltype(T()), int); + +template<IsInt T, int N> +struct Foo { + Foo(T const (&)[N]); +}; + +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}} +double abc[3]; +Bar s2 = {abc}; // expected-error {{no viable constructor or deduction guide for deduction }} +} // namespace test14 + +namespace test15 { +template <class T> struct Foo { Foo(T); }; + +template<class V> using AFoo = Foo<V *>; +template<typename> concept False = false; +template<False W> using BFoo = AFoo<W>; +int i = 0; +AFoo a1(&i); // OK, deduce Foo<int *> + +// FIXME: we should reject this case as the W is not deduced from the deduced +// type Foo<int *>. +BFoo b2(&i); +} // namespace test15 + +namespace test16 { +struct X { X(int); X(const X&); }; +template<class T> +struct Foo { + T t; + Foo(T t) : t(t) {} +}; +template<class T> +using AFoo = Foo<T>; +int i = 0; +AFoo s{i}; +static_assert(__is_same(decltype(s.t), int)); + +// explicit deduction guide. +Foo(int) -> Foo<X>; +AFoo s2{i}; +// FIXME: the type should be X because of the above explicit deduction guide. +static_assert(__is_same(decltype(s2.t), int)); +} // namespace test16 + +namespace test17 { +template <typename T> +struct Foo { T t; }; + +// CTAD for alias templates only works for the RHS of the alias of form of +// [typename] [nested-name-specifier] [template] simple-template-id +template <typename U> +using AFoo = Foo<U>*; // expected-note {{template is declared here}} + +AFoo s = {1}; // expected-error {{alias template 'AFoo' requires template arguments; argument deduction only allowed for}} +} // namespace test17 diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index fa00e7685610a6..a3090adb5d47ba 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -836,7 +836,13 @@ <h2 id="cxx20">C++20 implementation status</h2> <tr> <td>Class template argument deduction for alias templates</td> <td><a href="https://wg21.link/p1814r0">P1814R0</a></td> - <td class="none" align="center">No</td> + <td class="partial" align="center"> + <details> + <summary>Clang 19 (Partial)</summary> + The associated constraints (over.match.class.deduct#3.3) for the + synthesized deduction guides are not yet implemented. + </details> + </td> </tr> <tr> <td>Permit conversions to arrays of unknown bound</td> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits