mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-25 04:56:06 +00:00

When a vector is instantiated with a pointer type (`T` being `const Foo*`), the inferred annotation becomes `push_back(const Foo*& value [[clang::lifetime_capture_by(this)]])`. For reference parameters, the `lifetime_capture_by` attribute treats the lifetime as referring to the referenced object -- in this case, the **pointer** itself, not the pointee object. In the `push_back`, we copy the pointer's value, which does not establish a reference to the pointer. This behavior is safe and does not capture the pointer's lifetime. The annotation should not be inferred for cases where `T` is a pointer type, as the intended semantics do not align with the annotation. Fixes #121391
475 lines
23 KiB
C++
475 lines
23 KiB
C++
// RUN: %clang_cc1 --std=c++20 -fsyntax-only -verify -Wdangling-capture %s
|
|
|
|
#include "Inputs/lifetime-analysis.h"
|
|
|
|
// ****************************************************************************
|
|
// Capture an integer
|
|
// ****************************************************************************
|
|
namespace capture_int {
|
|
struct X {} x;
|
|
void captureInt(const int &i [[clang::lifetime_capture_by(x)]], X &x);
|
|
void captureRValInt(int &&i [[clang::lifetime_capture_by(x)]], X &x);
|
|
void noCaptureInt(int i [[clang::lifetime_capture_by(x)]], X &x);
|
|
|
|
void use() {
|
|
int local;
|
|
captureInt(1, // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
|
|
x);
|
|
captureRValInt(1, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
|
|
captureInt(local, x);
|
|
noCaptureInt(1, x);
|
|
noCaptureInt(local, x);
|
|
}
|
|
} // namespace capture_int
|
|
|
|
// ****************************************************************************
|
|
// Capture std::string (gsl owner types)
|
|
// ****************************************************************************
|
|
namespace capture_string {
|
|
struct X {} x;
|
|
void captureString(const std::string &s [[clang::lifetime_capture_by(x)]], X &x);
|
|
void captureRValString(std::string &&s [[clang::lifetime_capture_by(x)]], X &x);
|
|
|
|
void use() {
|
|
std::string local_string;
|
|
captureString(std::string(), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
|
|
captureString(local_string, x);
|
|
captureRValString(std::move(local_string), x);
|
|
captureRValString(std::string(), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
|
|
}
|
|
} // namespace capture_string
|
|
|
|
// ****************************************************************************
|
|
// Capture std::string_view (gsl pointer types)
|
|
// ****************************************************************************
|
|
namespace capture_string_view {
|
|
struct X {} x;
|
|
void captureStringView(std::string_view s [[clang::lifetime_capture_by(x)]], X &x);
|
|
void captureRValStringView(std::string_view &&sv [[clang::lifetime_capture_by(x)]], X &x);
|
|
void noCaptureStringView(std::string_view sv, X &x);
|
|
|
|
std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);
|
|
std::string_view getNotLifetimeBoundView(const std::string& s);
|
|
const std::string& getLifetimeBoundString(const std::string &s [[clang::lifetimebound]]);
|
|
const std::string& getLifetimeBoundString(std::string_view sv [[clang::lifetimebound]]);
|
|
|
|
void use() {
|
|
std::string_view local_string_view;
|
|
std::string local_string;
|
|
captureStringView(local_string_view, x);
|
|
captureStringView(std::string(), // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
|
|
x);
|
|
|
|
captureStringView(getLifetimeBoundView(local_string), x);
|
|
captureStringView(getNotLifetimeBoundView(std::string()), x);
|
|
captureRValStringView(std::move(local_string_view), x);
|
|
captureRValStringView(std::string(), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
|
|
captureRValStringView(std::string_view{"abcd"}, x);
|
|
|
|
noCaptureStringView(local_string_view, x);
|
|
noCaptureStringView(std::string(), x);
|
|
|
|
// With lifetimebound functions.
|
|
captureStringView(getLifetimeBoundView(
|
|
std::string() // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
|
|
), x);
|
|
captureRValStringView(getLifetimeBoundView(local_string), x);
|
|
captureRValStringView(getLifetimeBoundView(std::string()), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
|
|
captureRValStringView(getNotLifetimeBoundView(std::string()), x);
|
|
noCaptureStringView(getLifetimeBoundView(std::string()), x);
|
|
captureStringView(getLifetimeBoundString(std::string()), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
|
|
captureStringView(getLifetimeBoundString(getLifetimeBoundView(std::string())), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
|
|
captureStringView(getLifetimeBoundString(getLifetimeBoundString(
|
|
std::string() // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
|
|
)), x);
|
|
}
|
|
} // namespace capture_string_view
|
|
|
|
// ****************************************************************************
|
|
// Capture pointer (eg: std::string*)
|
|
// ****************************************************************************
|
|
const std::string* getLifetimeBoundPointer(const std::string &s [[clang::lifetimebound]]);
|
|
const std::string* getNotLifetimeBoundPointer(const std::string &s);
|
|
|
|
namespace capture_pointer {
|
|
struct X {} x;
|
|
void capturePointer(const std::string* sp [[clang::lifetime_capture_by(x)]], X &x);
|
|
void use() {
|
|
capturePointer(getLifetimeBoundPointer(std::string()), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
|
|
capturePointer(getLifetimeBoundPointer(*getLifetimeBoundPointer(
|
|
std::string() // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
|
|
)), x);
|
|
capturePointer(getNotLifetimeBoundPointer(std::string()), x);
|
|
|
|
}
|
|
} // namespace capture_pointer
|
|
|
|
// ****************************************************************************
|
|
// Arrays and initializer lists.
|
|
// ****************************************************************************
|
|
namespace init_lists {
|
|
struct X {} x;
|
|
void captureVector(const std::vector<int> &a [[clang::lifetime_capture_by(x)]], X &x);
|
|
void captureArray(int array [[clang::lifetime_capture_by(x)]] [2], X &x);
|
|
void captureInitList(std::initializer_list<int> abc [[clang::lifetime_capture_by(x)]], X &x);
|
|
|
|
|
|
std::initializer_list<int> getLifetimeBoundInitList(std::initializer_list<int> abc [[clang::lifetimebound]]);
|
|
|
|
void use() {
|
|
captureVector({1, 2, 3}, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
|
|
captureVector(std::vector<int>{}, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
|
|
std::vector<int> local_vector;
|
|
captureVector(local_vector, x);
|
|
int local_array[2];
|
|
captureArray(local_array, x);
|
|
captureInitList({1, 2}, x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
|
|
captureInitList(getLifetimeBoundInitList({1, 2}), x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
|
|
}
|
|
} // namespace init_lists
|
|
|
|
// ****************************************************************************
|
|
// Implicit object param 'this' is captured
|
|
// ****************************************************************************
|
|
namespace this_is_captured {
|
|
struct X {} x;
|
|
struct S {
|
|
void capture(X &x) [[clang::lifetime_capture_by(x)]];
|
|
};
|
|
void use() {
|
|
S{}.capture(x); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
|
|
S s;
|
|
s.capture(x);
|
|
}
|
|
} // namespace this_is_captured
|
|
|
|
namespace temporary_capturing_object {
|
|
struct S {
|
|
void add(const int& x [[clang::lifetime_capture_by(this)]]);
|
|
};
|
|
|
|
void test() {
|
|
// We still give an warning even the capturing object is a temoprary.
|
|
// It is possible that the capturing object uses the captured object in its
|
|
// destructor.
|
|
S().add(1); // expected-warning {{object whose reference is captured}}
|
|
S{}.add(1); // expected-warning {{object whose reference is captured}}
|
|
}
|
|
} // namespace ignore_temporary_class_object
|
|
|
|
// ****************************************************************************
|
|
// Capture by Global and Unknown.
|
|
// ****************************************************************************
|
|
namespace capture_by_global_unknown {
|
|
void captureByGlobal(std::string_view s [[clang::lifetime_capture_by(global)]]);
|
|
void captureByUnknown(std::string_view s [[clang::lifetime_capture_by(unknown)]]);
|
|
|
|
std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);
|
|
|
|
void use() {
|
|
std::string_view local_string_view;
|
|
std::string local_string;
|
|
// capture by global.
|
|
captureByGlobal(std::string()); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}}
|
|
captureByGlobal(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}}
|
|
captureByGlobal(local_string);
|
|
captureByGlobal(local_string_view);
|
|
|
|
// capture by unknown.
|
|
captureByUnknown(std::string()); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}}
|
|
captureByUnknown(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured will be destroyed at the end of the full-expression}}
|
|
captureByUnknown(local_string);
|
|
captureByUnknown(local_string_view);
|
|
}
|
|
} // namespace capture_by_global_unknown
|
|
|
|
// ****************************************************************************
|
|
// Member functions: Capture by 'this'
|
|
// ****************************************************************************
|
|
namespace capture_by_this {
|
|
struct S {
|
|
void captureInt(const int& x [[clang::lifetime_capture_by(this)]]);
|
|
void captureView(std::string_view sv [[clang::lifetime_capture_by(this)]]);
|
|
};
|
|
std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);
|
|
std::string_view getNotLifetimeBoundView(const std::string& s);
|
|
const std::string& getLifetimeBoundString(const std::string &s [[clang::lifetimebound]]);
|
|
|
|
void use() {
|
|
S s;
|
|
s.captureInt(1); // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}}
|
|
s.captureView(std::string()); // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}}
|
|
s.captureView(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}}
|
|
s.captureView(getLifetimeBoundString(std::string())); // expected-warning {{object whose reference is captured by 's' will be destroyed at the end of the full-expression}}
|
|
s.captureView(getNotLifetimeBoundView(std::string()));
|
|
}
|
|
} // namespace capture_by_this
|
|
|
|
// ****************************************************************************
|
|
// Struct with field as a reference
|
|
// ****************************************************************************
|
|
namespace reference_field {
|
|
struct X {} x;
|
|
struct Foo {
|
|
const int& b;
|
|
};
|
|
void captureField(Foo param [[clang::lifetime_capture_by(x)]], X &x);
|
|
void use() {
|
|
captureField(Foo{
|
|
1 // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
|
|
}, x);
|
|
int local;
|
|
captureField(Foo{local}, x);
|
|
}
|
|
} // namespace reference_field
|
|
|
|
// ****************************************************************************
|
|
// Capture default argument.
|
|
// ****************************************************************************
|
|
namespace default_arg {
|
|
struct X {} x;
|
|
void captureDefaultArg(X &x, std::string_view s [[clang::lifetime_capture_by(x)]] = std::string());
|
|
|
|
std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);
|
|
|
|
void useCaptureDefaultArg() {
|
|
X x;
|
|
captureDefaultArg(x); // FIXME: Diagnose temporary default arg.
|
|
captureDefaultArg(x, std::string("temp")); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
|
|
captureDefaultArg(x, getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 'x' will be destroyed at the end of the full-expression}}
|
|
std::string local;
|
|
captureDefaultArg(x, local);
|
|
}
|
|
} // namespace default_arg
|
|
|
|
// ****************************************************************************
|
|
// Container: *No* distinction between pointer-like and other element type
|
|
// ****************************************************************************
|
|
namespace containers_no_distinction {
|
|
template<class T>
|
|
struct MySet {
|
|
void insert(T&& t [[clang::lifetime_capture_by(this)]]);
|
|
void insert(const T& t [[clang::lifetime_capture_by(this)]]);
|
|
};
|
|
void user_defined_containers() {
|
|
MySet<int> set_of_int;
|
|
set_of_int.insert(1); // expected-warning {{object whose reference is captured by 'set_of_int' will be destroyed at the end of the full-expression}}
|
|
MySet<std::string_view> set_of_sv;
|
|
set_of_sv.insert(std::string()); // expected-warning {{object whose reference is captured by 'set_of_sv' will be destroyed at the end of the full-expression}}
|
|
set_of_sv.insert(std::string_view());
|
|
}
|
|
} // namespace containers_no_distinction
|
|
|
|
// ****************************************************************************
|
|
// Container: Different for pointer-like and other element type.
|
|
// ****************************************************************************
|
|
namespace conatiners_with_different {
|
|
template<typename T> struct IsPointerLikeTypeImpl : std::false_type {};
|
|
template<> struct IsPointerLikeTypeImpl<std::string_view> : std::true_type {};
|
|
template<typename T> concept IsPointerLikeType = std::is_pointer<T>::value || IsPointerLikeTypeImpl<T>::value;
|
|
|
|
template<class T> struct MyVector {
|
|
void push_back(T&& t [[clang::lifetime_capture_by(this)]]) requires IsPointerLikeType<T>;
|
|
void push_back(const T& t [[clang::lifetime_capture_by(this)]]) requires IsPointerLikeType<T>;
|
|
|
|
void push_back(T&& t) requires (!IsPointerLikeType<T>);
|
|
void push_back(const T& t) requires (!IsPointerLikeType<T>);
|
|
};
|
|
|
|
std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);
|
|
|
|
void use_container() {
|
|
std::string local;
|
|
|
|
MyVector<std::string> vector_of_string;
|
|
vector_of_string.push_back(std::string()); // Ok.
|
|
|
|
MyVector<std::string_view> vector_of_view;
|
|
vector_of_view.push_back(std::string()); // expected-warning {{object whose reference is captured by 'vector_of_view' will be destroyed at the end of the full-expression}}
|
|
vector_of_view.push_back(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 'vector_of_view' will be destroyed at the end of the full-expression}}
|
|
|
|
MyVector<const std::string*> vector_of_pointer;
|
|
vector_of_pointer.push_back(getLifetimeBoundPointer(std::string())); // expected-warning {{object whose reference is captured by 'vector_of_pointer' will be destroyed at the end of the full-expression}}
|
|
vector_of_pointer.push_back(getLifetimeBoundPointer(*getLifetimeBoundPointer(std::string()))); // expected-warning {{object whose reference is captured by 'vector_of_pointer' will be destroyed at the end of the full-expression}}
|
|
vector_of_pointer.push_back(getLifetimeBoundPointer(local));
|
|
vector_of_pointer.push_back(getNotLifetimeBoundPointer(std::string()));
|
|
}
|
|
|
|
// ****************************************************************************
|
|
// Container: For user defined view types
|
|
// ****************************************************************************
|
|
struct [[gsl::Pointer()]] MyStringView : public std::string_view {
|
|
MyStringView();
|
|
MyStringView(std::string_view&&);
|
|
MyStringView(const MyStringView&);
|
|
MyStringView(const std::string&);
|
|
};
|
|
template<> struct IsPointerLikeTypeImpl<MyStringView> : std::true_type {};
|
|
|
|
std::optional<std::string_view> getOptionalSV();
|
|
std::optional<std::string> getOptionalS();
|
|
std::optional<MyStringView> getOptionalMySV();
|
|
MyStringView getMySV();
|
|
|
|
class MyStringViewNotPointer : public std::string_view {};
|
|
std::optional<MyStringViewNotPointer> getOptionalMySVNotP();
|
|
MyStringViewNotPointer getMySVNotP();
|
|
|
|
std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);
|
|
std::string_view getNotLifetimeBoundView(const std::string& s);
|
|
const std::string& getLifetimeBoundString(const std::string &s [[clang::lifetimebound]]);
|
|
const std::string& getLifetimeBoundString(std::string_view sv [[clang::lifetimebound]]);
|
|
|
|
void use_my_view() {
|
|
std::string local;
|
|
MyVector<MyStringView> vector_of_my_view;
|
|
vector_of_my_view.push_back(getMySV());
|
|
vector_of_my_view.push_back(MyStringView{});
|
|
vector_of_my_view.push_back(std::string_view{});
|
|
vector_of_my_view.push_back(std::string{}); // expected-warning {{object whose reference is captured by 'vector_of_my_view' will be destroyed at the end of the full-expression}}
|
|
vector_of_my_view.push_back(getLifetimeBoundView(std::string{})); // expected-warning {{object whose reference is captured by 'vector_of_my_view' will be destroyed at the end of the full-expression}}
|
|
vector_of_my_view.push_back(getLifetimeBoundString(getLifetimeBoundView(std::string{}))); // expected-warning {{object whose reference is captured by 'vector_of_my_view' will be destroyed at the end of the full-expression}}
|
|
vector_of_my_view.push_back(getNotLifetimeBoundView(getLifetimeBoundString(getLifetimeBoundView(std::string{}))));
|
|
|
|
// Use with container of other view types.
|
|
MyVector<std::string_view> vector_of_view;
|
|
vector_of_view.push_back(getMySV());
|
|
vector_of_view.push_back(getMySVNotP());
|
|
}
|
|
|
|
// ****************************************************************************
|
|
// Container: Use with std::optional<view> (owner<pointer> types)
|
|
// ****************************************************************************
|
|
void use_with_optional_view() {
|
|
MyVector<std::string_view> vector_of_view;
|
|
|
|
std::optional<std::string_view> optional_of_view;
|
|
vector_of_view.push_back(optional_of_view.value());
|
|
vector_of_view.push_back(getOptionalS().value()); // expected-warning {{object whose reference is captured by 'vector_of_view' will be destroyed at the end of the full-expression}}
|
|
|
|
vector_of_view.push_back(getOptionalSV().value());
|
|
vector_of_view.push_back(getOptionalMySV().value());
|
|
vector_of_view.push_back(getOptionalMySVNotP().value());
|
|
}
|
|
} // namespace conatiners_with_different
|
|
|
|
// ****************************************************************************
|
|
// Capture 'temporary' views
|
|
// ****************************************************************************
|
|
namespace temporary_views {
|
|
void capture1(std::string_view s [[clang::lifetime_capture_by(x)]], std::vector<std::string_view>& x);
|
|
|
|
// Intended to capture the "string_view" itself
|
|
void capture2(const std::string_view& s [[clang::lifetime_capture_by(x)]], std::vector<std::string_view*>& x);
|
|
// Intended to capture the pointee of the "string_view"
|
|
void capture3(const std::string_view& s [[clang::lifetime_capture_by(x)]], std::vector<std::string_view>& x);
|
|
|
|
void use() {
|
|
std::vector<std::string_view> x1;
|
|
capture1(std::string(), x1); // expected-warning {{object whose reference is captured by 'x1' will be destroyed at the end of the full-expression}}
|
|
capture1(std::string_view(), x1);
|
|
|
|
std::vector<std::string_view*> x2;
|
|
// Clang considers 'const std::string_view&' to refer to the owner
|
|
// 'std::string' and not 'std::string_view'. Therefore no diagnostic here.
|
|
capture2(std::string_view(), x2);
|
|
capture2(std::string(), x2); // expected-warning {{object whose reference is captured by 'x2' will be destroyed at the end of the full-expression}}
|
|
|
|
std::vector<std::string_view> x3;
|
|
capture3(std::string_view(), x3);
|
|
capture3(std::string(), x3); // expected-warning {{object whose reference is captured by 'x3' will be destroyed at the end of the full-expression}}
|
|
}
|
|
} // namespace temporary_views
|
|
|
|
// ****************************************************************************
|
|
// Inferring annotation for STL containers
|
|
// ****************************************************************************
|
|
namespace inferred_capture_by {
|
|
const std::string* getLifetimeBoundPointer(const std::string &s [[clang::lifetimebound]]);
|
|
const std::string* getNotLifetimeBoundPointer(const std::string &s);
|
|
|
|
std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]);
|
|
std::string_view getNotLifetimeBoundView(const std::string& s);
|
|
void use() {
|
|
std::string local;
|
|
std::vector<std::string_view> views;
|
|
views.push_back(std::string()); // expected-warning {{object whose reference is captured by 'views' will be destroyed at the end of the full-expression}}
|
|
views.insert(views.begin(),
|
|
std::string()); // expected-warning {{object whose reference is captured by 'views' will be destroyed at the end of the full-expression}}
|
|
views.push_back(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 'views' will be destroyed at the end of the full-expression}}
|
|
views.push_back(getNotLifetimeBoundView(std::string()));
|
|
views.push_back(local);
|
|
views.insert(views.end(), local);
|
|
|
|
std::vector<std::string> strings;
|
|
strings.push_back(std::string());
|
|
strings.insert(strings.begin(), std::string());
|
|
|
|
std::vector<const std::string*> pointers;
|
|
pointers.push_back(getLifetimeBoundPointer(std::string()));
|
|
pointers.push_back(&local);
|
|
}
|
|
|
|
namespace with_span {
|
|
// Templated view types.
|
|
template<typename T>
|
|
struct [[gsl::Pointer]] Span {
|
|
Span(const std::vector<T> &V);
|
|
};
|
|
|
|
void use() {
|
|
std::vector<Span<int>> spans;
|
|
spans.push_back(std::vector<int>{1, 2, 3}); // expected-warning {{object whose reference is captured by 'spans' will be destroyed at the end of the full-expression}}
|
|
std::vector<int> local;
|
|
spans.push_back(local);
|
|
}
|
|
} // namespace with_span
|
|
} // namespace inferred_capture_by
|
|
|
|
namespace on_constructor {
|
|
struct T {
|
|
T(const int& t [[clang::lifetime_capture_by(this)]]);
|
|
};
|
|
struct T2 {
|
|
T2(const int& t [[clang::lifetime_capture_by(x)]], int& x);
|
|
};
|
|
struct T3 {
|
|
T3(const T& t [[clang::lifetime_capture_by(this)]]);
|
|
};
|
|
|
|
int foo(const T& t);
|
|
int bar(const T& t[[clang::lifetimebound]]);
|
|
|
|
void test() {
|
|
auto x = foo(T(1)); // OK. no diagnosic
|
|
T(1); // OK. no diagnostic
|
|
T t(1); // expected-warning {{temporary whose address is used}}
|
|
auto y = bar(T(1)); // expected-warning {{temporary whose address is used}}
|
|
T3 t3(T(1)); // expected-warning {{temporary whose address is used}}
|
|
|
|
int a;
|
|
T2(1, a); // expected-warning {{object whose reference is captured by}}
|
|
}
|
|
} // namespace on_constructor
|
|
|
|
namespace GH121391 {
|
|
|
|
struct Foo {};
|
|
|
|
template <typename T>
|
|
struct Container {
|
|
const T& tt() [[clang::lifetimebound]];
|
|
};
|
|
template<typename T>
|
|
struct StatusOr {
|
|
T* get() [[clang::lifetimebound]];
|
|
};
|
|
StatusOr<Container<const Foo*>> getContainer();
|
|
|
|
void test() {
|
|
std::vector<const Foo*> vv;
|
|
vv.push_back(getContainer().get()->tt()); // OK
|
|
}
|
|
|
|
} // namespace GH121391
|