// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety -Wno-dangling -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; }; class TriviallyDestructedClass { View a, b; }; //===----------------------------------------------------------------------===// // 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}} } void definite_if_branch(bool cond) { MyObj safe; MyObj* p = &safe; if (cond) { MyObj temp; p = &temp; // expected-warning {{object whose reference is captured does not live long enough}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} } 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}} if (!cond) (void)*p; // expected-note {{later used here}} else p = &safe; } void definite_if_branch_gsl(bool cond) { MyObj safe; View v = safe; if (cond) { MyObj temp; v = temp; // expected-warning {{object whose reference is captured does 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; if (cond) 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}} if (!cond) (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; } void potential_due_to_conditional_killing(bool cond) { MyObj safe; MyObj* q; { MyObj s; q = &s; // expected-warning {{may not live long enough}} } // expected-note {{destroyed here}} if (cond) { // 'q' is conditionally "rescued". 'p' is not. q = &safe; } (void)*q; // expected-note {{later used here}} } 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; // Prefer the earlier use for diagnsotics. for (int i = 0; i < 1; ++i) { (void)*p; // expected-note {{later used here}} MyObj s; p = &s; // expected-warning {{does not live long enough}} } // expected-note {{destroyed here}} (void)*p; } void definite_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 {{does not live long enough}} break; // expected-note {{destroyed here}} } } (void)*p; // expected-note {{later used here}} } void definite_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 does 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 (e.g., through scope-ends and break statements). MyObj safe; MyObj* p = &safe; for (int i = 0; i < 10; ++i) { MyObj unsafe; if (cond) { p = &unsafe; // expected-warning {{does 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 {{does 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 {{does not live long enough}} break; // expected-note {{destroyed here}} } } // TODO: This can be argued to be a "maybe" warning. This is because // we only check for confidence of liveness and not the confidence of // the loan contained in an origin. To deal with this, we can introduce // a confidence in loan propagation analysis as well like liveness. (void)*p; // expected-note {{later used here}} p = &safe; for (int i = 0; i < 10; ++i) { MyObj unsafe; if (cond) p = &unsafe; // expected-warning {{does not live long enough}} if (cond) 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; } } if (mode == 2) (void)*p; // expected-note {{later used here}} } void definite_switch(int mode) { MyObj safe; MyObj* p = &safe; // A use domintates all the loan expires --> all 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}} } void loan_from_previous_iteration(MyObj safe, bool condition) { MyObj* p = &safe; MyObj* q = &safe; while (condition) { MyObj x; p = &x; // expected-warning {{may not live long enough}} if (condition) q = p; (void)*p; (void)*q; // expected-note {{later used here}} } // expected-note {{destroyed here}} } void trivial_int_uaf() { int * a; { int b = 1; a = &b; // expected-warning {{object whose reference is captured does not live long enough}} } // expected-note {{destroyed here}} (void)*a; // expected-note {{later used here}} } void trivial_class_uaf() { TriviallyDestructedClass* ptr; { TriviallyDestructedClass s; ptr = &s; // expected-warning {{object whose reference is captured does not live long enough}} } // expected-note {{destroyed here}} (void)ptr; // expected-note {{later used here}} } //===----------------------------------------------------------------------===// // Basic Definite Use-After-Return (Return-Stack-Address) (-W...permissive) // These are cases where the pointer is guaranteed to be dangling at the use site. //===----------------------------------------------------------------------===// MyObj* simple_return_stack_address() { MyObj s; MyObj* p = &s; // expected-warning {{address of stack memory is returned later}} return p; // expected-note {{returned here}} } MyObj* direct_return() { MyObj s; return &s; // expected-warning {{address of stack memory is returned later}} // expected-note@-1 {{returned here}} } const MyObj* conditional_assign_unconditional_return(const MyObj& safe, bool c) { MyObj s; const MyObj* p = &safe; if (c) { p = &s; // expected-warning {{address of stack memory is returned later}} } return p; // expected-note {{returned here}} } View conditional_assign_both_branches(const MyObj& safe, bool c) { MyObj s; View p; if (c) { p = s; // expected-warning {{address of stack memory is returned later}} } else { p = safe; } return p; // expected-note {{returned here}} } View reassign_safe_to_local(const MyObj& safe) { MyObj local; View p = safe; p = local; // expected-warning {{address of stack memory is returned later}} return p; // expected-note {{returned here}} } View pointer_chain_to_local() { MyObj local; View p1 = local; // expected-warning {{address of stack memory is returned later}} View p2 = p1; return p2; // expected-note {{returned here}} } View multiple_assign_multiple_return(const MyObj& safe, bool c1, bool c2) { MyObj local1; MyObj local2; View p; if (c1) { p = local1; // expected-warning {{address of stack memory is returned later}} return p; // expected-note {{returned here}} } else if (c2) { p = local2; // expected-warning {{address of stack memory is returned later}} return p; // expected-note {{returned here}} } p = safe; return p; } View multiple_assign_single_return(const MyObj& safe, bool c1, bool c2) { MyObj local1; MyObj local2; View p; if (c1) { p = local1; // expected-warning {{address of stack memory is returned later}} } else if (c2) { p = local2; // expected-warning {{address of stack memory is returned later}} } else { p = safe; } return p; // expected-note 2 {{returned here}} } View direct_return_of_local() { MyObj stack; return stack; // expected-warning {{address of stack memory is returned later}} // expected-note@-1 {{returned here}} } MyObj& reference_return_of_local() { MyObj stack; return stack; // expected-warning {{address of stack memory is returned later}} // expected-note@-1 {{returned here}} } int* trivial_int_uar() { int *a; int b = 1; a = &b; // expected-warning {{address of stack memory is returned later}} return a; // expected-note {{returned here}} } TriviallyDestructedClass* trivial_class_uar () { TriviallyDestructedClass *ptr; TriviallyDestructedClass s; ptr = &s; // expected-warning {{address of stack memory is returned later}} return ptr; // expected-note {{returned here}} } const int& return_parameter(int a) { return a; // expected-warning {{address of stack memory is returned later}} // expected-note@-1 {{returned here}} } int* return_pointer_to_parameter(int a) { return &a; // expected-warning {{address of stack memory is returned later}} // expected-note@-1 {{returned here}} } const int& return_reference_to_parameter(int a) { const int &b = a; return b; // expected-warning {{address of stack memory is returned later}} // expected-note@-1 {{returned here}} } const int& get_ref_to_local() { int a = 42; return a; // expected-warning {{address of stack memory is returned later}} // expected-note@-1 {{returned here}} } //===----------------------------------------------------------------------===// // Use-After-Scope & Use-After-Return (Return-Stack-Address) Combined // These are cases where the diagnostic kind is determined by location //===----------------------------------------------------------------------===// MyObj* uaf_before_uar() { MyObj* p; { MyObj local_obj; p = &local_obj; // expected-warning {{object whose reference is captured does not live long enough}} } // expected-note {{destroyed here}} return p; // expected-note {{later used here}} } View uar_before_uaf(const MyObj& safe, bool c) { View p; { MyObj local_obj; p = local_obj; // expected-warning {{address of stack memory is returned later}} if (c) { return p; // expected-note {{returned here}} } } p.use(); p = safe; return p; } //===----------------------------------------------------------------------===// // 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. } void no_error_loan_from_current_iteration(bool cond) { // See https://github.com/llvm/llvm-project/issues/156959. MyObj b; while (cond) { MyObj a; View p = b; if (cond) { p = a; } (void)p; } } View safe_return(const MyObj& safe) { MyObj local; View p = local; p = safe; // p has been reassigned return p; // This is safe } //===----------------------------------------------------------------------===// // Lifetimebound Attribute Tests //===----------------------------------------------------------------------===// View Identity(View v [[clang::lifetimebound]]); const MyObj& IdentityRef(const MyObj& obj [[clang::lifetimebound]]); MyObj* Identity(MyObj* 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 does not live long enough}} obj2); // expected-warning {{object whose reference is captured does 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 does not live long enough}} } // expected-note {{destroyed here}} v.use(); // expected-note {{later used here}} } // FIXME: Warning should be on the 'GetObject' call, not the assignment to 'ptr'. // The loan from the lifetimebound argument is not propagated to the call expression itself. 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; // expected-warning {{object whose reference is captured does not live long enough}} } // expected-note {{destroyed here}} (void)*ptr; // expected-note {{later used here}} } // 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; } View lifetimebound_return_of_local() { MyObj stack; return Identity(stack); // expected-warning {{address of stack memory is returned later}} // expected-note@-1 {{returned here}} } const MyObj& lifetimebound_return_ref_to_local() { MyObj stack; return IdentityRef(stack); // expected-warning {{address of stack memory is returned later}} // expected-note@-1 {{returned here}} } View lifetimebound_return_by_value_param(MyObj stack_param) { return Identity(stack_param); // expected-warning {{address of stack memory is returned later}} // expected-note@-1 {{returned here}} } View lifetimebound_return_by_value_multiple_param(int cond, MyObj a, MyObj b, MyObj c) { if (cond == 1) return Identity(a); // expected-warning {{address of stack memory is returned later}} // expected-note@-1 {{returned here}} if (cond == 2) return Identity(b); // expected-warning {{address of stack memory is returned later}} // expected-note@-1 {{returned here}} return Identity(c); // expected-warning {{address of stack memory is returned later}} // expected-note@-1 {{returned here}} } template View lifetimebound_return_by_value_param_template(T t) { return Identity(t); // expected-warning {{address of stack memory is returned later}} // expected-note@-1 {{returned here}} } void use_lifetimebound_return_by_value_param_template() { lifetimebound_return_by_value_param_template(MyObj{}); // expected-note {{in instantiation of}} } void lambda_uar_param() { auto lambda = [](MyObj stack_param) { return Identity(stack_param); // expected-warning {{address of stack memory is returned later}} // expected-note@-1 {{returned here}} }; lambda(MyObj{}); } // FIXME: This should be detected. We see correct destructors but origin flow breaks somewhere. namespace VariadicTemplatedParamsUAR{ template View Max(Args... args [[clang::lifetimebound]]); template View lifetimebound_return_of_variadic_param(Args... args) { return Max(args...); } void test_variadic() { lifetimebound_return_of_variadic_param(MyObj{1}, MyObj{2}, MyObj{3}); } } // FIXME: Fails to diagnose UAF when a reference to a by-value param escapes via an out-param. void uaf_from_by_value_param_failing(MyObj param, View* out_p) { *out_p = Identity(param); } // Conditional operator. void conditional_operator_one_unsafe_branch(bool cond) { MyObj safe; MyObj* p = &safe; { MyObj temp; p = cond ? &temp // expected-warning {{object whose reference is captured may not live long enough}} : &safe; } // expected-note {{destroyed here}} // This is not a use-after-free for any value of `cond` but the analysis // cannot reason this and marks the above as a false positive. This // ensures safety regardless of cond's value. if (cond) p = &safe; (void)*p; // expected-note {{later used here}} } void conditional_operator_two_unsafe_branches(bool cond) { MyObj* p; { MyObj a, b; p = cond ? &a // expected-warning {{object whose reference is captured does not live long enough}} : &b; // expected-warning {{object whose reference is captured does not live long enough}} } // expected-note 2 {{destroyed here}} (void)*p; // expected-note 2 {{later used here}} } void conditional_operator_nested(bool cond) { MyObj* p; { MyObj a, b, c, d; p = cond ? cond ? &a // expected-warning {{object whose reference is captured does not live long enough}}. : &b // expected-warning {{object whose reference is captured does not live long enough}}. : cond ? &c // expected-warning {{object whose reference is captured does not live long enough}}. : &d; // expected-warning {{object whose reference is captured does not live long enough}}. } // expected-note 4 {{destroyed here}} (void)*p; // expected-note 4 {{later used here}} } void conditional_operator_lifetimebound(bool cond) { MyObj* p; { MyObj a, b; p = Identity(cond ? &a // expected-warning {{object whose reference is captured does not live long enough}} : &b); // expected-warning {{object whose reference is captured does not live long enough}} } // expected-note 2 {{destroyed here}} (void)*p; // expected-note 2 {{later used here}} } void conditional_operator_lifetimebound_nested(bool cond) { MyObj* p; { MyObj a, b; p = Identity(cond ? Identity(&a) // expected-warning {{object whose reference is captured does not live long enough}} : Identity(&b)); // expected-warning {{object whose reference is captured does not live long enough}} } // expected-note 2 {{destroyed here}} (void)*p; // expected-note 2 {{later used here}} } void conditional_operator_lifetimebound_nested_deep(bool cond) { MyObj* p; { MyObj a, b, c, d; p = Identity(cond ? Identity(cond ? &a // expected-warning {{object whose reference is captured does not live long enough}} : &b) // expected-warning {{object whose reference is captured does not live long enough}} : Identity(cond ? &c // expected-warning {{object whose reference is captured does not live long enough}} : &d)); // expected-warning {{object whose reference is captured does not live long enough}} } // expected-note 4 {{destroyed here}} (void)*p; // expected-note 4 {{later used here}} } void parentheses(bool cond) { MyObj* p; { MyObj a; p = &((((a)))); // expected-warning {{object whose reference is captured does not live long enough}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} { MyObj a; p = ((GetPointer((a)))); // expected-warning {{object whose reference is captured does not live long enough}} } // expected-note {{destroyed here}} (void)*p; // expected-note {{later used here}} { MyObj a, b, c, d; p = &(cond ? (cond ? a // expected-warning {{object whose reference is captured does not live long enough}}. : b) // expected-warning {{object whose reference is captured does not live long enough}}. : (cond ? c // expected-warning {{object whose reference is captured does not live long enough}}. : d)); // expected-warning {{object whose reference is captured does not live long enough}}. } // expected-note 4 {{destroyed here}} (void)*p; // expected-note 4 {{later used here}} { MyObj a, b, c, d; p = ((cond ? (((cond ? &a : &b))) // expected-warning 2 {{object whose reference is captured does not live long enough}}. : &(((cond ? c : d))))); // expected-warning 2 {{object whose reference is captured does not live long enough}}. } // expected-note 4 {{destroyed here}} (void)*p; // expected-note 4 {{later used here}} }