[clang] [BoundsSafety][Sema] Allow counted_by and counted_by_or_null on pointers where the pointee type is incomplete but potentially completable (PR #106321)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Aug 27 18:11:33 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Dan Liew (delcypher)
<details>
<summary>Changes</summary>
Previously using the `counted_by` or `counted_by_or_null` attribute on a pointer with an incomplete pointee type was forbidden. Unfortunately this prevented a situation like the following from being allowed.
Header file:
```
struct EltTy; // Incomplete type
struct Buffer {
size_t count;
struct EltTy* __counted_by(count) buffer; // error before this patch
};
```
Implementation file:
```
struct EltTy {
// definition
};
void allocBuffer(struct Buffer* b) {
b->buffer = malloc(sizeof(EltTy)* b->count);
}
```
To allow code like the above but still enforce that the pointee type size is known in locations where `-fbounds-safety` needs to emit bounds checks the following scheme is used.
* For incomplete pointee types that can never be completed (e.g. `void`) these are treated as error where the attribute is written (just like before this patch).
* For incomplete pointee types that might be completable later on (struct, union, and enum forward declarations) in the translation unit, writing the attribute on the incomplete pointee type is allowed on the FieldDecl declaration but "uses" of the declared pointer are forbidden if at the point of "use" the pointee type is still incomplete.
For this patch a "use" of a FieldDecl covers:
* Explicit and Implicit initialization (note see **Tentative Definition Initialization** for an exception to this)
* Assignment
* Conversion to an lvalue (e.g. for use in an expression)
In Apple's internal fork of Clang the `counted_by` and `counted_by_or_null` attribute are allowed in many more contexts. That isn't the case for upstream Clang so the "use" checks for the attribute on VarDecl, ParamVarDecl, and function return type have been omitted from this patch because they can't be tested. However, the `BoundsSafetyCheckAssignmentToCountAttrPtrWithIncompletePointeeTy` and `BoundsSafetyCheckUseOfCountAttrPtrWithIncompletePointeeTy` functions retain the ability to emit diagnostics for these other contexts to avoid unnecessary divergence between upstream Clang and Apple's internal fork. Support for checking "uses" will be upstreamed when upstream Clang allows the `counted_by` and `counted_by_or_null` attribute in additional contexts.
This patch also includes the new `Sema::BoundsSafetySourceRangeFor` method which allows a more accurate SourceRange for the `counted_by` and `counted_by_or_null` attributes to be shown in diagnostics. This is used by the new `diag::note_named_attribute` diagnostic introduced in this patch. This method has several shortcomings but it didn't seem worth covering them given this is essentially a workaround for the fact `CountAttributedType` doesn't store the SourceLocation for its attribute.
This patch has a few limitations:
** 1. Tentative Defition Initialization **
This patch currently allows something like:
```
struct IncompleteTy;
struct Buffer {
int count;
struct IncompleteTy* __counted_by(count) buf;
};
// Tentative definition
struct Buffer GlobalBuf;
```
The Tentative definition in this example becomes an actual definition whose initialization **should be checked** but it currently isn't. Addressing this problem will be done in a subseqent patch.
** 2. When the incomplete pointee type is a typedef diagnostics are slightly misleading **
For this situation:
```
struct IncompleteTy;
typedef struct IncompleteTy Incomplete_t;
struct Buffer {
int count;
struct IncompleteTy* __counted_by(count) buf;
};
void use(struct Buffer b) {
b.buf = 0x0;
}
```
This code emits `note: forward declaration of 'Incomplete_t' (aka 'struct IncompleteTy')` but the location is on the `struct IncompleteTy;` forward declaration. This is misleading because `Incomplete_t` isn't actually forward declared there (instead the underlying type is). This could be resolved by additional diagnostics that walk the chain of typedefs and explain each step of the walk. However, that would be very verbose and didn't seem like a direction worth pursuing.
rdar://133600117
---
Patch is 109.87 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/106321.diff
16 Files Affected:
- (modified) clang/include/clang/AST/Type.h (+22)
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+37)
- (modified) clang/include/clang/Sema/Sema.h (+51)
- (modified) clang/lib/AST/Type.cpp (+37)
- (modified) clang/lib/Sema/SemaBoundsSafety.cpp (+399-1)
- (modified) clang/lib/Sema/SemaExpr.cpp (+24)
- (modified) clang/lib/Sema/SemaInit.cpp (+17)
- (modified) clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs.c (+2-3)
- (modified) clang/test/Sema/attr-counted-by-or-null-last-field.c (+2-2)
- (modified) clang/test/Sema/attr-counted-by-or-null-late-parsed-struct-ptrs.c (+2-3)
- (modified) clang/test/Sema/attr-counted-by-or-null-struct-ptrs.c (+4-8)
- (added) clang/test/Sema/attr-counted-by-struct-ptrs-completable-incomplete-pointee.c (+584)
- (modified) clang/test/Sema/attr-counted-by-struct-ptrs.c (+4-8)
- (modified) clang/test/Sema/attr-counted-by-vla.c (+1-2)
- (modified) clang/unittests/Sema/CMakeLists.txt (+1)
- (added) clang/unittests/Sema/GetCountedByAttrSourceRange.cpp (+573)
``````````diff
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index a4804e4c6f61ca..fb4638d10f121c 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -2417,6 +2417,26 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
return !isFunctionType();
}
+ /// \returns True if the type is incomplete and it is also a type that
+ /// cannot be completed by a later type definition.
+ ///
+ /// E.g. For `void` this is true but for `struct ForwardDecl;` this is false
+ /// because a definition for `ForwardDecl` could be provided later on in the
+ /// translation unit.
+ ///
+ /// Note even for types that this function returns true for it is still
+ /// possible for the declarations that contain this type to later have a
+ /// complete type in a translation unit. E.g.:
+ ///
+ /// \code{.c}
+ /// // This decl has type 'char[]' which is incomplete and cannot be later
+ /// // completed by another by another type declaration.
+ /// extern char foo[];
+ /// // This decl how has complete type 'char[5]'.
+ /// char foo[5]; // foo has a complete type
+ /// \endcode
+ bool isIncompletableIncompleteType() const;
+
/// Determine whether this type is an object type.
bool isObjectType() const {
// C++ [basic.types]p8:
@@ -3320,6 +3340,8 @@ class CountAttributedType final
static bool classof(const Type *T) {
return T->getTypeClass() == CountAttributed;
}
+
+ StringRef GetAttributeName(bool WithMacroPrefix) const;
};
/// Represents a type which was implicitly adjusted by the semantic
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 5cdf36660b2a66..305fab7cd0c8db 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4370,6 +4370,7 @@ def err_mismatched_visibility: Error<"visibility does not match previous declara
def note_previous_attribute : Note<"previous attribute is here">;
def note_conflicting_attribute : Note<"conflicting attribute is here">;
def note_attribute : Note<"attribute is here">;
+def note_named_attribute : Note<"%0 attribute is here">;
def err_mismatched_ms_inheritance : Error<
"inheritance model does not match %select{definition|previous declaration}0">;
def warn_ignored_ms_inheritance : Warning<
@@ -6619,6 +6620,42 @@ def err_counted_by_attr_pointee_unknown_size : Error<
"%select{|. This will be an error in a future compiler version}3"
""
"}2">;
+def err_counted_by_on_incomplete_type_on_assign : Error <
+ "cannot %select{"
+ "assign to %select{object|'%1'}2 that has|" // AA_Assigning,
+ "pass argument to %select{parameter|parameter '%1'}2 that has|" // AA_Passing,
+ "return|" // AA_Returning,
+ "convert to|" // AA_Converting (UNUSED)
+ "%select{|implicitly }3initialize %select{object|'%1'}2 that has|" // AA_Initializing,
+ "pass argument to parameter that has|" // AA_Sending (UNUSED)
+ "cast to|" // AA_Casting (UNUSED)
+ "pass argument to parameter that has" // AA_Passing_CFAudited (UNUSED)
+ "}0 type %4 because the pointee type %6 is incomplete and the '%5' attribute "
+ "requires the pointee type be complete when %select{"
+ "assigning|" // AA_Assigning,
+ "passing|" // AA_Passing,
+ "returning|" // AA_Returning,
+ "converting|" // AA_Converting (UNUSED)
+ "initializing|" // AA_Initializing,
+ "passing|" // AA_Sending (UNUSED)
+ "casting|" // AA_Casting (UNUSED)
+ "passing" // AA_Passing_CFAudited (UNUSED)
+ "}0; "
+ "consider providing a complete definition for %6 or using the "
+ "'__sized_by%select{|_or_null}7' attribute"
+>;
+def err_counted_by_on_incomplete_type_on_use : Error <
+ "cannot %select{"
+ "use '%1'|" // Generic expr
+ "call '%1'" // CallExpr
+ "}0 with%select{"
+ "|" // Generic expr
+ " return" // CallExpr
+ "}0 type %2 because the pointee type %3 is incomplete "
+ "and the '%4' attribute requires the pointee type be complete in this context; "
+ "consider providing a complete definition for %3 or using the "
+ "'__sized_by%select{|_or_null}5' attribute"
+>;
def warn_counted_by_attr_elt_type_unknown_size :
Warning<err_counted_by_attr_pointee_unknown_size.Summary>,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index b7bd6c2433efd6..b6a194cf32f0a6 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -15086,6 +15086,57 @@ class Sema final : public SemaBase {
bool CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
bool OrNull);
+ /// Perform Bounds Safety Semantic checks for assigning to a `__counted_by` or
+ /// `__counted_by_or_null` pointer type \param LHSTy.
+ ///
+ /// \param LHSTy The type being assigned to. Checks will only be performed if
+ /// the type is a `counted_by` or `counted_by_or_null ` pointer.
+ /// \param RHSExpr The expression being assigned from.
+ /// \param Action The type assignment being performed
+ /// \param Loc The SourceLocation to use for error diagnostics
+ /// \param ComputeAssignee If provided this function will be called before
+ /// emitting a diagnostic. The function should return the name of
+ /// entity being assigned to or an empty string if this cannot be
+ /// determined.
+ ///
+ /// \returns True iff no diagnostic where emitted, false otherwise.
+ bool BoundsSafetyCheckAssignmentToCountAttrPtr(
+ QualType LHSTy, Expr *RHSExpr, Sema::AssignmentAction Action,
+ SourceLocation Loc,
+ std::function<std::string()> ComputeAssignee = nullptr);
+
+ /// Perform Bounds Safety Semantic checks for initializing a Bounds Safety
+ /// pointer.
+ ///
+ /// \param Entity The entity being initialized
+ /// \param Kind The kind of initialization being performed
+ /// \param Action The type assignment being performed
+ /// \param LHSTy The type being assigned to. Checks will only be performed if
+ /// the type is a `counted_by` or `counted_by_or_null ` pointer.
+ /// \param RHSExpr The expression being used for initialization.
+ ///
+ /// \returns True iff no diagnostic where emitted, false otherwise.
+ bool BoundsSafetyCheckInitialization(const InitializedEntity &Entity,
+ const InitializationKind &Kind,
+ Sema::AssignmentAction Action,
+ QualType LHSType, Expr *RHSExpr);
+
+ /// Perform Bounds Safety semantic checks for uses of invalid uses counted_by
+ /// or counted_by_or_null pointers in \param E.
+ ///
+ /// \param E the expression to check
+ ///
+ /// \returns True iff no diagnostic where emitted, false otherwise.
+ bool BoundsSafetyCheckUseOfCountAttrPtr(Expr *E);
+
+ /// \returns A SourceRange for \param CATy.
+ ///
+ /// This method tries to compute the most useful SourceRange for diagnostics.
+ /// If this fails the returned SourceRange will be the same as
+ /// `CATy->getCountExpr()->getSourceRange()`.
+ ///
+ SourceRange BoundsSafetySourceRangeFor(const CountAttributedType *CATy);
+
///@}
};
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index e89ce2e4b38445..137609743fc02c 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -2438,6 +2438,22 @@ bool Type::isIncompleteType(NamedDecl **Def) const {
}
}
+bool Type::isIncompletableIncompleteType() const {
+ if (!isIncompleteType())
+ return false;
+
+ // Forward declarations of structs, classes, enums, and unions could be later
+ // completed in a compilation unit by providing a type definition.
+ if (isStructureOrClassType() || isEnumeralType() || isUnionType())
+ return false;
+
+ // Other types are incompletable.
+ //
+ // E.g. `char[]` and `void`. The type is incomplete and no future
+ // type declarations can make the type complete.
+ return true;
+}
+
bool Type::isSizelessBuiltinType() const {
if (isSizelessVectorType())
return true;
@@ -3862,6 +3878,27 @@ CountAttributedType::CountAttributedType(
DeclSlot[i] = CoupledDecls[i];
}
+StringRef CountAttributedType::GetAttributeName(bool WithMacroPrefix) const {
+#define ENUMERATE_ATTRS(PREFIX) \
+ do { \
+ if (isCountInBytes()) { \
+ if (isOrNull()) \
+ return PREFIX "sized_by_or_null"; \
+ return PREFIX "sized_by"; \
+ } \
+ if (isOrNull()) \
+ return PREFIX "counted_by_or_null"; \
+ return PREFIX "counted_by"; \
+ } while (0)
+
+ if (WithMacroPrefix)
+ ENUMERATE_ATTRS("__");
+ else
+ ENUMERATE_ATTRS("");
+
+#undef ENUMERATE_ATTRS
+}
+
TypedefType::TypedefType(TypeClass tc, const TypedefNameDecl *D,
QualType Underlying, QualType can)
: Type(tc, can, toSemanticDependence(can->getDependence())),
diff --git a/clang/lib/Sema/SemaBoundsSafety.cpp b/clang/lib/Sema/SemaBoundsSafety.cpp
index d63a2389ea11de..fa72553317a810 100644
--- a/clang/lib/Sema/SemaBoundsSafety.cpp
+++ b/clang/lib/Sema/SemaBoundsSafety.cpp
@@ -11,6 +11,9 @@
/// (e.g. `counted_by`)
///
//===----------------------------------------------------------------------===//
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Sema/Initialization.h"
#include "clang/Sema/Sema.h"
namespace clang {
@@ -102,7 +105,36 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
// only `PointeeTy->isStructureTypeWithFlexibleArrayMember()` is reachable
// when `FieldTy->isArrayType()`.
bool ShouldWarn = false;
- if (PointeeTy->isIncompleteType() && !CountInBytes) {
+ if (PointeeTy->isIncompletableIncompleteType() && !CountInBytes) {
+ // In general using `counted_by` or `counted_by_or_null` on
+ // pointers where the pointee is an incomplete type are problematic. This is
+ // because it isn't possible to compute the pointer's bounds without knowing
+ // the pointee type size. At the same time it is common to forward declare
+ // types in header files.
+ //
+ // E.g.:
+ //
+ // struct Handle;
+ // struct Wrapper {
+ // size_t size;
+ // struct Handle* __counted_by(count) handles;
+ // }
+ //
+ // To allow the above code pattern but still prevent the pointee type from
+ // being incomplete in places where bounds checks are needed the following
+ // scheme is used:
+ //
+ // * When the pointee type is a "completable incomplete" type (i.e.
+ // a type that is currently incomplete but might be completed later
+ // on in the translation unit) the attribute is allowed by this method
+ // but later uses of the FieldDecl are checked that the pointee type
+ // is complete see `BoundsSafetyCheckAssignmentToCountAttrPtr`,
+ // `BoundsSafetyCheckInitialization`, and
+ // `BoundsSafetyCheckUseOfCountAttrPtr`
+ //
+ // * When the pointee type is a "incompletable incomplete" type (e.g.
+ // `void`) the attribute is disallowed by this method because we know the
+ // type can never be completed so there's no reason to allow it.
InvalidTypeKind = CountedByInvalidPointeeTypeKind::INCOMPLETE;
} else if (PointeeTy->isSizelessType()) {
InvalidTypeKind = CountedByInvalidPointeeTypeKind::SIZELESS;
@@ -186,4 +218,370 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
return false;
}
+SourceRange Sema::BoundsSafetySourceRangeFor(const CountAttributedType *CATy) {
+ // Note: This implementation relies on `CountAttributedType` being unique.
+ // E.g.:
+ //
+ // struct Foo {
+ // int count;
+ // char* __counted_by(count) buffer;
+ // char* __counted_by(count) buffer2;
+ // };
+ //
+ // The types of `buffer` and `buffer2` are unique. The types being
+ // unique means the SourceLocation of the `counted_by` expression can be used
+ // to find where the attribute was written.
+
+ auto Fallback = CATy->getCountExpr()->getSourceRange();
+ auto CountExprBegin = CATy->getCountExpr()->getBeginLoc();
+
+ // FIXME: We currently don't support the count expression being a macro
+ // itself. E.g.:
+ //
+ // #define ZERO 0
+ // int* __counted_by(ZERO) x;
+ //
+ if (SourceMgr.isMacroBodyExpansion(CountExprBegin))
+ return Fallback;
+
+ auto FetchIdentifierTokenFromOffset =
+ [&](ssize_t Offset) -> std::optional<Token> {
+ SourceLocation OffsetLoc = CountExprBegin.getLocWithOffset(Offset);
+ Token Result;
+ if (Lexer::getRawToken(OffsetLoc, Result, SourceMgr, getLangOpts()))
+ return std::optional<Token>(); // Failed
+
+ if (!Result.isAnyIdentifier())
+ return std::optional<Token>(); // Failed
+
+ return Result; // Success
+ };
+
+ auto CountExprEnd = CATy->getCountExpr()->getEndLoc();
+ auto FindRParenTokenAfter = [&]() -> std::optional<Token> {
+ auto CountExprEndSpelling = SourceMgr.getSpellingLoc(CountExprEnd);
+ auto MaybeRParenTok = Lexer::findNextToken(
+ CountExprEndSpelling, getSourceManager(), getLangOpts());
+
+ if (!MaybeRParenTok.has_value())
+ return std::nullopt;
+
+ if (!MaybeRParenTok->is(tok::r_paren))
+ return std::nullopt;
+
+ return *MaybeRParenTok;
+ };
+
+ // Step back two characters to point at the last character of the attribute
+ // text.
+ //
+ // __counted_by(count)
+ // ^ ^
+ // | |
+ // ---
+ auto MaybeLastAttrCharToken = FetchIdentifierTokenFromOffset(-2);
+ if (!MaybeLastAttrCharToken)
+ return Fallback;
+
+ auto LastAttrCharToken = MaybeLastAttrCharToken.value();
+
+ if (LastAttrCharToken.getLength() > 1) {
+ // Special case: When the character is part of a macro the Token we get
+ // is the whole macro name (e.g. `__counted_by`).
+ if (LastAttrCharToken.getRawIdentifier() !=
+ CATy->GetAttributeName(/*WithMacroPrefix=*/true))
+ return Fallback;
+
+ // Found the beginning of the `__counted_by` macro
+ SourceLocation Begin = LastAttrCharToken.getLocation();
+ // Now try to find the closing `)` of the macro.
+ auto MaybeRParenTok = FindRParenTokenAfter();
+ if (!MaybeRParenTok.has_value())
+ return Fallback;
+
+ return SourceRange(Begin, MaybeRParenTok->getLocation());
+ }
+
+ assert(LastAttrCharToken.getLength() == 1);
+ // The Token we got back is just the last character of the identifier.
+ // This means a macro is not being used and instead the attribute is being
+ // used directly. We need to find the beginning of the identifier. We support
+ // two cases:
+ //
+ // * Non-affixed version. E.g: `counted_by`
+ // * Affixed version. E.g.: `__counted_by__`
+
+ // Try non-affixed version. E.g.:
+ //
+ // __attribute__((counted_by(count)))
+ // ^ ^
+ // | |
+ // ------------
+
+ // +1 is for `(`
+ const ssize_t NonAffixedSkipCount =
+ CATy->GetAttributeName(/*WithMacroPrefix=*/false).size() + 1;
+ auto MaybeNonAffixedBeginToken =
+ FetchIdentifierTokenFromOffset(-NonAffixedSkipCount);
+ if (!MaybeNonAffixedBeginToken)
+ return Fallback;
+
+ auto NonAffixedBeginToken = MaybeNonAffixedBeginToken.value();
+ if (NonAffixedBeginToken.getRawIdentifier() ==
+ CATy->GetAttributeName(/*WithMacroPrefix=*/false)) {
+ // Found the beginning of the `counted_by`-like attribute
+ auto SL = NonAffixedBeginToken.getLocation();
+
+ // Now try to find the closing `)` of the attribute
+ auto MaybeRParenTok = FindRParenTokenAfter();
+ if (!MaybeRParenTok.has_value())
+ return Fallback;
+
+ return SourceRange(SL, MaybeRParenTok->getLocation());
+ }
+
+ // Try affixed version. E.g.:
+ //
+ // __attribute__((__counted_by__(count)))
+ // ^ ^
+ // | |
+ // ----------------
+ std::string AffixedTokenStr =
+ (llvm::Twine("__") + CATy->GetAttributeName(/*WithMacroPrefix=*/false) +
+ llvm::Twine("__"))
+ .str();
+ // +1 is for `(`
+ // +4 is for the 4 `_` characters
+ const ssize_t AffixedSkipCount =
+ CATy->GetAttributeName(/*WithMacroPrefix=*/false).size() + 1 + 4;
+ auto MaybeAffixedBeginToken =
+ FetchIdentifierTokenFromOffset(-AffixedSkipCount);
+ if (!MaybeAffixedBeginToken)
+ return Fallback;
+
+ auto AffixedBeginToken = MaybeAffixedBeginToken.value();
+ if (AffixedBeginToken.getRawIdentifier() != AffixedTokenStr)
+ return Fallback;
+
+ // Found the beginning of the `__counted_by__`-like like attribute.
+ auto SL = AffixedBeginToken.getLocation();
+ // Now try to find the closing `)` of the attribute
+ auto MaybeRParenTok = FindRParenTokenAfter();
+ if (!MaybeRParenTok.has_value())
+ return Fallback;
+
+ return SourceRange(SL, MaybeRParenTok->getLocation());
+}
+
+static void EmitIncompleteCountedByPointeeNotes(Sema &S,
+ const CountAttributedType *CATy,
+ NamedDecl *IncompleteTyDecl,
+ bool NoteAttrLocation = true) {
+ assert(IncompleteTyDecl == nullptr || isa<TypeDecl>(IncompleteTyDecl));
+
+ if (NoteAttrLocation) {
+ // Note where the attribute is declared
+ auto AttrSrcRange = S.BoundsSafetySourceRangeFor(CATy);
+ S.Diag(AttrSrcRange.getBegin(), diag::note_named_attribute)
+ << CATy->GetAttributeName(/*WithMacroPrefix=*/true) << AttrSrcRange;
+ }
+
+ if (!IncompleteTyDecl)
+ return;
+
+ // If there's an associated forward declaration display it to emphasize
+ // why the type is incomplete (all we have is a forward declaration).
+
+ // Note the `IncompleteTyDecl` type is the underlying type which might not
+ // be the same as `CATy->getPointeeType()` which could be a typedef.
+ //
+ // The diagnostic printed will be at the location of the underlying type but
+ // the diagnostic text will print the type of `CATy->getPointeeType()` which
+ // could be a typedef name rather than the underlying type. This is ok
+ // though because the diagnostic will print the underlying type name too.
+ // E.g:
+ //
+ // `forward declaration of 'Incomplete_Struct_t'
+ // (aka 'struct IncompleteStructTy')`
+ //
+ // If this ends up being confusing we could emit a second diagnostic (one
+ // explaining where the typedef is) but that seems overly verbose.
+
+ S.Diag(IncompleteTyDecl->getBeginLoc(), diag::note_forward_declaration)
+ << CATy->getPointeeType();
+}
+
+static bool
+HasCountedByAttrOnIncompletePointee(QualType Ty, NamedDecl **ND,
+ const CountAttributedType **CATyOut,
+ QualType *PointeeTyOut) {
+ auto *CATy = Ty->getAs<CountAttributedType>();
+ if (!CATy)
+ return false;
+
+ // Incomplete pointee type is only a problem for
+ // counted_by/counted_by_or_null
+ if (CATy->isCountInBytes())
+ return false;
+
+ auto PointeeTy = CATy->getPointeeType();
+ if (PointeeTy.isNull())
+ return false; // Reachable?
+
+ if (!PointeeTy->isIncompleteType(ND))
+ return false;
+
+ if (CATyOut)
+ *CATyOut = CATy;
+ if (PointeeTyOut)
+ *PointeeTyOut = PointeeTy;
+ return true;
+}
+
+/// Perform Checks for assigning to a `__counted_by` or
+/// `__counted_by_or_null` pointer type \param LHSTy where the pointee type
+/// is incomplete which is invalid.
+///
+/// \param S The Sema instance.
+/// \param LHSTy The type being assigned to. Checks will only be performed if
+/// the type is a `counted_by` or `counted_by_or_null ` pointer.
+/// \param RHSExpr The expression being assigned from.
+/// \param Action The type assignment being performed
+/// \param Loc The SourceLocation to use for error diagnostics
+/// \param ComputeAssignee If provided this function will be called...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/106321
More information about the cfe-commits
mailing list