Skip to content

[Cherry-pick][BoundsSafety][Sema] Allow counted_by and counted_by_or_null on pointers where the pointee type is incomplete but potentially completable #10514

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -2479,6 +2479,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
bool isIncompleteOrSizelessType() const {
return isIncompleteType() || isSizelessType() || isFunctionType();
}
/* TO_UPSTREAM(BoundsSafety) OFF*/

/// \returns True if the type is incomplete and it is also a type that
/// cannot be completed by a later type definition.
Expand All @@ -2495,11 +2496,10 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
/// // 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]'.
/// // This decl now has complete type 'char[5]'.
/// char foo[5]; // foo has a complete type
/// \endcode
bool isIncompletableIncompleteType() const;
/* TO_UPSTREAM(BoundsSafety) OFF*/
bool isAlwaysIncompleteType() const;

/// Determine whether this type is an object type.
bool isObjectType() const {
Expand Down Expand Up @@ -3690,9 +3690,7 @@ class CountAttributedType final
return T->getTypeClass() == CountAttributed;
}

/* TO_UPSTREAM(BoundsSafety) ON*/
StringRef GetAttributeName(bool WithMacroPrefix) const;
/* TO_UPSTREAM(BoundsSafety) OFF*/
StringRef getAttributeName(bool WithMacroPrefix) const;
};

/* TO_UPSTREAM(BoundsSafety) ON */
Expand Down
27 changes: 25 additions & 2 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -6790,6 +6790,30 @@ 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 with|" // AA_Assigning,
"pass argument to %select{parameter|parameter '%1'}2 with|" // AA_Passing,
"return|" // AA_Returning,
"convert to|" // AA_Converting (UNUSED)
"%select{|implicitly }3initialize %select{object|'%1'}2 with|" // AA_Initializing,
"pass argument to parameter with|" // AA_Sending (UNUSED)
"cast to|" // AA_Casting (UNUSED)
"pass argument to parameter with" // AA_Passing_CFAudited (UNUSED)
"}0 '%5' attributed type %4 because the pointee type %6 is incomplete">;

def err_counted_by_on_incomplete_type_on_use : Error <
"cannot %select{"
"use '%1' with '%4' attributed|" // Generic expr
"call '%1' with '%4' attributed return" // CallExpr
"}0 type %2 because the pointee type %3 is incomplete">;

def note_counted_by_consider_completing_pointee_ty : Note<
"consider providing a complete definition for %0">;

def note_counted_by_consider_using_sized_by : Note<
"consider using '__sized_by%select{|_or_null}0' instead of "
"'__counted_by%select{|_or_null}0'">;

def warn_counted_by_attr_elt_type_unknown_size :
Warning<err_counted_by_attr_pointee_unknown_size.Summary>,
Expand Down Expand Up @@ -12895,8 +12919,7 @@ def err_bounds_safety_counted_by_on_incomplete_type_on_func_def : Error<
" '%3'" // named parameter
"}2 with"
"}1 type %4 on a function definition because the pointee type %5 is "
"incomplete; consider providing a complete definition for %5 before the "
"function body or using the '__sized_by%select{|_or_null}6' attribute"
"incomplete"
>;

def err_bounds_safety_counted_by_on_incomplete_type_on_var_decl : Error<
Expand Down
40 changes: 9 additions & 31 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -2263,7 +2263,6 @@ class Sema final : public SemaBase {
bool CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
bool OrNull);

/* TO_UPSTREAM(BoundsSafety) ON*/
/// Perform Bounds Safety Semantic checks for assigning to a `__counted_by` or
/// `__counted_by_or_null` pointer type \param LHSTy.
///
Expand All @@ -2285,28 +2284,6 @@ class Sema final : public SemaBase {
SourceLocation Loc, const ValueDecl *Assignee,
bool ShowFullyQualifiedAssigneeName);

/// 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 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 Assignee The ValueDecl being assigned. This is used to compute
/// the name of the assignee. If the assignee isn't known this can
/// be set to nullptr.
/// \param ShowFullyQualifiedAssigneeName If set to true when using \p
/// Assignee to compute the name of the assignee use the fully
/// qualified name, otherwise use the unqualified name.
///
/// \returns True iff no diagnostic where emitted, false otherwise.
bool BoundsSafetyCheckAssignmentToCountAttrPtrWithIncompletePointeeTy(
QualType LHSTy, Expr *RHSExpr, AssignmentAction Action,
SourceLocation Loc, const ValueDecl *Assignee,
bool ShowFullyQualifiedAssigneeName);

/// Perform Bounds Safety Semantic checks for initializing a Bounds Safety
/// pointer.
///
Expand All @@ -2323,6 +2300,15 @@ class Sema final : public SemaBase {
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(const Expr *E);

/* TO_UPSTREAM(BoundsSafety) ON*/
/// Perform Bounds Safety semantic checks on function parameters on a function
/// definition. This only performs checks that can be made by looking at
/// \param PVD in isolation (i.e. not looking at other parameters in the
Expand Down Expand Up @@ -2351,14 +2337,6 @@ class Sema final : public SemaBase {
/// \returns True iff no diagnostic where emitted, false otherwise.
bool BoundsSafetyCheckReturnTyForFunctionDef(FunctionDecl *FD);

/// 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);

/// Perform Bounds Safety semantic checks on variable declaration \param VD.
///
/// \param VD The VarDecl to check
Expand Down
40 changes: 22 additions & 18 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2591,6 +2591,22 @@ bool Type::isIncompleteType(NamedDecl **Def) const {
}
}

bool Type::isAlwaysIncompleteType() 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 (getAsTagDecl())
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;
Expand Down Expand Up @@ -2708,22 +2724,6 @@ bool Type::isSizeMeaningless() const {
return true;
return false;
}

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 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;
}
/* TO_UPSTREAM(BoundsSafety) OFF*/

QualType Type::getSizelessVectorEltType(const ASTContext &Ctx) const {
Expand Down Expand Up @@ -4072,8 +4072,11 @@ CountAttributedType::CountAttributedType(
DeclSlot[i] = CoupledDecls[i];
}

/* TO_UPSTREAM(BoundsSafety) ON*/
StringRef CountAttributedType::GetAttributeName(bool WithMacroPrefix) const {
StringRef CountAttributedType::getAttributeName(bool WithMacroPrefix) const {
// TODO: This method isn't really ideal because it doesn't return the spelling
// of the attribute that was used in the user's code. This method is used for
// diagnostics so the fact it doesn't use the spelling of the attribute in
// the user's code could be confusing (#113585).
#define ENUMERATE_ATTRS(PREFIX) \
do { \
if (isCountInBytes()) { \
Expand All @@ -4094,6 +4097,7 @@ StringRef CountAttributedType::GetAttributeName(bool WithMacroPrefix) const {
#undef ENUMERATE_ATTRS
}

/* TO_UPSTREAM(BoundsSafety) ON*/
DynamicRangePointerType::DynamicRangePointerType(
QualType PointerTy, QualType CanPointerTy, Expr *StartPtr, Expr *EndPtr,
ArrayRef<TypeCoupledDeclRefInfo> StartPtrDecls,
Expand Down
Loading