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

via cfe-commits cfe-commits at lists.llvm.org
Wed Nov 20 23:12:54 PST 2024


================
@@ -942,34 +946,145 @@ getRHSTemplateDeclAndArgs(Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate) {
   return {Template, AliasRhsTemplateArgs};
 }
 
+struct InheritedConstructorDeductionInfo {
+  // Class template for which we are declaring deduction guides
+  // This is `C` in the standard wording
+  TemplateDecl *DerivedClassTemplate;
+
+  // `template<typename> CC` in the standard wording
+  // This is the type of template that is substituted in the deduction guide
+  // return type `CC<R>`
+  TypeSourceInfo *CCType;
+};
+
+// Build the function type and return type for a deduction guide generated from
+// an inherited constructor C++23 [over.match.class.deduct]p1.10:
+// ... the set contains the guides of A with the return type R
+// of each guide replaced with `typename CC<R>::type` ...
+std::pair<TypeSourceInfo *, QualType>
+buildInheritedConstructorDeductionGuideType(
+    Sema &SemaRef, const InheritedConstructorDeductionInfo &Info,
+    TypeSourceInfo *SourceGuideTSI) {
+  ASTContext &Context = SemaRef.Context;
+  const auto *FPT = SourceGuideTSI->getType()->getAs<FunctionProtoType>();
+  assert(FPT && "Source Guide type should be a FunctionProtoType");
+
+  // This substitution can fail in cases where the source return type
+  // is not dependent and the derived class is not deducible
+  // FIXME: There is currently no diagnostic emitted in this case,
+  // as it is nontrivial to propagate substitution failure messages up
+  // to the point where deduction guides are used-- we do not have a type
+  // with which we can create a deduction guide AST node and must encode the
+  // SFINAE message.
+  Sema::SFINAETrap Trap(SemaRef);
+
+  MultiLevelTemplateArgumentList Args;
+  Args.addOuterTemplateArguments(Info.DerivedClassTemplate,
+                                 TemplateArgument(FPT->getReturnType()),
+                                 /*Final=*/false);
+  Args.addOuterRetainedLevels(Info.DerivedClassTemplate->getTemplateDepth());
+  TypeSourceInfo *ReturnTypeTSI = SemaRef.SubstType(
+      Info.CCType, Args, Info.DerivedClassTemplate->getBeginLoc(),
+      DeclarationName());
+  if (!ReturnTypeTSI || Trap.hasErrorOccurred())
+    return {nullptr, QualType()};
+  QualType ReturnType = ReturnTypeTSI->getType();
+
+  TypeLocBuilder TLB;
+  TLB.pushFullCopy(ReturnTypeTSI->getTypeLoc());
+
+  QualType FT = Context.getFunctionType(ReturnType, FPT->getParamTypes(),
+                                        FPT->getExtProtoInfo());
+  FunctionProtoTypeLoc NewTL = TLB.push<FunctionProtoTypeLoc>(FT);
+  const FunctionProtoTypeLoc &TL =
+      SourceGuideTSI->getTypeLoc().getAs<FunctionProtoTypeLoc>();
+  NewTL.setLocalRangeBegin(TL.getLocalRangeBegin());
+  NewTL.setLParenLoc(TL.getLParenLoc());
+  NewTL.setRParenLoc(TL.getRParenLoc());
+  NewTL.setExceptionSpecRange(TL.getExceptionSpecRange());
+  NewTL.setLocalRangeEnd(TL.getLocalRangeEnd());
+  for (unsigned I = 0, E = NewTL.getNumParams(); I != E; ++I)
+    NewTL.setParam(I, TL.getParam(I));
+
+  TypeSourceInfo *DGuideType = TLB.getTypeSourceInfo(Context, FT);
+  return {DGuideType, ReturnType};
+}
+
+static ArrayRef<TemplateArgument>
+getTemplateArgumentsFromDeductionGuideReturnType(ASTContext &Context,
+                                                 CXXDeductionGuideDecl *DG) {
+  auto RType = DG->getReturnType();
+
+  if (const auto *TST = RType->getAs<TemplateSpecializationType>())
+    return TST->template_arguments();
+
+  // implicitly-generated deduction guide.
+  if (const auto *InjectedCNT = RType->getAs<InjectedClassNameType>())
+    return InjectedCNT->getInjectedTST()->template_arguments();
+
+  // explicit deduction guide.
+  if (const auto *ET = RType->getAs<ElaboratedType>()) {
+    auto *TST = ET->getNamedType()->getAs<TemplateSpecializationType>();
+    assert(TST &&
+           "Expected ElaboratedType to name a TemplateSpecializationType");
+    return TST->template_arguments();
+  }
+
+  // inherited constructor deduction guide.
+  if (const auto *DNT = RType->getAs<DependentNameType>()) {
+    // This is of the form `typename CC<Base<...>>::type`. We need to extract
+    // the template arguments from the CC partial specialization,
+    // which are the template arguments of the derived template.
+    const Type *QualifierType = DNT->getQualifier()->getAsType();
+    assert(QualifierType && "Expected an inherited ctor deduction guide to "
+                            "have a type stored specifier");
+
+    const auto *CCSpecializationType =
+        QualifierType->getAs<TemplateSpecializationType>();
+    assert(CCSpecializationType &&
+           "Expected an inherited ctor deduction guide to have a "
+           "TemplateSpecializationType qualifier");
+
+    const auto *TD = cast<ClassTemplateDecl>(
+        CCSpecializationType->getTemplateName().getAsTemplateDecl());
+    SmallVector<ClassTemplatePartialSpecializationDecl *, 1> PS;
+    TD->getPartialSpecializations(PS);
+    assert(PS.size() == 1 &&
+           "Expected the CC template for inherited ctor deduction guide to "
+           "have a single partial specialization");
+
+    return PS[0]->getInjectedTemplateArgs(Context);
+  }
+
+  llvm_unreachable("Unhandled deduction guide return type");
+}
+
 // Build deduction guides for a type alias template from the given underlying
 // deduction guide F.
