Skip to content

Commit

Permalink
[clang][ASTImporter] Improve import of variable template specializati…
Browse files Browse the repository at this point in the history
…ons. (#78284)

Code of `VisitVarTemplateSpecializationDecl` was rewritten based on code
of `VisitVarDecl`. Additional changes (in structural equivalence) were
made to make tests pass.
  • Loading branch information
balazske committed Jan 29, 2024
1 parent e3a38a7 commit 9f80ecb
Show file tree
Hide file tree
Showing 4 changed files with 234 additions and 110 deletions.
217 changes: 118 additions & 99 deletions clang/lib/AST/ASTImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6385,16 +6385,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 @@ -6422,116 +6425,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,
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(),
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 @@ -1389,9 +1389,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 @@ -1400,6 +1397,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

0 comments on commit 9f80ecb

Please sign in to comment.