[clang] [clang] Implement P2582R1: CTAD from inherited constructors (PR #98788)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Jul 19 00:16:36 PDT 2024
https://github.com/antangelo updated https://github.com/llvm/llvm-project/pull/98788
>From 3f0dfdfdbad3921da42be23fb0bbf1f1f00a42fb Mon Sep 17 00:00:00 2001
From: antangelo <contact at antangelo.com>
Date: Sat, 1 Jun 2024 13:15:40 -0400
Subject: [PATCH 1/4] [clang] Implement P2582R1: CTAD from inherited
constructors
---
clang/docs/ReleaseNotes.rst | 2 +
clang/include/clang/AST/DeclCXX.h | 36 +-
.../clang/Basic/DiagnosticSemaKinds.td | 4 +
clang/lib/AST/ASTImporter.cpp | 8 +-
clang/lib/AST/DeclCXX.cpp | 10 +-
clang/lib/Sema/SemaOverload.cpp | 67 +++
clang/lib/Sema/SemaTemplate.cpp | 389 ++++++++++++++++--
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 3 +-
clang/lib/Serialization/ASTReaderDecl.cpp | 2 +
clang/lib/Serialization/ASTWriterDecl.cpp | 2 +
.../SemaCXX/cxx23-ctad-inherited-ctors.cpp | 260 ++++++++++++
clang/unittests/AST/ASTImporterTest.cpp | 43 ++
clang/www/cxx_status.html | 2 +-
13 files changed, 786 insertions(+), 42 deletions(-)
create mode 100644 clang/test/SemaCXX/cxx23-ctad-inherited-ctors.cpp
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 5dc0f8b7e0bbb..b6924f61814fb 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -263,6 +263,8 @@ C++23 Feature Support
- Implemented `P2797R0: Static and explicit object member functions with the same parameter-type-lists <https://wg21.link/P2797R0>`_.
This completes the support for "deducing this".
+- Implemented `P2582R1: Wording for class template argument deduction from inherited constructors <https://wg21.link/P2582R1>`_.
+
C++2c Feature Support
^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index fb52ac804849d..c8e74f32b2ccb 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -1957,10 +1957,14 @@ class CXXDeductionGuideDecl : public FunctionDecl {
ExplicitSpecifier ES,
const DeclarationNameInfo &NameInfo, QualType T,
TypeSourceInfo *TInfo, SourceLocation EndLocation,
- CXXConstructorDecl *Ctor, DeductionCandidate Kind)
+ CXXConstructorDecl *Ctor, DeductionCandidate Kind,
+ CXXDeductionGuideDecl *GeneratedFrom,
+ bool IsGeneratedFromInheritedConstructor)
: FunctionDecl(CXXDeductionGuide, C, DC, StartLoc, NameInfo, T, TInfo,
SC_None, false, false, ConstexprSpecKind::Unspecified),
- Ctor(Ctor), ExplicitSpec(ES) {
+ Ctor(Ctor), ExplicitSpec(ES),
+ SourceDeductionGuide(GeneratedFrom,
+ IsGeneratedFromInheritedConstructor) {
if (EndLocation.isValid())
setRangeEnd(EndLocation);
setDeductionCandidateKind(Kind);
@@ -1968,6 +1972,11 @@ class CXXDeductionGuideDecl : public FunctionDecl {
CXXConstructorDecl *Ctor;
ExplicitSpecifier ExplicitSpec;
+ // The deduction guide, if any, that this deduction guide was generated from,
+ // in the case of alias template deduction or CTAD from inherited
+ // constructors. The bool member indicates whether this deduction guide is
+ // generated from an inherited constructor.
+ llvm::PointerIntPair<CXXDeductionGuideDecl *, 1, bool> SourceDeductionGuide;
void setExplicitSpecifier(ExplicitSpecifier ES) { ExplicitSpec = ES; }
public:
@@ -1979,7 +1988,9 @@ class CXXDeductionGuideDecl : public FunctionDecl {
ExplicitSpecifier ES, const DeclarationNameInfo &NameInfo, QualType T,
TypeSourceInfo *TInfo, SourceLocation EndLocation,
CXXConstructorDecl *Ctor = nullptr,
- DeductionCandidate Kind = DeductionCandidate::Normal);
+ DeductionCandidate Kind = DeductionCandidate::Normal,
+ CXXDeductionGuideDecl *SourceDG = nullptr,
+ bool IsGeneratedFromInheritedConstructor = false);
static CXXDeductionGuideDecl *CreateDeserialized(ASTContext &C,
GlobalDeclID ID);
@@ -1999,6 +2010,25 @@ class CXXDeductionGuideDecl : public FunctionDecl {
/// this is an implicit deduction guide.
CXXConstructorDecl *getCorrespondingConstructor() const { return Ctor; }
+ /// Get the deduction guide from which this deduction guide was generated,
+ /// if it was generated as part of alias template deduction or from an
+ /// inherited constructor.
+ CXXDeductionGuideDecl *getSourceDeductionGuide() const {
+ return SourceDeductionGuide.getPointer();
+ }
+
+ void setSourceDeductionGuide(CXXDeductionGuideDecl *DG) {
+ SourceDeductionGuide.setPointer(DG);
+ }
+
+ bool isGeneratedFromInheritedConstructor() const {
+ return SourceDeductionGuide.getInt();
+ }
+
+ void setGeneratedFromInheritedConstructor(bool G = true) {
+ SourceDeductionGuide.setInt(G);
+ }
+
void setDeductionCandidateKind(DeductionCandidate K) {
FunctionDeclBits.DeductionCandidateKind = static_cast<unsigned char>(K);
}
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 0ea3677355169..da144704957ca 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4808,6 +4808,10 @@ def note_ovl_candidate_underqualified : Note<
"make %2 equal %1">;
def note_ovl_candidate_substitution_failure : Note<
"candidate template ignored: substitution failure%0%1">;
+def note_ovl_candidate_inherited_constructor_deduction_failure: Note<
+ "candidate template ignored: could not deduce template arguments for %0 from %1%2">;
+def note_ovl_candidate_inherited_constructor_deduction_failure_source: Note<
+ "generated from %0 constructor">;
def note_ovl_candidate_disabled_by_enable_if : Note<
"candidate template ignored: disabled by %0%1">;
def note_ovl_candidate_disabled_by_requirement : Note<
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 9bb035c07b8ae..e4c6ef256872a 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -3937,14 +3937,16 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
importExplicitSpecifier(Err, Guide->getExplicitSpecifier());
CXXConstructorDecl *Ctor =
importChecked(Err, Guide->getCorrespondingConstructor());
+ CXXDeductionGuideDecl *SourceDG =
+ importChecked(Err, Guide->getSourceDeductionGuide());
if (Err)
return std::move(Err);
if (GetImportedOrCreateDecl<CXXDeductionGuideDecl>(
ToFunction, D, Importer.getToContext(), DC, ToInnerLocStart, ESpec,
- NameInfo, T, TInfo, ToEndLoc, Ctor))
+ NameInfo, T, TInfo, ToEndLoc, Ctor,
+ Guide->getDeductionCandidateKind(), SourceDG,
+ Guide->isGeneratedFromInheritedConstructor()))
return ToFunction;
- cast<CXXDeductionGuideDecl>(ToFunction)
- ->setDeductionCandidateKind(Guide->getDeductionCandidateKind());
} else {
if (GetImportedOrCreateDecl(
ToFunction, D, Importer.getToContext(), DC, ToInnerLocStart,
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index d5c140fd34389..91877a49dcf5e 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -2157,9 +2157,11 @@ CXXDeductionGuideDecl *CXXDeductionGuideDecl::Create(
ASTContext &C, DeclContext *DC, SourceLocation StartLoc,
ExplicitSpecifier ES, const DeclarationNameInfo &NameInfo, QualType T,
TypeSourceInfo *TInfo, SourceLocation EndLocation, CXXConstructorDecl *Ctor,
- DeductionCandidate Kind) {
- return new (C, DC) CXXDeductionGuideDecl(C, DC, StartLoc, ES, NameInfo, T,
- TInfo, EndLocation, Ctor, Kind);
+ DeductionCandidate Kind, CXXDeductionGuideDecl *SourceDG,
+ bool IsGeneratedFromInheritedConstructor) {
+ return new (C, DC) CXXDeductionGuideDecl(
+ C, DC, StartLoc, ES, NameInfo, T, TInfo, EndLocation, Ctor, Kind,
+ SourceDG, IsGeneratedFromInheritedConstructor);
}
CXXDeductionGuideDecl *
@@ -2167,7 +2169,7 @@ CXXDeductionGuideDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
return new (C, ID) CXXDeductionGuideDecl(
C, nullptr, SourceLocation(), ExplicitSpecifier(), DeclarationNameInfo(),
QualType(), nullptr, SourceLocation(), nullptr,
- DeductionCandidate::Normal);
+ DeductionCandidate::Normal, nullptr, false);
}
RequiresExprBodyDecl *RequiresExprBodyDecl::Create(
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index d4a48858ec419..df2b00edaf940 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -10531,6 +10531,40 @@ bool clang::isBetterOverloadCandidate(
auto *Guide1 = dyn_cast_or_null<CXXDeductionGuideDecl>(Cand1.Function);
auto *Guide2 = dyn_cast_or_null<CXXDeductionGuideDecl>(Cand2.Function);
if (Guide1 && Guide2) {
+ // -- F1 and F2 are generated from class template argument deduction
+ // for a class D, and F2 is generated from inheriting constructors
+ // from a base class of D while F1 is not, ...
+ if (Guide1->isImplicit() && Guide2->isImplicit() &&
+ Guide1->isGeneratedFromInheritedConstructor() !=
+ Guide2->isGeneratedFromInheritedConstructor()) {
+ const FunctionProtoType *FPT1 =
+ Guide1->getType()->getAs<FunctionProtoType>();
+ const FunctionProtoType *FPT2 =
+ Guide2->getType()->getAs<FunctionProtoType>();
+ assert(FPT1 && FPT2);
+
+ // ... and for each explicit function argument, the parameters of F1 and
+ // F2 are either both ellipses or have the same type
+ if (FPT1->isVariadic() == FPT2->isVariadic() &&
+ FPT1->getNumParams() == FPT2->getNumParams()) {
+ bool ParamsHaveSameType = true;
+ const auto &A1 = FPT1->getParamTypes();
+ const auto &A2 = FPT2->getParamTypes();
+ for (unsigned I = 0, N = FPT1->getNumParams(); I != N; ++I) {
+ llvm::FoldingSetNodeID ID1, ID2;
+ S.Context.getCanonicalType(A1[I]).Profile(ID1);
+ S.Context.getCanonicalType(A2[I]).Profile(ID2);
+ if (ID1 != ID2) {
+ ParamsHaveSameType = false;
+ break;
+ }
+ }
+
+ if (ParamsHaveSameType)
+ return Guide2->isGeneratedFromInheritedConstructor();
+ }
+ }
+
// -- F1 is generated from a deduction-guide and F2 is not
if (Guide1->isImplicit() != Guide2->isImplicit())
return Guide2->isImplicit();
@@ -11671,6 +11705,39 @@ static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated,
return;
}
+ // Errors in deduction guides from inherited constructors
+ // will present as substitution failures in the mapping
+ // partial specialization, so we show a generic diagnostic
+ // in this case.
+ if (auto *DG = dyn_cast<CXXDeductionGuideDecl>(Templated);
+ DG && DG->isGeneratedFromInheritedConstructor()) {
+ CXXDeductionGuideDecl *Source = DG->getSourceDeductionGuide();
+ assert(Source &&
+ "Inherited constructor deduction guides must have a source");
+ auto DeducedRecordType =
+ QualType(cast<ClassTemplateDecl>(DG->getDeducedTemplate())
+ ->getTemplatedDecl()
+ ->getTypeForDecl(),
+ 0);
+ auto InheritedRecordType =
+ QualType(cast<ClassTemplateDecl>(Source->getDeducedTemplate())
+ ->getTemplatedDecl()
+ ->getTypeForDecl(),
+ 0);
+ S.Diag(Templated->getLocation(),
+ diag::note_ovl_candidate_inherited_constructor_deduction_failure)
+ << DeducedRecordType << InheritedRecordType << TemplateArgString;
+
+ CXXConstructorDecl *Ctor = DG->getCorrespondingConstructor();
+ if (Ctor && Ctor->getBeginLoc().isValid())
+ S.Diag(
+ Ctor->getBeginLoc(),
+ diag::
+ note_ovl_candidate_inherited_constructor_deduction_failure_source)
+ << InheritedRecordType;
+ return;
+ }
+
// Format the SFINAE diagnostic into the argument string.
// FIXME: Add a general mechanism to include a PartialDiagnostic *'s
// formatted message in another diagnostic.
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 9296cc66d6918..ec939a2fb718d 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -2746,7 +2746,7 @@ struct ConvertConstructorToDeductionGuideTransform {
}
};
-unsigned getTemplateParameterDepth(NamedDecl *TemplateParam) {
+unsigned getTemplateParameterDepth(const NamedDecl *TemplateParam) {
if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(TemplateParam))
return TTP->getDepth();
if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(TemplateParam))
@@ -2768,7 +2768,7 @@ unsigned getTemplateParameterIndex(NamedDecl *TemplateParam) {
// Find all template parameters that appear in the given DeducedArgs.
// Return the indices of the template parameters in the TemplateParams.
-SmallVector<unsigned> TemplateParamsReferencedInTemplateArgumentList(
+llvm::SmallSet<unsigned, 8> TemplateParamsReferencedInTemplateArgumentList(
const TemplateParameterList *TemplateParamsList,
ArrayRef<TemplateArgument> DeducedArgs) {
struct TemplateParamsReferencedFinder
@@ -2806,17 +2806,19 @@ SmallVector<unsigned> TemplateParamsReferencedInTemplateArgumentList(
}
void Mark(unsigned Depth, unsigned Index) {
if (Index < TemplateParamList->size() &&
- TemplateParamList->getParam(Index)->getTemplateDepth() == Depth)
+ getTemplateParameterDepth(TemplateParamList->getParam(Index)) ==
+ Depth) {
ReferencedTemplateParams.set(Index);
+ }
}
};
TemplateParamsReferencedFinder Finder(TemplateParamsList);
Finder.TraverseTemplateArguments(DeducedArgs);
- SmallVector<unsigned> Results;
+ llvm::SmallSet<unsigned, 8> Results;
for (unsigned Index = 0; Index < TemplateParamsList->size(); ++Index) {
if (Finder.ReferencedTemplateParams[Index])
- Results.push_back(Index);
+ Results.insert(Index);
}
return Results;
}
@@ -3011,9 +3013,12 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
Expr *buildIsDeducibleConstraint(Sema &SemaRef,
TypeAliasTemplateDecl *AliasTemplate,
QualType ReturnType,
- SmallVector<NamedDecl *> TemplateParams) {
+ SmallVector<NamedDecl *> TemplateParams,
+ TemplateDecl *DeducingTemplate = nullptr) {
ASTContext &Context = SemaRef.Context;
// Constraint AST nodes must use uninstantiated depth.
+ assert(!DeducingTemplate || DeducingTemplate->getTemplateDepth() ==
+ AliasTemplate->getTemplateDepth());
if (auto *PrimaryTemplate =
AliasTemplate->getInstantiatedFromMemberTemplate();
PrimaryTemplate && TemplateParams.size() > 0) {
@@ -3046,19 +3051,21 @@ Expr *buildIsDeducibleConstraint(Sema &SemaRef,
Context.DeclarationNames.getCXXDeductionGuideName(AliasTemplate));
};
+ TemplateDecl *TD = DeducingTemplate ? DeducingTemplate : AliasTemplate;
+
SmallVector<TypeSourceInfo *> IsDeducibleTypeTraitArgs = {
Context.getTrivialTypeSourceInfo(
Context.getDeducedTemplateSpecializationType(
- TemplateName(AliasTemplate), /*DeducedType=*/QualType(),
+ TemplateName(TD), /*DeducedType=*/QualType(),
/*IsDependent=*/true)), // template specialization type whose
// arguments will be deduced.
Context.getTrivialTypeSourceInfo(
ReturnType), // type from which template arguments are deduced.
};
- return TypeTraitExpr::Create(
- Context, Context.getLogicalOperationType(), AliasTemplate->getLocation(),
- TypeTrait::BTT_IsDeducible, IsDeducibleTypeTraitArgs,
- AliasTemplate->getLocation(), /*Value*/ false);
+ return TypeTraitExpr::Create(Context, Context.getLogicalOperationType(),
+ TD->getLocation(), TypeTrait::BTT_IsDeducible,
+ IsDeducibleTypeTraitArgs, TD->getLocation(),
+ /*Value*/ false);
}
std::pair<TemplateDecl *, llvm::ArrayRef<TemplateArgument>>
@@ -3090,12 +3097,59 @@ getRHSTemplateDeclAndArgs(Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate) {
return {Template, AliasRhsTemplateArgs};
}
+// Build the type for a deduction guide generated from an inherited constructor
+// [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, TypeSourceInfo *DerivedClassMapperType,
+ TemplateDecl *DeducingTemplate, TypeSourceInfo *SourceGuideTSI) {
+ auto &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
+ Sema::SFINAETrap Trap(SemaRef);
+
+ MultiLevelTemplateArgumentList Args;
+ Args.addOuterTemplateArguments(DeducingTemplate,
+ TemplateArgument(FPT->getReturnType()), false);
+ Args.addOuterRetainedLevels(DeducingTemplate->getTemplateDepth());
+ TypeSourceInfo *ReturnTypeTSI =
+ SemaRef.SubstType(DerivedClassMapperType, Args,
+ DeducingTemplate->getBeginLoc(), DeclarationName());
+ if (!ReturnTypeTSI || Trap.hasErrorOccurred())
+ return {nullptr, QualType()};
+ const 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 auto &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 *TSI = TLB.getTypeSourceInfo(Context, FT);
+ return {TSI, ReturnType};
+}
+
// 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) {
+FunctionTemplateDecl *BuildDeductionGuideForTypeAlias(
+ Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate,
+ FunctionTemplateDecl *F, SourceLocation Loc,
+ TemplateDecl *DeducingTemplate = nullptr,
+ TypeSourceInfo *DerivedClassMapperType = nullptr) {
LocalInstantiationScope Scope(SemaRef);
Sema::InstantiatingTemplate BuildingDeductionGuides(
SemaRef, AliasTemplate->getLocation(), F,
@@ -3103,6 +3157,9 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
if (BuildingDeductionGuides.isInvalid())
return nullptr;
+ if (!DeducingTemplate)
+ DeducingTemplate = AliasTemplate;
+
auto &Context = SemaRef.Context;
auto [Template, AliasRhsTemplateArgs] =
getRHSTemplateDeclAndArgs(SemaRef, AliasTemplate);
@@ -3268,8 +3325,17 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
Sema::CodeSynthesisContext::BuildingDeductionGuides)) {
auto *GG = cast<CXXDeductionGuideDecl>(FPrime);
- Expr *IsDeducible = buildIsDeducibleConstraint(
- SemaRef, AliasTemplate, FPrime->getReturnType(), FPrimeTemplateParams);
+ TypeSourceInfo *TSI = GG->getTypeSourceInfo();
+ QualType ReturnType = FPrime->getReturnType();
+ if (DerivedClassMapperType)
+ std::tie(TSI, ReturnType) = buildInheritedConstructorDeductionGuideType(
+ SemaRef, DerivedClassMapperType, DeducingTemplate, TSI);
+ if (!TSI)
+ return nullptr;
+
+ Expr *IsDeducible =
+ buildIsDeducibleConstraint(SemaRef, AliasTemplate, ReturnType,
+ FPrimeTemplateParams, DeducingTemplate);
Expr *RequiresClause =
buildAssociatedConstraints(SemaRef, F, AliasTemplate, DeduceResults,
FirstUndeducedParamIdx, IsDeducible);
@@ -3281,27 +3347,37 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
AliasTemplate->getTemplateParameters()->getRAngleLoc(),
/*RequiresClause=*/RequiresClause);
auto *Result = cast<FunctionTemplateDecl>(buildDeductionGuide(
- SemaRef, AliasTemplate, FPrimeTemplateParamList,
- GG->getCorrespondingConstructor(), GG->getExplicitSpecifier(),
- GG->getTypeSourceInfo(), AliasTemplate->getBeginLoc(),
- AliasTemplate->getLocation(), AliasTemplate->getEndLoc(),
- F->isImplicit()));
- cast<CXXDeductionGuideDecl>(Result->getTemplatedDecl())
- ->setDeductionCandidateKind(GG->getDeductionCandidateKind());
+ SemaRef, DeducingTemplate, FPrimeTemplateParamList,
+ GG->getCorrespondingConstructor(), GG->getExplicitSpecifier(), TSI,
+ AliasTemplate->getBeginLoc(), AliasTemplate->getLocation(),
+ AliasTemplate->getEndLoc(), F->isImplicit()));
+ auto *DGuide = cast<CXXDeductionGuideDecl>(Result->getTemplatedDecl());
+ DGuide->setDeductionCandidateKind(GG->getDeductionCandidateKind());
+ DGuide->setSourceDeductionGuide(
+ cast<CXXDeductionGuideDecl>(F->getTemplatedDecl()));
+ if (DerivedClassMapperType)
+ DGuide->setGeneratedFromInheritedConstructor();
return Result;
}
return nullptr;
}
void DeclareImplicitDeductionGuidesForTypeAlias(
- Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate, SourceLocation Loc) {
+ Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate, SourceLocation Loc,
+ TemplateDecl *DeducingTemplate = nullptr,
+ TypeSourceInfo *DerivedClassMapperType = nullptr) {
if (AliasTemplate->isInvalidDecl())
return;
+ if (!DeducingTemplate)
+ DeducingTemplate = AliasTemplate;
auto &Context = SemaRef.Context;
// FIXME: if there is an explicit deduction guide after the first use of the
// type alias usage, we will not cover this explicit deduction guide. fix this
// case.
- if (hasDeclaredDeductionGuides(
+ // This check is already performed in the inherited constructor case by
+ // DeclareImplicitDeductionGuides
+ if (!DerivedClassMapperType &&
+ hasDeclaredDeductionGuides(
Context.DeclarationNames.getCXXDeductionGuideName(AliasTemplate),
AliasTemplate->getDeclContext()))
return;
@@ -3334,17 +3410,29 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
NewParam->setScopeInfo(0, I);
FPTL.setParam(I, NewParam);
}
- auto *Transformed = cast<FunctionDecl>(buildDeductionGuide(
- SemaRef, AliasTemplate, /*TemplateParams=*/nullptr,
+
+ QualType ReturnType =
+ cast<FunctionProtoType>(FunctionType->getType())->getReturnType();
+ if (DerivedClassMapperType)
+ std::tie(FunctionType, ReturnType) =
+ buildInheritedConstructorDeductionGuideType(
+ SemaRef, DerivedClassMapperType, DeducingTemplate,
+ FunctionType);
+ if (!FunctionType)
+ continue;
+
+ auto *Transformed = cast<CXXDeductionGuideDecl>(buildDeductionGuide(
+ SemaRef, DeducingTemplate, /*TemplateParams=*/nullptr,
/*Constructor=*/nullptr, DG->getExplicitSpecifier(), FunctionType,
AliasTemplate->getBeginLoc(), AliasTemplate->getLocation(),
AliasTemplate->getEndLoc(), DG->isImplicit()));
+ Transformed->setSourceDeductionGuide(DG);
// FIXME: Here the synthesized deduction guide is not a templated
// function. Per [dcl.decl]p4, the requires-clause shall be present only
// if the declarator declares a templated function, a bug in standard?
auto *Constraint = buildIsDeducibleConstraint(
- SemaRef, AliasTemplate, Transformed->getReturnType(), {});
+ SemaRef, AliasTemplate, ReturnType, {}, DeducingTemplate);
if (auto *RC = DG->getTrailingRequiresClause()) {
auto Conjunction =
SemaRef.BuildBinOp(SemaRef.getCurScope(), SourceLocation{},
@@ -3364,8 +3452,221 @@ 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,
+ const CXXBaseSpecifier &Base, unsigned BaseIdx) {
+ auto &Context = SemaRef.Context;
+ DeclContext *DC = Template->getDeclContext();
+ const auto *BaseTST = Base.getType()->getAs<TemplateSpecializationType>();
+ if (!BaseTST)
+ return;
+
+ 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 [Base] ...
+ auto *TransformedBase =
+ SemaRef.SubstType(Base.getTypeSourceInfo(), Args, Base.getBaseTypeLoc(),
+ DeclarationName(), true);
+ auto *BaseAD = TypeAliasDecl::Create(
+ Context, DC, SourceLocation(), Base.getBaseTypeLoc(),
+ 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, Base.getBaseTypeLoc(), 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);
+ 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,
+ CanonType, nullptr);
+ MapperSpecialization->setImplicit();
+ MapperSpecialization->startDefinition();
+
+ TemplateArgumentListInfo TemplateArgs;
+ TemplateArgs.addArgument({AliasTA, TransformedBase});
+ MapperSpecialization->setTemplateArgsAsWritten(TemplateArgs);
+
+ // ... having a member typedef `type` designating a template specialization
+ // with the template argument list of A
+ // but with [Template] as the template
+ auto DerivedTN = Context.getCanonicalTemplateName(TemplateName(Template));
+ QualType DerivedTST =
+ Context.getTemplateSpecializationType(DerivedTN, SubstArgs);
+
+ TypeLocBuilder MapperTypedefTLB;
+ TemplateSpecializationTypeLoc TSTL =
+ MapperTypedefTLB.push<TemplateSpecializationTypeLoc>(DerivedTST);
+ TSTL.setTemplateNameLoc(Template->getLocation());
+ TSTL.setLAngleLoc(Template->getTemplateParameters()->getLAngleLoc());
+ TSTL.setRAngleLoc(Template->getTemplateParameters()->getRAngleLoc());
+ TSTL.setTemplateKeywordLoc(Template->getBeginLoc());
+ for (unsigned I = 0, C = SubstArgs.size(); I < C; ++I)
+ TSTL.setArgLocInfo(I,
+ TemplateArgumentLocInfo(Context.getTrivialTypeSourceInfo(
+ SubstArgs[I].getAsType(),
+ TemplateTPL->getParam(I)->getLocation())));
+
+ const auto &MapperTypedefII = Context.Idents.get("type");
+ TypeSourceInfo *MapperTypedefTSI =
+ MapperTypedefTLB.getTypeSourceInfo(Context, DerivedTST);
+ TypedefDecl *DerivedTypedef = TypedefDecl::Create(
+ Context, MapperSpecialization, Base.getBeginLoc(), Base.getBeginLoc(),
+ &MapperTypedefII, MapperTypedefTSI);
+
+ DerivedTypedef->setImplicit();
+ DerivedTypedef->setAccess(AS_public);
+ MapperSpecialization->addDecl(DerivedTypedef);
+
+ MapperSpecialization->completeDefinition();
+
+ MapperTD->AddPartialSpecialization(MapperSpecialization, nullptr);
+ DC->addDecl(MapperSpecialization);
+
+ // ... the set contains the guides of A with the return type R
+ // of each guide replaced with `typename CC<R>::type` ...
+ QualType MapperSpecializationType = Context.getTemplateSpecializationType(
+ MapperTemplateName, Context.getInjectedTemplateArg(TParam));
+
+ auto *NNS = NestedNameSpecifier::Create(
+ Context, nullptr, false, MapperSpecializationType.getTypePtr());
+ QualType MapperReturnType = Context.getDependentNameType(
+ ElaboratedTypeKeyword::Typename, NNS, &MapperTypedefII);
+
+ NestedNameSpecifierLocBuilder NNSLocBuilder;
+ NNSLocBuilder.MakeTrivial(Context, NNS, SourceRange(Template->getBeginLoc()));
+ NestedNameSpecifierLoc QualifierLoc =
+ NNSLocBuilder.getWithLocInContext(Context);
+
+ TypeLocBuilder ReturnTypeTLB;
+ DependentNameTypeLoc DepTL =
+ ReturnTypeTLB.push<DependentNameTypeLoc>(MapperReturnType);
+ DepTL.setQualifierLoc(QualifierLoc);
+
+ TypeSourceInfo *MapperTSI =
+ ReturnTypeTLB.getTypeSourceInfo(Context, MapperReturnType);
+
+ DeclareImplicitDeductionGuidesForTypeAlias(
+ SemaRef, BaseATD, Base.getBeginLoc(), Template, MapperTSI);
}
// Build an aggregate deduction guide for a type alias template.
@@ -3535,6 +3836,34 @@ void Sema::DeclareImplicitDeductionGuides(TemplateDecl *Template,
if (!AddedAny)
Transform.buildSimpleDeductionGuide(std::nullopt);
+ // FIXME: Handle explicit deduction guides from inherited constructors
+ // when the base deduction guides are declared after this has first run
+ if (getLangOpts().CPlusPlus23 &&
+ Pattern->getTemplatedDecl()->hasDefinition()) {
+ unsigned BaseIdx = 0;
+ for (const auto &Base : Pattern->getTemplatedDecl()->bases()) {
+ ++BaseIdx;
+ if (!Base.getType()->isDependentType())
+ continue;
+
+ // The InheritConstructors field is not set for dependent
+ // bases because a using decl that inherits constructors
+ // for that base is unresolved.
+ // FIXME: This does not work for name specifiers
+ // like `using Derived::Base::Base;`
+ CanQualType T = Context.getCanonicalType(Base.getType());
+ DeclarationName Name = Context.DeclarationNames.getCXXConstructorName(T);
+ auto LR = Pattern->getTemplatedDecl()->lookup(Name);
+ assert((LR.empty() || LR.isSingleResult()) &&
+ "Expected at most one UsingDecl for a given base");
+ if (LR.empty() || !isa<UnresolvedUsingValueDecl>(LR.front()))
+ continue;
+
+ DeclareImplicitDeductionGuidesFromInheritedConstructors(
+ *this, Template, Pattern, Base, BaseIdx - 1);
+ }
+ }
+
// -- An additional function template derived as above from a hypothetical
// constructor C(C), called the copy deduction candidate.
cast<CXXDeductionGuideDecl>(
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 01432301633ed..1378b0664601a 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2192,7 +2192,8 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
SemaRef.Context, DC, D->getInnerLocStart(),
InstantiatedExplicitSpecifier, NameInfo, T, TInfo,
D->getSourceRange().getEnd(), DGuide->getCorrespondingConstructor(),
- DGuide->getDeductionCandidateKind());
+ DGuide->getDeductionCandidateKind(), DGuide->getSourceDeductionGuide(),
+ DGuide->isGeneratedFromInheritedConstructor());
Function->setAccess(D->getAccess());
} else {
Function = FunctionDecl::Create(
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index cbaf1b0a98c61..509a04708c059 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2293,6 +2293,8 @@ void ASTDeclReader::VisitCXXDeductionGuideDecl(CXXDeductionGuideDecl *D) {
VisitFunctionDecl(D);
D->setDeductionCandidateKind(
static_cast<DeductionCandidate>(Record.readInt()));
+ D->SourceDeductionGuide.setPointer(readDeclAs<CXXDeductionGuideDecl>());
+ D->setGeneratedFromInheritedConstructor(Record.readBool());
}
void ASTDeclReader::VisitCXXMethodDecl(CXXMethodDecl *D) {
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index b6583c54c9ba1..5b165096dbb50 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -790,6 +790,8 @@ void ASTDeclWriter::VisitCXXDeductionGuideDecl(CXXDeductionGuideDecl *D) {
Record.AddDeclRef(D->Ctor);
VisitFunctionDecl(D);
Record.push_back(static_cast<unsigned char>(D->getDeductionCandidateKind()));
+ Record.AddDeclRef(D->SourceDeductionGuide.getPointer());
+ Record.push_back(D->isGeneratedFromInheritedConstructor());
Code = serialization::DECL_CXX_DEDUCTION_GUIDE;
}
diff --git a/clang/test/SemaCXX/cxx23-ctad-inherited-ctors.cpp b/clang/test/SemaCXX/cxx23-ctad-inherited-ctors.cpp
new file mode 100644
index 0000000000000..cd55af9146734
--- /dev/null
+++ b/clang/test/SemaCXX/cxx23-ctad-inherited-ctors.cpp
@@ -0,0 +1,260 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++23 -verify %s
+
+namespace test1 {
+ template<typename T> struct Base {
+ template<typename V = T> requires true
+ Base(T);
+ };
+
+ template<typename T> struct InheritsCtors : public Base<T> {
+ using Base<T>::Base;
+ };
+
+ InheritsCtors inheritsCtors(1);
+ using IC = InheritsCtors<int>;
+ using IC = decltype(inheritsCtors);
+
+ template<typename T> struct DoesNotInheritCtors : public Base<T> {}; // expected-note {{candidate template ignored: could not match 'DoesNotInheritCtors<T>' against 'int'}} \
+ // expected-note 3{{implicit deduction guide declared as}} \
+ // expected-note {{candidate function template not viable: requires 0 arguments, but 1 was provided}} \
+ // expected-note {{candidate template ignored: could not match 'Base<T>' against 'int'}}
+ DoesNotInheritCtors doesNotInheritCtors(100); // expected-error {{no viable constructor or deduction guide for deduction of template arguments}}
+
+ template<typename T> struct InheritsSecond : public Base<T> {
+ using Base<T>::Base;
+ };
+
+ InheritsSecond inheritsSecond('a');
+ using IS = InheritsSecond<char>;
+ using IS = decltype(inheritsSecond);
+
+ template<typename T> struct NonTemplateDGuideBase {
+ NonTemplateDGuideBase(T); // expected-note {{generated from 'NonTemplateDGuideBase<T>' constructor}}
+ };
+ NonTemplateDGuideBase(int) -> NonTemplateDGuideBase<char>;
+ NonTemplateDGuideBase(const char *) -> NonTemplateDGuideBase<const char *>;
+
+ template<typename T>
+ concept NoPointers = !requires (T t) { *t; };
+
+ template<NoPointers T>
+ struct NonTemplateDGuideDerived : public NonTemplateDGuideBase<T> { // expected-note {{candidate function not viable: no known conversion from 'const char[1]' to 'int' for 1st argument}} \
+ // expected-note {{candidate template ignored: could not match 'NonTemplateDGuideDerived<T>' against 'const char *'}} \
+ // expected-note {{candidate template ignored: could not deduce template arguments for 'NonTemplateDGuideDerived<T>' from 'NonTemplateDGuideBase<T>' [with T = const char *]}} \
+ // expected-note {{implicit deduction guide declared as 'template <NoPointers<> T> requires __is_deducible(test1::NonTemplateDGuideDerived, typename __ctad_mapper_NonTemplateDGuideBase_to_NonTemplateDGuideDerived_0<NonTemplateDGuideBase<type-parameter-0-0>>::type) NonTemplateDGuideDerived(type-parameter-0-0) -> typename __ctad_mapper_NonTemplateDGuideBase_to_NonTemplateDGuideDerived_0<NonTemplateDGuideBase<type-parameter-0-0>>::type'}} \
+ // expected-note {{candidate template ignored: could not match 'NonTemplateDGuideBase<type-parameter-0-0>' against 'const char *'}} \
+ // expected-note {{implicit deduction guide declared as 'template <NoPointers<> T> requires __is_deducible(test1::NonTemplateDGuideDerived, typename __ctad_mapper_NonTemplateDGuideBase_to_NonTemplateDGuideDerived_0<NonTemplateDGuideBase<type-parameter-0-0>>::type) NonTemplateDGuideDerived(NonTemplateDGuideBase<type-parameter-0-0>) -> typename __ctad_mapper_NonTemplateDGuideBase_to_NonTemplateDGuideDerived_0<NonTemplateDGuideBase<type-parameter-0-0>>::type'}} \
+ // expected-note {{candidate function template not viable: requires 0 arguments, but 1 was provided}} \
+ // expected-note 2{{implicit deduction guide declared as }}
+ using NonTemplateDGuideBase<T>::NonTemplateDGuideBase;
+ };
+
+ NonTemplateDGuideDerived ntdg(1);
+ using NTDG = NonTemplateDGuideDerived<char>;
+ using NTDG = decltype(ntdg);
+
+ NonTemplateDGuideDerived ntdg_char(""); // expected-error {{no viable constructor or deduction guide for deduction of template arguments}}
+
+ template<typename T>
+ struct ExplicitBase {
+ template<typename V>
+ ExplicitBase(V);
+ };
+
+ template<typename T>
+ ExplicitBase(T) -> ExplicitBase<T>;
+
+ template<NoPointers T>
+ struct ExplicitDerived : public ExplicitBase<T> { // expected-note {{candidate template ignored: couldn't infer template argument 'T'}} \
+ // expected-note {{implicit deduction guide declared as 'template <NoPointers<> T, typename V> requires __is_deducible(test1::ExplicitDerived, typename __ctad_mapper_ExplicitBase_to_ExplicitDerived_0<ExplicitBase<type-parameter-0-0>>::type) ExplicitDerived(type-parameter-0-1) -> typename __ctad_mapper_ExplicitBase_to_ExplicitDerived_0<ExplicitBase<type-parameter-0-0>>::type'}} \
+ // expected-note {{candidate template ignored: could not match 'ExplicitDerived<T>' against 'const char *'}} \
+ // expected-note {{candidate template ignored: could not match 'ExplicitBase<type-parameter-0-0>' against 'const char *'}} \
+ // expected-note {{implicit deduction guide declared as 'template <NoPointers<> T> requires __is_deducible(test1::ExplicitDerived, typename __ctad_mapper_ExplicitBase_to_ExplicitDerived_0<ExplicitBase<type-parameter-0-0>>::type) ExplicitDerived(ExplicitBase<type-parameter-0-0>) -> typename __ctad_mapper_ExplicitBase_to_ExplicitDerived_0<ExplicitBase<type-parameter-0-0>>::type'}} \
+ // expected-note {{candidate template ignored: could not deduce template arguments for 'ExplicitDerived<T>' from 'ExplicitBase<T>' [with T = const char *]}} \
+ // expected-note {{candidate function template not viable: requires 0 arguments, but 1 was provided}} \
+ // expected-note 2{{implicit deduction guide declared as }}
+
+ using ExplicitBase<T>::ExplicitBase;
+ };
+
+ ExplicitDerived ed(10);
+ using ED = ExplicitDerived<int>;
+ using ED = decltype(ed);
+
+ ExplicitDerived substFail(""); // expected-error {{no viable constructor or deduction guide for deduction of template arguments}}
+
+ // FIXME: Support deduction guides that were declared
+ // after the initial implicit guides are declared for
+ // the derived template.
+#if 0
+ Base(int) -> Base<char>;
+
+ InheritsCtors ic2(1);
+ using IC2 = InheritsCtors<char>;
+ using IC2 = decltype(ic2);
+#endif
+}
+
+namespace test2 {
+ template<typename T, typename U, typename V> struct Base {
+ Base(T, U, V);
+ };
+
+ template<typename T, typename U> struct Derived : public Base<U, T, int> {
+ // FIXME: Current implementation does not find Base's constructors correctly
+ // with the below using decl
+ //using Derived::Base::Base;
+
+ using Base<U, T, int>::Base;
+ };
+
+ Derived derived(true, 'a', 1);
+ using D = Derived<char, bool>;
+ using D = decltype(derived);
+}
+
+namespace test3 {
+ template<typename T> struct Base {
+ Base(T); // expected-note {{generated from 'Base<T>' constructor}}
+ };
+
+ template<typename T, typename U> struct NotEnoughParams : public Base<T> { // expected-note {{candidate template ignored: could not deduce template arguments for 'NotEnoughParams<T, U>' from 'Base<T>' [with T = int]}} \
+ // expected-note {{implicit deduction guide declared as 'template <typename T> requires __is_deducible(test3::NotEnoughParams, typename __ctad_mapper_Base_to_NotEnoughParams_0<Base<type-parameter-0-0>>::type) NotEnoughParams(type-parameter-0-0) -> typename __ctad_mapper_Base_to_NotEnoughParams_0<Base<type-parameter-0-0>>::type'}} \
+ // expected-note {{candidate template ignored: could not match 'Base<type-parameter-0-0>' against 'int'}} \
+ // expected-note {{implicit deduction guide declared as 'template <typename T> requires __is_deducible(test3::NotEnoughParams, typename __ctad_mapper_Base_to_NotEnoughParams_0<Base<type-parameter-0-0>>::type) NotEnoughParams(Base<type-parameter-0-0>) -> typename __ctad_mapper_Base_to_NotEnoughParams_0<Base<type-parameter-0-0>>::type'}} \
+ // expected-note {{candidate template ignored: could not match 'NotEnoughParams<T, U>' against 'int'}} \
+ // expected-note {{candidate template ignored: could not match 'Base<T>' against 'int'}} \
+ // expected-note {{candidate function template not viable: requires 0 arguments, but 1 was provided}} \
+ // expected-note 3{{implicit deduction guide declared as}}
+ using Base<T>::Base;
+ };
+
+ NotEnoughParams notEnoughParams(1); // expected-error {{no viable constructor or deduction guide for deduction of template arguments}}
+}
+
+namespace test4 {
+ template<typename T> struct B {
+ B(T t);
+ };
+
+ template<typename T = int, typename V = int>
+ struct DefaultArgsNotInBase : public B<V> {
+ using B<V>::B;
+ };
+
+ DefaultArgsNotInBase d('d');
+ using D = DefaultArgsNotInBase<int, char>;
+ using D = decltype(d);
+
+ template<typename T> struct BaseEmptyCtor {
+ BaseEmptyCtor();
+ };
+
+ template<typename T = int, typename V = int>
+ struct DefaultArgsNotInBaseEmpty : public BaseEmptyCtor<V> {
+ using BaseEmptyCtor<V>::BaseEmptyCtor;
+ };
+
+ DefaultArgsNotInBaseEmpty d2;
+ using D2 = DefaultArgsNotInBaseEmpty<>;
+ using D2 = decltype(d2);
+}
+
+namespace test5 {
+ template<typename T> struct Base {
+ Base(T);
+ };
+ template<typename T> struct Outer {
+ template<typename U> struct Inner : public Base<U> {
+ using Base<U>::Base;
+ };
+ };
+
+ Outer<int>::Inner i(10);
+ using I = Outer<int>::Inner<int>;
+ using I = decltype(i);
+}
+
+namespace test6 {
+ template<typename T>
+ concept False = false;
+
+ template<typename T>
+ concept True = true;
+
+ template<typename T>
+ struct Base {
+ Base(T); // expected-note {{generated from 'Base<T>' constructor}}
+ };
+
+ template<False F>
+ struct DerivedFalse : public Base<F> { // expected-note {{candidate template ignored: could not deduce template arguments for 'DerivedFalse<F>' from 'Base<T>' [with F = int]}} \
+ // expected-note {{implicit deduction guide declared as 'template <False<> F> requires __is_deducible(test6::DerivedFalse, typename __ctad_mapper_Base_to_DerivedFalse_0<Base<type-parameter-0-0>>::type) DerivedFalse(type-parameter-0-0) -> typename __ctad_mapper_Base_to_DerivedFalse_0<Base<type-parameter-0-0>>::type'}} \
+ // expected-note {{candidate template ignored: could not match 'Base<type-parameter-0-0>' against 'int'}} \
+ // expected-note {{implicit deduction guide declared as 'template <False<> F> requires __is_deducible(test6::DerivedFalse, typename __ctad_mapper_Base_to_DerivedFalse_0<Base<type-parameter-0-0>>::type) DerivedFalse(Base<type-parameter-0-0>) -> typename __ctad_mapper_Base_to_DerivedFalse_0<Base<type-parameter-0-0>>::type'}} \
+ // expected-note {{candidate template ignored: could not match 'DerivedFalse<F>' against 'int'}} \
+ // expected-note {{candidate template ignored: could not match 'Base<F>' against 'int'}} \
+ // expected-note {{candidate function template not viable: requires 0 arguments, but 1 was provided}} \
+ // expected-note 3{{implicit deduction guide declared as}}
+ using Base<F>::Base;
+ };
+
+ template<True F>
+ struct DerivedTrue : public Base<F> {
+ using Base<F>::Base;
+ };
+
+ DerivedFalse df(10); // expected-error {{no viable constructor or deduction guide for deduction of template arguments}}
+
+ DerivedTrue dt(10);
+ using DT = DerivedTrue<int>;
+ using DT = decltype(dt);
+}
+
+namespace test7 {
+ template<typename T, typename U>
+ struct Base1 {
+ Base1();
+ Base1(T, U);
+ };
+
+ template<typename T, typename U>
+ struct Base2 {
+ Base2();
+ Base2(T, U);
+ };
+
+ template<typename T = int, typename U = int>
+ struct MultipleInheritance : public Base1<T, U*> , Base2<U*, T> { // expected-note {{candidate function [with T = int, U = int]}} \
+ // expected-note {{candidate function [with T = int, U = int]}}
+ using Base1<T, U*>::Base1;
+ using Base2<U*, T>::Base2;
+ };
+
+ MultipleInheritance mi1(1, "");
+ using MI1 = MultipleInheritance<int, const char>;
+ using MI1 = decltype(mi1);
+
+ MultipleInheritance mi2("", 1);
+ using MI2 = MultipleInheritance<int, const char>;
+ using MI2 = decltype(mi2);
+
+ // This is an odd case.
+ // Since the base DGs have the deducible constraint, they are more specialized than MultipleInheritance's
+ // ctor, and take priority before the new clause in P2582R1 is applied.
+ MultipleInheritance mi3; // expected-error {{ambiguous deduction for template arguments of 'MultipleInheritance'}}
+
+ template<typename T>
+ struct MultipleInheritanceSameBase : public Base1<T, const T*>, Base1<const T*, T> {
+ using Base1<T, const T*>::Base1;
+ using Base1<const T*, T>::Base1;
+ };
+
+ MultipleInheritanceSameBase misb1('a', "");
+ using MISB1 = MultipleInheritanceSameBase<char>;
+ using MISB1 = decltype(misb1);
+
+ MultipleInheritanceSameBase misb2("", 'a');
+ using MISB2 = MultipleInheritanceSameBase<char>;
+ using MISB2 = decltype(misb2);
+}
diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp
index 92f9bae6cb064..bb27280303c9b 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -8077,6 +8077,8 @@ TEST_P(ImportFunctions, CTADImplicit) {
auto *ToD = Import(FromD, Lang_CXX17);
ASSERT_TRUE(ToD);
EXPECT_EQ(ToD->getDeductionCandidateKind(), DeductionCandidate::Copy);
+ EXPECT_EQ(ToD->getSourceDeductionGuide(), nullptr);
+ EXPECT_FALSE(ToD->isGeneratedFromInheritedConstructor());
// Check that the deduced class template is also imported.
EXPECT_TRUE(findFromTU(FromD)->Importer->GetAlreadyImportedOrNull(
FromD->getDeducedTemplate()));
@@ -8101,6 +8103,8 @@ TEST_P(ImportFunctions, CTADUserDefinedExplicit) {
ASSERT_TRUE(ToD);
EXPECT_FALSE(FromD->isImplicit());
EXPECT_TRUE(ToD->isExplicit());
+ EXPECT_EQ(ToD->getSourceDeductionGuide(), nullptr);
+ EXPECT_FALSE(ToD->isGeneratedFromInheritedConstructor());
}
TEST_P(ImportFunctions, CTADWithLocalTypedef) {
@@ -8119,6 +8123,45 @@ TEST_P(ImportFunctions, CTADWithLocalTypedef) {
ASSERT_TRUE(ToD);
}
+TEST_P(ImportFunctions, CTADAliasTemplate) {
+ Decl *TU = getTuDecl(
+ R"(
+ template <typename T> struct A {
+ A(T);
+ };
+ template<typename T>
+ using B = A<T>;
+ B b{(int)0};
+ )",
+ Lang_CXX20, "input.cc");
+ auto *FromD = FirstDeclMatcher<CXXDeductionGuideDecl>().match(
+ TU, cxxDeductionGuideDecl(hasParameter(0, hasType(asString("int")))));
+ auto *ToD = Import(FromD, Lang_CXX20);
+ ASSERT_TRUE(ToD);
+ EXPECT_FALSE(ToD->isGeneratedFromInheritedConstructor());
+ EXPECT_TRUE(ToD->getSourceDeductionGuide());
+}
+
+TEST_P(ImportFunctions, CTADInheritedCtor) {
+ Decl *TU = getTuDecl(
+ R"(
+ template <typename T> struct A {
+ A(T);
+ };
+ template <typename T> struct B : public A<T> {
+ using A<T>::A;
+ };
+ B b{(int)0};
+ )",
+ Lang_CXX23, "input.cc");
+ auto *FromD = FirstDeclMatcher<CXXDeductionGuideDecl>().match(
+ TU, cxxDeductionGuideDecl(hasParameter(0, hasType(asString("int")))));
+ auto *ToD = Import(FromD, Lang_CXX23);
+ ASSERT_TRUE(ToD);
+ EXPECT_TRUE(ToD->isGeneratedFromInheritedConstructor());
+ EXPECT_TRUE(ToD->getSourceDeductionGuide());
+}
+
TEST_P(ImportFunctions, ParmVarDeclDeclContext) {
constexpr auto FromTUCode = R"(
void f(int P);
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 27e2213e54caa..241e250e2bcc5 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -429,7 +429,7 @@ <h2 id="cxx23">C++23 implementation status</h2>
<tr>
<td>Class template argument deduction from inherited constructors</td>
<td><a href="https://wg21.link/P2582R1">P2582R1</a></td>
- <td class="none" align="center">No</td>
+ <td class="unreleased" align="center">Clang 20</td>
</tr>
<tr>
<td>Portable assumptions</td>
>From 9d3c66426bed5d751c5fdecfa96c083595e8bbd9 Mon Sep 17 00:00:00 2001
From: antangelo <contact at antangelo.com>
Date: Mon, 15 Jul 2024 23:32:12 -0400
Subject: [PATCH 2/4] Fix PR comment
---
clang/lib/AST/DeclCXX.cpp | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 91877a49dcf5e..87b64b95f1f5b 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -2157,11 +2157,11 @@ CXXDeductionGuideDecl *CXXDeductionGuideDecl::Create(
ASTContext &C, DeclContext *DC, SourceLocation StartLoc,
ExplicitSpecifier ES, const DeclarationNameInfo &NameInfo, QualType T,
TypeSourceInfo *TInfo, SourceLocation EndLocation, CXXConstructorDecl *Ctor,
- DeductionCandidate Kind, CXXDeductionGuideDecl *SourceDG,
+ DeductionCandidate Kind, CXXDeductionGuideDecl *GeneratedFrom,
bool IsGeneratedFromInheritedConstructor) {
return new (C, DC) CXXDeductionGuideDecl(
C, DC, StartLoc, ES, NameInfo, T, TInfo, EndLocation, Ctor, Kind,
- SourceDG, IsGeneratedFromInheritedConstructor);
+ GeneratedFrom, IsGeneratedFromInheritedConstructor);
}
CXXDeductionGuideDecl *
@@ -2169,7 +2169,8 @@ CXXDeductionGuideDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
return new (C, ID) CXXDeductionGuideDecl(
C, nullptr, SourceLocation(), ExplicitSpecifier(), DeclarationNameInfo(),
QualType(), nullptr, SourceLocation(), nullptr,
- DeductionCandidate::Normal, nullptr, false);
+ DeductionCandidate::Normal, /*GeneratedFrom=*/nullptr,
+ /*IsGeneratedFromInheritedConstructor=*/false);
}
RequiresExprBodyDecl *RequiresExprBodyDecl::Create(
>From 2b537b57e20ca634ef4d7a88c1781f15055f8628 Mon Sep 17 00:00:00 2001
From: antangelo <contact at antangelo.com>
Date: Tue, 16 Jul 2024 00:18:07 -0400
Subject: [PATCH 3/4] clang-format
---
clang/lib/Sema/SemaTemplateDeductionGuide.cpp | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
index 3867c7cfafebc..bf80bbbc7ae3b 100644
--- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
+++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
@@ -951,10 +951,10 @@ Expr *buildIsDeducibleConstraint(Sema &SemaRef,
Context.getTrivialTypeSourceInfo(
ReturnType), // type from which template arguments are deduced.
};
- return TypeTraitExpr::Create(
- Context, Context.getLogicalOperationType(), TD->getLocation(),
- TypeTrait::BTT_IsDeducible, IsDeducibleTypeTraitArgs,
- TD->getLocation(), /*Value*/ false);
+ return TypeTraitExpr::Create(Context, Context.getLogicalOperationType(),
+ TD->getLocation(), TypeTrait::BTT_IsDeducible,
+ IsDeducibleTypeTraitArgs, TD->getLocation(),
+ /*Value*/ false);
}
std::pair<TemplateDecl *, llvm::ArrayRef<TemplateArgument>>
@@ -1650,7 +1650,8 @@ void Sema::DeclareImplicitDeductionGuides(TemplateDecl *Template,
}
if (CXXRecordDecl *DefRecord =
cast<CXXRecordDecl>(Template->getTemplatedDecl())->getDefinition()) {
- if (TemplateDecl *DescribedTemplate = DefRecord->getDescribedClassTemplate())
+ if (TemplateDecl *DescribedTemplate =
+ DefRecord->getDescribedClassTemplate())
Template = DescribedTemplate;
}
>From c17fe150e39101bfa7fd0eb9e5cc7031fd746156 Mon Sep 17 00:00:00 2001
From: antangelo <contact at antangelo.com>
Date: Fri, 19 Jul 2024 03:15:37 -0400
Subject: [PATCH 4/4] Remove QualType reference and set mapper template param
to implicit
---
clang/lib/Sema/SemaTemplateDeductionGuide.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
index bf80bbbc7ae3b..f13a13345e9d7 100644
--- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
+++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
@@ -1011,7 +1011,7 @@ buildInheritedConstructorDeductionGuideType(
DeducingTemplate->getBeginLoc(), DeclarationName());
if (!ReturnTypeTSI || Trap.hasErrorOccurred())
return {nullptr, QualType()};
- const QualType &ReturnType = ReturnTypeTSI->getType();
+ QualType ReturnType = ReturnTypeTSI->getType();
TypeLocBuilder TLB;
TLB.pushFullCopy(ReturnTypeTSI->getTypeLoc());
@@ -1454,6 +1454,7 @@ void DeclareImplicitDeductionGuidesFromInheritedConstructors(
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);
More information about the cfe-commits
mailing list