Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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
3 changes: 3 additions & 0 deletions include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -1917,6 +1917,9 @@ class Type : public ExtQualsTypeCommonBase {
/// \brief Whether this type is or contains a checked type
bool isOrContainsCheckedType() const;

/// \brief check whether an object of a struct or union type contains a checked value
bool containsCheckedValue() const;

/// \brief Whether this type is or contains an unchecked type.
/// This ignores the presence of bounds-safe interface types.
bool isOrContainsUncheckedType() const;
Expand Down
12 changes: 12 additions & 0 deletions include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -9569,6 +9569,18 @@ def err_bounds_type_annotation_lost_checking : Error<

def err_initializer_expected_for_ptr : Error<
"automatic variable %0 with _Ptr type must have initializer">;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please use the name integer in place of int in the error messages.

def err_initializer_expected_for_int_with_bounds_expr : Error<
"int variable %0 with a bounds expression must have an initializer">;

def err_initializer_expected_for_unchecked_ptr_in_checked_scope_with_bounds_expr : Error<
"unchecked pointer variable %0 in a checked scope with a bounds expression must have an initializer">;

def err_initializer_expected_for_array : Error<
"array variable %0 with elements containing checked pointers must have an initializer">;

def err_initializer_expected_for_record : Error<
"%select{struct|interface|union|class|enum}0 variable %1 containing a checked pointer must have an initializer">;

def err_cast_to_checked_fn_ptr_must_be_named : Error<
"cast to checked function pointer type %0 must be from named top-level function">;
Expand Down
54 changes: 54 additions & 0 deletions lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4109,6 +4109,60 @@ bool Type::isOrContainsUncheckedType() const {
}
}

// containsCheckedValue - check whether a field type is a checked type or is a

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This comment needs to be updated.

// constructed type (array, pointer, function) that uses a checked type.
bool Type::containsCheckedValue() const {
const Type *current = CanonicalType.getTypePtr();
switch (current->getTypeClass()) {
case Type::Pointer: {
const PointerType *ptr = cast<PointerType>(current);
return ptr->isCheckedPointerType();
}
case Type::ConstantArray:
case Type::DependentSizedArray:
case Type::IncompleteArray:
case Type::VariableArray: {
return current->getPointeeOrArrayElementType()->containsCheckedValue();
}

//Use RecordType to process Struct/Union
case Type::Record: {
const RecordType *RT = cast<RecordType>(current);
// if this is an illegal type, we don't proceed (e.g. struct S{ S s; int a;...})
if (RT->getDecl()->isInvalidDecl())
return false;

// if this is a struct/union type, iterate all its members

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Wording suggestion: iterate all -> iterate over all

bool hasCheckedField = false;
for (FieldDecl *FD : RT->getDecl()->fields()) {
if (FD->getType()->isRecordType()) {
hasCheckedField = FD->getType()->containsCheckedValue();
}
// if this field is not a RecordType variable but contains a checked pointer,
// its type must be (1) _Ptr (2) _Array_ptr or (3) _Nt_array_ptr
else if (FD->getType()->containsCheckedValue()) {
// Case 1: _Ptr always needs to be initialized
if (FD->getType()->isCheckedPointerPtrType())
hasCheckedField = true;
// Case 2: _Array_ptr needs to be initialized if it has bounds and the bounds are NOT unknown;
// Case 3: _Nt_array_ptr needs to be initialized if (1) it has no bounds specified
// or (2) it has bounds but the bounds are unknown;
// since for _Nt_array_ptr we always attach default bounds of count(0) to a decl,
// if no bounds are specified (done in ActOnBoundsDecl and before this checking),
// we can simplified the checking by combining case 2 and 3
else if (FD->getType()->isCheckedPointerArrayType() && FD->hasBoundsExpr()) {
if (!FD->getBoundsExpr()->isUnknown())
hasCheckedField = true;
}
break;
}
}
return hasCheckedField;
}
default:
return false;
}
}
// hasVariadicType - check whether a type has variable arguments
// or is a constructed type(array, pointer, function) having variable arguments.
bool Type::hasVariadicType() const {
Expand Down
27 changes: 27 additions & 0 deletions lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11393,6 +11393,33 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
else if (B && !B->isInvalid() && !B->isUnknown() && !Ty->isArrayType())
Diag(Var->getLocation(), diag::err_initializer_expected_with_bounds)
<< Var;

// An integer with a bounds expression must be initialized
if (Ty->isIntegerType() && B)
Diag(Var->getLocation(), diag::err_initializer_expected_for_int_with_bounds_expr)
<< Var;

// An unchecked pointer in a checked scope with a bounds expression must be initialized
if (Ty->isUncheckedPointerType() && getCurScope()->isCheckedScope() &&
Var->hasBoundsExpr())
Diag(Var->getLocation(), diag::err_initializer_expected_for_unchecked_ptr_in_checked_scope_with_bounds_expr)
<< Var;

// struct/union and array with checked pointer members must have initializers
// array with checked ptr element
if (Ty->isArrayType()) {
// if this is an array type, check the element type of the array, potentially with type qualifiers missing
if (Ty->getPointeeOrArrayElementType()->containsCheckedValue())
Diag(Var->getLocation(), diag::err_initializer_expected_for_array)
<< Var;
}
// RecordType(struct/union) with checked pointer member
if (Ty->isRecordType()) {
const RecordType *RT = Ty->getAs<RecordType>();
if (RT->containsCheckedValue())
Diag(Var->getLocation(), diag::err_initializer_expected_for_record)
<< RT->getDecl()->getTagKind() << Var;
}
}

switch (Var->isThisDeclarationADefinition()) {
Expand Down