mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-25 00:16:05 +00:00

…heckCountedByAttrOnField' llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls is a vector of declarations referred to by the argument of 'counted_by' attributes and fields. 'BuildCountAttributedArrayOrPointerType' had been made self-contained to produce the 'Decls' within itself to allow 'TreeTransform' to invoke the function without having to call 'Sema::CheckCountedByAttrOnField' again. Thus, 'Decls' produced by `Sema::CheckCountedByAttrOnField` is never used.
190 lines
7.0 KiB
C++
190 lines
7.0 KiB
C++
//===-- 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, 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;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // namespace clang
|