Skip to content

Commit 8522c58

Browse files
committed
add tests for template containers and implement capture by global/unknown
1 parent 2cef37e commit 8522c58

File tree

5 files changed

+155
-38
lines changed

5 files changed

+155
-38
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10231,7 +10231,7 @@ def warn_dangling_pointer_assignment : Warning<
1023110231
"will be destroyed at the end of the full-expression">,
1023210232
InGroup<DanglingAssignment>;
1023310233
def warn_dangling_reference_captured : Warning<
10234-
"object captured by '%0' will be destroyed at the end of the full-expression">,
10234+
"object whose reference is captured%select{| by '%1'}0 will be destroyed at the end of the full-expression">,
1023510235
InGroup<DanglingCapture>;
1023610236

1023710237
// For non-floating point, expressions of the form x == x or x != x

clang/lib/Sema/CheckExprLifetime.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,8 +1214,12 @@ checkExprLifetimeImpl(Sema &SemaRef, const InitializedEntity *InitEntity,
12141214
assert(shouldLifetimeExtendThroughPath(Path) ==
12151215
PathLifetimeKind::NoExtend &&
12161216
"No lifetime extension in function calls");
1217-
SemaRef.Diag(DiagLoc, diag::warn_dangling_reference_captured)
1218-
<< CapEntity->Entity << DiagRange;
1217+
if (CapEntity->Entity != nullptr)
1218+
SemaRef.Diag(DiagLoc, diag::warn_dangling_reference_captured)
1219+
<< true << CapEntity->Entity << DiagRange;
1220+
else
1221+
SemaRef.Diag(DiagLoc, diag::warn_dangling_reference_captured)
1222+
<< false << "<ignore>" << DiagRange;
12191223
return false;
12201224
}
12211225

clang/lib/Sema/CheckExprLifetime.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@ struct AssignedEntity {
2626
};
2727

2828
struct CapturingEntity {
29-
// In an function call involving a lifetime capture, this would be the
30-
// argument capturing the lifetime of another argument.
29+
// In an function call involving a lifetime capture, this would be the
30+
// argument capturing the lifetime of another argument.
3131
// void addToSet(std::string_view sv [[clang::lifetime_capture_by(setsv)]],
3232
// set<std::string_view>& setsv);
3333
// set<std::string_view> setsv;
3434
// addToSet(std::string(), setsv); // Here 'setsv' is the 'Entity'.
35+
//
36+
// This is 'nullptr' when the capturing entity is 'global' or 'unknown'.
3537
Expr *Entity = nullptr;
3638
};
3739

