Skip to content

[Clang] Make the SizeType, SignedSizeType and PtrdiffType be named sugar types instead of built-in types #143653

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

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
9628219
Enable __ptrdiff_t
YexuanXiao Jun 10, 2025
33f05ab
Enable __size_t and __unsigned_size_t
YexuanXiao Jun 11, 2025
7e2593e
Revert changes made outside the intended purpose
YexuanXiao Jun 11, 2025
bcc45f0
Add ASTContext::getCanonicalSizeType()
YexuanXiao Jun 11, 2025
9d6235b
Check typedefs are defined in the appropriate scope
YexuanXiao Jun 11, 2025
85cbd4a
Move ssize_t to the global scope, as it must ultimately be provided i…
YexuanXiao Jun 11, 2025
210fe3b
Fix code format
YexuanXiao Jun 12, 2025
ddd73d8
Clarifying support for C89
YexuanXiao Jun 12, 2025
6e207dd
Try add PredefinedSugarType
YexuanXiao Jun 13, 2025
1519032
Fix tests
YexuanXiao Jun 14, 2025
41f1433
Merge pull request #1 from YexuanXiao/predefined-sugar
YexuanXiao Jun 14, 2025
fc57313
Manually resolve conflicts
YexuanXiao Jun 14, 2025
c702c18
Add support for the new Type in FormatString
YexuanXiao Jun 14, 2025
4b62452
Enable __signed_size_t and __ptrdiff_t
YexuanXiao Jun 14, 2025
3ab170b
Update the comment to match the latest impl
YexuanXiao Jun 14, 2025
c26bb5f
Fix comment
YexuanXiao Jun 14, 2025
b53c418
Update tests to match the latest impl
YexuanXiao Jun 14, 2025
46a8ede
Fix the errors pointed out in the review
YexuanXiao Jun 14, 2025
aabc866
Fix enum-to-integer conversion
YexuanXiao Jun 14, 2025
f5acec2
Fix the test of clangd
YexuanXiao Jun 14, 2025
6d427d3
Fix code format
YexuanXiao Jun 14, 2025
b487226
Fix tests
YexuanXiao Jun 14, 2025
a60ff6e
Remove unnecessary #include added by clangd
YexuanXiao Jun 14, 2025
17ef365
Actively initialize SizeType, SignedSizeType, PtrdiffType instead of …
YexuanXiao Jun 15, 2025
dd2a256
Remove the unused function declaration and minor tweaks
YexuanXiao Jun 15, 2025
0bbc668
Use using declarations to expose protected members to simplify the code
YexuanXiao Jun 15, 2025
895efce
Resolve a conflict
YexuanXiao Jun 26, 2025
9ed218e
Temporary remove to resolve the conflict
YexuanXiao Jun 26, 2025
a60f96d
Merge branch 'main' into main
YexuanXiao Jun 26, 2025
faf82b1
Update ASTReader.cpp
YexuanXiao Jun 26, 2025
a10bd96
Fix the test of libc++
YexuanXiao Jun 28, 2025
6a473e0
Fix some simple issues
YexuanXiao Jun 28, 2025
9b9b90a
Fix other issues
YexuanXiao Jun 28, 2025
a753834
Enable HLSL
YexuanXiao Jun 28, 2025
816fef9
Use FoldingSetNode
YexuanXiao Jun 28, 2025
e8420f0
Improved based on the review
YexuanXiao Jun 29, 2025
2d1cb40
Assert out of bounds in advance
YexuanXiao Jun 29, 2025
1274282
Fixed the unset value
YexuanXiao Jun 29, 2025
6b8f27d
Oops, std::array should not be default-initialized
YexuanXiao Jun 29, 2025
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
4 changes: 2 additions & 2 deletions clang-tools-extra/clangd/unittests/HoverTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2794,7 +2794,7 @@ TEST(Hover, All) {
})cpp",
[](HoverInfo &HI) {
HI.Name = "expression";
HI.Type = "unsigned long";
HI.Type = {"__size_t", "unsigned long"};
HI.Value = "1";
}},
{
Expand All @@ -2804,7 +2804,7 @@ TEST(Hover, All) {
})cpp",
[](HoverInfo &HI) {
HI.Name = "expression";
HI.Type = "unsigned long";
HI.Type = {"__size_t", "unsigned long"};
HI.Value = "1";
}},
{
Expand Down
14 changes: 12 additions & 2 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -1567,6 +1567,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// and bit count.
QualType getDependentBitIntType(bool Unsigned, Expr *BitsExpr) const;

QualType getPredefinedSugarType(uint32_t KD) const;

/// Gets the struct used to keep track of the extended descriptor for
/// pointer to blocks.
QualType getBlockDescriptorExtendedType() const;
Expand Down Expand Up @@ -1990,6 +1992,12 @@ class ASTContext : public RefCountedBase<ASTContext> {
bool IsDependent,
QualType Canon) const;

// The core language uses these types as the result types of some expressions,
// which are typically standard integer types and consistent with it's
// typedefs (if any).
QualType SizeType; // __size_t
QualType SignedSizeType; // __signed_size_t
QualType PtrdiffType; // __ptrdiff_t
public:
/// Return the unique reference to the type for the specified TagDecl
/// (struct/union/class/enum) decl.
Expand All @@ -1999,11 +2007,13 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// <stddef.h>.
///
/// The sizeof operator requires this (C99 6.5.3.4p4).
CanQualType getSizeType() const;
QualType getSizeType() const;

CanQualType getCanonicalSizeType() const;

/// Return the unique signed counterpart of
/// the integer type corresponding to size_t.
CanQualType getSignedSizeType() const;
QualType getSignedSizeType() const;

/// Return the unique type for "intmax_t" (C99 7.18.1.5), defined in
/// <stdint.h>.
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/AST/FormatString.h
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,8 @@ class FormatSpecifier {

/// For a TypedefType QT, if it is a named integer type such as size_t,
/// assign the appropriate value to LM and return true.
static bool namedTypeToLengthModifier(QualType QT, LengthModifier &LM);
static bool namedTypeToLengthModifier(ASTContext &Ctx, QualType QT,
LengthModifier &LM);
};

} // end analyze_format_string namespace
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -1210,6 +1210,8 @@ DEF_TRAVERSE_TYPE(BitIntType, {})
DEF_TRAVERSE_TYPE(DependentBitIntType,
{ TRY_TO(TraverseStmt(T->getNumBitsExpr())); })

