llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Haojian Wu (hokein) <details> <summary>Changes</summary> Fixes #<!-- -->54051 This patch implements the C++20 feature -- CTAD for alias templates. It 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 it in a followup). This patch also refactors the existing `ConvertConstructorToDeductionGuideTransform` to allow code reuse. CC @<!-- -->ilya-biryukov, @<!-- -->sam-mccall , @<!-- -->usx95 --- Patch is 52.50 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/77890.diff 10 Files Affected: - (modified) clang/include/clang/Sema/Sema.h (+11-3) - (modified) clang/lib/Sema/CMakeLists.txt (+1) - (added) clang/lib/Sema/CTAD.cpp (+209) - (added) clang/lib/Sema/CTAD.h (+65) - (modified) clang/lib/Sema/SemaInit.cpp (+367-7) - (modified) clang/lib/Sema/SemaTemplate.cpp (+23-145) - (modified) clang/lib/Sema/SemaTemplateDeduction.cpp (+9) - (modified) clang/lib/Sema/SemaTemplateInstantiate.cpp (+22-3) - (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+16-9) - (added) clang/test/SemaCXX/cxx20-ctad-type-alias.cpp (+133) ``````````diff diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index b5946e3f3178ff..c3fba8add4b660 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9305,6 +9305,12 @@ 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, @@ -10457,9 +10463,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..745878c717fc4c --- /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..88110230318f81 --- /dev/null +++ b/clang/lib/Sema/CTAD.h @@ -0,0 +1,65 @@ +//===--- 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 457fa377355a97..e6105040af8111 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" @@ -10583,6 +10591,213 @@ 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... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/77890 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits