Skip to content

Commit d8708e0

Browse files
committed
[experimental] Support templates
Support returned dependent types and class tempate instantiations.
1 parent 2faecf2 commit d8708e0

File tree

4 files changed

+87
-13
lines changed

4 files changed

+87
-13
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5105,6 +5105,11 @@ class Sema final : public SemaBase {
51055105
/// Essentially, this just moves them to the current pool.
51065106
void redelayDiagnostics(sema::DelayedDiagnosticPool &pool);
51075107

5108+
/// Check that the type is a plain record with one field being a pointer
5109+
/// type and the other field being an integer. This matches the common
5110+
/// implementation of std::span or sized_allocation_t in P0901R11.
5111+
bool CheckSpanLikeType(const AttributeCommonInfo &CI, const QualType &Ty);
5112+
51085113
/// Check if IdxExpr is a valid parameter index for a function or
51095114
/// instance method D. May output an error.
51105115
///

clang/lib/Sema/SemaDeclAttr.cpp

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

1842-
static bool checkSpanLikeType(Sema &S, const ParsedAttr &AL,
1843-
const QualType &Ty) {
1844-
// Check that the type is a plain record with one field being a pointer
1845-
// type and the other field being an integer. This matches the common
1846-
// implementation of std::span or sized_allocation_t in P0901R11.
1842+
bool Sema::CheckSpanLikeType(const AttributeCommonInfo &CI,
1843+
const QualType &Ty) {
18471844
// Note that there may also be numerous cases of pointer + integer /
18481845
// pointer + pointer / integer + pointer structures not actually exhibiting
18491846
// a span-like semantics, so sometimes these heuristics expectedly
18501847
// 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-
return S.Diag(AL.getLoc(), NoteDiagID);
1848+
auto emitWarning = [this, &CI](unsigned NoteDiagID) {
1849+
Diag(CI.getLoc(), diag::warn_attribute_return_span_only) << CI;
1850+
return Diag(CI.getLoc(), NoteDiagID);
18541851
};
1852+
if (!Ty->isDependentType()) {
1853+
// If the type is a class template specialization, it may not be
1854+
// instantiated at this stage. We must force it to be complete to examine
1855+
// its fields.
1856+
// The returned value is discarded since the code below emits a warning
1857+
// if the type keeps being incomplete.
1858+
(void)isCompleteType(CI.getLoc(), Ty);
1859+
}
18551860
if (Ty->isIncompleteType())
18561861
return emitWarning(diag::note_returned_incomplete_type);
18571862
const RecordDecl *RD = Ty->getAsRecordDecl();
@@ -1867,13 +1872,13 @@ static bool checkSpanLikeType(Sema &S, const ParsedAttr &AL,
18671872
// It must not point to functions.
18681873
return T->isPointerType() && !T->isFunctionPointerType();
18691874
};
1870-
auto checkIntegerType = [&S, emitWarning](const QualType &T,
1871-
const int FieldNo) -> bool {
1875+
auto checkIntegerType = [this, emitWarning](const QualType &T,
1876+
const int FieldNo) -> bool {
18721877
const auto *BT = dyn_cast<BuiltinType>(T.getCanonicalType());
18731878
if (!BT || !BT->isInteger())
18741879
return emitWarning(diag::note_returned_not_integer_field) << FieldNo;
1875-
const auto IntSize = S.Context.getTypeSize(S.Context.IntTy);
1876-
if (S.Context.getTypeSize(BT) < IntSize)
1880+
const auto IntSize = Context.getTypeSize(Context.IntTy);
1881+
if (Context.getTypeSize(BT) < IntSize)
18771882
return emitWarning(diag::note_returned_not_wide_enough_field)
18781883
<< FieldNo << IntSize;
18791884
return false;
@@ -1894,7 +1899,9 @@ static bool checkSpanLikeType(Sema &S, const ParsedAttr &AL,
18941899

18951900
static void handleMallocSpanAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
18961901
QualType ResultType = getFunctionOrMethodResultType(D);
1897-
if (!checkSpanLikeType(S, AL, ResultType)) {
1902+
if (ResultType->isDependentType() || !S.CheckSpanLikeType(AL, ResultType)) {
1903+
// If it's a dependent type, the attribute will be re-checked upon
1904+
// instantiation.
18981905
D->addAttr(::new (S.Context) MallocSpanAttr(S.Context, AL));
18991906
}
19001907
}

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,20 @@ static void instantiateDependentHLSLParamModifierAttr(
796796
"out or inout parameter type must be a reference and restrict qualified");
797797
}
798798

799+
static void instantiateDependentMallocSpanAttr(Sema &S,
800+
const MallocSpanAttr *Attr,
801+
Decl *New) {
802+
QualType RT = getFunctionOrMethodResultType(New);
803+
if (RT->isDependentType()) {
804+
// The type is still dependent.
805+
// Clone the attribute, it will be checked later.
806+
New->addAttr(Attr->clone(S.getASTContext()));
807+
} else if (!S.CheckSpanLikeType(*Attr, RT)) {
808+
// The conditions have been successfully validated.
809+
New->addAttr(Attr->clone(S.getASTContext()));
810+
}
811+
}
812+
799813
void Sema::InstantiateAttrsForDecl(
800814
const MultiLevelTemplateArgumentList &TemplateArgs, const Decl *Tmpl,
801815
Decl *New, LateInstantiatedAttrVec *LateAttrs,
@@ -1007,6 +1021,11 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
10071021
continue;
10081022
}
10091023

1024+
if (auto *A = dyn_cast<MallocSpanAttr>(TmplAttr)) {
1025+
instantiateDependentMallocSpanAttr(*this, A, New);
1026+
continue;
1027+
}
1028+
10101029
assert(!TmplAttr->isPackExpansion());
10111030
if (TmplAttr->isLateParsed() && LateAttrs) {
10121031
// Late parsed attributes must be instantiated and attached after the

clang/test/SemaCXX/attr-malloc_span.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,46 @@ MemberFuncSpan returns_member_func_span(void) __attribute((malloc_span)) {
2929
return MemberFuncSpan{};
3030
}
3131

32+
template<typename FirstType, typename SecondType>
33+
struct Pair {
34+
FirstType first;
35+
SecondType second;
36+
};
37+
38+
Pair<int*, int> returns_templated_span1(void) __attribute((malloc_span)) { // no-warning
39+
return Pair<int*, int>{};
40+
}
41+
42+
Pair<int*, int*> returns_templated_span2(void) __attribute((malloc_span)) { // no-warning
43+
return Pair<int*, int*>{};
44+
}
45+
46+
// expected-warning@+2 {{attribute only applies to functions that return span-like structures}}
47+
// expected-note@+1 {{returned struct/class fields are not a supported combination for a span-like type}}
48+
Pair<int, int> returns_templated_span3(void) __attribute((malloc_span)) {
49+
return Pair<int, int>{};
50+
}
51+
52+
// Verify that semantic checks are done on dependent types.
53+
54+
struct GoodSpan {
55+
void *ptr;
56+
int n;
57+
};
58+
59+
struct BadSpan {
60+
int n;
61+
};
62+
63+
template <typename T>
64+
// expected-warning@+2 {{'malloc_span' attribute only applies to functions that return span-like structures}}
65+
// expected-note@+1 {{returned struct/class has 1 fields, expected 2}}
66+
T produce_span() __attribute((malloc_span)) {
67+
return T{};
68+
}
69+
70+
void TestGoodBadSpan() {
71+
produce_span<GoodSpan>(); // no-warnings
72+
// expected-note@+1 {{in instantiation of function template specialization 'produce_span<BadSpan>' requested here}}
73+
produce_span<BadSpan>();
74+
}

0 commit comments

Comments
 (0)