DEF_TRAVERSE_TYPE(PredefinedSugarType, {})

#undef DEF_TRAVERSE_TYPE

// ----------------- TypeLoc traversal -----------------
Expand Down Expand Up @@ -1526,6 +1528,8 @@ DEF_TRAVERSE_TYPELOC(DependentBitIntType, {
TRY_TO(TraverseStmt(TL.getTypePtr()->getNumBitsExpr()));
})

DEF_TRAVERSE_TYPELOC(PredefinedSugarType, {})

#undef DEF_TRAVERSE_TYPELOC

// ----------------- Decl traversal -----------------
Expand Down
48 changes: 48 additions & 0 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -2258,6 +2258,27 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
unsigned NumExpansions;
};

enum class PredefinedSugarKind {
/// The "size_t" type.
SizeT,

/// The "signed size_t" type.
SignedSizeT,

/// The "ptrdiff_t" type.
PtrdiffT
};

class PresefinedSugarTypeBitfields {
friend class PredefinedSugarType;

LLVM_PREFERRED_TYPE(TypeBitfields)
unsigned : NumTypeBits;

LLVM_PREFERRED_TYPE(PredefinedSugarKind)
unsigned Kind : 8;
};

class CountAttributedTypeBitfields {
friend class CountAttributedType;

Expand Down Expand Up @@ -2297,6 +2318,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
DependentTemplateSpecializationTypeBits;
PackExpansionTypeBitfields PackExpansionTypeBits;
CountAttributedTypeBitfields CountAttributedTypeBits;
PresefinedSugarTypeBitfields PredefinedSugarTypeBits;
};

