Skip to content

Commit babf109

Browse files
committed
[clang] Add lifetimebound attr to std::span/std::string_view constructor
With this patch, clang now automatically adds ``[[clang::lifetimebound]]`` to the parameters of ``std::span, std::string_view`` constructors, this enables Clang to capture more cases where the returned reference outlives the object.
1 parent 3c5509d commit babf109

File tree

5 files changed

+83
-19
lines changed

5 files changed

+83
-19
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,11 @@ Attribute Changes in Clang
151151
- The ``hybrid_patchable`` attribute is now supported on ARM64EC targets. It can be used to specify
152152
that a function requires an additional x86-64 thunk, which may be patched at runtime.
153153

154+
- Clang now automatically adds ``[[clang::lifetimebound]]`` to the parameters of
155+
``std::span, std::string_view`` constructors, this enables Clang to capture
156+
more cases where the returned reference outlives the object.
157+
(#GH100567)
158+
154159
Improvements to Clang's diagnostics
155160
-----------------------------------
156161

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1827,6 +1827,9 @@ class Sema final : public SemaBase {
18271827
/// Add [[gsl::Owner]] and [[gsl::Pointer]] attributes for std:: types.
18281828
void inferGslOwnerPointerAttribute(CXXRecordDecl *Record);
18291829

1830+
/// Add [[clang:::lifetimebound]] attr for std:: functions and methods.
1831+
void inferLifetimeBoundAttribute(FunctionDecl *FD);
1832+
18301833
/// Add [[gsl::Pointer]] attributes for std:: types.
18311834
void inferGslPointerAttribute(TypedefNameDecl *TD);
18321835

clang/lib/Sema/SemaAttr.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,59 @@ void Sema::inferGslOwnerPointerAttribute(CXXRecordDecl *Record) {
216216
inferGslPointerAttribute(Record, Record);
217217
}
218218

219+
void Sema::inferLifetimeBoundAttribute(FunctionDecl *FD) {
220+
if (FD->getNumParams() == 0)
221+
return;
222+
223+
if (unsigned BuiltinID = FD->getBuiltinID()) {
224+
// Add lifetime attribute to std::move, std::fowrard et al.
225+
switch (BuiltinID) {
226+
case Builtin::BIaddressof:
227+
case Builtin::BI__addressof:
228+
case Builtin::BI__builtin_addressof:
229+
case Builtin::BIas_const:
230+
case Builtin::BIforward:
231+
case Builtin::BIforward_like:
232+
case Builtin::BImove:
233+
case Builtin::BImove_if_noexcept:
234+
if (ParmVarDecl *P = FD->getParamDecl(0u);
235+
!P->hasAttr<LifetimeBoundAttr>())
236+
P->addAttr(
237+
LifetimeBoundAttr::CreateImplicit(Context, FD->getLocation()));
238+
break;
239+
default:
240+
break;
241+
}
242+
return;
243+
}
244+
if (auto *CMD = dyn_cast<CXXMethodDecl>(FD)) {
245+
const auto *CRD = CMD->getParent();
246+
if (!CRD->isInStdNamespace() || !CRD->getIdentifier())
247+
return;
248+
249+
if (isa<CXXConstructorDecl>(CMD)) {
250+
auto *Param = CMD->getParamDecl(0);
251+
if (Param->hasAttr<LifetimeBoundAttr>())
252+
return;
253+
if (CRD->getName() == "basic_string_view" &&
254+
Param->getType()->isPointerType()) {
255+
// construct from a char array pointed by a pointer.
256+
// basic_string_view(const CharT* s);
257+
// basic_string_view(const CharT* s, size_type count);
258+
Param->addAttr(
259+
LifetimeBoundAttr::CreateImplicit(Context, FD->getLocation()));
260+
} else if (CRD->getName() == "span") {
261+
// construct from a reference of array.
262+
// span(std::type_identity_t<element_type> (&arr)[N]);
263+
const auto *LRT = Param->getType()->getAs<LValueReferenceType>();
264+
if (LRT && LRT->getPointeeType().IgnoreParens()->isArrayType())
265+
Param->addAttr(
266+
LifetimeBoundAttr::CreateImplicit(Context, FD->getLocation()));
267+
}
268+
}
269+
}
270+
}
271+
219272
void Sema::inferNullableClassAttribute(CXXRecordDecl *CRD) {
220273
static const llvm::StringSet<> Nullable{
221274
"auto_ptr", "shared_ptr", "unique_ptr", "exception_ptr",

clang/lib/Sema/SemaDecl.cpp

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16593,27 +16593,9 @@ void Sema::AddKnownFunctionAttributes(FunctionDecl *FD) {
1659316593
default:
1659416594
break;
1659516595
}
16596-
16597-
// Add lifetime attribute to std::move, std::fowrard et al.
16598-
switch (BuiltinID) {
16599-
case Builtin::BIaddressof:
16600-
case Builtin::BI__addressof:
16601-
case Builtin::BI__builtin_addressof:
16602-
case Builtin::BIas_const:
16603-
case Builtin::BIforward:
16604-
case Builtin::BIforward_like:
16605-
case Builtin::BImove:
16606-
case Builtin::BImove_if_noexcept:
16607-
if (ParmVarDecl *P = FD->getParamDecl(0u);
16608-
!P->hasAttr<LifetimeBoundAttr>())
16609-
P->addAttr(
16610-
LifetimeBoundAttr::CreateImplicit(Context, FD->getLocation()));
16611-
break;
16612-
default:
16613-
break;
16614-
}
1661516596
}
1661616597

16598+
inferLifetimeBoundAttribute(FD);
1661716599
AddKnownFunctionAttributesForReplaceableGlobalAllocationFunction(FD);
1661816600

1661916601
// If C++ exceptions are enabled but we are told extern "C" functions cannot

clang/test/SemaCXX/attr-lifetimebound.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,16 @@ template <class T> T *addressof(T &arg) {
237237
&const_cast<char &>(reinterpret_cast<const volatile char &>(arg)));
238238
}
239239

240+
template<typename T>
241+
struct basic_string_view {
242+
basic_string_view(const T *);
243+
};
244+
245+
template <class T> struct span {
246+
template<size_t _ArrayExtent>
247+
span(T (&__arr)[_ArrayExtent]) noexcept;
248+
};
249+
240250
} // namespace foo
241251
} // namespace std
242252

@@ -265,3 +275,14 @@ namespace move_forward_et_al_examples {
265275
S *AddressOfOk = std::addressof(X);
266276
} // namespace move_forward_et_al_examples
267277

278+
namespace ctor_cases {
279+
std::basic_string_view<char> test1() {
280+
char abc[10];
281+
return abc; // expected-warning {{address of stack memory associated with local variable}}
282+
}
283+
284+
std::span<int> test2() {
285+
int abc[10];
286+
return abc; // expected-warning {{address of stack memory associated with local variable}}
287+
}
288+
} // namespace ctor_cases

0 commit comments

Comments
 (0)