Skip to content

Commit 3eb9ff3

Browse files
authored
Turn 'counted_by' into a type attribute and parse it into 'CountAttributedType' (#78000)
In `-fbounds-safety`, bounds annotations are considered type attributes rather than declaration attributes. Constructing them as type attributes allows us to extend the attribute to apply nested pointers, which is essential to annotate functions that involve out parameters: `void foo(int *__counted_by(*out_count) *out_buf, int *out_count)`. We introduce a new sugar type to support bounds annotated types, `CountAttributedType`. In order to maintain extra data (the bounds expression and the dependent declaration information) that is not trackable in `AttributedType` we create a new type dedicate to this functionality. This patch also extends the parsing logic to parse the `counted_by` argument as an expression, which will allow us to extend the model to support arguments beyond an identifier, e.g., `__counted_by(n + m)` in the future as specified by `-fbounds-safety`. This also adjusts `__bdos` and array-bounds sanitizer code that already uses `CountedByAttr` to check `CountAttributedType` instead to get the field referred to by the attribute.
1 parent b2082a9 commit 3eb9ff3

38 files changed

+835
-187
lines changed

clang/include/clang/AST/ASTContext.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
250250
DependentBitIntTypes;
251251
llvm::FoldingSet<BTFTagAttributedType> BTFTagAttributedTypes;
252252

253+
mutable llvm::FoldingSet<CountAttributedType> CountAttributedTypes;
254+
253255
mutable llvm::FoldingSet<QualifiedTemplateName> QualifiedTemplateNames;
254256
mutable llvm::FoldingSet<DependentTemplateName> DependentTemplateNames;
255257
mutable llvm::FoldingSet<SubstTemplateTemplateParmStorage>
@@ -1341,6 +1343,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
13411343
return CanQualType::CreateUnsafe(getPointerType((QualType) T));
13421344
}
13431345

1346+
QualType
1347+
getCountAttributedType(QualType T, Expr *CountExpr, bool CountInBytes,
1348+
bool OrNull,
1349+
ArrayRef<TypeCoupledDeclRefInfo> DependentDecls) const;
1350+
13441351
/// Return the uniqued reference to a type adjusted from the original
13451352
/// type to a new type.
13461353
QualType getAdjustedType(QualType Orig, QualType New) const;

clang/include/clang/AST/PropertiesBase.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ def UInt32 : CountPropertyType<"uint32_t">;
143143
def UInt64 : CountPropertyType<"uint64_t">;
144144
def UnaryTypeTransformKind : EnumPropertyType<"UnaryTransformType::UTTKind">;
145145
def VectorKind : EnumPropertyType<"VectorKind">;
146+
def TypeCoupledDeclRefInfo : PropertyType;
146147