private:
Expand Down Expand Up @@ -8038,6 +8060,32 @@ class DependentBitIntType final : public Type, public llvm::FoldingSetNode {
}
};

class PredefinedSugarType final : public Type {
public:
friend class ASTContext;
using Kind = PredefinedSugarKind;

private:
PredefinedSugarType(Kind KD, QualType UnderlyingType)
: Type(PredefinedSugar, UnderlyingType->getCanonicalTypeInternal(),
TypeDependence::None) {
PredefinedSugarTypeBits.Kind = llvm::to_underlying(KD);
}

public:
bool isSugared() const { return true; }

QualType desugar() const { return getCanonicalTypeInternal(); }

Kind getKind() const { return Kind(PredefinedSugarTypeBits.Kind); }

StringRef getName() const;

static bool classof(const Type *T) {
return T->getTypeClass() == PredefinedSugar;
}
};

/// A qualifier set is used to build a set of qualifiers.
class QualifierCollector : public Qualifiers {
public:
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/AST/TypeLoc.h
Original file line number Diff line number Diff line change
Expand Up @@ -2767,6 +2767,10 @@ class DependentBitIntTypeLoc final
: public InheritingConcreteTypeLoc<TypeSpecTypeLoc, DependentBitIntTypeLoc,
DependentBitIntType> {};

class PredefinedSugarTypeLoc final
: public InheritingConcreteTypeLoc<TypeSpecTypeLoc, PredefinedSugarTypeLoc,
PredefinedSugarType> {};
Comment on lines +2770 to +2772
Copy link
Contributor

@mizvekov mizvekov Jun 27, 2025

Choose a reason for hiding this comment

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

These should ideally never be used in practice, as a PredefinedSugarType is never written in source code.
I wonder how hard it is to avoid it. Where is this coming up?

Copy link
Author

Choose a reason for hiding this comment

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

// Predeclare all the type nodes.
#define ABSTRACT_TYPELOC(Class, Base)
#define TYPELOC(Class, Base) \
class Class##TypeLoc;
#include "clang/AST/TypeLocNodes.def"
declares it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, but for the current uses of PredefinedSugarType in this patch, we should never have to produce these TypeLocs, since this type is never written in source code.

It would be interesting to watch for this. I suspect that if we end up producing it, it would be because of some workaround somewhere, where some function expects a TypeLoc / TypeSourceInfo and we just produce a trivial one.

Likewise, serializing these TypeLocs seems also to be unnecessary.

Copy link
Author

Choose a reason for hiding this comment

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

switch (TL.getTypeLocClass()) {
#define ABSTRACT_TYPELOC(CLASS, BASE)
#define TYPELOC(CLASS, BASE) \
case TypeLoc::CLASS: \
return getDerived().Traverse##CLASS##TypeLoc(TL.castAs<CLASS##TypeLoc>());
#include "clang/AST/TypeLocNodes.def"
}
uses it, and TypeLocNodes.def is actually TypeNodes.td. I don't think there's currently a simple way to avoid it.


class ObjCProtocolLoc {
ObjCProtocolDecl *Protocol = nullptr;
SourceLocation Loc = SourceLocation();
Expand Down
9 changes: 9 additions & 0 deletions clang/include/clang/AST/TypeProperties.td
Original file line number Diff line number Diff line change
Expand Up @@ -1028,3 +1028,12 @@ let Class = DependentBitIntType in {
return ctx.getDependentBitIntType(isUnsigned, numBitsExpr);
}]>;
}

