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 
16 namespace clang {
17 
19 getCountAttrKind(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 
44  INCOMPLETE,
45  SIZELESS,
46  FUNCTION,
48  VALID,
49 };
50 
51 bool 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`.
174  auto *RD = GetEnclosingNamedOrTopAnonRecord(FD);
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::@1659::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:3576
QualType getElementType() const
Definition: Type.h:3588
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:784
This represents one expression.
Definition: Expr.h:110
Represents a member of a struct/union/class.
Definition: Decl.h:3031
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition: Decl.cpp:4656
const RecordDecl * getParent() const
Returns the parent of this field declaration, which is the struct in which this field is defined.
Definition: Decl.h:3248
@ 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:4146
SemaDiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID, bool DeferHint=false)
Emit a diagnostic.
Definition: SemaBase.cpp:64
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...
ASTContext & getASTContext() const
Definition: Sema.h:560
bool isUnion() const
Definition: Decl.h:3768
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:8009
bool isStructureTypeWithFlexibleArrayMember() const
Definition: Type.cpp:635
QualType getType() const
Definition: Decl.h:679
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)