147148
def ExceptionSpecInfo : PropertyType<"FunctionProtoType::ExceptionSpecInfo"> {
148149
let BufferElementTypes = [ QualType ];

clang/include/clang/AST/RecursiveASTVisitor.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,12 @@ DEF_TRAVERSE_TYPE(InjectedClassNameType, {})
11101110
DEF_TRAVERSE_TYPE(AttributedType,
11111111
{ TRY_TO(TraverseType(T->getModifiedType())); })
11121112

1113+
DEF_TRAVERSE_TYPE(CountAttributedType, {
1114+
if (T->getCountExpr())
1115+
TRY_TO(TraverseStmt(T->getCountExpr()));
1116+
TRY_TO(TraverseType(T->desugar()));
1117+
})
1118+
11131119
DEF_TRAVERSE_TYPE(BTFTagAttributedType,
11141120
{ TRY_TO(TraverseType(T->getWrappedType())); })
11151121

@@ -1401,6 +1407,9 @@ DEF_TRAVERSE_TYPELOC(MacroQualifiedType,
14011407
DEF_TRAVERSE_TYPELOC(AttributedType,
14021408
{ TRY_TO(TraverseTypeLoc(TL.getModifiedLoc())); })
14031409

1410+
DEF_TRAVERSE_TYPELOC(CountAttributedType,
1411+
{ TRY_TO(TraverseTypeLoc(TL.getInnerLoc())); })
1412+
14041413
DEF_TRAVERSE_TYPELOC(BTFTagAttributedType,
14051414
{ TRY_TO(TraverseTypeLoc(TL.getWrappedLoc())); })
14061415

clang/include/clang/AST/Type.h

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class BTFTypeTagAttr;
6161
class ExtQuals;
6262
class QualType;
6363
class ConceptDecl;
64+
class ValueDecl;
6465
class TagDecl;
6566
class TemplateParameterList;
6667
class Type;
@@ -2000,6 +2001,21 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
20002001
unsigned NumExpansions;
20012002
};
20022003

2004+
class CountAttributedTypeBitfields {
2005+
friend class CountAttributedType;
2006+
2007+
LLVM_PREFERRED_TYPE(TypeBitfields)
2008+
unsigned : NumTypeBits;
2009+
2010+
static constexpr unsigned NumCoupledDeclsBits = 4;
2011+
unsigned NumCoupledDecls : NumCoupledDeclsBits;
2012+
LLVM_PREFERRED_TYPE(bool)
2013+
unsigned CountInBytes : 1;
2014+
LLVM_PREFERRED_TYPE(bool)
2015+
unsigned OrNull : 1;
2016+
};
2017+
static_assert(sizeof(CountAttributedTypeBitfields) <= sizeof(unsigned));
2018+
20032019
union {
20042020
TypeBitfields TypeBits;
20052021
ArrayTypeBitfields ArrayTypeBits;
@@ -2022,6 +2038,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
20222038
DependentTemplateSpecializationTypeBitfields
20232039
DependentTemplateSpecializationTypeBits;
20242040
PackExpansionTypeBitfields PackExpansionTypeBits;
2041+
CountAttributedTypeBitfields CountAttributedTypeBits;
20252042
};
20262043

20272044
private:
@@ -2264,6 +2281,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
22642281
bool isFunctionProtoType() const { return getAs<FunctionProtoType>(); }
22652282
bool isPointerType() const;
22662283
bool isAnyPointerType() const; // Any C pointer or ObjC object pointer
2284+
bool isCountAttributedType() const;
22672285
bool isBlockPointerType() const;
22682286
bool isVoidPointerType() const;
22692287
bool isReferenceType() const;
@@ -2724,6 +2742,14 @@ template <> const TemplateSpecializationType *Type::getAs() const;
27242742
/// until it reaches an AttributedType or a non-sugared type.
27252743
template <> const AttributedType *Type::getAs() const;
27262744

2745+
/// This will check for a BoundsAttributedType by removing any existing
2746+
/// sugar until it reaches an BoundsAttributedType or a non-sugared type.
2747+
template <> const BoundsAttributedType *Type::getAs() const;
2748+
2749+
/// This will check for a CountAttributedType by removing any existing
2750+
/// sugar until it reaches an CountAttributedType or a non-sugared type.
2751+
template <> const CountAttributedType *Type::getAs() const;
2752+
27272753
// We can do canonical leaf types faster, because we don't have to
27282754
// worry about preserving child type decoration.
27292755
#define TYPE(Class, Base)
@@ -2922,6 +2948,136 @@ class PointerType : public Type, public llvm::FoldingSetNode {
29222948
static bool classof(const Type *T) { return T->getTypeClass() == Pointer; }
29232949
};
29242950

2951+
/// [BoundsSafety] Represents information of declarations referenced by the
2952+
/// arguments of the `counted_by` attribute and the likes.
2953+
class TypeCoupledDeclRefInfo {
2954+
public:
2955+
using BaseTy = llvm::PointerIntPair<ValueDecl *, 1, unsigned>;
2956+
2957+
private:
2958+
enum {
2959+
DerefShift = 0,
2960+
DerefMask = 1,
2961+
};
2962+
BaseTy Data;
2963+
2964+
public:
2965+
/// \p D is to a declaration referenced by the argument of attribute. \p Deref
2966+
/// indicates whether \p D is referenced as a dereferenced form, e.g., \p
2967+
/// Deref is true for `*n` in `int *__counted_by(*n)`.
2968+
TypeCoupledDeclRefInfo(ValueDecl *D = nullptr, bool Deref = false);
2969+
2970+
bool isDeref() const;
2971+
ValueDecl *getDecl() const;
2972+
unsigned getInt() const;
2973+
void *getOpaqueValue() const;
2974+
bool operator==(const TypeCoupledDeclRefInfo &Other) const;
2975+
void setFromOpaqueValue(void *V);
2976+
};
2977+
2978+
/// [BoundsSafety] Represents a parent type class for CountAttributedType and
2979+
/// similar sugar types that will be introduced to represent a type with a
2980+
/// bounds attribute.
2981+
///
2982+
/// Provides a common interface to navigate declarations referred to by the
2983+
/// bounds expression.
2984+
2985+
class BoundsAttributedType : public Type, public llvm::FoldingSetNode {
2986+
QualType WrappedTy;
2987+
2988+
protected:
2989+
ArrayRef<TypeCoupledDeclRefInfo> Decls; // stored in trailing objects
2990+
2991+
BoundsAttributedType(TypeClass TC, QualType Wrapped, QualType Canon);
2992+
2993+
public:
2994+
bool isSugared() const { return true; }
2995+
QualType desugar() const { return WrappedTy; }
2996+
2997+
using decl_iterator = const TypeCoupledDeclRefInfo *;
2998+
using decl_range = llvm::iterator_range<decl_iterator>;
2999+
3000+
decl_iterator dependent_decl_begin() const { return Decls.begin(); }
3001+
decl_iterator dependent_decl_end() const { return Decls.end(); }
3002+
3003+
unsigned getNumCoupledDecls() const { return Decls.size(); }
3004+
3005+
decl_range dependent_decls() const {
3006+
return decl_range(dependent_decl_begin(), dependent_decl_end());
3007+
}
3008+
3009+
ArrayRef<TypeCoupledDeclRefInfo> getCoupledDecls() const {
3010+
return {dependent_decl_begin(), dependent_decl_end()};
3011+
}
3012+
3013+
bool referencesFieldDecls() const;
3014+
3015+
static bool classof(const Type *T) {
3016+
// Currently, only `class CountAttributedType` inherits
3017+
// `BoundsAttributedType` but the subclass will grow as we add more bounds
3018+
// annotations.
3019+
switch (T->getTypeClass()) {
3020+
case CountAttributed:
3021+
return true;
3022+
default:
3023+
return false;
3024+
}
3025+
}
3026+
};
3027+
3028+
/// Represents a sugar type with `__counted_by` or `__sized_by` annotations,
3029+
/// including their `_or_null` variants.
3030+
class CountAttributedType final
3031+
: public BoundsAttributedType,
3032+
public llvm::TrailingObjects<CountAttributedType,
3033+
TypeCoupledDeclRefInfo> {
3034+
friend class ASTContext;
3035+
3036+
Expr *CountExpr;
3037+
/// \p CountExpr represents the argument of __counted_by or the likes. \p
3038+
/// CountInBytes indicates that \p CountExpr is a byte count (i.e.,
3039+
/// __sized_by(_or_null)) \p OrNull means it's an or_null variant (i.e.,
3040+
/// __counted_by_or_null or __sized_by_or_null) \p CoupledDecls contains the
3041+
/// list of declarations referenced by \p CountExpr, which the type depends on
3042+
/// for the bounds information.
3043+
CountAttributedType(QualType Wrapped, QualType Canon, Expr *CountExpr,
3044+
bool CountInBytes, bool OrNull,
3045+
ArrayRef<TypeCoupledDeclRefInfo> CoupledDecls);
3046+
3047+
unsigned numTrailingObjects(OverloadToken<TypeCoupledDeclRefInfo>) const {
3048+
return CountAttributedTypeBits.NumCoupledDecls;
3049+
}
3050+
3051+
public:
3052+
enum DynamicCountPointerKind {
3053+
CountedBy = 0,
3054+
SizedBy,
3055+
CountedByOrNull,
3056+
SizedByOrNull,
3057+
};
3058+
3059+
Expr *getCountExpr() const { return CountExpr; }
3060+
bool isCountInBytes() const { return CountAttributedTypeBits.CountInBytes; }
3061+
bool isOrNull() const { return CountAttributedTypeBits.OrNull; }
3062+
3063+
DynamicCountPointerKind getKind() const {
3064+
if (isOrNull())
3065+
return isCountInBytes() ? SizedByOrNull : CountedByOrNull;
3066+
return isCountInBytes() ? SizedBy : CountedBy;
3067+
}
3068+
3069+
void Profile(llvm::FoldingSetNodeID &ID) {
3070+
Profile(ID, desugar(), CountExpr, isCountInBytes(), isOrNull());
3071+
}
3072+
3073+
static void Profile(llvm::FoldingSetNodeID &ID, QualType WrappedTy,
3074+
Expr *CountExpr, bool CountInBytes, bool Nullable);
3075+
3076+
static bool classof(const Type *T) {
3077+
return T->getTypeClass() == CountAttributed;
3078+
}
3079+
};
3080+
29253081
/// Represents a type which was implicitly adjusted by the semantic
29263082
/// engine for arbitrary reasons. For example, array and function types can
29273083
/// decay, and function types can have their calling conventions adjusted.

clang/include/clang/AST/TypeLoc.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,6 +1120,32 @@ class ObjCInterfaceTypeLoc : public ConcreteTypeLoc<ObjCObjectTypeLoc,
11201120
}
11211121
};
11221122

