-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[clang] Implement P2582R1: CTAD from inherited constructors #98788
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
3f0dfdf
9d3c664
35d7284
2b537b5
db8853a
c17fe15
22f8327
2ede0b3
cd0e4de
d77c3d2
5c272cf
c89b4ea
b9fb81b
993d9d6
b44f26a
167d6a2
819cbb1
ea6a946
18b8161
a28bb6f
4acd25e
471a421
c7071c8
ca70caf
b9dff6f
121870d
da2a9cf
48d2bb4
0245459
ec4e5c6
f36a4cf
a2c8167
4ca159d
16465d3
cfbb7a0
5fe08dc
207359b
b04742b
f23d838
f0faf97
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1960,24 +1960,44 @@ class ExplicitSpecifier { | |
class CXXDeductionGuideDecl : public FunctionDecl { | ||
void anchor() override; | ||
|
||
public: | ||
// Represents the relationship between this deduction guide and the | ||
// deduction guide that it was generated from (or lack thereof). | ||
// See the SourceDeductionGuide member for more details. | ||
enum class SourceDeductionGuideKind : unsigned char { | ||
None, | ||
Alias, | ||
InheritedConstructor, | ||
}; | ||
|
||
private: | ||
CXXDeductionGuideDecl(ASTContext &C, DeclContext *DC, SourceLocation StartLoc, | ||
ExplicitSpecifier ES, | ||
const DeclarationNameInfo &NameInfo, QualType T, | ||
TypeSourceInfo *TInfo, SourceLocation EndLocation, | ||
CXXConstructorDecl *Ctor, DeductionCandidate Kind, | ||
Expr *TrailingRequiresClause) | ||
Expr *TrailingRequiresClause, | ||
const CXXDeductionGuideDecl *GeneratedFrom, | ||
SourceDeductionGuideKind SourceKind) | ||
: FunctionDecl(CXXDeductionGuide, C, DC, StartLoc, NameInfo, T, TInfo, | ||
SC_None, false, false, ConstexprSpecKind::Unspecified, | ||
TrailingRequiresClause), | ||
Ctor(Ctor), ExplicitSpec(ES) { | ||
Ctor(Ctor), ExplicitSpec(ES), | ||
SourceDeductionGuide(GeneratedFrom, SourceKind) { | ||
if (EndLocation.isValid()) | ||
setRangeEnd(EndLocation); | ||
setDeductionCandidateKind(Kind); | ||
} | ||
|
||
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 SourceDeductionGuideKind member indicates which of these | ||
// sources applies, or is None otherwise. | ||
llvm::PointerIntPair<const CXXDeductionGuideDecl *, 2, | ||
SourceDeductionGuideKind> | ||
SourceDeductionGuide; | ||
void setExplicitSpecifier(ExplicitSpecifier ES) { ExplicitSpec = ES; } | ||
|
||
public: | ||
|
@@ -1990,7 +2010,9 @@ class CXXDeductionGuideDecl : public FunctionDecl { | |
TypeSourceInfo *TInfo, SourceLocation EndLocation, | ||
CXXConstructorDecl *Ctor = nullptr, | ||
DeductionCandidate Kind = DeductionCandidate::Normal, | ||
Expr *TrailingRequiresClause = nullptr); | ||
Expr *TrailingRequiresClause = nullptr, | ||
const CXXDeductionGuideDecl *SourceDG = nullptr, | ||
SourceDeductionGuideKind SK = SourceDeductionGuideKind::None); | ||
|
||
static CXXDeductionGuideDecl *CreateDeserialized(ASTContext &C, | ||
GlobalDeclID ID); | ||
|
@@ -2010,6 +2032,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. | ||
const CXXDeductionGuideDecl *getSourceDeductionGuide() const { | ||
return SourceDeductionGuide.getPointer(); | ||
} | ||
|
||
void setSourceDeductionGuide(CXXDeductionGuideDecl *DG) { | ||
SourceDeductionGuide.setPointer(DG); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want to assert for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I opted not to assert here and in |
||
} | ||
|
||
SourceDeductionGuideKind getSourceDeductionGuideKind() const { | ||
return SourceDeductionGuide.getInt(); | ||
} | ||
|
||
void setSourceDeductionGuideKind(SourceDeductionGuideKind SK) { | ||
SourceDeductionGuide.setInt(SK); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PERHAPS here too? Assert |
||
} | ||
|
||
void setDeductionCandidateKind(DeductionCandidate K) { | ||
FunctionDeclBits.DeductionCandidateKind = static_cast<unsigned char>(K); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2246,18 +2246,21 @@ CXXDeductionGuideDecl *CXXDeductionGuideDecl::Create( | |
ASTContext &C, DeclContext *DC, SourceLocation StartLoc, | ||
ExplicitSpecifier ES, const DeclarationNameInfo &NameInfo, QualType T, | ||
TypeSourceInfo *TInfo, SourceLocation EndLocation, CXXConstructorDecl *Ctor, | ||
DeductionCandidate Kind, Expr *TrailingRequiresClause) { | ||
return new (C, DC) | ||
CXXDeductionGuideDecl(C, DC, StartLoc, ES, NameInfo, T, TInfo, | ||
EndLocation, Ctor, Kind, TrailingRequiresClause); | ||
DeductionCandidate Kind, Expr *TrailingRequiresClause, | ||
const CXXDeductionGuideDecl *GeneratedFrom, | ||
SourceDeductionGuideKind SourceKind) { | ||
return new (C, DC) CXXDeductionGuideDecl( | ||
C, DC, StartLoc, ES, NameInfo, T, TInfo, EndLocation, Ctor, Kind, | ||
TrailingRequiresClause, GeneratedFrom, SourceKind); | ||
} | ||
|
||
CXXDeductionGuideDecl * | ||
CXXDeductionGuideDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) { | ||
return new (C, ID) CXXDeductionGuideDecl( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For |
||
C, nullptr, SourceLocation(), ExplicitSpecifier(), DeclarationNameInfo(), | ||
QualType(), nullptr, SourceLocation(), nullptr, | ||
DeductionCandidate::Normal, nullptr); | ||
DeductionCandidate::Normal, nullptr, | ||
/*GeneratedFrom=*/nullptr, SourceDeductionGuideKind::None); | ||
} | ||
|
||
RequiresExprBodyDecl *RequiresExprBodyDecl::Create( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10612,6 +10612,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, ... | ||
bool G1Inherited = Guide1->getSourceDeductionGuide() && | ||
Guide1->getSourceDeductionGuideKind() == | ||
CXXDeductionGuideDecl::SourceDeductionGuideKind:: | ||
InheritedConstructor; | ||
bool G2Inherited = Guide2->getSourceDeductionGuide() && | ||
Guide2->getSourceDeductionGuideKind() == | ||
CXXDeductionGuideDecl::SourceDeductionGuideKind:: | ||
InheritedConstructor; | ||
if (Guide1->isImplicit() && Guide2->isImplicit() && | ||
G1Inherited != G2Inherited) { | ||
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()) { | ||
Comment on lines
+10636
to
+10637
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems wrong compared to the comment -- the comment says we should be looking at each explicit function argument, but this predicate only handles the case where both functions are variadic and have the same number of params. Am I missing something? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To my understanding of the implications of the wording, if they do not have the same number of parameters, then at least one of the parameters must not have the same type (since the corresponding parameter in the other function wouldn't exist). If this is the case, then we don't need to do the work of checking all the parameters individually. The variadic check is that the functions are either both variadic, or both not variadic, for handling of the ellipses parameters. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason we can't just check the types of the functions here instead? I would assume their prototypes to compare equal. That said, the wording of If so, I think I'd rather a variant of Also, I don't think the initial check is right? Shouldn't that be an 'or' there instead of '&&'? If both are There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The return types will not be equal as the inherited deduction guides will have some If both functions are variadic and have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could assert that |
||
bool ParamsHaveSameType = | ||
llvm::all_of_zip(FPT1->getParamTypes(), FPT2->getParamTypes(), | ||
[&S](const QualType &P1, const QualType &P2) { | ||
return S.Context.hasSameType(P1, P2); | ||
}); | ||
|
||
if (ParamsHaveSameType) | ||
return G2Inherited; | ||
} | ||
} | ||
|
||
// -- F1 is generated from a deduction-guide and F2 is not | ||
if (Guide1->isImplicit() != Guide2->isImplicit()) | ||
return Guide2->isImplicit(); | ||
|
@@ -11755,6 +11789,35 @@ static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated, | |
return; | ||
} | ||
|
||
// Errors in deduction guides from inherited constructors | ||
// will manifest as substitution failures in the return type | ||
// partial specialization, so we show a generic diagnostic | ||
// in this case. | ||
if (const auto *DG = dyn_cast<CXXDeductionGuideDecl>(Templated); | ||
DG && DG->getSourceDeductionGuideKind() == | ||
CXXDeductionGuideDecl::SourceDeductionGuideKind:: | ||
InheritedConstructor) { | ||
const CXXDeductionGuideDecl *Source = DG->getSourceDeductionGuide(); | ||
assert(Source && | ||
"Inherited constructor deduction guides must have a source"); | ||
|
||
auto GetDGDeducedTemplateType = | ||
[](const CXXDeductionGuideDecl *DG) -> QualType { | ||
return QualType(cast<ClassTemplateDecl>(DG->getDeducedTemplate()) | ||
->getTemplatedDecl() | ||
->getTypeForDecl(), | ||
0); | ||
}; | ||
|
||
QualType DeducedRecordType = GetDGDeducedTemplateType(DG); | ||
QualType InheritedRecordType = GetDGDeducedTemplateType(Source); | ||
S.Diag(Templated->getLocation(), | ||
diag::note_ovl_candidate_inherited_constructor_deduction_failure) | ||
<< DeducedRecordType << InheritedRecordType << TemplateArgString | ||
<< DG->getParametersSourceRange(); | ||
return; | ||
} | ||
|
||
// Format the SFINAE diagnostic into the argument string. | ||
// FIXME: Add a general mechanism to include a PartialDiagnostic *'s | ||
// formatted message in another diagnostic. | ||
|
@@ -11969,9 +12032,21 @@ static void DiagnoseFailedExplicitSpec(Sema &S, OverloadCandidate *Cand) { | |
} | ||
|
||
static void NoteImplicitDeductionGuide(Sema &S, FunctionDecl *Fn) { | ||
auto *DG = dyn_cast<CXXDeductionGuideDecl>(Fn); | ||
const auto *DG = dyn_cast<CXXDeductionGuideDecl>(Fn); | ||
if (!DG) | ||
return; | ||
// The definition of inherited constructor deduction guides is not | ||
// of particular use to end users, as the CC<R> return type cannot | ||
// be manually constructed. Instead, we show the guide we started with. | ||
while ( | ||
DG->getSourceDeductionGuideKind() == | ||
CXXDeductionGuideDecl::SourceDeductionGuideKind::InheritedConstructor) { | ||
DG = DG->getSourceDeductionGuide(); | ||
S.Diag(DG->getLocation(), | ||
diag::note_ovl_candidate_inherited_constructor_source) | ||
<< (DG->isImplicit() ? 1 : 0); | ||
} | ||
|
||
TemplateDecl *OriginTemplate = | ||
DG->getDeclName().getCXXDeductionGuideTemplate(); | ||
// We want to always print synthesized deduction guides for type aliases. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought I saw elsewhere we do this reasonably consistently? Else, feel free to ignore.