-FunctionTemplateDecl *
-BuildDeductionGuideForTypeAlias(Sema &SemaRef,
-                                TypeAliasTemplateDecl *AliasTemplate,
-                                FunctionTemplateDecl *F, SourceLocation Loc) {
+// If F is synthesized from a base class (as an inherited constructor),
+// then the return type will be transformed using FromInheritedCtor->CCType.
+// The resulting deduction guide is added to the
+// FromInheritedCtor->DerivedClassTemplate, as opposed to the given
+// AliasTemplate.
+FunctionTemplateDecl *BuildDeductionGuideForTypeAlias(
+    Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate,
+    FunctionTemplateDecl *F, SourceLocation Loc,
+    InheritedConstructorDeductionInfo *FromInheritedCtor = nullptr) {
   LocalInstantiationScope Scope(SemaRef);
   Sema::InstantiatingTemplate BuildingDeductionGuides(
       SemaRef, AliasTemplate->getLocation(), F,
       Sema::InstantiatingTemplate::BuildingDeductionGuidesTag{});
   if (BuildingDeductionGuides.isInvalid())
     return nullptr;
 
-  auto &Context = SemaRef.Context;
+  ASTContext &Context = SemaRef.Context;
   auto [Template, AliasRhsTemplateArgs] =
       getRHSTemplateDeclAndArgs(SemaRef, AliasTemplate);
 
-  auto RType = F->getTemplatedDecl()->getReturnType();
-  // The (trailing) return type of the deduction guide.
-  const TemplateSpecializationType *FReturnType =
-      RType->getAs<TemplateSpecializationType>();
-  if (const auto *InjectedCNT = RType->getAs<InjectedClassNameType>())
-    // implicitly-generated deduction guide.
-    FReturnType = InjectedCNT->getInjectedTST();
-  else if (const auto *ET = RType->getAs<ElaboratedType>())
-    // explicit deduction guide.
-    FReturnType = ET->getNamedType()->getAs<TemplateSpecializationType>();
-  assert(FReturnType && "expected to see a return type");
+  ArrayRef<TemplateArgument> FTemplateArgs =
+      getTemplateArgumentsFromDeductionGuideReturnType(
----------------
antangelo wrote:

@hokein I would like to re-request your review for these new changes to `BuildDeductionGuideForTypeAlias` to handle declaring alias and inherited deduction guides from an inherited deduction guide as the source (e.g. in cases with deeper class hierarchies or aliased class templates that inherit deduction guides).

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


More information about the cfe-commits mailing list