1123+
struct BoundsAttributedLocInfo {};
1124+
class BoundsAttributedTypeLoc
1125+
: public ConcreteTypeLoc<UnqualTypeLoc, BoundsAttributedTypeLoc,
1126+
BoundsAttributedType, BoundsAttributedLocInfo> {
1127+
public:
1128+
TypeLoc getInnerLoc() const { return getInnerTypeLoc(); }
1129+
QualType getInnerType() const { return getTypePtr()->desugar(); }
1130+
void initializeLocal(ASTContext &Context, SourceLocation Loc) {
1131+
// nothing to do
1132+
}
1133+
// LocalData is empty and TypeLocBuilder doesn't handle DataSize 1.
1134+
unsigned getLocalDataSize() const { return 0; }
1135+
};
1136+
1137+
class CountAttributedTypeLoc final
1138+
: public InheritingConcreteTypeLoc<BoundsAttributedTypeLoc,
1139+
CountAttributedTypeLoc,
1140+
CountAttributedType> {
1141+
public:
1142+
Expr *getCountExpr() const { return getTypePtr()->getCountExpr(); }
1143+
bool isCountInBytes() const { return getTypePtr()->isCountInBytes(); }
1144+
bool isOrNull() const { return getTypePtr()->isOrNull(); }
1145+
1146+
SourceRange getLocalSourceRange() const;
1147+
};
1148+
11231149
struct MacroQualifiedLocInfo {
11241150
SourceLocation ExpansionLoc;
11251151
};

