[clang] [clang] Implement P2582R1: CTAD from inherited constructors (PR #98788)

via cfe-commits cfe-commits at lists.llvm.org
Tue Oct 29 15:56:52 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();
+    }
+  }
+
+  // C++23 [over.match.class.deduct]p1.10
+  // Let A be an alias template whose template parameter list is that of
+  // [Template] and whose defining-type-id is [BaseTSI] ...
+  auto *TransformedBase =
+      SemaRef.SubstType(BaseTSI, Args, BaseLoc, DeclarationName(), true);
+  auto *BaseAD = TypeAliasDecl::Create(
+      Context, DC, SourceLocation(), BaseLoc,
+      TransformedBase->getType().getBaseTypeIdentifier(), TransformedBase);
+  std::string AliasDeclName = (Twine("__ctad_A_") + BaseTD->getName() + "_to_" +
+                               Template->getName() + "_" + Twine(BaseIdx))
+                                  .str();
+  IdentifierInfo *AliasIdentifier = &Context.Idents.get(AliasDeclName);
+  auto *BaseATD = TypeAliasTemplateDecl::Create(
+      Context, DC, BaseLoc, DeclarationName(AliasIdentifier), AliasTPL, BaseAD);
+  BaseAD->setDescribedAliasTemplate(BaseATD);
+  BaseAD->setImplicit();
+  BaseATD->setImplicit();
+
+  DC->addDecl(BaseATD);
+
+  // ... given a class template `template <typename> class CC;`
+  // whose primary template is not defined ...
+  auto *TParam = TemplateTypeParmDecl::Create(
+      Context, DC, SourceLocation(), SourceLocation(),
+      Template->getTemplateDepth(), 0, nullptr,
+      /*Typename=*/true, /*ParameterPack=*/false);
+  TParam->setImplicit();
+  auto *CCTemplateTPL = TemplateParameterList::Create(
+      Context, SourceLocation(), SourceLocation(),
+      ArrayRef<NamedDecl *>(TParam), SourceLocation(), nullptr);
+
+  std::string CCTemplateDeclName =
+      (Twine("__ctad_CC_") + BaseTD->getName() + "_to_" + Template->getName() +
+       "_" + Twine(BaseIdx))
+          .str();
+  IdentifierInfo *CCTemplateII = &Context.Idents.get(CCTemplateDeclName);
+  CXXRecordDecl *CCTemplateRD = CXXRecordDecl::Create(
----------------
antangelo wrote:

I initially tried it as anonymous, and it worked in normal use, but I experienced some ICE crashes while debugging/dumping types that included it. I am not sure which internal assumptions are broken by this, if any, given that anonymous templates are not supported by the language, which is why I ended up naming it.

I also imagine that looking this up by its name will be the most efficient way to retrieve the `CC` and alias types again when we implement handling for deduction guides declared after the first use (since we won't be recreating these AST nodes, and storing references to them anywhere else seems too wasteful).

https://github.com/llvm/llvm-project/pull/98788


More information about the cfe-commits mailing list