let Class = PredefinedSugarType in {
def : Property<"kind", UInt32> {
let Read = [{ static_cast<uint32_t>(node->getKind()) }];
}
def : Creator<[{
return ctx.getPredefinedSugarType(kind);
}]>;
}
1 change: 1 addition & 0 deletions clang/include/clang/Basic/TypeNodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,4 @@ def PipeType : TypeNode<Type>;
def AtomicType : TypeNode<Type>;
def BitIntType : TypeNode<Type>;
def DependentBitIntType : TypeNode<Type>, AlwaysDependent;
def PredefinedSugarType : TypeNode<Type>, NeverCanonical;
1 change: 1 addition & 0 deletions clang/include/clang/Serialization/TypeBitCodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,6 @@ TYPE_BIT_CODE(CountAttributed, COUNT_ATTRIBUTED, 57)
TYPE_BIT_CODE(ArrayParameter, ARRAY_PARAMETER, 58)
TYPE_BIT_CODE(HLSLAttributedResource, HLSLRESOURCE_ATTRIBUTED, 59)
TYPE_BIT_CODE(HLSLInlineSpirv, HLSL_INLINE_SPIRV, 60)
TYPE_BIT_CODE(PredefinedSugar, PREDEFINED_SUGAR, 61)

#undef TYPE_BIT_CODE
73 changes: 57 additions & 16 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1516,6 +1516,23 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target,
MSGuidTagDecl = buildImplicitRecord("_GUID");
getTranslationUnitDecl()->addDecl(MSGuidTagDecl);
}

// size_t (C99TC3 6.5.3.4), signed size_t (C++23 5.13.2) and
// ptrdiff_t (C99TC3 6.5.6) Although these types are not built-in, they are
// part of the core language and are widely used.
if (!LangOpts.HLSL) {
// Using PredefinedSugarType makes these types as named sugar types rather
// than standard integer types, enabling better hints and diagnostics.
using Kind = PredefinedSugarType::Kind;
SizeType = getPredefinedSugarType(llvm::to_underlying(Kind::SizeT));
SignedSizeType =
getPredefinedSugarType(llvm::to_underlying(Kind::SignedSizeT));
PtrdiffType = getPredefinedSugarType(llvm::to_underlying(Kind::PtrdiffT));
} else {
SizeType = getFromTargetType(Target.getSizeType());
SignedSizeType = getFromTargetType(Target.getSignedSizeType());
PtrdiffType = getFromTargetType(Target.getPtrDiffType(LangAS::Default));
}
}

DiagnosticsEngine &ASTContext::getDiagnostics() const {
Expand Down Expand Up @@ -2595,8 +2612,12 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
Align = static_cast<unsigned>(Width);
}
}

break;

case Type::PredefinedSugar:
return getTypeInfo(cast<PredefinedSugarType>(T)->desugar().getTypePtr());

case Type::Pipe:
Width = Target->getPointerWidth(LangAS::opencl_global);
Align = Target->getPointerAlign(LangAS::opencl_global);
Expand Down Expand Up @@ -5216,6 +5237,25 @@ QualType ASTContext::getDependentBitIntType(bool IsUnsigned,
return QualType(New, 0);
}

QualType ASTContext::getPredefinedSugarType(uint32_t KD) const {
using Kind = PredefinedSugarType::Kind;
auto getUnderlyingType = [](const ASTContext &Ctx, Kind KDI) -> QualType {
switch (KDI) {
case Kind::SizeT:
return Ctx.getFromTargetType(Ctx.Target->getSizeType());
case Kind::SignedSizeT:
return Ctx.getFromTargetType(Ctx.Target->getSignedSizeType());
case Kind::PtrdiffT:
return Ctx.getFromTargetType(Ctx.Target->getPtrDiffType(LangAS::Default));
}
llvm_unreachable("unexpected kind");
};
auto *New = new (*this, alignof(PredefinedSugarType)) PredefinedSugarType(
static_cast<Kind>(KD), getUnderlyingType(*this, static_cast<Kind>(KD)));
Types.push_back(New);
return QualType(New, 0);
}

