[clang] [clang] Implement P2582R1: CTAD from inherited constructors (PR #98788)
Aaron Ballman via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 31 08:40:23 PDT 2024
================
@@ -1203,10 +1333,291 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
->getDeductionCandidateKind() == DeductionCandidate::Aggregate)
continue;
- BuildDeductionGuideForTypeAlias(SemaRef, AliasTemplate, F, Loc);
+ BuildDeductionGuideForTypeAlias(SemaRef, AliasTemplate, F, Loc,
+ FromInheritedCtor);
}
}
+// Check if a template is deducible as per [dcl.type.simple]p3
+static bool IsDeducibleTemplate(TemplateDecl *TD) {
+ while (TD) {
+ // [dcl.type.simple]p3: A deducible template is either a class template ...
+ if (isa<ClassTemplateDecl>(TD))
+ return true;
+
+ // ... or is an alias template ...
+ auto *Alias = dyn_cast<TypeAliasTemplateDecl>(TD);
+ if (!Alias)
+ return false;
+
+ QualType AliasType =
+ Alias->getTemplatedDecl()->getUnderlyingType().getCanonicalType();
+
+ // ... whose defining-type-id is of the form
+ // [typename] [nested-name-specifier] [template] simple-template-id ...
+ if (const auto *TST = AliasType->getAs<TemplateSpecializationType>()) {
+ // ... and the template-name of the simple-template-id names a deducible
+ // template
+ TD = TST->getTemplateName().getAsTemplateDecl();
+ continue;
+ }
+
+ // Handle the case that the RHS of the alias is not dependent
+ // e.g. using AliasFoo = Foo<bool>;
+ if (const auto *RT = AliasType->getAs<RecordType>())
+ return isa<ClassTemplateSpecializationDecl>(RT->getAsCXXRecordDecl());
+
+ return false;
+ }
+
+ return false;
+}
+
+void DeclareImplicitDeductionGuidesFromInheritedConstructors(
+ Sema &SemaRef, TemplateDecl *Template, ClassTemplateDecl *Pattern,
+ TypeSourceInfo *BaseTSI, unsigned BaseIdx) {
+ auto &Context = SemaRef.Context;
+ DeclContext *DC = Template->getDeclContext();
+ const auto *BaseTST = BaseTSI->getType()->getAs<TemplateSpecializationType>();
+ if (!BaseTST)
+ return;
+ SourceLocation BaseLoc = BaseTSI->getTypeLoc().getBeginLoc();
+
+ TemplateDecl *BaseTD = BaseTST->getTemplateName().getAsTemplateDecl();
+
+ // The alias template `A` that we build out of the base type must be a
+ // deducible template. `A` will be of the correct form, so it is deducible iff
+ // BaseTD is deducible
+ if (!BaseTD || !IsDeducibleTemplate(BaseTD))
+ return;
+
+ // Substitute any parameters with default arguments not present in the base,
+ // since partial specializations cannot have default parameters
+ // See https://github.com/cplusplus/CWG/issues/627
+ TemplateParameterList *TemplateTPL = Pattern->getTemplateParameters();
+ SmallVector<unsigned int> BaseDeducedTemplateParamsList =
+ TemplateParamsReferencedInTemplateArgumentList(
+ TemplateTPL, BaseTST->template_arguments());
+ llvm::SmallSet<unsigned int, 8> BaseDeducedTemplateParamsSet(
+ BaseDeducedTemplateParamsList.begin(),
+ BaseDeducedTemplateParamsList.end());
+ SmallVector<NamedDecl *, 8> AliasTemplateParams;
+ SmallVector<TemplateArgument, 8> SubstArgs;
+ AliasTemplateParams.reserve(TemplateTPL->size());
+ SubstArgs.reserve(TemplateTPL->size());
+ LocalInstantiationScope Scope(SemaRef);
+ for (unsigned I = 0, N = TemplateTPL->size(); I < N; ++I) {
+ NamedDecl *Param = TemplateTPL->getParam(I);
+ if (!BaseDeducedTemplateParamsSet.contains(I)) {
+ if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(Param);
+ TTP && TTP->hasDefaultArgument()) {
+ SubstArgs.push_back(TTP->getDefaultArgument().getArgument());
+ continue;
+ }
+
+ if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param);
+ NTTP && NTTP->hasDefaultArgument()) {
+ SubstArgs.push_back(NTTP->getDefaultArgument().getArgument());
+ continue;
+ }
+
+ if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(Param);
+ TTP && TTP->hasDefaultArgument()) {
+ SubstArgs.push_back(TTP->getDefaultArgument().getArgument());
+ continue;
+ }
+
+ // We have a template parameter that is not present in the base
+ // and does not have a default argument. We create the deduction
+ // guide anyway to display a diagnostic.
+ }
+
+ MultiLevelTemplateArgumentList Args;
+ Args.setKind(TemplateSubstitutionKind::Rewrite);
+ Args.addOuterTemplateArguments(SubstArgs);
+ Args.addOuterRetainedLevels(Template->getTemplateDepth());
+
+ NamedDecl *NewParam = transformTemplateParameter(
+ SemaRef, DC, Param, Args, AliasTemplateParams.size(),
+ Template->getTemplateDepth());
+ if (!NewParam)
+ return;
+
+ AliasTemplateParams.push_back(NewParam);
+ SubstArgs.push_back(Context.getInjectedTemplateArg(NewParam));
+ }
+
+ Expr *RequiresClause = nullptr;
+ MultiLevelTemplateArgumentList Args;
+ Args.setKind(TemplateSubstitutionKind::Rewrite);
+ Args.addOuterTemplateArguments(SubstArgs);
+ Args.addOuterRetainedLevels(Template->getTemplateDepth());
+ if (Expr *TemplateRC = TemplateTPL->getRequiresClause()) {
+ ExprResult E = SemaRef.SubstExpr(TemplateRC, Args);
+ if (E.isInvalid())
+ return;
+ RequiresClause = E.getAs<Expr>();
+ }
+ auto *AliasTPL = TemplateParameterList::Create(
+ Context, TemplateTPL->getTemplateLoc(), TemplateTPL->getLAngleLoc(),
+ AliasTemplateParams, TemplateTPL->getRAngleLoc(), RequiresClause);
+
+ // Clone AliasTPL into a new parameter list for the partial specialization,
+ // but with default arguments removed, using the template instantiator
+ // for heavy lifting.
+ LocalInstantiationScope CloneScope(SemaRef);
+ MultiLevelTemplateArgumentList CloneArgs;
+ CloneArgs.setKind(TemplateSubstitutionKind::Rewrite);
+ CloneArgs.addOuterRetainedLevels(Template->getTemplateDepth());
+ TemplateDeclInstantiator CloneTDI(SemaRef, DC, CloneArgs);
+ TemplateParameterList *PartialSpecTPL =
+ CloneTDI.SubstTemplateParams(AliasTPL);
+ CloneScope.Exit();
+ for (NamedDecl *Param : *PartialSpecTPL) {
+ if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(Param)) {
+ TTP->removeDefaultArgument();
+ } else if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) {
+ NTTP->removeDefaultArgument();
+ } else if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(Param)) {
+ TTP->removeDefaultArgument();
+ }
----------------
AaronBallman wrote:
```suggestion
if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(Param))
TTP->removeDefaultArgument();
else if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param))
NTTP->removeDefaultArgument();
else if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(Param))
TTP->removeDefaultArgument();
```
https://github.com/llvm/llvm-project/pull/98788
More information about the cfe-commits
mailing list