diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/Sema/SemaBoundsSafety.cpp')
-rw-r--r-- | contrib/llvm-project/clang/lib/Sema/SemaBoundsSafety.cpp | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/contrib/llvm-project/clang/lib/Sema/SemaBoundsSafety.cpp b/contrib/llvm-project/clang/lib/Sema/SemaBoundsSafety.cpp new file mode 100644 index 000000000000..290c82093889 --- /dev/null +++ b/contrib/llvm-project/clang/lib/Sema/SemaBoundsSafety.cpp @@ -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 |