clang/include/clang/AST/TypeProperties.td

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,25 @@ let Class = PointerType in {
2525
def : Creator<[{ return ctx.getPointerType(pointeeType); }]>;
2626
}
2727

28+
let Class = CountAttributedType in {
29+
def : Property<"WrappedTy", QualType> {
30+
let Read = [{ node->desugar() }];
31+
}
32+
def : Property<"CountExpr", ExprRef> {
33+
let Read = [{ node->getCountExpr() }];
34+
}
35+
def : Property<"CountInBytes", Bool> {
36+
let Read = [{ node->isCountInBytes() }];
37+
}
38+
def : Property<"OrNull", Bool> {
39+
let Read = [{ node->isOrNull() }];
40+
}
41+
def : Property<"CoupledDecls", Array<TypeCoupledDeclRefInfo>> {
42+
let Read = [{ node->getCoupledDecls() }];
43+
}
44+
def : Creator<[{ return ctx.getCountAttributedType(WrappedTy, CountExpr, CountInBytes, OrNull, CoupledDecls); }]>;
45+
}
46+
2847
let Class = AdjustedType in {
2948
def : Property<"originalType", QualType> {
3049
let Read = [{ node->getOriginalType() }];

clang/include/clang/Basic/Attr.td

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2193,6 +2193,15 @@ def TypeNullUnspecified : TypeAttr {
21932193
let Documentation = [TypeNullUnspecifiedDocs];
21942194
}
21952195

2196+
def CountedBy : DeclOrTypeAttr {
2197+
let Spellings = [Clang<"counted_by">];
2198+
let Subjects = SubjectList<[Field], ErrorDiag>;
2199+
let Args = [ExprArgument<"Count">, IntArgument<"NestedLevel">];
2200+
let ParseArgumentsAsUnevaluated = 1;
2201+
let Documentation = [CountedByDocs];
2202+
let LangOpts = [COnly];
2203+
}
2204+
21962205
// This is a marker used to indicate that an __unsafe_unretained qualifier was
21972206
// ignored because ARC is not enabled. The usual representation for this
21982207
// qualifier is as an ObjCOwnership attribute with Kind == "none".
@@ -4487,21 +4496,3 @@ def CodeAlign: StmtAttr {
44874496
static constexpr int MaximumAlignment = 4096;
44884497
}];
44894498
}
4490-
4491-
def CountedBy : InheritableAttr {
4492-
let Spellings = [Clang<"counted_by">];
4493-
let Subjects = SubjectList<[Field]>;
4494-
let Args = [IdentifierArgument<"CountedByField">];
4495-
let Documentation = [CountedByDocs];
4496-
let LangOpts = [COnly];
4497-
// FIXME: This is ugly. Let using a DeclArgument would be nice, but a Decl
4498-
// isn't yet available due to the fact that we're still parsing the
4499-
// structure. Maybe that code could be changed sometime in the future.
4500-
code AdditionalMembers = [{
4501-
private:
4502-
SourceRange CountedByFieldLoc;
4503-
public:
4504-
SourceRange getCountedByFieldLoc() const { return CountedByFieldLoc; }
4505-
void setCountedByFieldLoc(SourceRange Loc) { CountedByFieldLoc = Loc; }
4506-
}];
4507-
}

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6519,12 +6519,18 @@ def err_flexible_array_count_not_in_same_struct : Error<
65196519
"'counted_by' field %0 isn't within the same struct as the flexible array">;
65206520
def err_counted_by_attr_not_on_flexible_array_member : Error<
65216521
"'counted_by' only applies to C99 flexible array members">;
6522-
def err_counted_by_attr_refers_to_flexible_array : Error<
6523-
"'counted_by' cannot refer to the flexible array %0">;
6522+
def err_counted_by_attr_refer_to_itself : Error<
6523+
"'counted_by' cannot refer to the flexible array member %0">;
65246524
def err_counted_by_must_be_in_structure : Error<
65256525
"field %0 in 'counted_by' not inside structure">;
6526-
def err_flexible_array_counted_by_attr_field_not_integer : Error<
6527-
"field %0 in 'counted_by' must be a non-boolean integer type">;
6526+
def err_counted_by_attr_argument_not_integer : Error<
6527+
"'counted_by' requires a non-boolean integer type argument">;
6528+
def err_counted_by_attr_only_support_simple_decl_reference : Error<
6529+
"'counted_by' argument must be a simple declaration reference">;
6530+
def err_counted_by_attr_in_union : Error<
6531+
"'counted_by' cannot be applied to a union member">;
6532+
def err_counted_by_attr_refer_to_union : Error<
6533+
"'counted_by' argument cannot refer to a union member">;
65286534
def note_flexible_array_counted_by_attr_field : Note<
65296535
"field %0 declared here">;
65306536

