[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