#ifndef NDEBUG
static bool NeedsInjectedClassNameType(const RecordDecl *D) {
if (!isa<CXXRecordDecl>(D)) return false;
Expand Down Expand Up @@ -6796,14 +6836,25 @@ QualType ASTContext::getTagDeclType(const TagDecl *Decl) const {
/// getSizeType - Return the unique type for "size_t" (C99 7.17), the result
/// of the sizeof operator (C99 6.5.3.4p4). The value is target dependent and
/// needs to agree with the definition in <stddef.h>.
CanQualType ASTContext::getSizeType() const {
QualType ASTContext::getSizeType() const { return SizeType; }

CanQualType ASTContext::getCanonicalSizeType() const {
return getFromTargetType(Target->getSizeType());
}

/// Return the unique signed counterpart of the integer type
/// corresponding to size_t.
CanQualType ASTContext::getSignedSizeType() const {
return getFromTargetType(Target->getSignedSizeType());
QualType ASTContext::getSignedSizeType() const { return SignedSizeType; }

/// getPointerDiffType - Return the unique type for "ptrdiff_t" (C99 7.17)
/// defined in <stddef.h>. Pointer - pointer requires this (C99 6.5.6p9).
QualType ASTContext::getPointerDiffType() const { return PtrdiffType; }

/// Return the unique unsigned counterpart of "ptrdiff_t"
/// integer type. The standard (C11 7.21.6.1p7) refers to this type
/// in the definition of %tu format specifier.
QualType ASTContext::getUnsignedPointerDiffType() const {
return getFromTargetType(Target->getUnsignedPtrDiffType(LangAS::Default));
}

/// getIntMaxType - Return the unique type for "intmax_t" (C99 7.18.1.5).
Expand Down Expand Up @@ -6838,19 +6889,6 @@ QualType ASTContext::getUIntPtrType() const {
return getCorrespondingUnsignedType(getIntPtrType());
}

/// getPointerDiffType - Return the unique type for "ptrdiff_t" (C99 7.17)
/// defined in <stddef.h>. Pointer - pointer requires this (C99 6.5.6p9).
QualType ASTContext::getPointerDiffType() const {
return getFromTargetType(Target->getPtrDiffType(LangAS::Default));
}

/// Return the unique unsigned counterpart of "ptrdiff_t"
/// integer type. The standard (C11 7.21.6.1p7) refers to this type
/// in the definition of %tu format specifier.
QualType ASTContext::getUnsignedPointerDiffType() const {
return getFromTargetType(Target->getUnsignedPtrDiffType(LangAS::Default));
}

/// Return the unique type for "pid_t" defined in
/// <sys/types.h>. We need this to compute the correct type for vfork().
QualType ASTContext::getProcessIDType() const {
Expand Down Expand Up @@ -14526,6 +14564,9 @@ static QualType getCommonSugarTypeNode(ASTContext &Ctx, const Type *X,
DX->isCountInBytes(), DX->isOrNull(),
CDX);
}
case Type::PredefinedSugar:
// FIXME: Should this be reachable here?
return QualType();
}
llvm_unreachable("Unhandled Type Class");
}
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/ASTImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1893,6 +1893,12 @@ ExpectedType clang::ASTNodeImporter::VisitDependentBitIntType(
*ToNumBitsExprOrErr);
}

ExpectedType clang::ASTNodeImporter::VisitPredefinedSugarType(
const clang::PredefinedSugarType *T) {
return Importer.getToContext().getPredefinedSugarType(
llvm::to_underlying(T->getKind()));
}

ExpectedType clang::ASTNodeImporter::VisitDependentSizedMatrixType(
const clang::DependentSizedMatrixType *T) {
Error Err = Error::success();
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/AST/ASTStructuralEquivalence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1480,6 +1480,16 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
return false;
break;
}
case Type::PredefinedSugar: {
const auto *TP1 = cast<PredefinedSugarType>(T1);
const auto *TP2 = cast<PredefinedSugarType>(T2);
if (TP1->getKind() != TP2->getKind())
return false;
else
assert(TP1->getCanonicalTypeInternal() ==
TP2->getCanonicalTypeInternal());
break;
}
} // end switch

return true;
Expand Down
Loading
Loading