1 //===-- SemaBoundsSafety.cpp - Bounds Safety specific routines-*- C++ -*---===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 /// This file declares semantic analysis functions specific to `-fbounds-safety`
10 /// (Bounds Safety) and also its attributes when used without `-fbounds-safety`
11 /// (e.g. `counted_by`)
13 //===----------------------------------------------------------------------===//
14 #include "clang/Sema/Sema.h"
18 static CountAttributedType::DynamicCountPointerKind
19 getCountAttrKind(bool CountInBytes
, bool OrNull
) {
21 return OrNull
? CountAttributedType::SizedByOrNull
22 : CountAttributedType::SizedBy
;
23 return OrNull
? CountAttributedType::CountedByOrNull
24 : CountAttributedType::CountedBy
;
27 static const RecordDecl
*GetEnclosingNamedOrTopAnonRecord(const FieldDecl
*FD
) {
28 const auto *RD
= FD
->getParent();
29 // An unnamed struct is anonymous struct only if it's not instantiated.
30 // However, the struct may not be fully processed yet to determine
31 // whether it's anonymous or not. In that case, this function treats it as
32 // an anonymous struct and tries to find a named parent.
33 while (RD
&& (RD
->isAnonymousStructOrUnion() ||
34 (!RD
->isCompleteDefinition() && RD
->getName().empty()))) {
35 const auto *Parent
= dyn_cast
<RecordDecl
>(RD
->getParent());
43 enum class CountedByInvalidPointeeTypeKind
{
47 FLEXIBLE_ARRAY_MEMBER
,
51 bool Sema::CheckCountedByAttrOnField(FieldDecl
*FD
, Expr
*E
, bool CountInBytes
,
53 // Check the context the attribute is used in
55 unsigned Kind
= getCountAttrKind(CountInBytes
, OrNull
);
57 if (FD
->getParent()->isUnion()) {
58 Diag(FD
->getBeginLoc(), diag::err_count_attr_in_union
)
59 << Kind
<< FD
->getSourceRange();
63 const auto FieldTy
= FD
->getType();
64 if (FieldTy
->isArrayType() && (CountInBytes
|| OrNull
)) {
65 Diag(FD
->getBeginLoc(),
66 diag::err_count_attr_not_on_ptr_or_flexible_array_member
)
67 << Kind
<< FD
->getLocation() << /* suggest counted_by */ 1;
70 if (!FieldTy
->isArrayType() && !FieldTy
->isPointerType()) {
71 Diag(FD
->getBeginLoc(),
72 diag::err_count_attr_not_on_ptr_or_flexible_array_member
)
73 << Kind
<< FD
->getLocation() << /* do not suggest counted_by */ 0;
77 LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel
=
78 LangOptions::StrictFlexArraysLevelKind::IncompleteOnly
;
79 if (FieldTy
->isArrayType() &&
80 !Decl::isFlexibleArrayMemberLike(getASTContext(), FD
, FieldTy
,
81 StrictFlexArraysLevel
, true)) {
82 Diag(FD
->getBeginLoc(),
83 diag::err_counted_by_attr_on_array_not_flexible_array_member
)
84 << Kind
<< FD
->getLocation();
88 CountedByInvalidPointeeTypeKind InvalidTypeKind
=
89 CountedByInvalidPointeeTypeKind::VALID
;
91 int SelectPtrOrArr
= 0;
92 if (FieldTy
->isPointerType()) {
93 PointeeTy
= FieldTy
->getPointeeType();
96 assert(FieldTy
->isArrayType());
97 const ArrayType
*AT
= getASTContext().getAsArrayType(FieldTy
);
98 PointeeTy
= AT
->getElementType();
101 // Note: The `Decl::isFlexibleArrayMemberLike` check earlier on means
102 // only `PointeeTy->isStructureTypeWithFlexibleArrayMember()` is reachable
103 // when `FieldTy->isArrayType()`.
104 bool ShouldWarn
= false;
105 if (PointeeTy
->isIncompleteType() && !CountInBytes
) {
106 InvalidTypeKind
= CountedByInvalidPointeeTypeKind::INCOMPLETE
;
107 } else if (PointeeTy
->isSizelessType()) {
108 InvalidTypeKind
= CountedByInvalidPointeeTypeKind::SIZELESS
;
109 } else if (PointeeTy
->isFunctionType()) {
110 InvalidTypeKind
= CountedByInvalidPointeeTypeKind::FUNCTION
;
111 } else if (PointeeTy
->isStructureTypeWithFlexibleArrayMember()) {
112 if (FieldTy
->isArrayType() && !getLangOpts().BoundsSafety
) {
113 // This is a workaround for the Linux kernel that has already adopted
114 // `counted_by` on a FAM where the pointee is a struct with a FAM. This
115 // should be an error because computing the bounds of the array cannot be
116 // done correctly without manually traversing every struct object in the
117 // array at runtime. To allow the code to be built this error is
118 // downgraded to a warning.
121 InvalidTypeKind
= CountedByInvalidPointeeTypeKind::FLEXIBLE_ARRAY_MEMBER
;
124 if (InvalidTypeKind
!= CountedByInvalidPointeeTypeKind::VALID
) {
125 unsigned DiagID
= ShouldWarn
126 ? diag::warn_counted_by_attr_elt_type_unknown_size
127 : diag::err_counted_by_attr_pointee_unknown_size
;
128 Diag(FD
->getBeginLoc(), DiagID
)
129 << SelectPtrOrArr
<< PointeeTy
<< (int)InvalidTypeKind
130 << (ShouldWarn
? 1 : 0) << Kind
<< FD
->getSourceRange();
134 // Check the expression
136 if (!E
->getType()->isIntegerType() || E
->getType()->isBooleanType()) {
137 Diag(E
->getBeginLoc(), diag::err_count_attr_argument_not_integer
)
138 << Kind
<< E
->getSourceRange();
142 auto *DRE
= dyn_cast
<DeclRefExpr
>(E
);
144 Diag(E
->getBeginLoc(),
145 diag::err_count_attr_only_support_simple_decl_reference
)
146 << Kind
<< E
->getSourceRange();
150 auto *CountDecl
= DRE
->getDecl();
151 FieldDecl
*CountFD
= dyn_cast
<FieldDecl
>(CountDecl
);
152 if (auto *IFD
= dyn_cast
<IndirectFieldDecl
>(CountDecl
)) {
153 CountFD
= IFD
->getAnonField();
156 Diag(E
->getBeginLoc(), diag::err_count_attr_must_be_in_structure
)
157 << CountDecl
<< Kind
<< E
->getSourceRange();
159 Diag(CountDecl
->getBeginLoc(),
160 diag::note_flexible_array_counted_by_attr_field
)
161 << CountDecl
<< CountDecl
->getSourceRange();
165 if (FD
->getParent() != CountFD
->getParent()) {
166 if (CountFD
->getParent()->isUnion()) {
167 Diag(CountFD
->getBeginLoc(), diag::err_count_attr_refer_to_union
)
168 << Kind
<< CountFD
->getSourceRange();
171 // Whether CountRD is an anonymous struct is not determined at this
172 // point. Thus, an additional diagnostic in case it's not anonymous struct
173 // is done later in `Parser::ParseStructDeclaration`.
174 auto *RD
= GetEnclosingNamedOrTopAnonRecord(FD
);
175 auto *CountRD
= GetEnclosingNamedOrTopAnonRecord(CountFD
);
178 Diag(E
->getBeginLoc(), diag::err_count_attr_param_not_in_same_struct
)
179 << CountFD
<< Kind
<< FieldTy
->isArrayType() << E
->getSourceRange();
180 Diag(CountFD
->getBeginLoc(),
181 diag::note_flexible_array_counted_by_attr_field
)
182 << CountFD
<< CountFD
->getSourceRange();