Skip to content

Commit 9f80ecb

Browse files
authored
[clang][ASTImporter] Improve import of variable template specializations. (#78284)
Code of `VisitVarTemplateSpecializationDecl` was rewritten based on code of `VisitVarDecl`. Additional changes (in structural equivalence) were made to make tests pass.
1 parent e3a38a7 commit 9f80ecb

File tree

4 files changed

+234
-110
lines changed

4 files changed

+234
-110
lines changed

clang/lib/AST/ASTImporter.cpp

Lines changed: 118 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -6385,16 +6385,19 @@ ExpectedDecl ASTNodeImporter::VisitVarTemplateDecl(VarTemplateDecl *D) {
63856385

63866386
ExpectedDecl ASTNodeImporter::VisitVarTemplateSpecializationDecl(
63876387
VarTemplateSpecializationDecl *D) {
6388-
// If this record has a definition in the translation unit we're coming from,
6389-
// but this particular declaration is not that definition, import the
6390-
// definition and map to that.
6391-
VarDecl *Definition = D->getDefinition();
6392-
if (Definition && Definition != D) {
6393-
if (ExpectedDecl ImportedDefOrErr = import(Definition))
6394-
return Importer.MapImported(D, *ImportedDefOrErr);
6395-
else
6396-
return ImportedDefOrErr.takeError();
6388+
// A VarTemplateSpecializationDecl inherits from VarDecl, the import is done
6389+
// in an analog way (but specialized for this case).
6390+
6391+
SmallVector<Decl *, 2> Redecls = getCanonicalForwardRedeclChain(D);
6392+
auto RedeclIt = Redecls.begin();
6393+
// Import the first part of the decl chain. I.e. import all previous
6394+
// declarations starting from the canonical decl.
6395+
for (; RedeclIt != Redecls.end() && *RedeclIt != D; ++RedeclIt) {
6396+
ExpectedDecl RedeclOrErr = import(*RedeclIt);
6397+
if (!RedeclOrErr)
6398+
return RedeclOrErr.takeError();
63976399
}
6400+
assert(*RedeclIt == D);
63986401

63996402
VarTemplateDecl *VarTemplate = nullptr;
64006403
if (Error Err = importInto(VarTemplate, D->getSpecializedTemplate()))
@@ -6422,116 +6425,132 @@ ExpectedDecl ASTNodeImporter::VisitVarTemplateSpecializationDecl(
64226425

64236426
// Try to find an existing specialization with these template arguments.
64246427
void *InsertPos = nullptr;
6425-
VarTemplateSpecializationDecl *D2 = VarTemplate->findSpecialization(
6426-
TemplateArgs, InsertPos);
6427-
if (D2) {
6428-
// We already have a variable template specialization with these template
6429-
// arguments.
6430-
6431-
// FIXME: Check for specialization vs. instantiation errors.
6432-
6433-
if (VarDecl *FoundDef = D2->getDefinition()) {
6434-
if (!D->isThisDeclarationADefinition() ||
6435-
IsStructuralMatch(D, FoundDef)) {
6436-
// The record types structurally match, or the "from" translation
6437-
// unit only had a forward declaration anyway; call it the same
6438-
// variable.
6439-
return Importer.MapImported(D, FoundDef);
6428+
VarTemplateSpecializationDecl *FoundSpecialization =
6429+
VarTemplate->findSpecialization(TemplateArgs, InsertPos);
6430+
if (FoundSpecialization) {
6431+
if (IsStructuralMatch(D, FoundSpecialization)) {
6432+
VarDecl *FoundDef = FoundSpecialization->getDefinition();
6433+
if (D->getDeclContext()->isRecord()) {
6434+
// In a record, it is allowed only to have one optional declaration and
6435+
// one definition of the (static or constexpr) variable template.
6436+
assert(
6437+
FoundSpecialization->getDeclContext()->isRecord() &&
6438+
"Member variable template specialization imported as non-member, "
6439+
"inconsistent imported AST?");
6440+
if (FoundDef)
6441+
return Importer.MapImported(D, FoundDef);
6442+
if (!D->isThisDeclarationADefinition())
6443+
return Importer.MapImported(D, FoundSpecialization);
6444+
} else {
6445+
// If definition is imported and there is already one, map to it.
6446+
// Otherwise create a new variable and link it to the existing.
6447+
if (FoundDef && D->isThisDeclarationADefinition())
6448+
return Importer.MapImported(D, FoundDef);
64406449
}
6450+
} else {
6451+
return make_error<ASTImportError>(ASTImportError::NameConflict);
64416452
}
6442-
} else {
6443-
TemplateArgumentListInfo ToTAInfo;
6444-
if (const ASTTemplateArgumentListInfo *Args = D->getTemplateArgsInfo()) {
6445-
if (Error Err = ImportTemplateArgumentListInfo(*Args, ToTAInfo))
6446-
return std::move(Err);
6447-
}
6453+
}
64486454

6449-
using PartVarSpecDecl = VarTemplatePartialSpecializationDecl;
6450-
// Create a new specialization.
6451-
if (auto *FromPartial = dyn_cast<PartVarSpecDecl>(D)) {
6452-
// Import TemplateArgumentListInfo
6453-
TemplateArgumentListInfo ArgInfos;
6454-
const auto *FromTAArgsAsWritten = FromPartial->getTemplateArgsAsWritten();
6455-
// NOTE: FromTAArgsAsWritten and template parameter list are non-null.
6456-
if (Error Err = ImportTemplateArgumentListInfo(
6457-
*FromTAArgsAsWritten, ArgInfos))
6458-
return std::move(Err);
6455+
VarTemplateSpecializationDecl *D2 = nullptr;
64596456

6460-
auto ToTPListOrErr = import(FromPartial->getTemplateParameters());
6461-
if (!ToTPListOrErr)
6462-
return ToTPListOrErr.takeError();
6457+
TemplateArgumentListInfo ToTAInfo;
6458+
if (const ASTTemplateArgumentListInfo *Args = D->getTemplateArgsInfo()) {
6459+
if (Error Err = ImportTemplateArgumentListInfo(*Args, ToTAInfo))
6460+
return std::move(Err);
6461+
}
64636462

6464-
PartVarSpecDecl *ToPartial;
6465-
if (GetImportedOrCreateDecl(ToPartial, D, Importer.getToContext(), DC,
6466-
*BeginLocOrErr, *IdLocOrErr, *ToTPListOrErr,
6467-
VarTemplate, QualType(), nullptr,
6468-
D->getStorageClass(), TemplateArgs, ArgInfos))
6469-
return ToPartial;
6463+
using PartVarSpecDecl = VarTemplatePartialSpecializationDecl;
6464+
// Create a new specialization.
6465+
if (auto *FromPartial = dyn_cast<PartVarSpecDecl>(D)) {
6466+
// Import TemplateArgumentListInfo
6467+
TemplateArgumentListInfo ArgInfos;
6468+
const auto *FromTAArgsAsWritten = FromPartial->getTemplateArgsAsWritten();
6469+
// NOTE: FromTAArgsAsWritten and template parameter list are non-null.
6470+
if (Error Err =
6471+
ImportTemplateArgumentListInfo(*FromTAArgsAsWritten, ArgInfos))
6472+
return std::move(Err);
64706473

6471-
if (Expected<PartVarSpecDecl *> ToInstOrErr = import(
6472-
FromPartial->getInstantiatedFromMember()))
6473-
ToPartial->setInstantiatedFromMember(*ToInstOrErr);
6474-
else
6475-
return ToInstOrErr.takeError();
6476-
6477-
if (FromPartial->isMemberSpecialization())
6478-
ToPartial->setMemberSpecialization();
6479-
6480-
D2 = ToPartial;
6481-
6482-
// FIXME: Use this update if VarTemplatePartialSpecializationDecl is fixed
6483-
// to adopt template parameters.
6484-
// updateLookupTableForTemplateParameters(**ToTPListOrErr);
6485-
} else { // Full specialization
6486-
if (GetImportedOrCreateDecl(D2, D, Importer.getToContext(), DC,
6487-
*BeginLocOrErr, *IdLocOrErr, VarTemplate,
6488-
QualType(), nullptr, D->getStorageClass(),
6489-
TemplateArgs))
6490-
return D2;
6491-
}
6474+
auto ToTPListOrErr = import(FromPartial->getTemplateParameters());
6475+
if (!ToTPListOrErr)
6476+
return ToTPListOrErr.takeError();
64926477

6493-
QualType T;
6494-
if (Error Err = importInto(T, D->getType()))
6495-
return std::move(Err);
6496-
D2->setType(T);
6478+
PartVarSpecDecl *ToPartial;
6479+
if (GetImportedOrCreateDecl(ToPartial, D, Importer.getToContext(), DC,
6480+
*BeginLocOrErr, *IdLocOrErr, *ToTPListOrErr,
6481+
VarTemplate, QualType(), nullptr,
6482+
D->getStorageClass(), TemplateArgs, ArgInfos))
6483+
return ToPartial;
64976484

6498-
auto TInfoOrErr = import(D->getTypeSourceInfo());
6499-
if (!TInfoOrErr)
6500-
return TInfoOrErr.takeError();
6501-
D2->setTypeSourceInfo(*TInfoOrErr);
6485+
if (Expected<PartVarSpecDecl *> ToInstOrErr =
6486+
import(FromPartial->getInstantiatedFromMember()))
6487+
ToPartial->setInstantiatedFromMember(*ToInstOrErr);
6488+
else
6489+
return ToInstOrErr.takeError();
65026490

6503-
if (D->getPointOfInstantiation().isValid()) {
6504-
if (ExpectedSLoc POIOrErr = import(D->getPointOfInstantiation()))
6505-
D2->setPointOfInstantiation(*POIOrErr);
6506-
else
6507-
return POIOrErr.takeError();
6508-
}
6491+
if (FromPartial->isMemberSpecialization())
6492+
ToPartial->setMemberSpecialization();
6493+
6494+
D2 = ToPartial;
65096495

6510-
D2->setSpecializationKind(D->getSpecializationKind());
6511-
D2->setTemplateArgsInfo(ToTAInfo);
6496+
// FIXME: Use this update if VarTemplatePartialSpecializationDecl is fixed
6497+
// to adopt template parameters.
6498+
// updateLookupTableForTemplateParameters(**ToTPListOrErr);
6499+
} else { // Full specialization
6500+
if (GetImportedOrCreateDecl(D2, D, Importer.getToContext(), DC,
6501+
*BeginLocOrErr, *IdLocOrErr, VarTemplate,
6502+
QualType(), nullptr, D->getStorageClass(),
6503+
TemplateArgs))
6504+
return D2;
6505+
}
65126506

6513-
// Add this specialization to the class template.
6514-
VarTemplate->AddSpecialization(D2, InsertPos);
6507+
QualType T;
6508+
if (Error Err = importInto(T, D->getType()))
6509+
return std::move(Err);
6510+
D2->setType(T);
65156511

6516-
// Import the qualifier, if any.
6517-
if (auto LocOrErr = import(D->getQualifierLoc()))
6518-
D2->setQualifierInfo(*LocOrErr);
6512+
auto TInfoOrErr = import(D->getTypeSourceInfo());
6513+
if (!TInfoOrErr)
6514+
return TInfoOrErr.takeError();
6515+
D2->setTypeSourceInfo(*TInfoOrErr);
6516+
6517+
if (D->getPointOfInstantiation().isValid()) {
6518+
if (ExpectedSLoc POIOrErr = import(D->getPointOfInstantiation()))
6519+
D2->setPointOfInstantiation(*POIOrErr);
65196520
else
6520-
return LocOrErr.takeError();
6521+
return POIOrErr.takeError();
6522+
}
65216523

6522-
if (D->isConstexpr())
6523-
D2->setConstexpr(true);
6524+
D2->setSpecializationKind(D->getSpecializationKind());
6525+
D2->setTemplateArgsInfo(ToTAInfo);
65246526

6525-
// Add the specialization to this context.
6526-
D2->setLexicalDeclContext(LexicalDC);
6527-
LexicalDC->addDeclInternal(D2);
6527+
if (auto LocOrErr = import(D->getQualifierLoc()))
6528+
D2->setQualifierInfo(*LocOrErr);
6529+
else
6530+
return LocOrErr.takeError();
65286531

6529-
D2->setAccess(D->getAccess());
6530-
}
6532+
if (D->isConstexpr())
6533+
D2->setConstexpr(true);
6534+
6535+
D2->setAccess(D->getAccess());
65316536

65326537
if (Error Err = ImportInitializer(D, D2))
65336538
return std::move(Err);
65346539

6540+
if (FoundSpecialization)
6541+
D2->setPreviousDecl(FoundSpecialization->getMostRecentDecl());
6542+
6543+
VarTemplate->AddSpecialization(D2, InsertPos);
6544+
6545+
addDeclToContexts(D, D2);
6546+
6547+
// Import the rest of the chain. I.e. import all subsequent declarations.
6548+
for (++RedeclIt; RedeclIt != Redecls.end(); ++RedeclIt) {
6549+
ExpectedDecl RedeclOrErr = import(*RedeclIt);
6550+
if (!RedeclOrErr)
6551+
return RedeclOrErr.takeError();
6552+
}
6553+
65356554
return D2;
65366555
}
65376556

clang/lib/AST/ASTStructuralEquivalence.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1389,9 +1389,6 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
13891389

13901390
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
13911391
VarDecl *D1, VarDecl *D2) {
1392-
if (D1->getStorageClass() != D2->getStorageClass())
1393-
return false;
1394-
13951392
IdentifierInfo *Name1 = D1->getIdentifier();
13961393
IdentifierInfo *Name2 = D2->getIdentifier();
13971394
if (!::IsStructurallyEquivalent(Name1, Name2))
@@ -1400,6 +1397,15 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
14001397
if (!IsStructurallyEquivalent(Context, D1->getType(), D2->getType()))
14011398
return false;
14021399

1400+
// Compare storage class and initializer only if none or both are a
1401+
// definition. Like a forward-declaration matches a class definition, variable
1402+
// declarations that are not definitions should match with the definitions.
1403+
if (D1->isThisDeclarationADefinition() != D2->isThisDeclarationADefinition())
1404+
return true;
1405+
1406+
if (D1->getStorageClass() != D2->getStorageClass())
1407+
return false;
1408+
14031409
return IsStructurallyEquivalent(Context, D1->getInit(), D2->getInit());
14041410
}
14051411

0 commit comments

Comments
 (0)