Skip to content

Commit 45c1a77

Browse files
committed
fixup: rework malloc_span validation and error reporting
1 parent 749f34b commit 45c1a77

File tree

4 files changed

+102
-28
lines changed

4 files changed

+102
-28
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3450,10 +3450,17 @@ def warn_attribute_return_pointers_only : Warning<
34503450
"%0 attribute only applies to return values that are pointers">,
34513451
InGroup<IgnoredAttributes>;
34523452
def warn_attribute_return_span_only
3453-
: Warning<"%0 attribute only applies to functions that return span-like structures: "
3454-
"one field is a pointer to the allocated memory and another field is an integer with "
3455-
"the size of the allocated memory">,
3453+
: Warning<"%0 attribute only applies to functions that return span-like "
3454+
"structures">,
34563455
InGroup<IgnoredAttributes>;
3456+
def note_span_must_be_struct : Note<"span-like type must be a struct">;
3457+
def note_span_must_be_complete : Note<"span-like type must be complete">;
3458+
def note_wrong_span_field_count : Note<"span-like type must have 2 fields">;
3459+
def note_wrong_span_field_types
3460+
: Note<"span-like type must have a pointer and an integer field or two "
3461+
"pointer fields">;
3462+
def note_span_invalid_integer : Note<"the integer field must be an actual "
3463+
"integer that is at least as big as int">;
34573464
def warn_attribute_return_pointers_refs_only : Warning<
34583465
"%0 attribute only applies to return values that are pointers or references">,
34593466
InGroup<IgnoredAttributes>;

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1839,43 +1839,72 @@ static void handleRestrictAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
18391839
RestrictAttr(S.Context, AL, DeallocE, DeallocPtrIdx));
18401840
}
18411841

1842-
static bool checkSpanLikeType(const QualType &Ty) {
1842+
static bool checkSpanLikeType(Sema &S, const ParsedAttr &AL,
1843+
const QualType &Ty) {
18431844
// Check that the type is a plain record with one field being a pointer
18441845
// type and the other field being an integer. This matches the common
18451846
// implementation of std::span or sized_allocation_t in P0901R11.
1846-
// Note that there may also be numerous cases of pointer+integer structures
1847-
// not actually exhibiting a span-like semantics, so sometimes
1848-
// this heuristic expectedly leads to false positive results.
1847+
// Note that there may also be numerous cases of pointer + integer /
1848+
// pointer + pointer / integer + pointer structures not actually exhibiting
1849+
// a span-like semantics, so sometimes these heuristics expectedly
1850+
// lead to false positive results.
1851+
auto emitWarning = [&S, &AL](unsigned NoteDiagID) {
1852+
S.Diag(AL.getLoc(), diag::warn_attribute_return_span_only) << AL;
1853+
S.Diag(AL.getLoc(), NoteDiagID);
1854+
};
18491855
const RecordDecl *RD = Ty->getAsRecordDecl();
1850-
if (!RD || RD->isUnion())
1856+
if (!RD || RD->isUnion()) {
1857+
emitWarning(diag::note_span_must_be_struct);
18511858
return false;
1859+
}
18521860
const RecordDecl *Def = RD->getDefinition();
1853-
if (!Def)
1861+
if (!Def) {
1862+
emitWarning(diag::note_span_must_be_complete);
18541863
return false; // This is an incomplete type.
1864+
}
18551865
auto FieldsBegin = Def->field_begin();
1856-
if (std::distance(FieldsBegin, Def->field_end()) != 2)
1866+
if (std::distance(FieldsBegin, Def->field_end()) != 2) {
1867+
emitWarning(diag::note_wrong_span_field_count);
18571868
return false;
1869+
}
18581870
const QualType FirstFieldType = FieldsBegin->getType();
18591871
const QualType SecondFieldType = std::next(FieldsBegin)->getType();
18601872
auto validatePointerType = [](const QualType &T) {
18611873
// It must not point to functions.
18621874
return T->isPointerType() && !T->isFunctionPointerType();
18631875
};
1864-
// Verify two possible orderings.
1865-
return (validatePointerType(FirstFieldType) &&
1866-
SecondFieldType->isIntegerType()) ||
1867-
(FirstFieldType->isIntegerType() &&
1868-
validatePointerType(SecondFieldType));
1876+
auto checkIntegerType = [&S, emitWarning](const QualType &T) {
1877+
bool valid = false;
1878+
// Must be an actual integer and at least as bit as int.
1879+
if (const auto *BT = dyn_cast<BuiltinType>(T.getCanonicalType())) {
1880+
const auto IntSize = S.Context.getTypeSize(S.Context.IntTy);
1881+
valid = BT->isInteger() && S.Context.getTypeSize(BT) >= IntSize;
1882+
}
1883+
if (!valid) {
1884+
emitWarning(diag::note_span_invalid_integer);
1885+
}
1886+
return valid;
1887+
};
1888+
if (validatePointerType(FirstFieldType) &&
1889+
validatePointerType(SecondFieldType)) {
1890+
// Pointer + pointer.
1891+
return true;
1892+
} else if (validatePointerType(FirstFieldType)) {
1893+
// Pointer + integer?
1894+
return checkIntegerType(SecondFieldType);
1895+
} else if (validatePointerType(SecondFieldType)) {
1896+
// Integer + pointer?
1897+
return checkIntegerType(FirstFieldType);
1898+
}
1899+
emitWarning(diag::note_wrong_span_field_types);
1900+
return false;
18691901
}
18701902

18711903
static void handleMallocSpanAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
18721904
QualType ResultType = getFunctionOrMethodResultType(D);
1873-
if (!checkSpanLikeType(ResultType)) {
1874-
S.Diag(AL.getLoc(), diag::warn_attribute_return_span_only)
1875-
<< AL << getFunctionOrMethodResultSourceRange(D);
1876-
return;
1905+
if (checkSpanLikeType(S, AL, ResultType)) {
1906+
D->addAttr(::new (S.Context) MallocSpanAttr(S.Context, AL));
18771907
}
1878-
D->addAttr(::new (S.Context) MallocSpanAttr(S.Context, AL));
18791908
}
18801909

18811910
static void handleCPUSpecificAttr(Sema &S, Decl *D, const ParsedAttr &AL) {

clang/test/Sema/attr-malloc_span.c

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,57 @@ typedef struct {
1616
} span2;
1717
span2 returns_span2 (void) __attribute((malloc_span)); // no-warning
1818

19-
// Ensure that a warning is produced on malloc_span precondition violation.
2019
typedef struct {
2120
void *ptr;
2221
void *ptr2;
23-
} invalid_span1;
24-
invalid_span1 returns_non_std_span1 (void) __attribute((malloc_span)); // expected-warning {{attribute only applies to functions that return span-like structures}}
22+
} span3;
23+
span3 returns_span3 (void) __attribute((malloc_span)); // no-warning
24+
// expected-warning@+2 {{attribute only applies to functions that return span-like structures}}
25+
// expected-note@+1 {{span-like type must be a struct}}
26+
int *returns_int_ptr (void) __attribute((malloc_span));
2527

2628
typedef struct {
2729
void *ptr;
2830
size_t n;
2931
size_t n2;
30-
} invalid_span2;
31-
invalid_span2 returns_non_std_span2 (void) __attribute((malloc_span)); // expected-warning {{attribute only applies to functions that return span-like structures}}
32+
} too_long_span;
33+
// expected-warning@+2 {{attribute only applies to functions that return span-like structures}}
34+
// expected-note@+1 {{span-like type must have 2 fields}}
35+
too_long_span returns_too_long_span (void) __attribute((malloc_span));
3236

