Skip to content

[clang] Track source deduction guide for alias template deduction guides #123875

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

Merged
merged 3 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 42 additions & 3 deletions clang/include/clang/AST/DeclCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -1967,24 +1967,42 @@ 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 : uint8_t {
None,
Alias,
};

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. 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:
Expand All @@ -1997,7 +2015,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);
Expand All @@ -2017,6 +2037,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);
}

SourceDeductionGuideKind getSourceDeductionGuideKind() const {
return SourceDeductionGuide.getInt();
}

void setSourceDeductionGuideKind(SourceDeductionGuideKind SK) {
SourceDeductionGuide.setInt(SK);
}

void setDeductionCandidateKind(DeductionCandidate K) {
FunctionDeclBits.DeductionCandidateKind = static_cast<unsigned char>(K);
}
Expand Down
8 changes: 5 additions & 3 deletions clang/lib/AST/ASTImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3999,14 +3999,16 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
importExplicitSpecifier(Err, Guide->getExplicitSpecifier());
CXXConstructorDecl *Ctor =
importChecked(Err, Guide->getCorrespondingConstructor());
const 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(), TrailingRequiresClause,
SourceDG, Guide->getSourceDeductionGuideKind()))
return ToFunction;
cast<CXXDeductionGuideDecl>(ToFunction)
->setDeductionCandidateKind(Guide->getDeductionCandidateKind());
} else {
if (GetImportedOrCreateDecl(
ToFunction, D, Importer.getToContext(), DC, ToInnerLocStart,
Expand Down
18 changes: 11 additions & 7 deletions clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2292,18 +2292,22 @@ 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(
C, nullptr, SourceLocation(), ExplicitSpecifier(), DeclarationNameInfo(),
QualType(), nullptr, SourceLocation(), nullptr,
DeductionCandidate::Normal, nullptr);
C, /*DC=*/nullptr, SourceLocation(), ExplicitSpecifier(),
DeclarationNameInfo(), QualType(), /*TInfo=*/nullptr, SourceLocation(),
/*Ctor=*/nullptr, DeductionCandidate::Normal,
/*TrailingRequiresClause=*/nullptr,
/*GeneratedFrom=*/nullptr, SourceDeductionGuideKind::None);
}

RequiresExprBodyDecl *RequiresExprBodyDecl::Create(
Expand Down
8 changes: 6 additions & 2 deletions clang/lib/Sema/SemaTemplateDeductionGuide.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1175,8 +1175,12 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
GG->getTypeSourceInfo(), AliasTemplate->getBeginLoc(),
AliasTemplate->getLocation(), AliasTemplate->getEndLoc(),
F->isImplicit()));
cast<CXXDeductionGuideDecl>(Result->getTemplatedDecl())
->setDeductionCandidateKind(GG->getDeductionCandidateKind());
auto *DGuide = cast<CXXDeductionGuideDecl>(Result->getTemplatedDecl());
DGuide->setDeductionCandidateKind(GG->getDeductionCandidateKind());
DGuide->setSourceDeductionGuide(
cast<CXXDeductionGuideDecl>(F->getTemplatedDecl()));
DGuide->setSourceDeductionGuideKind(
CXXDeductionGuideDecl::SourceDeductionGuideKind::Alias);
return Result;
}
return nullptr;
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2264,7 +2264,9 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
SemaRef.Context, DC, D->getInnerLocStart(),
InstantiatedExplicitSpecifier, NameInfo, T, TInfo,
D->getSourceRange().getEnd(), DGuide->getCorrespondingConstructor(),
DGuide->getDeductionCandidateKind(), TrailingRequiresClause);
DGuide->getDeductionCandidateKind(), TrailingRequiresClause,
DGuide->getSourceDeductionGuide(),
DGuide->getSourceDeductionGuideKind());
Function->setAccess(D->getAccess());
} else {
Function = FunctionDecl::Create(
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Serialization/ASTReaderDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2295,6 +2295,10 @@ void ASTDeclReader::VisitCXXDeductionGuideDecl(CXXDeductionGuideDecl *D) {
VisitFunctionDecl(D);
D->setDeductionCandidateKind(
static_cast<DeductionCandidate>(Record.readInt()));
D->setSourceDeductionGuide(readDeclAs<CXXDeductionGuideDecl>());
D->setSourceDeductionGuideKind(
static_cast<CXXDeductionGuideDecl::SourceDeductionGuideKind>(
Record.readInt()));
}

void ASTDeclReader::VisitCXXMethodDecl(CXXMethodDecl *D) {
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Serialization/ASTWriterDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,9 @@ void ASTDeclWriter::VisitCXXDeductionGuideDecl(CXXDeductionGuideDecl *D) {
Record.AddDeclRef(D->Ctor);
VisitFunctionDecl(D);
Record.push_back(static_cast<unsigned char>(D->getDeductionCandidateKind()));
Record.AddDeclRef(D->getSourceDeductionGuide());
Record.push_back(
static_cast<unsigned char>(D->getSourceDeductionGuideKind()));
Code = serialization::DECL_CXX_DEDUCTION_GUIDE;
}

Expand Down
26 changes: 26 additions & 0 deletions clang/unittests/AST/ASTImporterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8105,6 +8105,9 @@ 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_EQ(ToD->getSourceDeductionGuideKind(),
CXXDeductionGuideDecl::SourceDeductionGuideKind::None);
// Check that the deduced class template is also imported.
EXPECT_TRUE(findFromTU(FromD)->Importer->GetAlreadyImportedOrNull(
FromD->getDeducedTemplate()));
Expand All @@ -8129,6 +8132,9 @@ TEST_P(ImportFunctions, CTADUserDefinedExplicit) {
ASSERT_TRUE(ToD);
EXPECT_FALSE(FromD->isImplicit());
EXPECT_TRUE(ToD->isExplicit());
EXPECT_EQ(ToD->getSourceDeductionGuide(), nullptr);
EXPECT_EQ(ToD->getSourceDeductionGuideKind(),
CXXDeductionGuideDecl::SourceDeductionGuideKind::None);
}

TEST_P(ImportFunctions, CTADWithLocalTypedef) {
Expand All @@ -8147,6 +8153,26 @@ 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_TRUE(ToD->getSourceDeductionGuideKind() ==
CXXDeductionGuideDecl::SourceDeductionGuideKind::Alias);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also get a test that covers the None case to ensure we are serializing and deserializing correctly for both.

Testing should if possible always cover all path, otherwise this opens us to undetected regressions in the untested paths.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, I was already testing getSourceDeductionGuide for the nulltptr case so I have now added assertions for getSourceDeductionGuideKind being None.

EXPECT_TRUE(ToD->getSourceDeductionGuide());
}

TEST_P(ImportFunctions, ParmVarDeclDeclContext) {
constexpr auto FromTUCode = R"(
void f(int P);
Expand Down
Loading