clang/lib/Sema/SemaChecking.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "clang/AST/ASTContext.h"
1717
#include "clang/AST/Attr.h"
1818
#include "clang/AST/AttrIterator.h"
19+
#include "clang/AST/Attrs.inc"
1920
#include "clang/AST/CharUnits.h"
2021
#include "clang/AST/Decl.h"
2122
#include "clang/AST/DeclBase.h"
@@ -3233,7 +3234,10 @@ void Sema::CheckArgAlignment(SourceLocation Loc, NamedDecl *FDecl,
32333234
void Sema::checkLifetimeCaptureBy(FunctionDecl *FD, bool IsMemberFunction,
32343235
const Expr *ThisArg,
32353236
ArrayRef<const Expr *> Args) {
3236-
auto GetArgAt = [&](int Idx) {
3237+
auto GetArgAt = [&](int Idx) -> Expr * {
3238+
if (Idx == LifetimeCaptureByAttr::GLOBAL ||
3239+
Idx == LifetimeCaptureByAttr::UNKNOWN)
3240+
return nullptr;
32373241
if (IsMemberFunction && Idx == 0)
32383242
return const_cast<Expr *>(ThisArg);
32393243
return const_cast<Expr *>(Args[Idx - int(IsMemberFunction)]);

clang/test/Sema/warn-lifetime-analysis-nocfg.cpp

Lines changed: 139 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 -fsyntax-only -Wdangling -Wdangling-field -Wreturn-stack-address -verify %s
1+
// RUN: %clang_cc1 --std=c++20 -fsyntax-only -Wdangling -Wdangling-field -Wreturn-stack-address -verify %s
22
struct [[gsl::Owner(int)]] MyIntOwner {
33
MyIntOwner();
44
int &operator*();
@@ -252,6 +252,19 @@ struct reference_wrapper {
252252

253253
template<typename T>
254254
reference_wrapper<T> ref(T& t) noexcept;
255+
256+
struct false_type {
257+
static constexpr bool value = false;
258+
constexpr operator bool() const noexcept { return value; }
259+
};
260+
struct true_type {
261+
static constexpr bool value = true;
262+
constexpr operator bool() const noexcept { return value; }
263+
};
264+
265+
template<class T> struct is_pointer : false_type {};
266+
template<class T> struct is_pointer<T*> : true_type {};
267+
template<class T> struct is_pointer<T* const> : true_type {};
255268
}
256269

257270
struct Unannotated {
@@ -806,95 +819,189 @@ struct S {
806819
void captureInt(const int&x [[clang::lifetime_capture_by(s)]], S&s);
807820
void captureRValInt(int&&x [[clang::lifetime_capture_by(s)]], S&s);
808821
void noCaptureInt(int x [[clang::lifetime_capture_by(s)]], S&s);
822+
809823
std::string_view substr(const std::string& s [[clang::lifetimebound]]);
810824
std::string_view strcopy(const std::string& s);
825+
811826
void captureSV(std::string_view x [[clang::lifetime_capture_by(s)]], S&s);
812827
void captureRValSV(std::string_view&& x [[clang::lifetime_capture_by(s)]], S&s);
813828
void noCaptureSV(std::string_view x, S&s);
814829
void captureS(const std::string& x [[clang::lifetime_capture_by(s)]], S&s);
815830
void captureRValS(std::string&& x [[clang::lifetime_capture_by(s)]], S&s);
831+
832+
const std::string& getLB(const std::string& s[[clang::lifetimebound]]);
833+
const std::string& getLB(std::string_view sv[[clang::lifetimebound]]);
816834
const std::string* getPointerLB(const std::string& s[[clang::lifetimebound]]);
817835
const std::string* getPointerNoLB(const std::string& s);
836+
818837
void capturePointer(const std::string* x [[clang::lifetime_capture_by(s)]], S&s);
838+
819839
struct ThisIsCaptured {
820840
void capture(S& s) [[clang::lifetime_capture_by(s)]];
821841
void bar(S& s) [[clang::lifetime_capture_by(abcd)]]; // expected-error {{'lifetime_capture_by' attribute argument 'abcd' is not a known function parameter}}
822842
void baz(S& s) [[clang::lifetime_capture_by(this)]]; // expected-error {{'lifetime_capture_by' argument references itself}}
823843
};
844+
845+
void captureByGlobal(std::string_view s [[clang::lifetime_capture_by(global)]]);
846+
void captureByUnknown(std::string_view s [[clang::lifetime_capture_by(unknown)]]);
847+
824848
void use() {
825849
std::string_view local_sv;
826850
std::string local_s;
827851
S s;
828852
// Capture an 'int'.
829853
int local;
830-
captureInt(1, // expected-warning {{object captured by 's' will be destroyed at the end of the full-expression}}
854+
captureInt(1, // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}}
831855
s);
832-
captureRValInt(1, s); // expected-warning {{object captured by 's'}}
856+
captureRValInt(1, s); // expected-warning {{object whose reference is captured by 's'}}
833857
captureInt(local, s);
834858
noCaptureInt(1, s);
835859
noCaptureInt(local, s);
836860

837-
// Capture lifetimebound pointer.
838-
capturePointer(getPointerLB(std::string()), s); // expected-warning {{object captured by 's'}}
839-
capturePointer(getPointerLB(*getPointerLB(std::string())), s); // expected-warning {{object captured by 's'}}
840-
capturePointer(getPointerNoLB(std::string()), s);
841-
842861
// Capture using std::string_view.
843862
captureSV(local_sv, s);
844-
captureSV(std::string(), // expected-warning {{object captured by 's'}}
863+
captureSV(std::string(), // expected-warning {{object whose reference is captured by 's'}}
845864
s);
846865
captureSV(substr(
847-
std::string() // expected-warning {{object captured by 's'}}
866+
std::string() // expected-warning {{object whose reference is captured by 's'}}
848867
), s);
849868
captureSV(substr(local_s), s);
850869
captureSV(strcopy(std::string()), s);
851870
captureRValSV(std::move(local_sv), s);
852-
captureRValSV(std::string(), s); // expected-warning {{object captured by 's'}}
871+
captureRValSV(std::string(), s); // expected-warning {{object whose reference is captured by 's'}}
853872
captureRValSV(std::string_view{"abcd"}, s);
854873
captureRValSV(substr(local_s), s);
855-
captureRValSV(substr(std::string()), s); // expected-warning {{object captured by 's'}}
874+
captureRValSV(substr(std::string()), s); // expected-warning {{object whose reference is captured by 's'}}
856875
captureRValSV(strcopy(std::string()), s);
857876
noCaptureSV(local_sv, s);
858877
noCaptureSV(std::string(), s);
859878
noCaptureSV(substr(std::string()), s);
860879

861880
// Capture using std::string.
862-
captureS(std::string(), s); // expected-warning {{object captured by 's'}}
881+
captureS(std::string(), s); // expected-warning {{object whose reference is captured by 's'}}
863882
captureS(local_s, s);
864883
captureRValS(std::move(local_s), s);
865-
captureRValS(std::string(), s); // expected-warning {{object captured by 's'}}
884+
captureRValS(std::string(), s); // expected-warning {{object whose reference is captured by 's'}}
885+
886+
// Capture with lifetimebound.
887+
captureSV(getLB(std::string()), s); // expected-warning {{object whose reference is captured by 's'}}
888+
captureSV(getLB(substr(std::string())), s); // expected-warning {{object whose reference is captured by 's'}}
889+
captureSV(getLB(getLB(
890+
std::string() // expected-warning {{object whose reference is captured by 's'}}
891+
)), s);
892+
capturePointer(getPointerLB(std::string()), s); // expected-warning {{object whose reference is captured by 's'}}
893+
capturePointer(getPointerLB(*getPointerLB(
894+
std::string() // expected-warning {{object whose reference is captured by 's'}}
895+
)), s);
896+
capturePointer(getPointerNoLB(std::string()), s);
866897

867898
// Member functions.
868-
s.captureInt(1); // expected-warning {{object captured by 's'}}
869-
s.captureSV(std::string()); // expected-warning {{object captured by 's'}}
870-
s.captureSV(substr(std::string())); // expected-warning {{object captured by 's'}}
899+
s.captureInt(1); // expected-warning {{object whose reference is captured by 's'}}
900+
s.captureSV(std::string()); // expected-warning {{object whose reference is captured by 's'}}
901+
s.captureSV(substr(std::string())); // expected-warning {{object whose reference is captured by 's'}}
871902
s.captureSV(strcopy(std::string()));
872903

873904
// 'this' is captured.
874-
ThisIsCaptured{}.capture(s); // expected-warning {{object captured by 's'}}
905+
ThisIsCaptured{}.capture(s); // expected-warning {{object whose reference is captured by 's'}}
875906
ThisIsCaptured TIS;
876907
TIS.capture(s);
908+
909+
// capture by global.
910+
captureByGlobal(std::string()); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}}
911+
captureByGlobal(substr(std::string())); // expected-warning {{captured}}
912+
captureByGlobal(local_s);
913+
captureByGlobal(local_sv);
914+
915+
// // capture by unknown.
916+
captureByGlobal(std::string()); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}}
917+
captureByGlobal(substr(std::string())); // expected-warning {{captured}}
918+
captureByGlobal(local_s);
919+
captureByGlobal(local_sv);
877920
}
878-
class [[gsl::Pointer()]] my_string_view : public std::string_view {};
879-
class my_string_view_not_pointer : public std::string_view {};
880-
std::optional<std::string_view> getOptionalSV();
881-
std::optional<std::string> getOptionalS();
882-
std::optional<my_string_view> getOptionalMySV();
883-
std::optional<my_string_view_not_pointer> getOptionalMySVNotP();
884-
my_string_view getMySV();
885-
my_string_view_not_pointer getMySVNotP();
886921

922+
template<typename T> struct IsPointerLikeTypeImpl : std::false_type {};
923+
template<> struct IsPointerLikeTypeImpl<std::string_view> : std::true_type {};
924+
template<typename T> concept IsPointerLikeType = std::is_pointer<T>::value || IsPointerLikeTypeImpl<T>::value;
925+
926+
// Templated containers having no distinction between pointer-like and other element type.
887927
template<class T>
888928
struct MySet {
889-
void insert(T&& t [[clang::lifetime_capture_by(this)]]);
890-
void insert(const T& t [[clang::lifetime_capture_by(this)]]);
929+
void insert(T&& t [[clang::lifetime_capture_by(this)]]);
930+
void insert(const T& t [[clang::lifetime_capture_by(this)]]);
891931
};
892932
void user_defined_containers() {
893933
MySet<int> set_of_int;
894-
set_of_int.insert(1); // expected-warning {{object captured by 'set_of_int' will be destroyed}}
934+
set_of_int.insert(1); // expected-warning {{object whose reference is captured by 'set_of_int' will be destroyed}}
895935
MySet<std::string_view> set_of_sv;
896-
set_of_sv.insert(std::string()); // expected-warning {{object captured by 'set_of_sv' will be destroyed}}
936+
set_of_sv.insert(std::string()); // expected-warning {{object whose reference is captured by 'set_of_sv' will be destroyed}}
937+
}
938+
939+
// Templated containers having **which distinguishes** between pointer-like and other element type.
940+
template<class T>
941+
struct MyVector {
942+
void push_back(T&& t [[clang::lifetime_capture_by(this)]]) requires IsPointerLikeType<T>;
943+
void push_back(const T& t [[clang::lifetime_capture_by(this)]]) requires IsPointerLikeType<T>;
944+
945+
void push_back(T&& t) requires (!IsPointerLikeType<T>);
946+
void push_back(const T& t) requires (!IsPointerLikeType<T>);
947+
};
948+
949+
// Container of pointers.
950+
struct [[gsl::Pointer()]] MyStringView : public std::string_view {
951+
MyStringView();
952+
MyStringView(std::string_view&&);
953+
MyStringView(const MyStringView&);
954+
MyStringView(const std::string&);
955+
};
956+
template<> struct IsPointerLikeTypeImpl<MyStringView> : std::true_type {};
957+
958+
std::optional<std::string_view> getOptionalSV();
959+
std::optional<std::string> getOptionalS();
960+
std::optional<MyStringView> getOptionalMySV();
961+
MyStringView getMySV();
962+
963+
class MyStringViewNotPointer : public std::string_view {};
964+
std::optional<MyStringViewNotPointer> getOptionalMySVNotP();
965+
MyStringViewNotPointer getMySVNotP();
966+
967+
void container_of_pointers() {
968+
std::string local;
969+
MyVector<std::string> vs;
970+
vs.push_back(std::string()); // Ok.
971+
972+
MyVector<std::string_view> vsv;
973+
vsv.push_back(std::string()); // expected-warning {{object whose reference is captured by 'vsv'}}
974+
vsv.push_back(substr(std::string())); // expected-warning {{object whose reference is captured by 'vsv'}}
975+
976+
MyVector<const std::string*> vp;
977+
vp.push_back(getPointerLB(std::string())); // expected-warning {{object whose reference is captured by 'vp'}}
978+
vp.push_back(getPointerLB(*getPointerLB(std::string()))); // expected-warning {{object whose reference is captured by 'vp'}}
979+
vp.push_back(getPointerLB(local));
980+
vp.push_back(getPointerNoLB(std::string()));
981+
982+
// User-defined [[gsl::Pointer]]
983+
vsv.push_back(getMySV());
984+
vsv.push_back(getMySVNotP());
985+
986+
// Vector of user defined gsl::Pointer.
987+
MyVector<MyStringView> vmysv;
988+
vmysv.push_back(getMySV());
989+
vmysv.push_back(MyStringView{});
990+
vmysv.push_back(std::string_view{});
991+
vmysv.push_back(std::string{}); // expected-warning {{object whose reference is captured by 'vmysv'}}
992+
vmysv.push_back(substr(std::string{})); // expected-warning {{object whose reference is captured by 'vmysv'}}
993+
vmysv.push_back(getLB(substr(std::string{}))); // expected-warning {{object whose reference is captured by 'vmysv'}}
994+
vmysv.push_back(strcopy(getLB(substr(std::string{}))));
995+
996+
// With std::optional container.
997+
std::optional<std::string_view> optional;
998+
vsv.push_back(optional.value());
999+
vsv.push_back(getOptionalS().value()); // expected-warning {{object whose reference is captured by 'vsv'}}
1000+
vsv.push_back(getOptionalSV().value());
1001+
vsv.push_back(getOptionalMySV().value());
1002+
1003+
// (maybe) FIXME: We may choose to diagnose the following case.
1004+
// This happens because 'MyStringViewNotPointer' is not marked as a [[gsl::Pointer]] but is derived from one.
1005+
vsv.push_back(getOptionalMySVNotP().value()); // expected-warning {{object whose reference is captured by 'vsv'}}
8971006
}
8981007
} // namespace lifetime_capture_by
899-
// Test for templated code.
900-
// 2 nested function calls foo(sv, bar(sv, setsv));

0 commit comments

Comments
 (0)