Skip to content

[clang][ASTImporter] Improve import of variable template specializations. #78284

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 2 commits into from
Jan 29, 2024
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
217 changes: 118 additions & 99 deletions clang/lib/AST/ASTImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6360,16 +6360,19 @@ ExpectedDecl ASTNodeImporter::VisitVarTemplateDecl(VarTemplateDecl *D) {

ExpectedDecl ASTNodeImporter::VisitVarTemplateSpecializationDecl(
VarTemplateSpecializationDecl *D) {
// If this record has a definition in the translation unit we're coming from,
// but this particular declaration is not that definition, import the
// definition and map to that.
VarDecl *Definition = D->getDefinition();
if (Definition && Definition != D) {
if (ExpectedDecl ImportedDefOrErr = import(Definition))
return Importer.MapImported(D, *ImportedDefOrErr);
else
return ImportedDefOrErr.takeError();
// A VarTemplateSpecializationDecl inherits from VarDecl, the import is done
// in an analog way (but specialized for this case).

SmallVector<Decl *, 2> Redecls = getCanonicalForwardRedeclChain(D);
auto RedeclIt = Redecls.begin();
// Import the first part of the decl chain. I.e. import all previous
// declarations starting from the canonical decl.
for (; RedeclIt != Redecls.end() && *RedeclIt != D; ++RedeclIt) {
ExpectedDecl RedeclOrErr = import(*RedeclIt);
if (!RedeclOrErr)
return RedeclOrErr.takeError();
}
assert(*RedeclIt == D);

VarTemplateDecl *VarTemplate = nullptr;
if (Error Err = importInto(VarTemplate, D->getSpecializedTemplate()))
Expand Down Expand Up @@ -6397,116 +6400,132 @@ ExpectedDecl ASTNodeImporter::VisitVarTemplateSpecializationDecl(

// Try to find an existing specialization with these template arguments.
void *InsertPos = nullptr;
VarTemplateSpecializationDecl *D2 = VarTemplate->findSpecialization(
TemplateArgs, InsertPos);
if (D2) {
// We already have a variable template specialization with these template
// arguments.

// FIXME: Check for specialization vs. instantiation errors.

if (VarDecl *FoundDef = D2->getDefinition()) {
if (!D->isThisDeclarationADefinition() ||
IsStructuralMatch(D, FoundDef)) {
// The record types structurally match, or the "from" translation
// unit only had a forward declaration anyway; call it the same
// variable.
return Importer.MapImported(D, FoundDef);
VarTemplateSpecializationDecl *FoundSpecialization =
VarTemplate->findSpecialization(TemplateArgs, InsertPos);
if (FoundSpecialization) {
if (IsStructuralMatch(D, FoundSpecialization)) {
VarDecl *FoundDef = FoundSpecialization->getDefinition();
if (D->getDeclContext()->isRecord()) {
// In a record, it is allowed only to have one optional declaration and
// one definition of the (static or constexpr) variable template.
assert(
FoundSpecialization->getDeclContext()->isRecord() &&
"Member variable template specialization imported as non-member, "
"inconsistent imported AST?");
if (FoundDef)
return Importer.MapImported(D, FoundDef);
if (!D->isThisDeclarationADefinition())
return Importer.MapImported(D, FoundSpecialization);
} else {
// If definition is imported and there is already one, map to it.
// Otherwise create a new variable and link it to the existing.
if (FoundDef && D->isThisDeclarationADefinition())
return Importer.MapImported(D, FoundDef);
}
} else {
return make_error<ASTImportError>(ASTImportError::NameConflict);
}
} else {
TemplateArgumentListInfo ToTAInfo;
if (const ASTTemplateArgumentListInfo *Args = D->getTemplateArgsInfo()) {
if (Error Err = ImportTemplateArgumentListInfo(*Args, ToTAInfo))
return std::move(Err);
}
}

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

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

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

if (Expected<PartVarSpecDecl *> ToInstOrErr = import(
FromPartial->getInstantiatedFromMember()))
ToPartial->setInstantiatedFromMember(*ToInstOrErr);
else
return ToInstOrErr.takeError();

if (FromPartial->isMemberSpecialization())
ToPartial->setMemberSpecialization();

D2 = ToPartial;

// FIXME: Use this update if VarTemplatePartialSpecializationDecl is fixed
// to adopt template parameters.
// updateLookupTableForTemplateParameters(**ToTPListOrErr);
} else { // Full specialization
if (GetImportedOrCreateDecl(D2, D, Importer.getToContext(), DC,
*BeginLocOrErr, *IdLocOrErr, VarTemplate,
QualType(), nullptr, D->getStorageClass(),
TemplateArgs))
return D2;
}
auto ToTPListOrErr = import(FromPartial->getTemplateParameters());
if (!ToTPListOrErr)
return ToTPListOrErr.takeError();

QualType T;
if (Error Err = importInto(T, D->getType()))
return std::move(Err);
D2->setType(T);
PartVarSpecDecl *ToPartial;
if (GetImportedOrCreateDecl(ToPartial, D, Importer.getToContext(), DC,
*BeginLocOrErr, *IdLocOrErr, *ToTPListOrErr,
VarTemplate, QualType(), nullptr,
Copy link
Collaborator

Choose a reason for hiding this comment

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

nullptr -> /*TInfo=*/nullptr

I think TInfo is the right field here.

D->getStorageClass(), TemplateArgs, ArgInfos))
return ToPartial;

auto TInfoOrErr = import(D->getTypeSourceInfo());
if (!TInfoOrErr)
return TInfoOrErr.takeError();
D2->setTypeSourceInfo(*TInfoOrErr);
if (Expected<PartVarSpecDecl *> ToInstOrErr =
import(FromPartial->getInstantiatedFromMember()))
ToPartial->setInstantiatedFromMember(*ToInstOrErr);
else
return ToInstOrErr.takeError();

if (D->getPointOfInstantiation().isValid()) {
if (ExpectedSLoc POIOrErr = import(D->getPointOfInstantiation()))
D2->setPointOfInstantiation(*POIOrErr);
else
return POIOrErr.takeError();
}
if (FromPartial->isMemberSpecialization())
ToPartial->setMemberSpecialization();

D2 = ToPartial;

D2->setSpecializationKind(D->getSpecializationKind());
D2->setTemplateArgsInfo(ToTAInfo);
// FIXME: Use this update if VarTemplatePartialSpecializationDecl is fixed
// to adopt template parameters.
// updateLookupTableForTemplateParameters(**ToTPListOrErr);
} else { // Full specialization
if (GetImportedOrCreateDecl(D2, D, Importer.getToContext(), DC,
*BeginLocOrErr, *IdLocOrErr, VarTemplate,
QualType(), nullptr, D->getStorageClass(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same comment as above

TemplateArgs))
return D2;
}

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

// Import the qualifier, if any.
if (auto LocOrErr = import(D->getQualifierLoc()))
D2->setQualifierInfo(*LocOrErr);
auto TInfoOrErr = import(D->getTypeSourceInfo());
if (!TInfoOrErr)
return TInfoOrErr.takeError();
D2->setTypeSourceInfo(*TInfoOrErr);

if (D->getPointOfInstantiation().isValid()) {
if (ExpectedSLoc POIOrErr = import(D->getPointOfInstantiation()))
D2->setPointOfInstantiation(*POIOrErr);
else
return LocOrErr.takeError();
return POIOrErr.takeError();
}

if (D->isConstexpr())
D2->setConstexpr(true);
D2->setSpecializationKind(D->getSpecializationKind());
D2->setTemplateArgsInfo(ToTAInfo);

// Add the specialization to this context.
D2->setLexicalDeclContext(LexicalDC);
LexicalDC->addDeclInternal(D2);
if (auto LocOrErr = import(D->getQualifierLoc()))
D2->setQualifierInfo(*LocOrErr);
else
return LocOrErr.takeError();

D2->setAccess(D->getAccess());
}
if (D->isConstexpr())
D2->setConstexpr(true);

D2->setAccess(D->getAccess());

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

if (FoundSpecialization)
D2->setPreviousDecl(FoundSpecialization->getMostRecentDecl());

VarTemplate->AddSpecialization(D2, InsertPos);

addDeclToContexts(D, D2);

// Import the rest of the chain. I.e. import all subsequent declarations.
for (++RedeclIt; RedeclIt != Redecls.end(); ++RedeclIt) {
ExpectedDecl RedeclOrErr = import(*RedeclIt);
if (!RedeclOrErr)
return RedeclOrErr.takeError();
}

return D2;
}

Expand Down
12 changes: 9 additions & 3 deletions clang/lib/AST/ASTStructuralEquivalence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1319,9 +1319,6 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,

static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
VarDecl *D1, VarDecl *D2) {
if (D1->getStorageClass() != D2->getStorageClass())
return false;

IdentifierInfo *Name1 = D1->getIdentifier();
IdentifierInfo *Name2 = D2->getIdentifier();
if (!::IsStructurallyEquivalent(Name1, Name2))
Expand All @@ -1330,6 +1327,15 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
if (!IsStructurallyEquivalent(Context, D1->getType(), D2->getType()))
return false;

// Compare storage class and initializer only if none or both are a
// definition. Like a forward-declaration matches a class definition, variable
// declarations that are not definitions should match with the definitions.
if (D1->isThisDeclarationADefinition() != D2->isThisDeclarationADefinition())
return true;

if (D1->getStorageClass() != D2->getStorageClass())
return false;

return IsStructurallyEquivalent(Context, D1->getInit(), D2->getInit());
}

Expand Down
Loading