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

via cfe-commits cfe-commits at lists.llvm.org
Fri Aug 23 19:58:26 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
----------------
antangelo wrote:

Consider this example:

```c++
template<class T>
struct Base {
    Base(T t);
};

template<class InBase = int, class NotInBase = int>
struct A : public Base<InBase> {
    using Base<InBase>::Base;
};

A a(10);
```
(accepted by gcc: https://godbolt.org/z/jbj1WMq9d)

To my understanding of the standard, we should generate:
```c++
template<class InBase = int, class NotInBase = int>
using Alias = Base<InBase>;

template<typename T> class CC;
template<typename InBase, typename NotInBase>
class CC<Base<InBase>> {
    typedef A<InBase, NotInBase> type;
};
```
along with the transformed alias deduction guides. With these in place, that deduction will fail, because it is not possible to instantiate the partial specialization of `CC<R>` for the deduction guide return type `R` (which is `Base<InBase>` in practice).

The reason this substitution fails is because `NotInBase` cannot be determined from `Base<InBase>`. Since partial specializations cannot have default arguments, the fact that `NotInBase` is defaulted in the derived class is not respected.

The solution I came up with for this problem is to rewrite the template parameters to substitute in these template arguments which have defaults in the derived class, but do not appear in the base. Then, the partial specialization member typedef can refer to them directly. In practice, these arguments cannot be deduced to anything else anyway.

This issue does not exist for defaulted parameters that do appear in the base-- those will substitute fine into the partial specialization. I also opted not to bail in the case where there is a template parameter with no default that does not appear in the base; the template is not deducible in this case, but I opt to generate a deduction guide anyway so we can emit a diagnostic.

The TPL does indeed still contain the default arguments, but clang's implementation as it is will ignore them. I agree that we probably shouldn't rely on this, though.

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


More information about the cfe-commits mailing list