blob: f6bec6e66c3654f59f221090e36730c987c60203 [file] [log] [blame]
// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety -verify %s
struct MyObj {
int id;
~MyObj() {} // Non-trivial destructor
MyObj operator+(MyObj);
};
struct [[gsl::Pointer()]] View {
View(const MyObj&); // Borrows from MyObj
View();
void use() const;
};
//===----------------------------------------------------------------------===//
// Basic Definite Use-After-Free (-W...permissive)
// These are cases where the pointer is guaranteed to be dangling at the use site.
//===----------------------------------------------------------------------===//
void definite_simple_case() {
MyObj* p;
{
MyObj s;
p = &s; // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
void definite_simple_case_gsl() {
View v;
{
MyObj s;
v = s; // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
void no_use_no_error() {
MyObj* p;
{
MyObj s;
p = &s;
}
// 'p' is dangling here, but since it is never used, no warning is issued.
}
void no_use_no_error_gsl() {
View v;
{
MyObj s;
v = s;
}
// 'v' is dangling here, but since it is never used, no warning is issued.
}
void definite_pointer_chain() {
MyObj* p;
MyObj* q;
{
MyObj s;
p = &s; // expected-warning {{does not live long enough}}
q = p;
} // expected-note {{destroyed here}}
(void)*q; // expected-note {{later used here}}
}
void definite_propagation_gsl() {
View v1, v2;
{
MyObj s;
v1 = s; // expected-warning {{object whose reference is captured does not live long enough}}
v2 = v1;
} // expected-note {{destroyed here}}
v2.use(); // expected-note {{later used here}}
}
void definite_multiple_uses_one_warning() {
MyObj* p;
{
MyObj s;
p = &s; // expected-warning {{does not live long enough}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
// No second warning for the same loan.
p->id = 1;
MyObj* q = p;
(void)*q;
}
void definite_multiple_pointers() {
MyObj *p, *q, *r;
{
MyObj s;
p = &s; // expected-warning {{does not live long enough}}
q = &s; // expected-warning {{does not live long enough}}
r = &s; // expected-warning {{does not live long enough}}
} // expected-note 3 {{destroyed here}}
(void)*p; // expected-note {{later used here}}
(void)*q; // expected-note {{later used here}}
(void)*r; // expected-note {{later used here}}
}
void definite_single_pointer_multiple_loans(bool cond) {
MyObj *p;
if (cond){
MyObj s;
p = &s; // expected-warning {{does not live long enough}}
} // expected-note {{destroyed here}}
else {
MyObj t;
p = &t; // expected-warning {{does not live long enough}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note 2 {{later used here}}
}
void definite_single_pointer_multiple_loans_gsl(bool cond) {
View v;
if (cond){
MyObj s;
v = s; // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
else {
MyObj t;
v = t; // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
v.use(); // expected-note 2 {{later used here}}
}
//===----------------------------------------------------------------------===//
// Potential (Maybe) Use-After-Free (-W...strict)
// These are cases where the pointer *may* become dangling, depending on the path taken.
//===----------------------------------------------------------------------===//
void potential_if_branch(bool cond) {
MyObj safe;
MyObj* p = &safe;
if (cond) {
MyObj temp;
p = &temp; // expected-warning {{object whose reference is captured may not live long enough}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
void potential_if_branch_gsl(bool cond) {
MyObj safe;
View v = safe;
if (cond) {
MyObj temp;
v = temp; // expected-warning {{object whose reference is captured may not live long enough}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
void definite_potential_together(bool cond) {
MyObj safe;
MyObj* p_maybe = &safe;
MyObj* p_definite = nullptr;
{
MyObj s;
p_definite = &s; // expected-warning {{does not live long enough}}
if (cond) {
p_maybe = &s; // expected-warning {{may not live long enough}}
}
} // expected-note 2 {{destroyed here}}
(void)*p_definite; // expected-note {{later used here}}
(void)*p_maybe; // expected-note {{later used here}}
}
void definite_overrides_potential(bool cond) {
MyObj safe;
MyObj* p;
MyObj* q;
{
MyObj s;
q = &s; // expected-warning {{does not live long enough}}
p = q;
} // expected-note {{destroyed here}}
if (cond) {
// 'q' is conditionally "rescued". 'p' is not.
q = &safe;
}
// The use of 'p' is a definite error because it was never rescued.
(void)*q;
(void)*p; // expected-note {{later used here}}
(void)*q;
}
//===----------------------------------------------------------------------===//
// Control Flow Tests
//===----------------------------------------------------------------------===//
void potential_for_loop_use_after_loop_body(MyObj safe) {
MyObj* p = &safe;
for (int i = 0; i < 1; ++i) {
MyObj s;
p = &s; // expected-warning {{may not live long enough}}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
}
void potential_for_loop_gsl() {
MyObj safe;
View v = safe;
for (int i = 0; i < 1; ++i) {
MyObj s;
v = s; // expected-warning {{object whose reference is captured may not live long enough}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
void potential_for_loop_use_before_loop_body(MyObj safe) {
MyObj* p = &safe;
for (int i = 0; i < 1; ++i) {
(void)*p; // expected-note {{later used here}}
MyObj s;
p = &s; // expected-warning {{may not live long enough}}
} // expected-note {{destroyed here}}
(void)*p;
}
void potential_loop_with_break(bool cond) {
MyObj safe;
MyObj* p = &safe;
for (int i = 0; i < 10; ++i) {
if (cond) {
MyObj temp;
p = &temp; // expected-warning {{may not live long enough}}
break; // expected-note {{destroyed here}}
}
}
(void)*p; // expected-note {{later used here}}
}
void potential_loop_with_break_gsl(bool cond) {
MyObj safe;
View v = safe;
for (int i = 0; i < 10; ++i) {
if (cond) {
MyObj temp;
v = temp; // expected-warning {{object whose reference is captured may not live long enough}}
break; // expected-note {{destroyed here}}
}
}
v.use(); // expected-note {{later used here}}
}
void potential_multiple_expiry_of_same_loan(bool cond) {
// Choose the last expiry location for the loan.
MyObj safe;
MyObj* p = &safe;
for (int i = 0; i < 10; ++i) {
MyObj unsafe;
if (cond) {
p = &unsafe; // expected-warning {{may not live long enough}}
break;
}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
p = &safe;
for (int i = 0; i < 10; ++i) {
MyObj unsafe;
if (cond) {
p = &unsafe; // expected-warning {{may not live long enough}}
if (cond)
break;
}
} // expected-note {{destroyed here}}
(void)*p; // expected-note {{later used here}}
p = &safe;
for (int i = 0; i < 10; ++i) {
if (cond) {
MyObj unsafe2;
p = &unsafe2; // expected-warning {{may not live long enough}}
break; // expected-note {{destroyed here}}
}
}
(void)*p; // expected-note {{later used here}}
}
void potential_switch(int mode) {
MyObj safe;
MyObj* p = &safe;
switch (mode) {
case 1: {
MyObj temp;
p = &temp; // expected-warning {{object whose reference is captured may not live long enough}}
break; // expected-note {{destroyed here}}
}
case 2: {
p = &safe; // This path is okay.
break;
}
}
(void)*p; // expected-note {{later used here}}
}
void definite_switch(int mode) {
MyObj safe;
MyObj* p = &safe;
// All cases are UaF --> Definite error.
switch (mode) {
case 1: {
MyObj temp1;
p = &temp1; // expected-warning {{does not live long enough}}
break; // expected-note {{destroyed here}}
}
case 2: {
MyObj temp2;
p = &temp2; // expected-warning {{does not live long enough}}
break; // expected-note {{destroyed here}}
}
default: {
MyObj temp2;
p = &temp2; // expected-warning {{does not live long enough}}
break; // expected-note {{destroyed here}}
}
}
(void)*p; // expected-note 3 {{later used here}}
}
void definite_switch_gsl(int mode) {
View v;
switch (mode) {
case 1: {
MyObj temp1;
v = temp1; // expected-warning {{object whose reference is captured does not live long enough}}
break; // expected-note {{destroyed here}}
}
case 2: {
MyObj temp2;
v = temp2; // expected-warning {{object whose reference is captured does not live long enough}}
break; // expected-note {{destroyed here}}
}
default: {
MyObj temp3;
v = temp3; // expected-warning {{object whose reference is captured does not live long enough}}
break; // expected-note {{destroyed here}}
}
}
v.use(); // expected-note 3 {{later used here}}
}
//===----------------------------------------------------------------------===//
// No-Error Cases
//===----------------------------------------------------------------------===//
void no_error_if_dangle_then_rescue() {
MyObj safe;
MyObj* p;
{
MyObj temp;
p = &temp; // p is temporarily dangling.
}
p = &safe; // p is "rescued" before use.
(void)*p; // This is safe.
}
void no_error_if_dangle_then_rescue_gsl() {
MyObj safe;
View v;
{
MyObj temp;
v = temp; // 'v' is temporarily dangling.
}
v = safe; // 'v' is "rescued" before use by reassigning to a valid object.
v.use(); // This is safe.
}
//===----------------------------------------------------------------------===//
// Lifetimebound Attribute Tests
//===----------------------------------------------------------------------===//
View Identity(View v [[clang::lifetimebound]]);
View Choose(bool cond, View a [[clang::lifetimebound]], View b [[clang::lifetimebound]]);
MyObj* GetPointer(const MyObj& obj [[clang::lifetimebound]]);
struct [[gsl::Pointer()]] LifetimeBoundView {
LifetimeBoundView();
LifetimeBoundView(const MyObj& obj [[clang::lifetimebound]]);
LifetimeBoundView pass() [[clang::lifetimebound]] { return *this; }
operator View() const [[clang::lifetimebound]];
};
void lifetimebound_simple_function() {
View v;
{
MyObj obj;
v = Identity(obj); // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
void lifetimebound_multiple_args_definite() {
View v;
{
MyObj obj1, obj2;
v = Choose(true,
obj1, // expected-warning {{object whose reference is captured does not live long enough}}
obj2); // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note 2 {{destroyed here}}
v.use(); // expected-note 2 {{later used here}}
}
void lifetimebound_multiple_args_potential(bool cond) {
MyObj safe;
View v = safe;
{
MyObj obj1;
if (cond) {
MyObj obj2;
v = Choose(true,
obj1, // expected-warning {{object whose reference is captured may not live long enough}}
obj2); // expected-warning {{object whose reference is captured may not live long enough}}
} // expected-note {{destroyed here}}
} // expected-note {{destroyed here}}
v.use(); // expected-note 2 {{later used here}}
}
View SelectFirst(View a [[clang::lifetimebound]], View b);
void lifetimebound_mixed_args() {
View v;
{
MyObj obj1, obj2;
v = SelectFirst(obj1, // expected-warning {{object whose reference is captured does not live long enough}}
obj2);
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
void lifetimebound_member_function() {
LifetimeBoundView lbv, lbv2;
{
MyObj obj;
lbv = obj; // expected-warning {{object whose reference is captured does not live long enough}}
lbv2 = lbv.pass();
} // expected-note {{destroyed here}}
View v = lbv2; // expected-note {{later used here}}
v.use();
}
void lifetimebound_conversion_operator() {
View v;
{
MyObj obj;
LifetimeBoundView lbv = obj; // expected-warning {{object whose reference is captured does not live long enough}}
v = lbv; // Conversion operator is lifetimebound
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
void lifetimebound_chained_calls() {
View v;
{
MyObj obj;
v = Identity(Identity(Identity(obj))); // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
void lifetimebound_with_pointers() {
MyObj* ptr;
{
MyObj obj;
ptr = GetPointer(obj); // expected-warning {{object whose reference is captured does not live long enough}}
} // expected-note {{destroyed here}}
(void)*ptr; // expected-note {{later used here}}
}
void lifetimebound_no_error_safe_usage() {
MyObj obj;
View v1 = Identity(obj); // No warning - obj lives long enough
View v2 = Choose(true, v1, Identity(obj)); // No warning - all args are safe
v2.use(); // Safe usage
}
void lifetimebound_partial_safety(bool cond) {
MyObj safe_obj;
View v = safe_obj;
if (cond) {
MyObj temp_obj;
v = Choose(true,
safe_obj,
temp_obj); // expected-warning {{object whose reference is captured may not live long enough}}
} // expected-note {{destroyed here}}
v.use(); // expected-note {{later used here}}
}
// FIXME: Creating reference from lifetimebound call doesn't propagate loans.
const MyObj& GetObject(View v [[clang::lifetimebound]]);
void lifetimebound_return_reference() {
View v;
const MyObj* ptr;
{
MyObj obj;
View temp_v = obj;
const MyObj& ref = GetObject(temp_v);
ptr = &ref;
}
(void)*ptr;
}
// FIXME: No warning for non gsl::Pointer types. Origin tracking is only supported for pointer types.
struct LifetimeBoundCtor {
LifetimeBoundCtor();
LifetimeBoundCtor(const MyObj& obj [[clang::lifetimebound]]);
};
void lifetimebound_ctor() {
LifetimeBoundCtor v;
{
MyObj obj;
v = obj;
}
(void)v;
}