Skip to content

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

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
Show file tree
Hide file tree
Changes from 1 commit
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
22 changes: 22 additions & 0 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -2433,6 +2433,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 now has complete type 'char[5]'.
/// char foo[5]; // foo has a complete type
/// \endcode
bool isAlwaysIncompleteType() const;

/// Determine whether this type is an object type.
bool isObjectType() const {
// C++ [basic.types]p8:
Expand Down Expand Up @@ -3349,6 +3369,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
Expand Down
27 changes: 27 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -6710,6 +6710,33 @@ 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'|" // Generic expr
"call '%1'" // CallExpr
"}0 with%select{"
"|" // Generic expr
" return" // CallExpr
"}0 '%4' attributed 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
44 changes: 44 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -2098,6 +2098,50 @@ 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 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 BoundsSafetyCheckAssignmentToCountAttrPtr(
QualType LHSTy, Expr *RHSExpr, AssignmentAction Action,
SourceLocation Loc, const ValueDecl *Assignee,
bool ShowFullyQualifiedAssigneeName);

/// 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,
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);
///@}

//
Expand Down
41 changes: 41 additions & 0 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2490,6 +2490,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 @@ -3931,6 +3947,31 @@ CountAttributedType::CountAttributedType(
DeclSlot[i] = CoupledDecls[i];
}

StringRef CountAttributedType::getAttributeName(bool WithMacroPrefix) const {
// TODO: This method isn't really ideal because it doesn't return the spelling
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear to me why this is needed at all -- doesn't Attr::getSpelling() suffice on the attribute, rather than asking the type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this context AFAICT we do not have access to the attribute. All we have is the CountAttributedType and we have no easy way to find the corresponding Attr object from this type. This is what #113585 tracks fixing.

There's a related comment about this in this review here.

// 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()) { \
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())),
Expand Down
Loading
Loading