forked from llvm/llvm-project
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[clang][NFC] Move Bounds Safety Sema code to
SemaBoundsSafety.cpp
(l…
…lvm#99330) This patch adds a new `SemaBoundsSafety.cpp` source file and moves the existing `CheckCountedByAttrOnField` function and related helper functions and types from `SemaDeclAttr.cpp` into the new source file. The `CheckCountedByAttrOnField` function is now a method on the Sema class and now has doxygen comments. The goal behind this refactor is to clearly separate the `-fbounds-safety` Sema code from everything else. Although `counted_by(_or_null)` and `sized_by(_or_null)` attributes have a meaning outside of `-fbounds-safety` it seems reasonable to also have the Sema logic live in `SemaBoundsSafety.cpp` since the intention is that the attributes will have the same semantics (but not necessarily the same enforcement). As `-fbounds-safety` is upstreamed additional Sema checks will be added to `SemaBoundsSafety.cpp`. rdar://131777237
- Loading branch information
Showing
4 changed files
with
233 additions
and
176 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
//===-- SemaBoundsSafety.cpp - Bounds Safety specific routines-*- C++ -*---===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
/// \file | ||
/// This file declares semantic analysis functions specific to `-fbounds-safety` | ||
/// (Bounds Safety) and also its attributes when used without `-fbounds-safety` | ||
/// (e.g. `counted_by`) | ||
/// | ||
//===----------------------------------------------------------------------===// | ||
#include "clang/Sema/Sema.h" | ||
|
||
namespace clang { | ||
|
||
static CountAttributedType::DynamicCountPointerKind | ||
getCountAttrKind(bool CountInBytes, bool OrNull) { | ||
if (CountInBytes) | ||
return OrNull ? CountAttributedType::SizedByOrNull | ||
: CountAttributedType::SizedBy; | ||
return OrNull ? CountAttributedType::CountedByOrNull | ||
: CountAttributedType::CountedBy; | ||
} | ||
|
||
static const RecordDecl *GetEnclosingNamedOrTopAnonRecord(const FieldDecl *FD) { | ||
const auto *RD = FD->getParent(); | ||
// An unnamed struct is anonymous struct only if it's not instantiated. | ||
// However, the struct may not be fully processed yet to determine | ||
// whether it's anonymous or not. In that case, this function treats it as | ||
// an anonymous struct and tries to find a named parent. | ||
while (RD && (RD->isAnonymousStructOrUnion() || | ||
(!RD->isCompleteDefinition() && RD->getName().empty()))) { | ||
const auto *Parent = dyn_cast<RecordDecl>(RD->getParent()); | ||
if (!Parent) | ||
break; | ||
RD = Parent; | ||
} | ||
return RD; | ||
} | ||
|
||
enum class CountedByInvalidPointeeTypeKind { | ||
INCOMPLETE, | ||
SIZELESS, | ||
FUNCTION, | ||
FLEXIBLE_ARRAY_MEMBER, | ||
VALID, | ||
}; | ||
|
||
bool Sema::CheckCountedByAttrOnField( | ||
FieldDecl *FD, Expr *E, | ||
llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls, bool CountInBytes, | ||
bool OrNull) { | ||
// Check the context the attribute is used in | ||
|
||
unsigned Kind = getCountAttrKind(CountInBytes, OrNull); | ||
|
||
if (FD->getParent()->isUnion()) { | ||
Diag(FD->getBeginLoc(), diag::err_count_attr_in_union) | ||
<< Kind << FD->getSourceRange(); | ||
return true; | ||
} | ||
|
||
const auto FieldTy = FD->getType(); | ||
if (FieldTy->isArrayType() && (CountInBytes || OrNull)) { | ||
Diag(FD->getBeginLoc(), | ||
diag::err_count_attr_not_on_ptr_or_flexible_array_member) | ||
<< Kind << FD->getLocation() << /* suggest counted_by */ 1; | ||
return true; | ||
} | ||
if (!FieldTy->isArrayType() && !FieldTy->isPointerType()) { | ||
Diag(FD->getBeginLoc(), | ||
diag::err_count_attr_not_on_ptr_or_flexible_array_member) | ||
<< Kind << FD->getLocation() << /* do not suggest counted_by */ 0; | ||
return true; | ||
} | ||
|
||
LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel = | ||
LangOptions::StrictFlexArraysLevelKind::IncompleteOnly; | ||
if (FieldTy->isArrayType() && | ||
!Decl::isFlexibleArrayMemberLike(getASTContext(), FD, FieldTy, | ||
StrictFlexArraysLevel, true)) { | ||
Diag(FD->getBeginLoc(), | ||
diag::err_counted_by_attr_on_array_not_flexible_array_member) | ||
<< Kind << FD->getLocation(); | ||
return true; | ||
} | ||
|
||
CountedByInvalidPointeeTypeKind InvalidTypeKind = | ||
CountedByInvalidPointeeTypeKind::VALID; | ||
QualType PointeeTy; | ||
int SelectPtrOrArr = 0; | ||
if (FieldTy->isPointerType()) { | ||
PointeeTy = FieldTy->getPointeeType(); | ||
SelectPtrOrArr = 0; | ||
} else { | ||
assert(FieldTy->isArrayType()); | ||
const ArrayType *AT = getASTContext().getAsArrayType(FieldTy); | ||
PointeeTy = AT->getElementType(); | ||
SelectPtrOrArr = 1; | ||
} | ||
// Note: The `Decl::isFlexibleArrayMemberLike` check earlier on means | ||
// only `PointeeTy->isStructureTypeWithFlexibleArrayMember()` is reachable | ||
// when `FieldTy->isArrayType()`. | ||
bool ShouldWarn = false; | ||
if (PointeeTy->isIncompleteType() && !CountInBytes) { | ||
InvalidTypeKind = CountedByInvalidPointeeTypeKind::INCOMPLETE; | ||
} else if (PointeeTy->isSizelessType()) { | ||
InvalidTypeKind = CountedByInvalidPointeeTypeKind::SIZELESS; | ||
} else if (PointeeTy->isFunctionType()) { | ||
InvalidTypeKind = CountedByInvalidPointeeTypeKind::FUNCTION; | ||
} else if (PointeeTy->isStructureTypeWithFlexibleArrayMember()) { | ||
if (FieldTy->isArrayType() && !getLangOpts().BoundsSafety) { | ||
// This is a workaround for the Linux kernel that has already adopted | ||
// `counted_by` on a FAM where the pointee is a struct with a FAM. This | ||
// should be an error because computing the bounds of the array cannot be | ||
// done correctly without manually traversing every struct object in the | ||
// array at runtime. To allow the code to be built this error is | ||
// downgraded to a warning. | ||
ShouldWarn = true; | ||
} | ||
InvalidTypeKind = CountedByInvalidPointeeTypeKind::FLEXIBLE_ARRAY_MEMBER; | ||
} | ||
|
||
if (InvalidTypeKind != CountedByInvalidPointeeTypeKind::VALID) { | ||
unsigned DiagID = ShouldWarn | ||
? diag::warn_counted_by_attr_elt_type_unknown_size | ||
: diag::err_counted_by_attr_pointee_unknown_size; | ||
Diag(FD->getBeginLoc(), DiagID) | ||
<< SelectPtrOrArr << PointeeTy << (int)InvalidTypeKind | ||
<< (ShouldWarn ? 1 : 0) << Kind << FD->getSourceRange(); | ||
return true; | ||
} | ||
|
||
// Check the expression | ||
|
||
if (!E->getType()->isIntegerType() || E->getType()->isBooleanType()) { | ||
Diag(E->getBeginLoc(), diag::err_count_attr_argument_not_integer) | ||
<< Kind << E->getSourceRange(); | ||
return true; | ||
} | ||
|
||
auto *DRE = dyn_cast<DeclRefExpr>(E); | ||
if (!DRE) { | ||
Diag(E->getBeginLoc(), | ||
diag::err_count_attr_only_support_simple_decl_reference) | ||
<< Kind << E->getSourceRange(); | ||
return true; | ||
} | ||
|
||
auto *CountDecl = DRE->getDecl(); | ||
FieldDecl *CountFD = dyn_cast<FieldDecl>(CountDecl); | ||
if (auto *IFD = dyn_cast<IndirectFieldDecl>(CountDecl)) { | ||
CountFD = IFD->getAnonField(); | ||
} | ||
if (!CountFD) { | ||
Diag(E->getBeginLoc(), diag::err_count_attr_must_be_in_structure) | ||
<< CountDecl << Kind << E->getSourceRange(); | ||
|
||
Diag(CountDecl->getBeginLoc(), | ||
diag::note_flexible_array_counted_by_attr_field) | ||
<< CountDecl << CountDecl->getSourceRange(); | ||
return true; | ||
} | ||
|
||
if (FD->getParent() != CountFD->getParent()) { | ||
if (CountFD->getParent()->isUnion()) { | ||
Diag(CountFD->getBeginLoc(), diag::err_count_attr_refer_to_union) | ||
<< Kind << CountFD->getSourceRange(); | ||
return true; | ||
} | ||
// Whether CountRD is an anonymous struct is not determined at this | ||
// point. Thus, an additional diagnostic in case it's not anonymous struct | ||
// is done later in `Parser::ParseStructDeclaration`. | ||
auto *RD = GetEnclosingNamedOrTopAnonRecord(FD); | ||
auto *CountRD = GetEnclosingNamedOrTopAnonRecord(CountFD); | ||
|
||
if (RD != CountRD) { | ||
Diag(E->getBeginLoc(), diag::err_count_attr_param_not_in_same_struct) | ||
<< CountFD << Kind << FieldTy->isArrayType() << E->getSourceRange(); | ||
Diag(CountFD->getBeginLoc(), | ||
diag::note_flexible_array_counted_by_attr_field) | ||
<< CountFD << CountFD->getSourceRange(); | ||
return true; | ||
} | ||
} | ||
|
||
Decls.push_back(TypeCoupledDeclRefInfo(CountFD, /*IsDref*/ false)); | ||
return false; | ||
} | ||
|
||
} // namespace clang |
Oops, something went wrong.