3337
// Function pointers are not allowed.
3438
typedef struct {
3539
int (*func_ptr)(void);
3640
size_t n;
3741
} func_span;
38-
func_span returns_func_span (void) __attribute((malloc_span)); // expected-warning {{attribute only applies to functions that return span-like structures}}
42+
// expected-warning@+2 {{attribute only applies to functions that return span-like structures}}
43+
// expected-note@+1 {{span-like type must have a pointer and an integer field or two pointer fields}}
44+
func_span returns_func_span (void) __attribute((malloc_span));
45+
46+
// Integer should not be an enum.
47+
enum some_enum { some_value, other_value };
48+
typedef struct {
49+
void *ptr;
50+
enum some_enum field;
51+
} enum_span;
52+
// expected-warning@+2 {{attribute only applies to functions that return span-like structures}}
53+
// expected-note@+1 {{the integer field must be an actual integer}}
54+
enum_span returns_enum_span (void) __attribute((malloc_span));
55+
56+
// Bit integers are also not supported.
57+
typedef struct {
58+
void *ptr;
59+
_BitInt(16) n;
60+
} bit_span;
61+
// expected-warning@+2 {{attribute only applies to functions that return span-like structures}}
62+
// expected-note@+1 {{the integer field must be an actual integer}}
63+
bit_span returns_bit_span (void) __attribute((malloc_span));
64+
65+
// Integer must be at least as big as int.
66+
typedef struct {
67+
void *ptr;
68+
short n;
69+
} short_span;
70+
// expected-warning@+2 {{attribute only applies to functions that return span-like structures}}
71+
// expected-note@+1 {{the integer field must be an actual integer}}
72+
short_span returns_short_span (void) __attribute((malloc_span));

clang/test/SemaCXX/attr-malloc_span.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ struct DataMemberSpan {
1111
int n;
1212
};
1313

14-
DataMemberSpan returns_data_member_span(void) __attribute((malloc_span)) { // expected-warning {{attribute only applies to functions that return span-like structures}}
14+
// expected-warning@+2 {{attribute only applies to functions that return span-like structures}}
15+
// expected-note@+1 {{span-like type must have a pointer and an integer field or two pointer fields}}
16+
DataMemberSpan returns_data_member_span(void) __attribute((malloc_span)) {
1517
return DataMemberSpan{};
1618
}
1719

@@ -21,7 +23,9 @@ struct MemberFuncSpan {
2123
int n;
2224
};
2325

24-
MemberFuncSpan returns_member_func_span(void) __attribute((malloc_span)) { // expected-warning {{attribute only applies to functions that return span-like structures}}
26+
// expected-warning@+2 {{attribute only applies to functions that return span-like structures}}
27+
// expected-note@+1 {{span-like type must have a pointer and an integer field or two pointer fields}}
28+
MemberFuncSpan returns_member_func_span(void) __attribute((malloc_span)) {
2529
return MemberFuncSpan{};
2630
}
2731

0 commit comments

Comments
 (0)