https://github.com/hokein updated https://github.com/llvm/llvm-project/pull/77890
>From 8f7d83aed173688ff1413b7c4445d4576efee872 Mon Sep 17 00:00:00 2001 From: Haojian Wu <hokein...@gmail.com> Date: Wed, 24 Jan 2024 14:55:03 +0100 Subject: [PATCH] [clang] Implement Class Template Argument Deduction (CTAD) for type alias templates P1814R0. This patch implements the C++20 feature -- CTAD for alias templates. This is an initial patch, which covers most of pieces, the major missing piece is to implement the associated constraints (over.match.class.deduct#3.3) for the synthesized deduction guides (we can address in a followup). This patch also refactors the existing `ConvertConstructorToDeductionGuideTransform` to allow code reuse. --- clang/include/clang/Sema/Sema.h | 16 +- clang/lib/Sema/CMakeLists.txt | 1 + clang/lib/Sema/CTAD.cpp | 209 ++++++++++ clang/lib/Sema/CTAD.h | 64 +++ clang/lib/Sema/SemaInit.cpp | 379 +++++++++++++++++- clang/lib/Sema/SemaTemplate.cpp | 167 +------- clang/lib/Sema/SemaTemplateDeduction.cpp | 9 + clang/lib/Sema/SemaTemplateInstantiate.cpp | 22 +- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 18 +- clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 133 ++++++ 10 files changed, 858 insertions(+), 160 deletions(-) create mode 100644 clang/lib/Sema/CTAD.cpp create mode 100644 clang/lib/Sema/CTAD.h 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 1f1cbd11ff7358..24d7809ee17db3 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/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index 1856a88e9a3271..7a55406171ac74 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -14,6 +14,7 @@ clang_tablegen(OpenCLBuiltins.inc -gen-clang-opencl-builtins add_clang_library(clangSema AnalysisBasedWarnings.cpp + CTAD.cpp CodeCompleteConsumer.cpp DeclSpec.cpp DelayedDiagnostic.cpp diff --git a/clang/lib/Sema/CTAD.cpp b/clang/lib/Sema/CTAD.cpp new file mode 100644 index 00000000000000..f21e4c45df2fea --- /dev/null +++ b/clang/lib/Sema/CTAD.cpp @@ -0,0 +1,209 @@ +//===--- CTAD.cpp - -------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "CTAD.h" +#include "TreeTransform.h" +#include "TypeLocBuilder.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTMutationListener.h" +#include "clang/AST/ASTStructuralEquivalence.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Type.h" +#include "clang/AST/TypeLoc.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/Specifiers.h" +#include "clang/Sema/DeclSpec.h" +#include "clang/Sema/ScopeInfo.h" +#include "clang/Sema/Template.h" +#include "llvm/ADT/ArrayRef.h" +#include <optional> + +namespace clang { + +namespace { +/// 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; + } +}; +} // namespace + +ParmVarDecl *transformFunctionTypeParam( + Sema &SemaRef, ParmVarDecl *OldParam, DeclContext *DC, + 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->getDefaultArgRange().getBegin(), + ParamTy.getNonLValueExprType(SemaRef.Context), + ParamTy->isLValueReferenceType() ? VK_LValue + : ParamTy->isRValueReferenceType() ? VK_XValue + : VK_PRValue); + } + // Handle arrays and functions decay. + auto NewType = NewDI->getType(); + if (NewType->isArrayType() || NewType->isFunctionType()) + NewType = SemaRef.Context.getDecayedType(NewType); + + ParmVarDecl *NewParam = ParmVarDecl::Create( + SemaRef.Context, DC, OldParam->getInnerLocStart(), + OldParam->getLocation(), OldParam->getIdentifier(), NewType, NewDI, + OldParam->getStorageClass(), NewDefArg.get()); + NewParam->setScopeInfo(OldParam->getFunctionScopeDepth(), + OldParam->getFunctionScopeIndex()); + SemaRef.CurrentInstantiationScope->InstantiatedLocal(OldParam, NewParam); + return NewParam; +} + +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; +} + +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; +} + +} // namespace clang diff --git a/clang/lib/Sema/CTAD.h b/clang/lib/Sema/CTAD.h new file mode 100644 index 00000000000000..46da10fcda449e --- /dev/null +++ b/clang/lib/Sema/CTAD.h @@ -0,0 +1,64 @@ +//===--- CTAD.h - Helper functions for CTAD -------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines helper functions for the class template argument deduction +// (CTAD) implementation. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTMutationListener.h" +#include "clang/AST/ASTStructuralEquivalence.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Type.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Sema/DeclSpec.h" +#include "clang/Sema/ScopeInfo.h" +#include "clang/Sema/Template.h" +#include "llvm/ADT/ArrayRef.h" + +namespace clang { + +// Transform a given function parameter decl into a deduction guide parameter +// decl. +ParmVarDecl *transformFunctionTypeParam( + Sema &SemaRef, ParmVarDecl *OldParam, DeclContext *DC, + MultiLevelTemplateArgumentList &Args, + llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs); + +// Transform a given template type parameter into a deduction guide template +// parameter, rebuilding any internal references to earlier parameters and +// re-indexing as we go. +TemplateTypeParmDecl *transformTemplateTypeParam( + Sema &SemaRef, DeclContext *DC, TemplateTypeParmDecl *TPT, + MultiLevelTemplateArgumentList &Args, unsigned NewDepth, unsigned NewIndex); +// 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) { + // 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); + return NewParam; +} + +// 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 = {}); + +} // namespace clang diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 91e4cb7b68a24a..a7142f92e5f5f2 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -10,13 +10,19 @@ // //===----------------------------------------------------------------------===// +#include "CTAD.h" +#include "TypeLocBuilder.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/DeclAccessPair.h" +#include "clang/AST/DeclBase.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/RecursiveASTVisitor.h" +#include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/SourceManager.h" @@ -28,7 +34,9 @@ #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/ArrayRef.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/SmallString.h" @@ -10603,6 +10611,218 @@ static bool isOrIsDerivedFromSpecializationOf(CXXRecordDecl *RD, return !(NotSpecialization(RD) && RD->forallBases(NotSpecialization)); } +// 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... +class AliasTemplateDeductionGuideTransform { +public: + AliasTemplateDeductionGuideTransform(Sema &S, TypeAliasTemplateDecl *Alias) + : SemaRef(S), AliasTemplate(Alias), DC(Alias->getDeclContext()) {} + // Returns the result of substituting the deduced template arguments into F. + NamedDecl *transform(CXXDeductionGuideDecl *F, + ArrayRef<TemplateArgument> DeducedArgs, + ArrayRef<NamedDecl *> NonDeducedTemplateParamsInF) { + // Template parameters of the 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 *> TemplateParamsInFPrime = + FindAppearedTemplateParamsInAlias(DeducedArgs); + // ...followed by the template parameters of f that were not deduced + // (including their default template arguments) + TemplateParamsInFPrime.append(NonDeducedTemplateParamsInF.begin(), + NonDeducedTemplateParamsInF.end()); + + LocalInstantiationScope Scope(SemaRef); + SmallVector<TemplateArgument, 16> Depth1Args; + SmallVector<NamedDecl *, 16> AllParams; + SmallVector<TemplateArgument, 16> SubstArgs; + unsigned TemplateParamIndex = 0; + TemplateParameterList *TemplateParams = nullptr; + + for (NamedDecl* Param : TemplateParamsInFPrime) { + MultiLevelTemplateArgumentList Args; + + Args.setKind(TemplateSubstitutionKind::Rewrite); + Args.addOuterTemplateArguments(Depth1Args); + Args.addOuterRetainedLevel(); + NamedDecl *NewParam = + transformTemplateParameter(Param, Args, TemplateParamIndex++); + 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, + AliasTemplate->getTemplateParameters()->getTemplateLoc(), + AliasTemplate->getTemplateParameters()->getLAngleLoc(), AllParams, + AliasTemplate->getTemplateParameters()->getRAngleLoc(), + /*RequiresClause=*/nullptr); + + MultiLevelTemplateArgumentList Args; + Args.setKind(TemplateSubstitutionKind::Rewrite); + Args.addOuterTemplateArguments(SubstArgs); + Args.addOuterRetainedLevel(); + + FunctionProtoTypeLoc FPTL = F->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, F->getReturnType(), + MaterializedTypedefs); + if (NewType.isNull()) + return nullptr; + TypeSourceInfo *NewTInfo = TLB.getTypeSourceInfo(SemaRef.Context, NewType); + + return clang::buildDeductionGuide( + SemaRef, AliasTemplate, TemplateParams, + F->getCorrespondingConstructor(), + F->getExplicitSpecifier(), NewTInfo, + AliasTemplate->getBeginLoc(), + AliasTemplate->getLocation(), + AliasTemplate->getEndLoc(), + F->isImplicit(), + MaterializedTypedefs); + } + +private: + // Find all template parameters of the AliasTemplate that appear in the + // DeducedArgs. + SmallVector<NamedDecl *> + FindAppearedTemplateParamsInAlias(ArrayRef<TemplateArgument> DeducedArgs) { + struct FindAppearedTemplateParams + : public RecursiveASTVisitor<FindAppearedTemplateParams> { + llvm::DenseSet<NamedDecl *> TemplateParamsInAlias; + llvm::DenseSet<const NamedDecl *> AppearedTemplateParams; + + FindAppearedTemplateParams(ArrayRef<NamedDecl *> TemplateParamsInAlias) + : TemplateParamsInAlias(TemplateParamsInAlias.begin(), + TemplateParamsInAlias.end()) {} + + bool VisitTemplateTypeParmType(TemplateTypeParmType *TTP) { + MarkAppeared(TTP->getDecl()); + return true; + } + bool VisitDeclRefExpr(DeclRefExpr *DRE) { + MarkAppeared(DRE->getFoundDecl()); + return true; + } + + void MarkAppeared(NamedDecl *ND) { + if (TemplateParamsInAlias.contains(ND)) + AppearedTemplateParams.insert(ND); + } + }; + ArrayRef<NamedDecl *> TemplateParamsInAlias = + AliasTemplate->getTemplateParameters()->asArray(); + FindAppearedTemplateParams MarkAppeared(TemplateParamsInAlias); + MarkAppeared.TraverseTemplateArguments(DeducedArgs); + + SmallVector<NamedDecl *> Results; + for (auto* TP : TemplateParamsInAlias) + if (MarkAppeared.AppearedTemplateParams.contains(TP)) + Results.push_back(TP); + return Results; + } + + // Transform a template parameter of f into a template parameter of f' + // rebuilding any internal references to earlier parameters and renumbering as + // we go. + NamedDecl *transformTemplateParameter(NamedDecl *TemplateParam, + MultiLevelTemplateArgumentList &Args, + int NewIndex) { + if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(TemplateParam)) + return clang::transformTemplateTypeParam(SemaRef, DC, TTP, Args, + TTP->getDepth(), NewIndex); + + if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(TemplateParam)) + return clang::transformTemplateParam(SemaRef, DC, TTP, Args, NewIndex); + auto *NTTP = cast<NonTypeTemplateParmDecl>(TemplateParam); + return clang::transformTemplateParam(SemaRef, DC, NTTP, Args, NewIndex); + } + + // Transform the function proto type of f into a function prototype of f'. + 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(); + + for (auto *OldParam : TL.getParams()) { + ParmVarDecl *NewParam = clang::transformFunctionTypeParam( + SemaRef, OldParam, DC, Args, 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; + } + + Sema &SemaRef; + TypeAliasTemplateDecl *AliasTemplate = nullptr; + DeclContext *DC = nullptr; +}; + QualType Sema::DeduceTemplateSpecializationFromInitializer( TypeSourceInfo *TSInfo, const InitializedEntity &Entity, const InitializationKind &Kind, MultiExprArg Inits) { @@ -10614,10 +10834,47 @@ 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()); + + TypeAliasTemplateDecl* AliasTemplate = nullptr; + llvm::ArrayRef<TemplateArgument> AliasRhsTemplateArgs; + if (!Template && getLangOpts().CPlusPlus20) { // type alias template + if (AliasTemplate = dyn_cast_or_null<TypeAliasTemplateDecl>( + TemplateName.getAsTemplateDecl()); AliasTemplate) { + // Unrap the sugar ElaboratedType. + auto RhsType = AliasTemplate->getTemplatedDecl() + ->getUnderlyingType() + .getSingleStepDesugaredType(Context); + if (const auto *TST = + RhsType->getAs<TemplateSpecializationType>()) { + // TemplateName in TEST can be a TypeAliasTemplateDecl if + // the right hand side of the alias is also a type alias, 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: support 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 = 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(); + } + } + } + } if (!Template) { + // FIXME: update the diagnostic message to include C++20 alias templates Diag(Kind.getLocation(), diag::err_deduced_non_class_template_specialization_type) << (int)getTemplateNameKindForDiagnostics(TemplateName) << TemplateName; @@ -10656,6 +10913,114 @@ 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* G : Guides) { + FunctionTemplateDecl *F = dyn_cast<FunctionTemplateDecl>(G); + if (!F) + // FIXME: handle the non-template deduction guide case. + continue; + auto RType = F->getTemplatedDecl()->getReturnType(); + // The (trailing) return type of the deduction guide. + const TemplateSpecializationType * FReturnType = nullptr; + 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); + if (FReturnType) { + sema::TemplateDeductionInfo TDeduceInfo(Kind.getLocation()); + SmallVector<DeducedTemplateArgument> DeduceResults; + // DeduceResults.resize(FReturnType->template_arguments().size()); + DeduceResults.resize(F->getTemplateParameters()->size()); + + // 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 + // + // FIXME: DeduceTemplateArguments stops immediately at the first + // non-deduced template parameter, extend it to continue performing + // deduction for rest of parameters. + DeduceTemplateArguments( + F->getTemplateParameters(), FReturnType->template_arguments(), + AliasRhsTemplateArgs, TDeduceInfo, DeduceResults, + /*NumberOfArgumentsMustMatch=*/false); + + SmallVector<TemplateArgument> DeducedArgs; + SmallVector<NamedDecl *> NonDeducedTemplateParamsInF; + // !!NOTE: DeduceResults respects the sequence of template parameters. + for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) { + const auto &D = DeduceResults[Index]; + if (!D.isNull()) // Deduced + DeducedArgs.push_back(D); + else + NonDeducedTemplateParamsInF.push_back( + F->getTemplateParameters()->getParam(Index)); + } + auto *DeducedArgList = + TemplateArgumentList::CreateCopy(this->Context, DeducedArgs); + + // Let g denote the result of substituting these deductions into 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 the standard. + // + // FIXME: Cache the result. + if (auto *G = InstantiateFunctionDeclaration( + F, DeducedArgList, AliasTemplate->getLocation(), + Sema::CodeSynthesisContext::BuildingDeductionGuides)) { + InstantiatingTemplate BuildingDeductionGuides( + *this, AliasTemplate->getLocation(), AliasTemplate, + Sema::InstantiatingTemplate::BuildingDeductionGuidesTag{}); + AliasTemplateDeductionGuideTransform Transform(*this, AliasTemplate); + // If substitution succeeds, form a function or function template + // f' with the following properties and add it to the set of + // guides of A: + if (auto *FPrime = Transform.transform( + dyn_cast<CXXDeductionGuideDecl>(G), DeducedArgs, + NonDeducedTemplateParamsInF)) { + // 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(FPrime, 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) @@ -10813,9 +11178,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; @@ -10828,7 +11192,7 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( if (!GD->isImplicit()) HasAnyDeductionGuide = true; - addDeductionCandidate(TD, GD, I.getPair(), OnlyListConstructors, + addDeductionCandidate(TD, GD, I, OnlyListConstructors, /*AllowAggregateDeductionCandidate=*/false); } @@ -10868,7 +11232,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/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 9bfa71dc8bcf1d..147d6d9d04c938 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -8,10 +8,12 @@ // This file implements semantic analysis for C++ templates. //===----------------------------------------------------------------------===// +#include "CTAD.h" #include "TreeTransform.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" #include "clang/AST/DeclFriend.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" @@ -2330,7 +2332,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( @@ -2403,9 +2404,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 clang::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. @@ -2440,8 +2442,9 @@ struct ConvertConstructorToDeductionGuideTransform { Params.push_back(NewParam); } - return buildDeductionGuide(GetTemplateParameterList(Template), nullptr, - ExplicitSpecifier(), TSI, Loc, Loc, Loc); + return clang::buildDeductionGuide( + SemaRef, Template, GetTemplateParameterList(Template), nullptr, + ExplicitSpecifier(), TSI, Loc, Loc, Loc, /*IsImplicit=*/true); } private: @@ -2450,50 +2453,16 @@ 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 clang::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 clang::transformTemplateParam( + SemaRef, DC, TTP, Args, Depth1IndexAdjustment + TTP->getIndex()); + auto *NTTP = cast<NonTypeTemplateParmDecl>(TemplateParam); + return clang::transformTemplateParam( + SemaRef, DC, NTTP, Args, Depth1IndexAdjustment + NTTP->getIndex()); } QualType transformFunctionProtoType( @@ -2506,11 +2475,11 @@ struct ConvertConstructorToDeductionGuideTransform { // -- The types of the function parameters are those of the constructor. for (auto *OldParam : TL.getParams()) { - ParmVarDecl *NewParam = - transformFunctionTypeParam(OldParam, Args, MaterializedTypedefs); + ParmVarDecl *NewParam = clang::transformFunctionTypeParam( + SemaRef, OldParam, DC, Args, MaterializedTypedefs); if (NestedPattern && NewParam) - NewParam = transformFunctionTypeParam(NewParam, OuterInstantiationArgs, - MaterializedTypedefs); + NewParam = clang::transformFunctionTypeParam( + SemaRef, NewParam, DC, OuterInstantiationArgs, MaterializedTypedefs); if (!NewParam) return QualType(); ParamTypes.push_back(NewParam->getType()); @@ -2551,98 +2520,6 @@ struct ConvertConstructorToDeductionGuideTransform { return Result; } - - 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); - } - // Handle arrays and functions decay. - auto NewType = NewDI->getType(); - if (NewType->isArrayType() || NewType->isFunctionType()) - NewType = SemaRef.Context.getDecayedType(NewType); - - ParmVarDecl *NewParam = ParmVarDecl::Create( - SemaRef.Context, DC, OldParam->getInnerLocStart(), - OldParam->getLocation(), OldParam->getIdentifier(), NewType, 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, - llvm::ArrayRef<TypedefNameDecl *> MaterializedTypedefs = {}) { - 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(); - 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(); - Guide->setDescribedFunctionTemplate(GuideTemplate); - - if (isa<CXXRecordDecl>(DC)) { - Guide->setAccess(AS_public); - GuideTemplate->setAccess(AS_public); - } - - DC->addDecl(GuideTemplate); - return GuideTemplate; - } }; } diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index e9e7ab5bb6698a..5bd065aae45ee9 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2484,6 +2484,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 e12186d7d82f8d..e1e6df00c3ed7e 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,24 @@ namespace { return inherited::TransformFunctionProtoType(TLB, TL); } + QualType TransformInjectedClassNameType(TypeLocBuilder &TLB, + InjectedClassNameTypeLoc TL) { + auto Type = inherited::TransformInjectedClassNameType(TLB, TL); + 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 TST = SemaRef.Context.getTemplateSpecializationType( + ICT->getTemplateName(), TemplateArgs.getOutermost()); + TLB.pushTrivial(SemaRef.Context, TST, TL.getNameLoc()); + return TST; + } + } + return Type; + } + template<typename Fn> QualType TransformFunctionProtoType(TypeLocBuilder &TLB, FunctionProtoTypeLoc TL, diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index fbc8572ea0e0f0..7b470145c82c1c 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,8 +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(); - assert(SubstRecord && "class template id not a class type?"); + CXXRecordDecl *SubstRecord = T->getAsCXXRecordDecl(); + + if (!SubstRecord) { + // The template id T is a TemplateSpecializationType when performing + // a substitution for 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. + 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/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp new file mode 100644 index 00000000000000..5a5691e48fd17e --- /dev/null +++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp @@ -0,0 +1,133 @@ +// 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 { +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)); +} // namespace test4 + +namespace test5 { +template <int B> +struct Foo {}; + +template <int... C> +using AF = Foo<1>; +auto a = AF{}; +} // namespace test5 + +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}; // +} // namespace test6 + +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}} +} // 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: should we 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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits