[clang] [clang-tools-extra] [clang] Improve nested name specifier AST representation (PR #147835)
Matheus Izvekov via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 25 20:00:40 PDT 2025
================
@@ -5254,153 +5253,297 @@ ASTContext::getPredefinedSugarType(PredefinedSugarType::Kind KD) const {
return QualType(New, 0);
}
-#ifndef NDEBUG
-static bool NeedsInjectedClassNameType(const RecordDecl *D) {
- if (!isa<CXXRecordDecl>(D)) return false;
- const auto *RD = cast<CXXRecordDecl>(D);
- if (isa<ClassTemplatePartialSpecializationDecl>(RD))
- return true;
- if (RD->getDescribedClassTemplate() &&
- !isa<ClassTemplateSpecializationDecl>(RD))
- return true;
- return false;
-}
-#endif
+QualType ASTContext::getTypeDeclType(ElaboratedTypeKeyword Keyword,
+ NestedNameSpecifier Qualifier,
+ const TypeDecl *Decl) const {
+ if (auto *Tag = dyn_cast<TagDecl>(Decl))
+ return getTagType(Keyword, Qualifier, Tag,
+ /*OwnsTag=*/false);
+ if (auto *Typedef = dyn_cast<TypedefNameDecl>(Decl))
+ return getTypedefType(Keyword, Qualifier, Typedef);
+ if (auto *UD = dyn_cast<UnresolvedUsingTypenameDecl>(Decl))
+ return getUnresolvedUsingType(Keyword, Qualifier, UD);
-/// getInjectedClassNameType - Return the unique reference to the
-/// injected class name type for the specified templated declaration.
-QualType ASTContext::getInjectedClassNameType(CXXRecordDecl *Decl,
- QualType TST) const {
- assert(NeedsInjectedClassNameType(Decl));
- if (Decl->TypeForDecl) {
- assert(isa<InjectedClassNameType>(Decl->TypeForDecl));
- } else if (CXXRecordDecl *PrevDecl = Decl->getPreviousDecl()) {
- assert(PrevDecl->TypeForDecl && "previous declaration has no type");
- Decl->TypeForDecl = PrevDecl->TypeForDecl;
- assert(isa<InjectedClassNameType>(Decl->TypeForDecl));
- } else {
- Type *newType = new (*this, alignof(InjectedClassNameType))
- InjectedClassNameType(Decl, TST);
- Decl->TypeForDecl = newType;
- Types.push_back(newType);
- }
+ assert(Keyword == ElaboratedTypeKeyword::None);
+ assert(!Qualifier);
return QualType(Decl->TypeForDecl, 0);
}
-/// getTypeDeclType - Return the unique reference to the type for the
-/// specified type declaration.
-QualType ASTContext::getTypeDeclTypeSlow(const TypeDecl *Decl) const {
- assert(Decl && "Passed null for Decl param");
- assert(!Decl->TypeForDecl && "TypeForDecl present in slow case");
-
- if (const auto *Typedef = dyn_cast<TypedefNameDecl>(Decl))
- return getTypedefType(Typedef);
-
- assert(!isa<TemplateTypeParmDecl>(Decl) &&
- "Template type parameter types are always available.");
-
- if (const auto *Record = dyn_cast<RecordDecl>(Decl)) {
- assert(Record->isFirstDecl() && "struct/union has previous declaration");
- assert(!NeedsInjectedClassNameType(Record));
- return getRecordType(Record);
- } else if (const auto *Enum = dyn_cast<EnumDecl>(Decl)) {
- assert(Enum->isFirstDecl() && "enum has previous declaration");
- return getEnumType(Enum);
- } else if (const auto *Using = dyn_cast<UnresolvedUsingTypenameDecl>(Decl)) {
- return getUnresolvedUsingType(Using);
- } else
- llvm_unreachable("TypeDecl without a type?");
-
+CanQualType ASTContext::getCanonicalTypeDeclType(const TypeDecl *TD) const {
+ if (auto *Tag = dyn_cast<TagDecl>(TD))
+ return getCanonicalTagType(Tag);
+ if (auto *TN = dyn_cast<TypedefNameDecl>(TD))
+ return getCanonicalType(TN->getUnderlyingType());
+ if (const auto *UD = dyn_cast<UnresolvedUsingTypenameDecl>(TD))
+ return getCanonicalUnresolvedUsingType(UD);
+ assert(TD->TypeForDecl);
+ return TD->TypeForDecl->getCanonicalTypeUnqualified();
+}
+
+QualType ASTContext::getTypeDeclType(const TypeDecl *Decl) const {
+ if (const auto *TD = dyn_cast<TagDecl>(Decl))
+ return getCanonicalTagType(TD);
+ if (const auto *TD = dyn_cast<TypedefNameDecl>(Decl);
+ isa_and_nonnull<TypedefDecl, TypeAliasDecl>(TD))
+ return getTypedefType(ElaboratedTypeKeyword::None,
+ /*Qualifier=*/std::nullopt, TD);
+ if (const auto *Using = dyn_cast<UnresolvedUsingTypenameDecl>(Decl))
+ return getCanonicalUnresolvedUsingType(Using);
+
+ assert(Decl->TypeForDecl);
return QualType(Decl->TypeForDecl, 0);
}
/// getTypedefType - Return the unique reference to the type for the
/// specified typedef name decl.
-QualType ASTContext::getTypedefType(const TypedefNameDecl *Decl,
- QualType Underlying) const {
- if (!Decl->TypeForDecl) {
- if (Underlying.isNull())
- Underlying = Decl->getUnderlyingType();
- auto *NewType = new (*this, alignof(TypedefType)) TypedefType(
- Type::Typedef, Decl, Underlying, /*HasTypeDifferentFromDecl=*/false);
- Decl->TypeForDecl = NewType;
+QualType
+ASTContext::getTypedefType(ElaboratedTypeKeyword Keyword,
+ NestedNameSpecifier Qualifier,
+ const TypedefNameDecl *Decl, QualType UnderlyingType,
+ std::optional<bool> TypeMatchesDeclOrNone) const {
+ if (!TypeMatchesDeclOrNone) {
+ QualType DeclUnderlyingType = Decl->getUnderlyingType();
+ assert(!DeclUnderlyingType.isNull());
+ if (UnderlyingType.isNull())
+ UnderlyingType = DeclUnderlyingType;
+ else
+ assert(hasSameType(UnderlyingType, DeclUnderlyingType));
+ TypeMatchesDeclOrNone = UnderlyingType == DeclUnderlyingType;
+ } else {
+ // FIXME: This is a workaround for a serialization cycle: assume the decl
+ // underlying type is not available; don't touch it.
+ assert(!UnderlyingType.isNull());
+ }
+
+ if (Keyword == ElaboratedTypeKeyword::None && !Qualifier &&
+ *TypeMatchesDeclOrNone) {
+ if (Decl->TypeForDecl)
+ return QualType(Decl->TypeForDecl, 0);
+
+ auto *NewType = new (*this, alignof(TypedefType))
+ TypedefType(Type::Typedef, Keyword, Qualifier, Decl, UnderlyingType,
+ !*TypeMatchesDeclOrNone);
+
Types.push_back(NewType);
+ Decl->TypeForDecl = NewType;
return QualType(NewType, 0);
}
- if (Underlying.isNull() || Decl->getUnderlyingType() == Underlying)
- return QualType(Decl->TypeForDecl, 0);
- assert(hasSameType(Decl->getUnderlyingType(), Underlying));
llvm::FoldingSetNodeID ID;
- TypedefType::Profile(ID, Decl, Underlying);
+ TypedefType::Profile(ID, Keyword, Qualifier, Decl, UnderlyingType);
void *InsertPos = nullptr;
- if (TypedefType *T = TypedefTypes.FindNodeOrInsertPos(ID, InsertPos)) {
- assert(!T->typeMatchesDecl() &&
- "non-divergent case should be handled with TypeDecl");
- return QualType(T, 0);
- }
+ if (FoldingSetPlaceholder<TypedefType> *Placeholder =
+ TypedefTypes.FindNodeOrInsertPos(ID, InsertPos))
+ return QualType(Placeholder->getType(), 0);
- void *Mem = Allocate(TypedefType::totalSizeToAlloc<QualType>(true),
- alignof(TypedefType));
- auto *NewType = new (Mem) TypedefType(Type::Typedef, Decl, Underlying,
- /*HasTypeDifferentFromDecl=*/true);
- TypedefTypes.InsertNode(NewType, InsertPos);
+ void *Mem =
+ Allocate(TypedefType::totalSizeToAlloc<FoldingSetPlaceholder<TypedefType>,
+ NestedNameSpecifier, QualType>(
+ 1, !!Qualifier, !*TypeMatchesDeclOrNone),
+ alignof(TypedefType));
+ auto *NewType =
+ new (Mem) TypedefType(Type::Typedef, Keyword, Qualifier, Decl,
+ UnderlyingType, !*TypeMatchesDeclOrNone);
+ auto *Placeholder = new (NewType->getFoldingSetPlaceholder())
+ FoldingSetPlaceholder<TypedefType>();
+ TypedefTypes.InsertNode(Placeholder, InsertPos);
Types.push_back(NewType);
return QualType(NewType, 0);
}
-QualType ASTContext::getUsingType(const UsingShadowDecl *Found,
- QualType Underlying) const {
+QualType ASTContext::getUsingType(ElaboratedTypeKeyword Keyword,
+ NestedNameSpecifier Qualifier,
+ const UsingShadowDecl *D,
+ QualType UnderlyingType) const {
+ // FIXME: This is expensive to compute every time!
+ if (UnderlyingType.isNull()) {
+ const auto *UD = cast<UsingDecl>(D->getIntroducer());
+ UnderlyingType =
+ getTypeDeclType(UD->hasTypename() ? ElaboratedTypeKeyword::Typename
+ : ElaboratedTypeKeyword::None,
+ UD->getQualifier(), cast<TypeDecl>(D->getTargetDecl()));
+ }
+
llvm::FoldingSetNodeID ID;
- UsingType::Profile(ID, Found, Underlying);
+ UsingType::Profile(ID, Keyword, Qualifier, D, UnderlyingType);
void *InsertPos = nullptr;
- if (UsingType *T = UsingTypes.FindNodeOrInsertPos(ID, InsertPos))
+ if (const UsingType *T = UsingTypes.FindNodeOrInsertPos(ID, InsertPos))
return QualType(T, 0);
- const Type *TypeForDecl =
- cast<TypeDecl>(Found->getTargetDecl())->getTypeForDecl();
+ assert(!UnderlyingType.hasLocalQualifiers());
- assert(!Underlying.hasLocalQualifiers());
- QualType Canon = Underlying->getCanonicalTypeInternal();
- assert(TypeForDecl->getCanonicalTypeInternal() == Canon);
+ assert(
+ hasSameType(getCanonicalTypeDeclType(cast<TypeDecl>(D->getTargetDecl())),
+ UnderlyingType));
- if (Underlying.getTypePtr() == TypeForDecl)
- Underlying = QualType();
void *Mem =
- Allocate(UsingType::totalSizeToAlloc<QualType>(!Underlying.isNull()),
+ Allocate(UsingType::totalSizeToAlloc<NestedNameSpecifier>(!!Qualifier),
alignof(UsingType));
- UsingType *NewType = new (Mem) UsingType(Found, Underlying, Canon);
- Types.push_back(NewType);
- UsingTypes.InsertNode(NewType, InsertPos);
- return QualType(NewType, 0);
+ UsingType *T = new (Mem) UsingType(Keyword, Qualifier, D, UnderlyingType);
+ Types.push_back(T);
+ UsingTypes.InsertNode(T, InsertPos);
+ return QualType(T, 0);
}
-QualType ASTContext::getRecordType(const RecordDecl *Decl) const {
- if (Decl->TypeForDecl) return QualType(Decl->TypeForDecl, 0);
+TagType *ASTContext::getTagTypeInternal(ElaboratedTypeKeyword Keyword,
+ NestedNameSpecifier Qualifier,
+ const TagDecl *TD, bool OwnsTag,
+ bool IsInjected,
+ const Type *CanonicalType,
+ bool WithFoldingSetNode) const {
+ auto [TC, Size] = [&] {
+ switch (TD->getDeclKind()) {
+ case Decl::Enum:
+ static_assert(alignof(EnumType) == alignof(TagType));
+ return std::make_tuple(Type::Enum, sizeof(EnumType));
+ case Decl::ClassTemplatePartialSpecialization:
+ case Decl::ClassTemplateSpecialization:
+ case Decl::CXXRecord:
+ static_assert(alignof(RecordType) == alignof(TagType));
+ static_assert(alignof(InjectedClassNameType) == alignof(TagType));
+ if (cast<CXXRecordDecl>(TD)->hasInjectedClassType())
+ return std::make_tuple(Type::InjectedClassName,
+ sizeof(InjectedClassNameType));
+ [[fallthrough]];
+ case Decl::Record:
+ return std::make_tuple(Type::Record, sizeof(RecordType));
+ default:
+ llvm_unreachable("unexpected decl kind");
+ }
+ }();
- if (const RecordDecl *PrevDecl = Decl->getPreviousDecl())
- if (PrevDecl->TypeForDecl)
- return QualType(Decl->TypeForDecl = PrevDecl->TypeForDecl, 0);
+ if (Qualifier) {
+ static_assert(alignof(NestedNameSpecifier) <= alignof(TagType));
+ Size = llvm::alignTo(Size, alignof(NestedNameSpecifier)) +
+ sizeof(NestedNameSpecifier);
+ }
+ void *Mem;
+ if (WithFoldingSetNode) {
+ // FIXME: It would be more profitable to tail allocate the folding set node
+ // from the type, instead of the other way around, due to the greater
+ // alignment requirements of the type. But this makes it harder to deal with
+ // the different type node sizes. This would require either uniquing from
+ // different folding sets, or having the folding setaccept a
+ // contextual parameter which is not fixed at construction.
+ Mem = Allocate(
+ sizeof(TagTypeFoldingSetPlaceholder) +
+ TagTypeFoldingSetPlaceholder::getOffset() + Size,
+ std::max(alignof(TagTypeFoldingSetPlaceholder), alignof(TagType)));
+ auto *T = new (Mem) TagTypeFoldingSetPlaceholder();
+ Mem = T->getTagType();
+ } else {
+ Mem = Allocate(Size, alignof(TagType));
+ }
+
+ auto *T = [&, TC = TC]() -> TagType * {
+ switch (TC) {
+ case Type::Enum: {
+ assert(isa<EnumDecl>(TD));
+ auto *T = new (Mem) EnumType(TC, Keyword, Qualifier, TD, OwnsTag,
+ IsInjected, CanonicalType);
+ assert(reinterpret_cast<void *>(T) ==
+ reinterpret_cast<void *>(static_cast<TagType *>(T)) &&
+ "TagType must be the first base of EnumType");
+ return T;
+ }
+ case Type::Record: {
+ assert(isa<RecordDecl>(TD));
+ auto *T = new (Mem) RecordType(TC, Keyword, Qualifier, TD, OwnsTag,
+ IsInjected, CanonicalType);
+ assert(reinterpret_cast<void *>(T) ==
+ reinterpret_cast<void *>(static_cast<TagType *>(T)) &&
+ "TagType must be the first base of RecordType");
+ return T;
+ }
+ case Type::InjectedClassName: {
+ auto *T = new (Mem) InjectedClassNameType(Keyword, Qualifier, TD,
+ IsInjected, CanonicalType);
+ assert(reinterpret_cast<void *>(T) ==
+ reinterpret_cast<void *>(static_cast<TagType *>(T)) &&
+ "TagType must be the first base of InjectedClassNameType");
+ return T;
+ }
+ default:
+ llvm_unreachable("unexpected type class");
+ }
+ }();
+ assert(T->getKeyword() == Keyword);
+ assert(T->getQualifier() == Qualifier);
+ assert(T->getOriginalDecl() == TD);
+ assert(T->isInjected() == IsInjected);
+ assert(T->isTagOwned() == OwnsTag);
+ assert((T->isCanonicalUnqualified()
+ ? QualType()
+ : T->getCanonicalTypeInternal()) == QualType(CanonicalType, 0));
+ Types.push_back(T);
+ return T;
+}
- auto *newType = new (*this, alignof(RecordType)) RecordType(Decl);
- Decl->TypeForDecl = newType;
- Types.push_back(newType);
- return QualType(newType, 0);
+static bool getNonInjectedClassName(const TagDecl *&TD) {
+ if (const auto *RD = dyn_cast<CXXRecordDecl>(TD);
+ RD && RD->isInjectedClassName()) {
+ TD = cast<TagDecl>(RD->getDeclContext());
+ return true;
+ }
+ return false;
}
-QualType ASTContext::getEnumType(const EnumDecl *Decl) const {
- if (Decl->TypeForDecl) return QualType(Decl->TypeForDecl, 0);
+CanQualType ASTContext::getCanonicalTagType(const TagDecl *TD) const {
+ ::getNonInjectedClassName(TD);
+ TD = TD->getCanonicalDecl();
+ if (TD->TypeForDecl)
+ return TD->TypeForDecl->getCanonicalTypeUnqualified();
+
+ const Type *CanonicalType = getTagTypeInternal(
+ ElaboratedTypeKeyword::None,
+ /*Qualifier=*/std::nullopt, TD,
+ /*OwnsTag=*/false, /*IsInjected=*/false, /*CanonicalType=*/nullptr,
+ /*WithFoldingSetNode=*/false);
+ TD->TypeForDecl = CanonicalType;
+ return CanQualType::CreateUnsafe(QualType(CanonicalType, 0));
+}
+
+QualType ASTContext::getTagType(ElaboratedTypeKeyword Keyword,
+ NestedNameSpecifier Qualifier,
+ const TagDecl *TD, bool OwnsTag) const {
+ ElaboratedTypeKeyword PreferredKeyword =
+ getLangOpts().CPlusPlus
+ ? ElaboratedTypeKeyword::None
+ : KeywordHelpers::getKeywordForTagTypeKind(TD->getTagKind());
+
+ if (Keyword == PreferredKeyword && !Qualifier && !OwnsTag) {
+ if (const Type *T = TD->TypeForDecl; T && !T->isCanonicalUnqualified())
+ return QualType(T, 0);
+
+ bool IsInjected = ::getNonInjectedClassName(TD);
+ const Type *CanonicalType = getCanonicalTagType(TD).getTypePtr();
+ const Type *T =
+ getTagTypeInternal(Keyword,
+ /*Qualifier=*/std::nullopt, TD,
+ /*OwnsTag=*/false, IsInjected, CanonicalType,
+ /*WithFoldingSetNode=*/false);
+ TD->TypeForDecl = T;
----------------
mizvekov wrote:
This will soon be fixed here: https://github.com/llvm/llvm-project/pull/155347.
https://github.com/llvm/llvm-project/pull/147835
More information about the cfe-commits
mailing list