[clang] [lldb] [BoundsSafety] Introduce LateParsedAttrType AST placeholder type (PR #204125)
Mohammed Ashraf via cfe-commits
cfe-commits at lists.llvm.org
Sun Jul 19 10:30:54 PDT 2026
https://github.com/Holo-xy updated https://github.com/llvm/llvm-project/pull/204125
>From 0d9194315989b7088ca5de18dd9d85972494a103 Mon Sep 17 00:00:00 2001
From: Holo-xy <mohammedashraf4599 at gmail.com>
Date: Sat, 13 Jun 2026 06:46:32 +0300
Subject: [PATCH 1/4] Introduce LateParsedAttrType AST placeholder type
---
clang/include/clang/AST/ASTContext.h | 6 +++
clang/include/clang/AST/RecursiveASTVisitor.h | 6 +++
clang/include/clang/AST/TypeBase.h | 41 +++++++++++++++++++
clang/include/clang/AST/TypeLoc.h | 31 ++++++++++++++
clang/include/clang/AST/TypeProperties.td | 6 +++
clang/include/clang/Basic/TypeNodes.td | 1 +
clang/lib/AST/ASTContext.cpp | 18 ++++++++
clang/lib/AST/ASTImporter.cpp | 7 ++++
clang/lib/AST/ASTStructuralEquivalence.cpp | 7 ++++
clang/lib/AST/ItaniumMangle.cpp | 1 +
clang/lib/AST/TypePrinter.cpp | 15 +++++++
clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 1 +
clang/lib/CodeGen/CGDebugInfo.cpp | 1 +
clang/lib/CodeGen/CodeGenFunction.cpp | 1 +
clang/lib/Sema/SemaExpr.cpp | 1 +
clang/lib/Sema/TreeTransform.h | 20 +++++++++
clang/lib/Serialization/ASTReader.cpp | 4 ++
clang/lib/Serialization/ASTWriter.cpp | 7 ++++
clang/tools/libclang/CIndex.cpp | 4 ++
.../TypeSystem/Clang/TypeSystemClang.cpp | 9 ++++
20 files changed, 187 insertions(+)
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index a4ed852d36442..2095bc1a1df67 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1630,6 +1630,12 @@ class ASTContext : public RefCountedBase<ASTContext> {
bool OrNull,
ArrayRef<TypeCoupledDeclRefInfo> DependentDecls) const;
+ /// Return a placeholder type for a late-parsed type attribute.
+ /// This type wraps another type and holds the LateParsedAttribute
+ /// that will be parsed later.
+ QualType getLateParsedAttrType(QualType Wrapped,
+ LateParsedTypeAttribute *LateParsedAttr) const;
+
/// Return the uniqued reference to a type adjusted from the original
/// type to a new type.
QualType getAdjustedType(QualType Orig, QualType New) const;
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 529d657fc01f5..5a72ef0f17785 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1164,6 +1164,9 @@ DEF_TRAVERSE_TYPE(CountAttributedType, {
TRY_TO(TraverseType(T->desugar()));
})
+DEF_TRAVERSE_TYPE(LateParsedAttrType,
+ { TRY_TO(TraverseType(T->getWrappedType())); })
+
DEF_TRAVERSE_TYPE(BTFTagAttributedType,
{ TRY_TO(TraverseType(T->getWrappedType())); })
@@ -1522,6 +1525,9 @@ DEF_TRAVERSE_TYPELOC(AttributedType,
DEF_TRAVERSE_TYPELOC(CountAttributedType,
{ TRY_TO(TraverseTypeLoc(TL.getInnerLoc())); })
+DEF_TRAVERSE_TYPELOC(LateParsedAttrType,
+ { TRY_TO(TraverseTypeLoc(TL.getInnerLoc())); })
+
DEF_TRAVERSE_TYPELOC(BTFTagAttributedType,
{ TRY_TO(TraverseTypeLoc(TL.getWrappedLoc())); })
diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h
index e3844d0cefa78..dd46a58a6fa50 100644
--- a/clang/include/clang/AST/TypeBase.h
+++ b/clang/include/clang/AST/TypeBase.h
@@ -70,6 +70,7 @@ class TagDecl;
class TemplateParameterList;
class Type;
class Attr;
+struct LateParsedTypeAttribute;
enum {
TypeAlignmentInBits = 4,
@@ -3545,6 +3546,46 @@ class CountAttributedType final
StringRef getAttributeName(bool WithMacroPrefix) const;
};
+/// Represents a placeholder type for late-parsed type attributes.
+/// This type wraps another type and holds an opaque pointer to a
+/// LateParsedAttribute that will be parsed later (e.g., in ActOnFields).
+/// Once parsed, this type is replaced with the appropriate attributed type
+/// (e.g., CountAttributedType for counted_by).
+class LateParsedAttrType : public Type, public llvm::FoldingSetNode {
+ friend class ASTContext; // ASTContext creates these.
+
+ QualType WrappedTy;
+ LateParsedTypeAttribute *LateParsedTypeAttr;
+
+ LateParsedAttrType(QualType Wrapped, QualType Canon,
+ LateParsedTypeAttribute *Attr)
+ : Type(LateParsedAttr, Canon, Wrapped->getDependence()),
+ WrappedTy(Wrapped), LateParsedTypeAttr(Attr) {}
+
+public:
+ QualType getWrappedType() const { return WrappedTy; }
+ LateParsedTypeAttribute *getLateParsedAttribute() const {
+ return LateParsedTypeAttr;
+ }
+
+ bool isSugared() const { return true; }
+ QualType desugar() const { return WrappedTy; }
+
+ void Profile(llvm::FoldingSetNodeID &ID) {
+ Profile(ID, WrappedTy, LateParsedTypeAttr);
+ }
+
+ static void Profile(llvm::FoldingSetNodeID &ID, QualType Wrapped,
+ LateParsedTypeAttribute *Attr) {
+ ID.AddPointer(Wrapped.getAsOpaquePtr());
+ ID.AddPointer(Attr);
+ }
+
+ static bool classof(const Type *T) {
+ return T->getTypeClass() == LateParsedAttr;
+ }
+};
+
/// Represents a type which was implicitly adjusted by the semantic
/// engine for arbitrary reasons. For example, array and function types can
/// decay, and function types can have their calling conventions adjusted.
diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h
index 24df18dbaace4..2648806ac687a 100644
--- a/clang/include/clang/AST/TypeLoc.h
+++ b/clang/include/clang/AST/TypeLoc.h
@@ -1358,6 +1358,37 @@ class CountAttributedTypeLoc final
SourceRange getLocalSourceRange() const;
};
+struct LateParsedAttrLocInfo {
+ SourceLocation AttrNameLoc;
+};
+
+class LateParsedAttrTypeLoc
+ : public ConcreteTypeLoc<UnqualTypeLoc, LateParsedAttrTypeLoc,
+ LateParsedAttrType, LateParsedAttrLocInfo> {
+public:
+ TypeLoc getInnerLoc() const { return getInnerTypeLoc(); }
+
+ SourceLocation getAttrNameLoc() const { return getLocalData()->AttrNameLoc; }
+
+ void setAttrNameLoc(SourceLocation Loc) { getLocalData()->AttrNameLoc = Loc; }
+
+ SourceRange getLocalSourceRange() const {
+ return SourceRange(getAttrNameLoc(), getAttrNameLoc());
+ }
+
+ void initializeLocal(ASTContext &Context, SourceLocation Loc) {
+ setAttrNameLoc(Loc);
+ }
+
+ unsigned getLocalDataSize() const { return sizeof(LateParsedAttrLocInfo); }
+
+ QualType getInnerType() const { return getTypePtr()->getWrappedType(); }
+
+ LateParsedTypeAttribute *getLateParsedAttribute() const {
+ return getTypePtr()->getLateParsedAttribute();
+ }
+};
+
struct MacroQualifiedLocInfo {
SourceLocation ExpansionLoc;
};
diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td
index f16c10da430f9..c5402a1925d99 100644
--- a/clang/include/clang/AST/TypeProperties.td
+++ b/clang/include/clang/AST/TypeProperties.td
@@ -44,6 +44,12 @@ let Class = CountAttributedType in {
def : Creator<[{ return ctx.getCountAttributedType(WrappedTy, CountExpr, CountInBytes, OrNull, CoupledDecls); }]>;
}
+let Class = LateParsedAttrType in {
+ // Note: LateParsedAttrType is a transient placeholder type that should
+ // normally be replaced before serialization. So this won't be serialized.
+ def : Creator<[{ (void)ctx; llvm_unreachable("unreachable for serialization"); }]>;
+}
+
let Class = AdjustedType in {
def : Property<"originalType", QualType> {
let Read = [{ node->getOriginalType() }];
diff --git a/clang/include/clang/Basic/TypeNodes.td b/clang/include/clang/Basic/TypeNodes.td
index a9965a4a89aa1..700a73f669690 100644
--- a/clang/include/clang/Basic/TypeNodes.td
+++ b/clang/include/clang/Basic/TypeNodes.td
@@ -105,6 +105,7 @@ def ObjCInterfaceType : TypeNode<ObjCObjectType>, AlwaysCanonical;
def ObjCObjectPointerType : TypeNode<Type>;
def BoundsAttributedType : TypeNode<Type, 1>;
def CountAttributedType : TypeNode<BoundsAttributedType>, NeverCanonical;
+def LateParsedAttrType : TypeNode<Type>, NeverCanonical;
def PipeType : TypeNode<Type>;
def AtomicType : TypeNode<Type>;
def BitIntType : TypeNode<Type>;
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index abf0cd5e18c2b..562694ffc2b8c 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -2581,6 +2581,9 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
case Type::CountAttributed:
return getTypeInfo(cast<CountAttributedType>(T)->desugar().getTypePtr());
+ case Type::LateParsedAttr:
+ return getTypeInfo(cast<LateParsedAttrType>(T)->desugar().getTypePtr());
+
case Type::BTFTagAttributed:
return getTypeInfo(
cast<BTFTagAttributedType>(T)->getWrappedType().getTypePtr());
@@ -3751,6 +3754,17 @@ QualType ASTContext::getCountAttributedType(
return QualType(CATy, 0);
}
+QualType ASTContext::getLateParsedAttrType(
+ QualType WrappedTy, LateParsedTypeAttribute *LateParsedAttr) const {
+ QualType CanonTy = getCanonicalType(WrappedTy);
+
+ auto *LPATy = new (*this, alignof(LateParsedAttrType))
+ LateParsedAttrType(WrappedTy, CanonTy, LateParsedAttr);
+
+ Types.push_back(LPATy);
+ return QualType(LPATy, 0);
+}
+
QualType
ASTContext::adjustType(QualType Orig,
llvm::function_ref<QualType(QualType)> Adjust) const {
@@ -14920,6 +14934,10 @@ static QualType getCommonSugarTypeNode(const ASTContext &Ctx, const Type *X,
DX->isCountInBytes(), DX->isOrNull(),
CDX);
}
+
+ case Type::LateParsedAttr:
+ return QualType();
+
case Type::PredefinedSugar:
assert(cast<PredefinedSugarType>(X)->getKind() !=
cast<PredefinedSugarType>(Y)->getKind());
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index f5848d154f49e..d91da096b2466 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -1842,6 +1842,13 @@ ASTNodeImporter::VisitCountAttributedType(const CountAttributedType *T) {
ArrayRef(CoupledDecls));
}
+ExpectedType
+ASTNodeImporter::VisitLateParsedAttrType(const LateParsedAttrType *T) {
+ // LateParsedAttrType is a transient placeholder that should not normally
+ // appear during AST import. Import as the wrapped type.
+ return import(T->getWrappedType());
+}
+
ExpectedType ASTNodeImporter::VisitTemplateTypeParmType(
const TemplateTypeParmType *T) {
Expected<TemplateTypeParmDecl *> ToDeclOrErr = import(T->getDecl());
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index e0b62591a6a73..d8bbfbe5dac72 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -1208,6 +1208,13 @@ bool ASTStructuralEquivalence::isEquivalent(
return false;
break;
+ case Type::LateParsedAttr:
+ if (!IsStructurallyEquivalent(
+ Context, cast<LateParsedAttrType>(T1)->getWrappedType(),
+ cast<LateParsedAttrType>(T2)->getWrappedType()))
+ return false;
+ break;
+
case Type::BTFTagAttributed:
if (!IsStructurallyEquivalent(
Context, cast<BTFTagAttributedType>(T1)->getWrappedType(),
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index e5cdd6f31c507..60e77b66d92cf 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -2472,6 +2472,7 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty,
case Type::BitInt:
case Type::DependentBitInt:
case Type::CountAttributed:
+ case Type::LateParsedAttr:
llvm_unreachable("type is illegal as a nested name specifier");
case Type::SubstBuiltinTemplatePack:
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 53b869e019074..ae9838413ab44 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -288,6 +288,7 @@ bool TypePrinter::canPrefixQualifiers(const Type *T,
case Type::MacroQualified:
case Type::OverflowBehavior:
case Type::CountAttributed:
+ case Type::LateParsedAttr:
CanPrefixQualifiers = false;
break;
@@ -1832,6 +1833,20 @@ void TypePrinter::printCountAttributedAfter(const CountAttributedType *T,
printCountAttributedImpl(T, OS, Policy);
}
+void TypePrinter::printLateParsedAttrBefore(const LateParsedAttrType *T,
+ raw_ostream &OS) {
+ // LateParsedAttrType is a transient placeholder that should not appear
+ // in user-facing output. Just print the wrapped type.
+ printBefore(T->getWrappedType(), OS);
+}
+
+void TypePrinter::printLateParsedAttrAfter(const LateParsedAttrType *T,
+ raw_ostream &OS) {
+ // LateParsedAttrType is a transient placeholder that should not appear
+ // in user-facing output. Just print the wrapped type.
+ printAfter(T->getWrappedType(), OS);
+}
+
void TypePrinter::printAttributedBefore(const AttributedType *T,
raw_ostream &OS) {
// FIXME: Generate this with TableGen.
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 4b020c96964a7..7489a315af98c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -1642,6 +1642,7 @@ void CIRGenFunction::emitVariablyModifiedType(QualType type) {
case Type::HLSLAttributedResource:
case Type::HLSLInlineSpirv:
case Type::PredefinedSugar:
+ case Type::LateParsedAttr:
cgm.errorNYI("CIRGenFunction::emitVariablyModifiedType");
break;
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 3d429d0d78e82..6305c3a115dd4 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -4332,6 +4332,7 @@ llvm::DIType *CGDebugInfo::CreateTypeNode(QualType Ty, llvm::DIFile *Unit) {
case Type::PredefinedSugar:
return getOrCreateType(cast<PredefinedSugarType>(Ty)->desugar(), Unit);
case Type::CountAttributed:
+ case Type::LateParsedAttr:
case Type::Auto:
case Type::Attributed:
case Type::BTFTagAttributed:
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index b920266b59808..a5f71a88535b4 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -2569,6 +2569,7 @@ void CodeGenFunction::EmitVariablyModifiedType(QualType type) {
case Type::BitInt:
case Type::HLSLInlineSpirv:
case Type::PredefinedSugar:
+ case Type::LateParsedAttr:
llvm_unreachable("type class is never variably-modified!");
case Type::Adjusted:
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index ad6e7183cb3a4..0c051dc7d755a 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -4667,6 +4667,7 @@ static void captureVariablyModifiedType(ASTContext &Context, QualType T,
case Type::SubstTemplateTypeParm:
case Type::MacroQualified:
case Type::CountAttributed:
+ case Type::LateParsedAttr:
// Keep walking after single level desugaring.
T = T.getSingleStepDesugaredType(Context);
break;
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 53107c827006d..50a9d4feb9b6d 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -7768,6 +7768,26 @@ QualType TreeTransform<Derived>::TransformCountAttributedType(
return Result;
}
+template <typename Derived>
+QualType
+TreeTransform<Derived>::TransformLateParsedAttrType(TypeLocBuilder &TLB,
+ LateParsedAttrTypeLoc TL) {
+ const LateParsedAttrType *OldTy = TL.getTypePtr();
+ QualType InnerTy = getDerived().TransformType(TLB, TL.getInnerLoc());
+ if (InnerTy.isNull())
+ return QualType();
+
+ QualType Result = TL.getType();
+ if (getDerived().AlwaysRebuild() || InnerTy != OldTy->getWrappedType()) {
+ Result = SemaRef.Context.getLateParsedAttrType(
+ InnerTy, OldTy->getLateParsedAttribute());
+ }
+
+ LateParsedAttrTypeLoc newTL = TLB.push<LateParsedAttrTypeLoc>(Result);
+ newTL.setAttrNameLoc(TL.getAttrNameLoc());
+ return Result;
+}
+
template <typename Derived>
QualType TreeTransform<Derived>::TransformBTFTagAttributedType(
TypeLocBuilder &TLB, BTFTagAttributedTypeLoc TL) {
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index f8a6a38bb9b5c..5c8827ea32514 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -7701,6 +7701,10 @@ void TypeLocReader::VisitCountAttributedTypeLoc(CountAttributedTypeLoc TL) {
// Nothing to do
}
+void TypeLocReader::VisitLateParsedAttrTypeLoc(LateParsedAttrTypeLoc TL) {
+ // Nothing to do
+}
+
void TypeLocReader::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) {
// Nothing to do.
}
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 21dda6f3733e4..2c56a052a5fb4 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -156,6 +156,9 @@ static TypeCode getTypeCodeForTypeClass(Type::TypeClass id) {
#define TYPE_BIT_CODE(CLASS_ID, CODE_ID, CODE_VALUE) \
case Type::CLASS_ID: return TYPE_##CODE_ID;
#include "clang/Serialization/TypeBitCodes.def"
+ case Type::LateParsedAttr:
+ llvm_unreachable(
+ "should be replaced with a concrete type before serialization");
case Type::Builtin:
llvm_unreachable("shouldn't be serializing a builtin type this way");
}
@@ -587,6 +590,10 @@ void TypeLocWriter::VisitCountAttributedTypeLoc(CountAttributedTypeLoc TL) {
// Nothing to do
}
+void TypeLocWriter::VisitLateParsedAttrTypeLoc(LateParsedAttrTypeLoc TL) {
+ // Nothing to do
+}
+
void TypeLocWriter::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) {
// Nothing to do.
}
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index ac2fad38a1348..42d4b5bef7fb8 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -1738,6 +1738,10 @@ bool CursorVisitor::VisitCountAttributedTypeLoc(CountAttributedTypeLoc TL) {
return Visit(TL.getInnerLoc());
}
+bool CursorVisitor::VisitLateParsedAttrTypeLoc(LateParsedAttrTypeLoc TL) {
+ return Visit(TL.getInnerLoc());
+}
+
bool CursorVisitor::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) {
return Visit(TL.getWrappedLoc());
}
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 82fd9844cf96a..8adfe096a7624 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -4099,6 +4099,9 @@ TypeSystemClang::GetTypeClass(lldb::opaque_compiler_type_t type) {
case clang::Type::Using:
case clang::Type::PredefinedSugar:
llvm_unreachable("Handled in RemoveWrappingTypes!");
+ case clang::Type::LateParsedAttr:
+ llvm_unreachable("LateParsedAttrType is a transient parsing placeholder "
+ "that is resolved before the AST is finalized.");
case clang::Type::UnaryTransform:
break;
case clang::Type::FunctionNoProto:
@@ -4803,6 +4806,9 @@ lldb::Encoding TypeSystemClang::GetEncoding(lldb::opaque_compiler_type_t type) {
case clang::Type::Using:
case clang::Type::PredefinedSugar:
llvm_unreachable("Handled in RemoveWrappingTypes!");
+ case clang::Type::LateParsedAttr:
+ llvm_unreachable("LateParsedAttrType is a transient parsing placeholder "
+ "that is resolved before the AST is finalized.");
case clang::Type::UnaryTransform:
break;
@@ -5105,6 +5111,9 @@ lldb::Format TypeSystemClang::GetFormat(lldb::opaque_compiler_type_t type) {
case clang::Type::Using:
case clang::Type::PredefinedSugar:
llvm_unreachable("Handled in RemoveWrappingTypes!");
+ case clang::Type::LateParsedAttr:
+ llvm_unreachable("LateParsedAttrType is a transient parsing placeholder "
+ "that is resolved before the AST is finalized.");
case clang::Type::UnaryTransform:
break;
>From 90367bbe3755e98d035101a9fb595bc669241801 Mon Sep 17 00:00:00 2001
From: Holo-xy <mohammedashraf4599 at gmail.com>
Date: Fri, 19 Jun 2026 22:03:24 +0300
Subject: [PATCH 2/4] fix code placement
---
clang/lib/CodeGen/CodeGenFunction.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index a5f71a88535b4..51426183211ec 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -2569,7 +2569,6 @@ void CodeGenFunction::EmitVariablyModifiedType(QualType type) {
case Type::BitInt:
case Type::HLSLInlineSpirv:
case Type::PredefinedSugar:
- case Type::LateParsedAttr:
llvm_unreachable("type class is never variably-modified!");
case Type::Adjusted:
@@ -2663,6 +2662,7 @@ void CodeGenFunction::EmitVariablyModifiedType(QualType type) {
case Type::SubstTemplateTypeParm:
case Type::MacroQualified:
case Type::CountAttributed:
+ case Type::LateParsedAttr:
// Keep walking after single level desugaring.
type = type.getSingleStepDesugaredType(getContext());
break;
>From 45490e6b37694883c039c1a8680613ec02b9b8c3 Mon Sep 17 00:00:00 2001
From: Holo-xy <mohammedashraf4599 at gmail.com>
Date: Sun, 19 Jul 2026 20:12:04 +0300
Subject: [PATCH 3/4] make LateParsedAttrType unreachable in AST import and
serialization
---
clang/lib/AST/ASTImporter.cpp | 4 +---
clang/lib/Serialization/ASTReader.cpp | 3 ++-
clang/lib/Serialization/ASTWriter.cpp | 3 ++-
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index d91da096b2466..9c45fd3ffd185 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -1844,9 +1844,7 @@ ASTNodeImporter::VisitCountAttributedType(const CountAttributedType *T) {
ExpectedType
ASTNodeImporter::VisitLateParsedAttrType(const LateParsedAttrType *T) {
- // LateParsedAttrType is a transient placeholder that should not normally
- // appear during AST import. Import as the wrapped type.
- return import(T->getWrappedType());
+ llvm_unreachable("should be replaced with a concrete type before AST import");
}
ExpectedType ASTNodeImporter::VisitTemplateTypeParmType(
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 5c8827ea32514..49a78431506c5 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -7702,7 +7702,8 @@ void TypeLocReader::VisitCountAttributedTypeLoc(CountAttributedTypeLoc TL) {
}
void TypeLocReader::VisitLateParsedAttrTypeLoc(LateParsedAttrTypeLoc TL) {
- // Nothing to do
+ llvm_unreachable(
+ "should be replaced with a concrete type before serialization");
}
void TypeLocReader::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) {
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 2c56a052a5fb4..0da13cd518ecd 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -591,7 +591,8 @@ void TypeLocWriter::VisitCountAttributedTypeLoc(CountAttributedTypeLoc TL) {
}
void TypeLocWriter::VisitLateParsedAttrTypeLoc(LateParsedAttrTypeLoc TL) {
- // Nothing to do
+ llvm_unreachable(
+ "should be replaced with a concrete type before serialization");
}
void TypeLocWriter::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) {
>From 7c3277ba7ebbbb24419e54dcee0cf1345b01edb4 Mon Sep 17 00:00:00 2001
From: Holo-xy <mohammedashraf4599 at gmail.com>
Date: Sun, 19 Jul 2026 20:30:01 +0300
Subject: [PATCH 4/4] document LateParsedAttrType canonical type and drop
FoldingSetNode
---
clang/include/clang/AST/TypeBase.h | 20 +++++++-------------
1 file changed, 7 insertions(+), 13 deletions(-)
diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h
index dd46a58a6fa50..663ccb5a8a446 100644
--- a/clang/include/clang/AST/TypeBase.h
+++ b/clang/include/clang/AST/TypeBase.h
@@ -3548,10 +3548,14 @@ class CountAttributedType final
/// Represents a placeholder type for late-parsed type attributes.
/// This type wraps another type and holds an opaque pointer to a
-/// LateParsedAttribute that will be parsed later (e.g., in ActOnFields).
+/// LateParsedTypeAttribute that will be parsed later (e.g., in ActOnFields).
/// Once parsed, this type is replaced with the appropriate attributed type
-/// (e.g., CountAttributedType for counted_by).
-class LateParsedAttrType : public Type, public llvm::FoldingSetNode {
+/// (e.g., CountAttributedType for `__counted_by`).
+///
+/// Its canonical type is that of the wrapped type, so a consumer walking the
+/// AST during late parsing must treat this as "attribute unresolved", not "no
+/// attribute here".
+class LateParsedAttrType : public Type {
friend class ASTContext; // ASTContext creates these.
QualType WrappedTy;
@@ -3571,16 +3575,6 @@ class LateParsedAttrType : public Type, public llvm::FoldingSetNode {
bool isSugared() const { return true; }
QualType desugar() const { return WrappedTy; }
- void Profile(llvm::FoldingSetNodeID &ID) {
- Profile(ID, WrappedTy, LateParsedTypeAttr);
- }
-
- static void Profile(llvm::FoldingSetNodeID &ID, QualType Wrapped,
- LateParsedTypeAttribute *Attr) {
- ID.AddPointer(Wrapped.getAsOpaquePtr());
- ID.AddPointer(Attr);
- }
-
static bool classof(const Type *T) {
return T->getTypeClass() == LateParsedAttr;
}
More information about the cfe-commits
mailing list