clang/include/clang/Basic/TypeNodes.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ def ObjCTypeParamType : TypeNode<Type>, NeverCanonical;
108108
def ObjCObjectType : TypeNode<Type>;
109109
def ObjCInterfaceType : TypeNode<ObjCObjectType>, LeafType;
110110
def ObjCObjectPointerType : TypeNode<Type>;
111+
def BoundsAttributedType : TypeNode<Type, 1>;
112+
def CountAttributedType : TypeNode<BoundsAttributedType>, NeverCanonical;
111113
def PipeType : TypeNode<Type>;
112114
def AtomicType : TypeNode<Type>;
113115
def BitIntType : TypeNode<Type>;

clang/include/clang/Parse/Parser.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3073,6 +3073,11 @@ class Parser : public CodeCompletionHandler {
30733073
SourceLocation ScopeLoc,
30743074
ParsedAttr::Form Form);
30753075

3076+
void ParseBoundsAttribute(IdentifierInfo &AttrName,
3077+
SourceLocation AttrNameLoc, ParsedAttributes &Attrs,
3078+
IdentifierInfo *ScopeName, SourceLocation ScopeLoc,
3079+
ParsedAttr::Form Form);
3080+
30763081
void ParseTypeofSpecifier(DeclSpec &DS);
30773082
SourceLocation ParseDecltypeSpecifier(DeclSpec &DS);
30783083
void AnnotateExistingDecltypeSpecifier(const DeclSpec &DS,

0 commit comments

Comments
 (0)