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

Haojian Wu via cfe-commits cfe-commits at lists.llvm.org
Wed Aug 7 13:13:57 PDT 2024


================
@@ -1216,10 +1308,225 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
             ->getDeductionCandidateKind() == DeductionCandidate::Aggregate)
       continue;
 
-    BuildDeductionGuideForTypeAlias(SemaRef, AliasTemplate, F, Loc);
+    BuildDeductionGuideForTypeAlias(SemaRef, AliasTemplate, F, Loc,
+                                    DeducingTemplate, DerivedClassMapperType);
   }
 }
 
+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();
+  if (!BaseTD)
+    return;
+
+  // Subsitute any parameters with default arguments not present in the base,
+  // since partial specializations cannot have default parameters
+  TemplateParameterList *TemplateTPL = Pattern->getTemplateParameters();
+  auto BaseDeducedTemplateParams =
+      TemplateParamsReferencedInTemplateArgumentList(
+          TemplateTPL, BaseTST->template_arguments());
+  SmallVector<NamedDecl *, 8> PartialSpecParams;
+  SmallVector<TemplateArgument, 8> SubstArgs;
+  PartialSpecParams.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 (!BaseDeducedTemplateParams.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, PartialSpecParams.size(),
+        Template->getTemplateDepth());
+    if (!NewParam)
+      return;
+
+    PartialSpecParams.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 *PartialSpecTPL = TemplateParameterList::Create(
+      Context, TemplateTPL->getTemplateLoc(), TemplateTPL->getLAngleLoc(),
+      PartialSpecParams, TemplateTPL->getRAngleLoc(), RequiresClause);
+
+  // [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_alias_") + BaseTD->getName() + "_to_" +
+       Template->getName() + "_" + Twine(BaseIdx))
+          .str();
+  IdentifierInfo *AliasIdentifier = &Context.Idents.get(AliasDeclName);
+  auto *BaseATD = TypeAliasTemplateDecl::Create(
+      Context, DC, BaseLoc, DeclarationName(AliasIdentifier), PartialSpecTPL,
+      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, false, false);
+  TParam->setImplicit();
+  auto *MapperTPL = TemplateParameterList::Create(
+      Context, SourceLocation(), SourceLocation(),
+      ArrayRef<NamedDecl *>(TParam), SourceLocation(), nullptr);
+
+  std::string MapperDeclName =
+      (Twine("__ctad_mapper_") + BaseTD->getName() + "_to_" +
+       Template->getName() + "_" + Twine(BaseIdx))
+          .str();
+  IdentifierInfo *MapperII = &Context.Idents.get(MapperDeclName);
+  CXXRecordDecl *MapperRD = CXXRecordDecl::Create(
+      Context, CXXRecordDecl::TagKind::Struct, DC, SourceLocation(),
+      SourceLocation(), nullptr, nullptr);
+  ClassTemplateDecl *MapperTD =
+      ClassTemplateDecl::Create(Context, DC, SourceLocation(),
+                                DeclarationName(MapperII), MapperTPL, MapperRD);
+  MapperRD->setDescribedClassTemplate(MapperTD);
+  MapperTD->setImplicit();
+  MapperRD->setImplicit();
+
+  DC->addDecl(MapperTD);
+
+  // ... and with a single partial specialization whose template parameter list
+  // is that of A with the template argument list of A ...
+  TemplateArgument AliasTA(TransformedBase->getType());
+  ArrayRef<TemplateArgument> TAL(AliasTA);
+  auto MapperTemplateName =
+      Context.getCanonicalTemplateName(TemplateName(MapperTD));
+  QualType CanonType =
+      Context.getTemplateSpecializationType(MapperTemplateName, TAL);
+
+  auto *MapperSpecialization = ClassTemplatePartialSpecializationDecl::Create(
+      Context, ClassTemplatePartialSpecializationDecl::TagKind::Struct, DC,
+      SourceLocation(), SourceLocation(), PartialSpecTPL, MapperTD, TAL,
----------------
hokein wrote:

The `PartialSpecTPL` is also used to build the alias template decl (line 1410), now both the alias template decl and the partial specialization decl have the same template parameters, this is incorrect, we need to clone one for the alias template.

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


More information about the cfe-commits mailing list