https://github.com/hokein updated https://github.com/llvm/llvm-project/pull/77890
>From 89ae5aaf3bd6b4908191902b999ef9f6782d645f Mon Sep 17 00:00:00 2001 From: Haojian Wu <hokein...@gmail.com> Date: Wed, 26 Jul 2023 09:40:12 +0200 Subject: [PATCH 1/6] [clang] WIP: Implement CTAD for type alias template. This is a preliminary, WIP and messy implementation. While it is still missing many pieces (see FIXMEs), it works for happy cases. --- clang/include/clang/Sema/Sema.h | 16 +- clang/lib/Sema/SemaInit.cpp | 513 +++++++++++++++++- clang/lib/Sema/SemaTemplateDeduction.cpp | 9 + clang/lib/Sema/SemaTemplateInstantiate.cpp | 26 +- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 17 +- ...xx1z-class-template-argument-deduction.cpp | 6 +- clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 32 ++ 7 files changed, 603 insertions(+), 16 deletions(-) create mode 100644 clang/test/SemaCXX/cxx20-ctad-type-alias.cpp diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 0db39333b0ee347..d4348f03658200b 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9280,6 +9280,14 @@ class Sema final { const TemplateArgumentList &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, @@ -10432,9 +10440,11 @@ class Sema final { SourceLocation PointOfInstantiation, FunctionDecl *Decl, ArrayRef<TemplateArgument> TemplateArgs, ConstraintSatisfaction &Satisfaction); - 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 18440a69e3a3d92..208fcd20b428f70 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -10,13 +10,17 @@ // //===----------------------------------------------------------------------===// +#include "TreeTransform.h" +#include "TypeLocBuilder.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/DeclAccessPair.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" -#include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprOpenMP.h" #include "clang/AST/IgnoreExpr.h" +#include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/SourceManager.h" @@ -28,6 +32,7 @@ #include "clang/Sema/Lookup.h" #include "clang/Sema/Ownership.h" #include "clang/Sema/SemaInternal.h" +#include "clang/Sema/Template.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/PointerIntPair.h" @@ -10583,6 +10588,381 @@ static bool isOrIsDerivedFromSpecializationOf(CXXRecordDecl *RD, return !(NotSpecialization(RD) && RD->forallBases(NotSpecialization)); } + +/// FIXME: this is a copy-paste from SemaTemplate.cpp +/// Tree transform to "extract" a transformed type from a class template's +/// constructor to a deduction guide. +class ExtractTypeForDeductionGuide + : public TreeTransform<ExtractTypeForDeductionGuide> { + llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs; + +public: + typedef TreeTransform<ExtractTypeForDeductionGuide> Base; + ExtractTypeForDeductionGuide( + Sema &SemaRef, + llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs) + : Base(SemaRef), MaterializedTypedefs(MaterializedTypedefs) {} + + TypeSourceInfo *transform(TypeSourceInfo *TSI) { return TransformType(TSI); } + + QualType TransformTypedefType(TypeLocBuilder &TLB, TypedefTypeLoc TL) { + ASTContext &Context = SemaRef.getASTContext(); + TypedefNameDecl *OrigDecl = TL.getTypedefNameDecl(); + TypedefNameDecl *Decl = OrigDecl; + // Transform the underlying type of the typedef and clone the Decl only if + // the typedef has a dependent context. + if (OrigDecl->getDeclContext()->isDependentContext()) { + TypeLocBuilder InnerTLB; + QualType Transformed = + TransformType(InnerTLB, OrigDecl->getTypeSourceInfo()->getTypeLoc()); + TypeSourceInfo *TSI = InnerTLB.getTypeSourceInfo(Context, Transformed); + if (isa<TypeAliasDecl>(OrigDecl)) + Decl = TypeAliasDecl::Create( + Context, Context.getTranslationUnitDecl(), OrigDecl->getBeginLoc(), + OrigDecl->getLocation(), OrigDecl->getIdentifier(), TSI); + else { + assert(isa<TypedefDecl>(OrigDecl) && "Not a Type alias or typedef"); + Decl = TypedefDecl::Create( + Context, Context.getTranslationUnitDecl(), OrigDecl->getBeginLoc(), + OrigDecl->getLocation(), OrigDecl->getIdentifier(), TSI); + } + MaterializedTypedefs.push_back(Decl); + } + + QualType TDTy = Context.getTypedefType(Decl); + TypedefTypeLoc TypedefTL = TLB.push<TypedefTypeLoc>(TDTy); + TypedefTL.setNameLoc(TL.getNameLoc()); + + return TDTy; + } +}; + +// Transform to form a corresponding deduction guide for type alias template decl. +// +// This class implements the C++ [over.match.class.deduct]p3: +// ... Let g denote the result of substituting these deductions into f. If +// substitution succeeds, form a function or function template f' with the +// following properties and add it to the set of guides of A... +// +// FIXME: This is a messy copy of ConvertConstructorToDeductionGuideTransform +// in SemaTemplate.cpp with some adjustments. +struct AliasTemplateDeductionGuideTransform { + AliasTemplateDeductionGuideTransform(Sema &S, TypeAliasTemplateDecl *Alias) + : SemaRef(S), AliasTemplate(Alias) { + DC = AliasTemplate->getDeclContext(); + } + Sema &SemaRef; + TypeAliasTemplateDecl *AliasTemplate = nullptr; + + DeclContext *DC = nullptr; + + // FIXME: is this really needed? + ClassTemplateDecl *NestedPattern = nullptr; + // Instantiation arguments for the outermost depth-1 templates + // when the template is nested + MultiLevelTemplateArgumentList OuterInstantiationArgs; + + // Returns the result of substituting the deduced template arguments into f. + NamedDecl *transformUnderlyingFunctionTemplate( + CXXDeductionGuideDecl *UnderlyingCDGD, + SmallVector<TemplateArgument> DeducedArgs) { + SmallVector<TemplateTypeParmDecl *> DeducedTemplateTypeParDecls; + // Add all parameters that appear in the deductions. + // FIXME: add template parameters of f that were not deduced. + for (auto D : DeducedArgs) { + // FIXME: fix other template argument kind. + if (D.getKind() != TemplateArgument::Type) + continue; + if (auto *TT = + dyn_cast<TemplateTypeParmType>(D.getAsType().getTypePtr())) { + if (TemplateTypeParmDecl *DD = TT->getDecl()) { + DeducedTemplateTypeParDecls.push_back(DD); + } + } + } + + // 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, followed by the + // template parameters of f that were not deduced (including their default + // template arguments)... + LocalInstantiationScope Scope(SemaRef); + SmallVector<TemplateArgument, 16> Depth1Args; + SmallVector<NamedDecl *, 16> AllParams; + SmallVector<TemplateArgument, 16> SubstArgs; + int Index = 0; + TemplateParameterList *TemplateParams = nullptr; + + for (TemplateTypeParmDecl* Param : DeducedTemplateTypeParDecls) { + MultiLevelTemplateArgumentList Args; + + Args.setKind(TemplateSubstitutionKind::Rewrite); + Args.addOuterTemplateArguments(Depth1Args); + Args.addOuterRetainedLevel(); + NamedDecl *NewParam = transformTemplateParameter(Param, Args, Index++); + if (!NewParam) { + llvm::errs() << "Faile to generate new param!\n"; + return nullptr; + } + auto NewArgumentForNewParam = + SemaRef.Context.getCanonicalTemplateArgument( + SemaRef.Context.getInjectedTemplateArg(NewParam)); + Depth1Args.push_back(NewArgumentForNewParam); + AllParams.push_back(NewParam); + SubstArgs.push_back(NewArgumentForNewParam); + } + // FIXME: substitute new template parameters into the requires-clause + + TemplateParams = TemplateParameterList::Create( + SemaRef.Context, SourceLocation(), SourceLocation(), AllParams, + SourceLocation(), /*requiresClause */nullptr); + + MultiLevelTemplateArgumentList Args; + Args.setKind(TemplateSubstitutionKind::Rewrite); + Args.addOuterTemplateArguments(SubstArgs); + Args.addOuterRetainedLevel(); + + FunctionProtoTypeLoc FPTL = UnderlyingCDGD->getTypeSourceInfo() + ->getTypeLoc() + .getAsAdjusted<FunctionProtoTypeLoc>(); + assert(FPTL && "no prototype for underlying deduction guides"); + + // Transform the type of the function, adjusting the return type and + // replacing references to the old parameters with references to the + // new ones. + TypeLocBuilder TLB; + SmallVector<ParmVarDecl*, 8> Params; + SmallVector<TypedefNameDecl *, 4> MaterializedTypedefs; + QualType NewType = transformFunctionProtoType( + TLB, FPTL, Params, Args, UnderlyingCDGD->getReturnType(), + MaterializedTypedefs); + if (NewType.isNull()) + return nullptr; + TypeSourceInfo *NewTInfo = TLB.getTypeSourceInfo(SemaRef.Context, NewType); + + return buildDeductionGuide( + TemplateParams, UnderlyingCDGD->getCorrespondingConstructor(), + UnderlyingCDGD->getExplicitSpecifier(), NewTInfo, + UnderlyingCDGD->getBeginLoc(), UnderlyingCDGD->getLocation(), + UnderlyingCDGD->getEndLoc(), UnderlyingCDGD->isImplicit(), + MaterializedTypedefs); + } + +private: + /// Transform a template parameter of underlying deduction guide into + /// a deduction guide template parameter, rebuilding any internal references + /// to earlier parameters and renumbering as we go. + NamedDecl *transformTemplateParameter(NamedDecl *TemplateParam, + MultiLevelTemplateArgumentList &Args, + int Index ) { + 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(), + // FIXME: is the depth/index right? + TTP->getDepth(), Index, + 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<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"); + // FIXME + // NewParam->setPosition(NewParam->getPosition() + Depth1IndexAdjustment); + return NewParam; + } + + + QualType transformFunctionProtoType( + TypeLocBuilder &TLB, FunctionProtoTypeLoc TL, + SmallVectorImpl<ParmVarDecl *> &Params, + MultiLevelTemplateArgumentList &Args, + QualType ReturnType, + SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs) { + SmallVector<QualType, 4> ParamTypes; + const FunctionProtoType *T = TL.getTypePtr(); + + // -- The types of the function parameters are those of the constructor. + for (auto *OldParam : TL.getParams()) { + ParmVarDecl *NewParam = + transformFunctionTypeParam(OldParam, Args, MaterializedTypedefs); + if (NestedPattern && NewParam) + NewParam = transformFunctionTypeParam(NewParam, OuterInstantiationArgs, + MaterializedTypedefs); + if (!NewParam) + return QualType(); + ParamTypes.push_back(NewParam->getType()); + Params.push_back(NewParam); + } + + // The return type of the deduction guide f is InjectedClassNameType, transform + // it to a TemplateSpecializationType. + if (const auto *ET = ReturnType->getAs<InjectedClassNameType>()) { + ReturnType = ET->getInjectedSpecializationType(); + } + auto DeductionGuideName = + SemaRef.Context.DeclarationNames.getCXXDeductionGuideName( + AliasTemplate); + ReturnType = SemaRef.SubstType(ReturnType, Args, SourceLocation(), + DeductionGuideName); + + // Resolving a wording defect, we also inherit the variadicness of the + // constructor. + FunctionProtoType::ExtProtoInfo EPI; + EPI.Variadic = T->isVariadic(); + EPI.HasTrailingReturn = true; + + QualType FunctionTy = SemaRef.BuildFunctionType( + ReturnType, ParamTypes, TL.getBeginLoc(), DeductionGuideName, EPI); + if (FunctionTy.isNull()) + return QualType(); + assert(FunctionTy->getTypeClass() == Type::FunctionProto); + // Pushes spaces for the new FunctionProtoTypeLoc. + TLB.pushTrivial(SemaRef.Context, + TypeLoc(FunctionTy, nullptr).getNextTypeLoc().getType(), + SourceLocation()); + FunctionProtoTypeLoc TargetTL = TLB.push<FunctionProtoTypeLoc>(FunctionTy); + TargetTL.setLocalRangeBegin(TL.getLocalRangeBegin()); + TargetTL.setLParenLoc(TL.getLParenLoc()); + TargetTL.setRParenLoc(TL.getRParenLoc()); + TargetTL.setExceptionSpecRange(SourceRange()); + TargetTL.setLocalRangeEnd(TL.getLocalRangeEnd()); + for (unsigned I = 0, E = TargetTL.getNumParams(); I != E; ++I) + TargetTL.setParam(I, Params[I]); + return FunctionTy; + } + + ParmVarDecl *transformFunctionTypeParam( + ParmVarDecl *OldParam, MultiLevelTemplateArgumentList &Args, + llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs) { + TypeSourceInfo *OldDI = OldParam->getTypeSourceInfo(); + TypeSourceInfo *NewDI; + if (auto PackTL = OldDI->getTypeLoc().getAs<PackExpansionTypeLoc>()) { + // Expand out the one and only element in each inner pack. + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, 0); + NewDI = + SemaRef.SubstType(PackTL.getPatternLoc(), Args, + OldParam->getLocation(), OldParam->getDeclName()); + if (!NewDI) + return nullptr; + NewDI = + SemaRef.CheckPackExpansion(NewDI, PackTL.getEllipsisLoc(), + PackTL.getTypePtr()->getNumExpansions()); + } else + NewDI = SemaRef.SubstType(OldDI, Args, OldParam->getLocation(), + OldParam->getDeclName()); + if (!NewDI) + return nullptr; + + // Extract the type. This (for instance) replaces references to typedef + // members of the current instantiations with the definitions of those + // typedefs, avoiding triggering instantiation of the deduced type during + // deduction. + NewDI = ExtractTypeForDeductionGuide(SemaRef, MaterializedTypedefs) + .transform(NewDI); + + // Resolving a wording defect, we also inherit default arguments from the + // constructor. + ExprResult NewDefArg; + if (OldParam->hasDefaultArg()) { + // We don't care what the value is (we won't use it); just create a + // placeholder to indicate there is a default argument. + QualType ParamTy = NewDI->getType(); + NewDefArg = new (SemaRef.Context) + OpaqueValueExpr(OldParam->getDefaultArg()->getBeginLoc(), + ParamTy.getNonLValueExprType(SemaRef.Context), + ParamTy->isLValueReferenceType() ? VK_LValue + : ParamTy->isRValueReferenceType() ? VK_XValue + : VK_PRValue); + } + + ParmVarDecl *NewParam = ParmVarDecl::Create(SemaRef.Context, DC, + OldParam->getInnerLocStart(), + OldParam->getLocation(), + OldParam->getIdentifier(), + NewDI->getType(), + NewDI, + OldParam->getStorageClass(), + NewDefArg.get()); + NewParam->setScopeInfo(OldParam->getFunctionScopeDepth(), + OldParam->getFunctionScopeIndex()); + SemaRef.CurrentInstantiationScope->InstantiatedLocal(OldParam, NewParam); + return NewParam; + } + + FunctionTemplateDecl *buildDeductionGuide( + TemplateParameterList *TemplateParams, CXXConstructorDecl *Ctor, + ExplicitSpecifier ES, TypeSourceInfo *TInfo, SourceLocation LocStart, + SourceLocation Loc, SourceLocation LocEnd, bool IsImplicit, + llvm::ArrayRef<TypedefNameDecl *> MaterializedTypedefs = {}) { + auto DeductionGuideName = + SemaRef.Context.DeclarationNames.getCXXDeductionGuideName( + AliasTemplate); + + 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; + } +}; + QualType Sema::DeduceTemplateSpecializationFromInitializer( TypeSourceInfo *TSInfo, const InitializedEntity &Entity, const InitializationKind &Kind, MultiExprArg Inits) { @@ -10597,6 +10977,21 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( // We can only perform deduction for class templates. auto *Template = dyn_cast_or_null<ClassTemplateDecl>(TemplateName.getAsTemplateDecl()); + + TypeAliasTemplateDecl* AliasTemplate = nullptr; + if (!Template) { + if ((AliasTemplate = dyn_cast_or_null<TypeAliasTemplateDecl>( + TemplateName.getAsTemplateDecl()))) { + auto UnderlyingType = AliasTemplate->getTemplatedDecl() + ->getUnderlyingType() + .getDesugaredType(Context); + if (const auto *TST = + UnderlyingType->getAs<TemplateSpecializationType>()) { + Template = dyn_cast_or_null<ClassTemplateDecl>( + TST->getTemplateName().getAsTemplateDecl()); + } + } + } if (!Template) { Diag(Kind.getLocation(), diag::err_deduced_non_class_template_specialization_type) @@ -10636,6 +11031,112 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( // clear on this, but they're not found by name so access does not apply. Guides.suppressDiagnostics(); + SmallVector<DeclAccessPair> GuidesCandidates; + if (AliasTemplate) { + for (auto* Guide : Guides) { + if (!dyn_cast_or_null<FunctionTemplateDecl>(Guide)) + continue; + auto RType = dyn_cast<FunctionTemplateDecl>(Guide) + ->getTemplatedDecl() + ->getReturnType(); + // The (trailing) return type of the deduction guide. + const TemplateSpecializationType * ReturnTST = nullptr; + if (const auto *InjectedCNT = RType->getAs<InjectedClassNameType>()) { + // for implicitly-generated deduction guide. + ReturnTST = InjectedCNT->getInjectedTST(); + } else if (const auto *ET = RType->getAs<ElaboratedType>()) { + // For explicit deduction guide. + ReturnTST = ET->getNamedType()->getAs<TemplateSpecializationType>(); + } + assert(ReturnTST); + if (ReturnTST) { + SmallVector<DeducedTemplateArgument> DeduceResults; + SmallVector<TemplateArgument> DeducedArgs; + DeduceResults.resize(ReturnTST->template_arguments().size()); + sema::TemplateDeductionInfo TDeduceInfo({}); + // Deduce template arguments of the deduction guide 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 + const auto* AliasRhsTST = AliasTemplate->getTemplatedDecl() + ->getUnderlyingType() + .getDesugaredType(this->Context) + ->getAs<TemplateSpecializationType>(); + assert(AliasRhsTST); + + if (DeduceTemplateArguments(AliasTemplate->getTemplateParameters(), + ReturnTST->template_arguments(), + AliasRhsTST->template_arguments(), + TDeduceInfo, DeduceResults, + /*NumberOfArgumentsMustMatch*/ false)) { + // FIXME: not all template arguments are deduced, we should continue + // to proceed with all deduced results. + } else { + // Happy case, all template arguments are deduced. + for (auto D : DeduceResults) + DeducedArgs.push_back(D); + + auto *DeducedArgList = + TemplateArgumentList::CreateCopy(this->Context, DeducedArgs); + + AliasTemplateDeductionGuideTransform Transform( + *this, AliasTemplate); + + // Substitute all above deduced template arguments into the + // deduction guide f. + // + // FIXME: is using the InstantiateFunctionDeclaration API a right + // implement choice? It has some side effects which creates a + // specialization for the deduction guide function template, and + // the specialization is added to the the FunctionTemplateDecl, this + // is not specified by C++ standard. + // + // FIXME: Should we cache the result? + if (auto *K = InstantiateFunctionDeclaration( + dyn_cast<FunctionTemplateDecl>(Guide), DeducedArgList, + SourceLocation(), + Sema::CodeSynthesisContext::BuildingDeductionGuides)) { + InstantiatingTemplate BuildingDeductionGuides( + *this, SourceLocation(), AliasTemplate, + Sema::InstantiatingTemplate::BuildingDeductionGuidesTag{}); + auto *D = Transform.transformUnderlyingFunctionTemplate( + dyn_cast<CXXDeductionGuideDecl>(K), DeducedArgs); + // FIXME: implement the assoicated 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. + // This could be implemented as part of function overload + // resolution below. + GuidesCandidates.push_back( + DeclAccessPair::make(D, AccessSpecifier::AS_public)); + } + } + } + } + } + else { + for (auto I = Guides.begin(), E = Guides.end(); I != E; ++I) { + GuidesCandidates.push_back(I.getPair()); + } + } // Figure out if this is list-initialization. InitListExpr *ListInit = (Inits.size() == 1 && Kind.getKind() != InitializationKind::IK_Direct) @@ -10786,9 +11287,8 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( HasAnyDeductionGuide = true; } }; - - for (auto I = Guides.begin(), E = Guides.end(); I != E; ++I) { - NamedDecl *D = (*I)->getUnderlyingDecl(); + for (auto I : GuidesCandidates) { + NamedDecl *D = (I)->getUnderlyingDecl(); if (D->isInvalidDecl()) continue; @@ -10801,7 +11301,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( if (!GD->isImplicit()) HasAnyDeductionGuide = true; - addDeductionCandidate(TD, GD, I.getPair(), OnlyListConstructors, + addDeductionCandidate(TD, GD, I, OnlyListConstructors, /*AllowAggregateDeductionCandidate=*/false); } @@ -10841,7 +11341,8 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( // Try list constructors unless the list is empty and the class has one or // more default constructors, in which case those constructors win. if (!ListInit->getNumInits()) { - for (NamedDecl *D : Guides) { + for (auto D : GuidesCandidates) { + auto *FD = dyn_cast<FunctionDecl>(D->getUnderlyingDecl()); if (FD && FD->getMinRequiredArguments() == 0) { TryListConstructors = false; diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 015b0abaf0e5ee2..aa9941a955eba34 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2456,6 +2456,15 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, return Sema::TDK_Success; } +Sema::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); +} + static Sema::TemplateDeductionResult DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, const TemplateArgumentList &ParamList, diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index fc80515b45e35b4..2d23a985be7687a 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" @@ -521,7 +522,8 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( TemplateArgs, &DeductionInfo) { assert( Kind == CodeSynthesisContext::ExplicitTemplateArgumentSubstitution || - Kind == CodeSynthesisContext::DeducedTemplateArgumentSubstitution); + Kind == CodeSynthesisContext::DeducedTemplateArgumentSubstitution || + Kind == CodeSynthesisContext::BuildingDeductionGuides); } Sema::InstantiatingTemplate::InstantiatingTemplate( @@ -1418,6 +1420,28 @@ namespace { return inherited::TransformFunctionProtoType(TLB, TL); } + QualType TransformInjectedClassNameType(TypeLocBuilder &TLB, + InjectedClassNameTypeLoc TL) { + // Return a TemplateSpecializationType for building deduction guides + Decl *D = TransformDecl(TL.getNameLoc(), + TL.getTypePtr()->getDecl()); + if (!D) { + if (SemaRef.CodeSynthesisContexts.back().Kind != + Sema::CodeSynthesisContext::BuildingDeductionGuides) + return QualType(); + auto *ICT = TL.getType()->getAs<InjectedClassNameType>(); + auto TST = SemaRef.Context.getTemplateSpecializationType( + ICT->getTemplateName(), TemplateArgs.getOutermost()); + TLB.pushTrivial(SemaRef.Context, TST, {}); + return TST; + } + + // Default implementation. + QualType T = SemaRef.Context.getTypeDeclType(cast<TypeDecl>(D)); + TLB.pushTypeSpec(T).setNameLoc(TL.getNameLoc()); + return T; + } + template<typename Fn> QualType TransformFunctionProtoType(TypeLocBuilder &TLB, FunctionProtoTypeLoc TL, diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index fbc8572ea0e0f0c..ed57bf23668c5f3 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -4857,13 +4857,13 @@ bool TemplateDeclInstantiator::SubstDefaultedFunction(FunctionDecl *New, FunctionDecl * Sema::InstantiateFunctionDeclaration(FunctionTemplateDecl *FTD, const TemplateArgumentList *Args, - SourceLocation Loc) { + SourceLocation Loc, CodeSynthesisContext::SynthesisKind CSC) { FunctionDecl *FD = FTD->getTemplatedDecl(); sema::TemplateDeductionInfo Info(Loc); InstantiatingTemplate Inst( *this, Loc, FTD, Args->asArray(), - CodeSynthesisContext::ExplicitTemplateArgumentSubstitution, Info); + CSC, Info); if (Inst.isInvalid()) return nullptr; @@ -6284,7 +6284,18 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D, QualType T = CheckTemplateIdType(TemplateName(TD), Loc, Args); if (T.isNull()) return nullptr; - auto *SubstRecord = T->getAsCXXRecordDecl(); + CXXRecordDecl *SubstRecord = T->getAsCXXRecordDecl(); + + if (!SubstRecord) { + // FIXME: we encounter a new type here as we use the + // `InstantiateFunctionDeclaration` API to substitute the deduced + // template arguments into deduction guide. + if (auto TST = T->getAs<TemplateSpecializationType>()) + // Return a nullptr as a sentinel value, we handle it properly in + // the TemplateInstantiator::TransformInjectedClassNameType + // override. + return nullptr; + } assert(SubstRecord && "class template id not a class type?"); // Check that this template-id names the primary template and not a // partial or explicit specialization. (In the latter cases, it's diff --git a/clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp b/clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp index 33ed4295c2e48c8..2f067ea53a50290 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 000000000000000..6a78e7aa082954a --- /dev/null +++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -fsyntax-only -Wno-c++11-narrowing -Wno-literal-conversion -std=c++20 -verify %s +// expected-no-diagnostics + +template<typename T> +struct Foo { + T t; +}; + +template<typename U> +using Bar = Foo<U>; + +void test1() { + Bar s = {1}; +} + +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>; + +void test2() { + AliasXYpair xy = {1.1, 2}; // XYpair<int, double> + + static_assert(__is_same(decltype(xy.x), int)); + static_assert(__is_same(decltype(xy.y), double)); +} >From 3a7b0219910835e9a722484e66eb148e31ec77e9 Mon Sep 17 00:00:00 2001 From: Haojian Wu <hokein...@gmail.com> Date: Fri, 19 Jan 2024 14:46:24 +0100 Subject: [PATCH 2/6] Fix an assert hit with default arguments. --- clang/lib/Sema/SemaInit.cpp | 14 +++++++++----- clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 11 +++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 208fcd20b428f70..b7432a38235f097 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -10902,11 +10902,15 @@ struct AliasTemplateDeductionGuideTransform { // placeholder to indicate there is a default argument. QualType ParamTy = NewDI->getType(); NewDefArg = new (SemaRef.Context) - OpaqueValueExpr(OldParam->getDefaultArg()->getBeginLoc(), - ParamTy.getNonLValueExprType(SemaRef.Context), - ParamTy->isLValueReferenceType() ? VK_LValue - : ParamTy->isRValueReferenceType() ? VK_XValue - : VK_PRValue); + // FIXME: using getDefaultArg will crash hitting "Default argument is + // not yet instantiated!" erro, fixed in constructor transformer as + // well. + OpaqueValueExpr( + OldParam->getDefaultArgRange().getBegin(), + ParamTy.getNonLValueExprType(SemaRef.Context), + ParamTy->isLValueReferenceType() ? VK_LValue + : ParamTy->isRValueReferenceType() ? VK_XValue + : VK_PRValue); } ParmVarDecl *NewParam = ParmVarDecl::Create(SemaRef.Context, DC, diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp index 6a78e7aa082954a..4202e60e714b376 100644 --- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp +++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp @@ -30,3 +30,14 @@ void test2() { static_assert(__is_same(decltype(xy.x), int)); static_assert(__is_same(decltype(xy.y), double)); } + +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 \ No newline at end of file >From db1a2dc872c2abbdd714bdfa367d27c6c2453a60 Mon Sep 17 00:00:00 2001 From: Haojian Wu <hokein...@gmail.com> Date: Fri, 19 Jan 2024 16:46:11 +0100 Subject: [PATCH 3/6] Add a failing testcase where the template arguments of the return type f fail to be deduced from the rhs of the type alias. This is a FIXME for implementing "If deduction fails for another reason, proceed with an empty set of deduced template arguments." --- clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 24 +++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp index 4202e60e714b376..103620f037b3b64 100644 --- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp +++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp @@ -40,4 +40,26 @@ struct container { template<class T> using vector = container<T, int>; vector v(0, 0); -} // namespace test3 \ No newline at end of file +} // namespace test3 + + +namespace test4 { +template<class T> +struct X +{ + T t; + X(T) {} +}; + +template <class T> +X(T) -> X<double>; + +template <class T> +using AX = X<T>; + +void test1() { + AX s = {1}; + // FIXME: should select X<double> deduction guide + // static_assert(__is_same(decltype(s.t), double)); +} +} \ No newline at end of file >From 2490e5b779b919aa2416c81fb97b58b04f151a15 Mon Sep 17 00:00:00 2001 From: Haojian Wu <hokein...@gmail.com> Date: Sat, 20 Jan 2024 21:22:49 +0100 Subject: [PATCH 4/6] Handle cases where the RHS of type alias is not a template specialization type. --- clang/lib/Sema/SemaInit.cpp | 27 +++++++++++++------- clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 10 +++++++- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index b7432a38235f097..ee687220e837455 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -34,6 +34,7 @@ #include "clang/Sema/SemaInternal.h" #include "clang/Sema/Template.h" #include "llvm/ADT/APInt.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/SmallString.h" @@ -10983,16 +10984,29 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( dyn_cast_or_null<ClassTemplateDecl>(TemplateName.getAsTemplateDecl()); TypeAliasTemplateDecl* AliasTemplate = nullptr; + llvm::ArrayRef<TemplateArgument> AliasRhsTemplateArgs; if (!Template) { - if ((AliasTemplate = dyn_cast_or_null<TypeAliasTemplateDecl>( - TemplateName.getAsTemplateDecl()))) { + if (AliasTemplate = dyn_cast_or_null<TypeAliasTemplateDecl>( + TemplateName.getAsTemplateDecl()); AliasTemplate) { + llvm::errs() << "alias template decl\n"; auto UnderlyingType = AliasTemplate->getTemplatedDecl() ->getUnderlyingType() .getDesugaredType(Context); if (const auto *TST = UnderlyingType->getAs<TemplateSpecializationType>()) { + // normal cases: using AliasFoo = Foo<T, U>; Template = dyn_cast_or_null<ClassTemplateDecl>( TST->getTemplateName().getAsTemplateDecl()); + AliasRhsTemplateArgs = TST->template_arguments(); + } 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(); + AliasRhsTemplateArgs = CTSD->getTemplateArgs().asArray(); + } } } } @@ -11078,15 +11092,10 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( // // 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 - const auto* AliasRhsTST = AliasTemplate->getTemplatedDecl() - ->getUnderlyingType() - .getDesugaredType(this->Context) - ->getAs<TemplateSpecializationType>(); - assert(AliasRhsTST); - if (DeduceTemplateArguments(AliasTemplate->getTemplateParameters(), ReturnTST->template_arguments(), - AliasRhsTST->template_arguments(), + //AliasRhsTST->template_arguments(), + AliasRhsTemplateArgs, TDeduceInfo, DeduceResults, /*NumberOfArgumentsMustMatch*/ false)) { // FIXME: not all template arguments are deduced, we should continue diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp index 103620f037b3b64..72531f3399877db 100644 --- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp +++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp @@ -62,4 +62,12 @@ void test1() { // FIXME: should select X<double> deduction guide // static_assert(__is_same(decltype(s.t), double)); } -} \ No newline at end of file +} + +namespace test5 { +template<int B> +struct Foo {}; +template<int... C> +using AF = Foo<1>; +auto a = AF {}; +} >From 5a71b264a2bf88b1315d00bf882a51dca210b6b8 Mon Sep 17 00:00:00 2001 From: Haojian Wu <hokein...@gmail.com> Date: Sat, 20 Jan 2024 23:27:10 +0100 Subject: [PATCH 5/6] Fix an assertion crash when substitute deduced tempalte arguments into the deduction guide f. We tried to build a ConstantExpr on a ConstantExpr, which triggers the assertion in the ConstantExpr::Create method. --- clang/lib/Sema/SemaOverload.cpp | 4 +++- clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index bbbd0abc82d740e..7d1241906662a86 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -40,6 +40,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/Signals.h" #include <algorithm> #include <cstddef> #include <cstdlib> @@ -6199,7 +6200,8 @@ Sema::EvaluateConvertedConstantExpression(Expr *E, QualType T, APValue &Value, if (Notes.empty()) { // It's a constant expression. - Expr *E = ConstantExpr::Create(Context, Result.get(), Value); + if (!isa<ConstantExpr>(E)) + E = ConstantExpr::Create(Context, Result.get(), Value); if (!PreNarrowingValue.isAbsent()) Value = std::move(PreNarrowingValue); return E; diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp index 72531f3399877db..f00d414888a51fe 100644 --- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp +++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp @@ -71,3 +71,14 @@ template<int... C> using AF = Foo<1>; auto a = AF {}; } + +namespace test6 { +template<typename T, bool B = false> +struct Foo { + Foo(T); +}; +// non-type template argument. +template<typename T> +using AF = Foo<T, 1>; +AF b{0};// +} >From 075953ac1745ce4961c86c0147ec337c6aef11df Mon Sep 17 00:00:00 2001 From: Haojian Wu <hokein...@gmail.com> Date: Mon, 22 Jan 2024 10:50:29 +0100 Subject: [PATCH 6/6] Add a unsupported testcase. The implementations doesn't support the using-alias-chain usage at the moment. Tweak the implementation not crashing, and add a testcase for this --- clang/lib/Sema/SemaInit.cpp | 20 ++++++++++++++------ clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 14 +++++++++++++- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index ee687220e837455..d8640694a5ec2b8 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -10988,17 +10988,25 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( if (!Template) { if (AliasTemplate = dyn_cast_or_null<TypeAliasTemplateDecl>( TemplateName.getAsTemplateDecl()); AliasTemplate) { - llvm::errs() << "alias template decl\n"; - auto UnderlyingType = AliasTemplate->getTemplatedDecl() + // Unrap the sugar ElaboratedType. + auto RhsType = AliasTemplate->getTemplatedDecl() ->getUnderlyingType() - .getDesugaredType(Context); + .getSingleStepDesugaredType(Context); if (const auto *TST = - UnderlyingType->getAs<TemplateSpecializationType>()) { - // normal cases: using AliasFoo = Foo<T, U>; + RhsType->getAs<TemplateSpecializationType>()) { + // The template decl in the TST can be a TypeALiasTemplateDecl if + // the right hand side of the alias is a type alias as well. E.g. + // + // template<typename T> + // using AliasFoo1 = Foo<T>; // Foo<T> is a class template specialization + // + // template<typename T> + // using AliasFoo2 = AliasFoo1<T>; // AliasFoo1<T> is a type alias + // FIXME: handle this case, we need to recursively perform deductions. Template = dyn_cast_or_null<ClassTemplateDecl>( TST->getTemplateName().getAsTemplateDecl()); AliasRhsTemplateArgs = TST->template_arguments(); - } else if (const auto *RT = UnderlyingType->getAs<RecordType>()) { + } 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>; diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp index f00d414888a51fe..24cadcb75150a47 100644 --- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp +++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp @@ -1,5 +1,4 @@ // RUN: %clang_cc1 -fsyntax-only -Wno-c++11-narrowing -Wno-literal-conversion -std=c++20 -verify %s -// expected-no-diagnostics template<typename T> struct Foo { @@ -82,3 +81,16 @@ template<typename T> using AF = Foo<T, 1>; AF b{0};// } + +namespace test7 { + +template<typename T> +struct Foo { Foo(T); }; + +template<typename U> +using AF1 = Foo<U>; +template<typename K> +using AF2 = AF1<K>; // expected-note {{template is declared here}} +// FIXME: support this case. +AF2 b = 1; // expected-error {{alias template 'AF2' requires template arguments; argument deduction only allowed for class templates}} +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits