clang 20.0.0git
SemaBoundsSafety.cpp
Go to the documentation of this file.
1//===-- SemaBoundsSafety.cpp - Bounds Safety specific routines-*- C++ -*---===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8/// \file
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`)
12///
13//===----------------------------------------------------------------------===//
14#include "clang/Sema/Sema.h"
15
16namespace clang {
17
19getCountAttrKind(bool CountInBytes, bool OrNull) {
20 if (CountInBytes)
25}
26
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());
36 if (!Parent)
37 break;
38 RD = Parent;
39 }
40 return RD;
41}
42
48 VALID,
49};
50
51bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
52 bool OrNull) {
53 // Check the context the attribute is used in
54
55 unsigned Kind = getCountAttrKind(CountInBytes, OrNull);
56
57 if (FD->getParent()->isUnion()) {
58 Diag(FD->getBeginLoc(), diag::err_count_attr_in_union)
59 << Kind << FD->getSourceRange();
60 return true;
61 }
62
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;
68 return true;
69 }
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;
74 return true;
75 }
76
77 LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
79 if (FieldTy->isArrayType() &&
81 StrictFlexArraysLevel, true)) {
82 Diag(FD->getBeginLoc(),
83 diag::err_counted_by_attr_on_array_not_flexible_array_member)
84 << Kind << FD->getLocation();
85 return true;
86 }
87
88 CountedByInvalidPointeeTypeKind InvalidTypeKind =
90 QualType PointeeTy;
91 int SelectPtrOrArr = 0;
92 if (FieldTy->isPointerType()) {
93 PointeeTy = FieldTy->getPointeeType();
94 SelectPtrOrArr = 0;
95 } else {
96 assert(FieldTy->isArrayType());
97 const ArrayType *AT = getASTContext().getAsArrayType(FieldTy);
98 PointeeTy = AT->getElementType();
99 SelectPtrOrArr = 1;
100 }
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) {
107 } else if (PointeeTy->isSizelessType()) {
109 } else if (PointeeTy->isFunctionType()) {
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.
119 ShouldWarn = true;
120 }
122 }
123
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();
131 return true;
132 }
133
134 // Check the expression
135
136 if (!E->getType()->isIntegerType() || E->getType()->isBooleanType()) {
137 Diag(E->getBeginLoc(), diag::err_count_attr_argument_not_integer)
138 << Kind << E->getSourceRange();
139 return true;
140 }
141
142 auto *DRE = dyn_cast<DeclRefExpr>(E);
143 if (!DRE) {
144 Diag(E->getBeginLoc(),
145 diag::err_count_attr_only_support_simple_decl_reference)
146 << Kind << E->getSourceRange();
147 return true;
148 }
149
150 auto *CountDecl = DRE->getDecl();
151 FieldDecl *CountFD = dyn_cast<FieldDecl>(CountDecl);
152 if (auto *IFD = dyn_cast<IndirectFieldDecl>(CountDecl)) {
153 CountFD = IFD->getAnonField();
154 }
155 if (!CountFD) {
156 Diag(E->getBeginLoc(), diag::err_count_attr_must_be_in_structure)
157 << CountDecl << Kind << E->getSourceRange();
158
159 Diag(CountDecl->getBeginLoc(),
160 diag::note_flexible_array_counted_by_attr_field)
161 << CountDecl << CountDecl->getSourceRange();
162 return true;
163 }
164
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();
169 return true;
170 }
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`.
175 auto *CountRD = GetEnclosingNamedOrTopAnonRecord(CountFD);
176
177 if (RD != CountRD) {
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();
183 return true;
184 }
185 }
186 return false;
187}
188
189} // namespace clang
NodeId Parent
Definition: ASTDiff.cpp:191
enum clang::sema::@1653::IndirectLocalPathEntry::EntryKind Kind
Expr * E
__device__ int
const ArrayType * getAsArrayType(QualType T) const
Type Query functions.
Represents an array type, per C99 6.7.5.2 - Array Declarators.
Definition: Type.h:3566
QualType getElementType() const
Definition: Type.h:3578
SourceLocation getLocation() const
Definition: DeclBase.h:446
static bool isFlexibleArrayMemberLike(ASTContext &Context, const Decl *D, QualType Ty, LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel, bool IgnoreTemplateOrMacroSubstitution)
Whether it resembles a flexible array member.
Definition: DeclBase.cpp:435
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Decl.h:783
This represents one expression.
Definition: Expr.h:110
Represents a member of a struct/union/class.
Definition: Decl.h:3030
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition: Decl.cpp:4652
const RecordDecl * getParent() const
Returns the parent of this field declaration, which is the struct in which this field is defined.
Definition: Decl.h:3247
@ IncompleteOnly
Any trailing array member of undefined size is a FAM.
A (possibly-)qualified type.
Definition: Type.h:941
Represents a struct/union/class.
Definition: Decl.h:4145
SemaDiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID, bool DeferHint=false)
Emit a diagnostic.
Definition: SemaBase.cpp:60
ASTContext & getASTContext() const
Definition: Sema.h:560
const LangOptions & getLangOpts() const
Definition: Sema.h:553
bool CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes, bool OrNull)
Check if applying the specified attribute variant from the "counted by" family of attributes to Field...
bool isUnion() const
Definition: Decl.h:3767
bool isSizelessType() const
As an extension, we classify types as one of "sized" or "sizeless"; every type is one or the other.
Definition: Type.cpp:2477
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition: Type.cpp:705
bool isIncompleteType(NamedDecl **Def=nullptr) const
Types are partitioned into 3 broad categories (C99 6.2.5p1): object types, function types,...
Definition: Type.cpp:2362
bool isFunctionType() const
Definition: Type.h:7999
bool isStructureTypeWithFlexibleArrayMember() const
Definition: Type.cpp:635
QualType getType() const
Definition: Decl.h:678
The JSON file list parser is used to communicate input to InstallAPI.
static CountAttributedType::DynamicCountPointerKind getCountAttrKind(bool CountInBytes, bool OrNull)
CountedByInvalidPointeeTypeKind
static const RecordDecl * GetEnclosingNamedOrTopAnonRecord(const FieldDecl *FD)