blob: ae20cf33f22b186777f21a733a3f38ee934a9a20 [file] [log] [blame] [edit]
// RUN: %clang_cc1 -fsyntax-only -verify=expected,c -std=c99 %s
// RUN: %clang_cc1 -fsyntax-only -verify=expected,c -std=c11 %s
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cpp -std=c++11 -x c++ %s
/* WG14 N1285: Clang 21
* Extending the lifetime of temporary objects (factored approach)
*
* This paper introduced the notion of an object with a temporary lifetime. Any
* operation resulting in an rvalue of structure or union type which contains
* an array results in an object with temporary lifetime.
*
* Even though this is a change for C11, we treat it as a DR and apply it
* retroactively to earlier C language modes.
*/
// C11 6.2.4p8: A non-lvalue expression with structure or union type, where the
// structure or union contains a member with array type (including,
// recursively, members of all contained structures and unions) refers to an
// object with automatic storage duration and temporary lifetime. Its lifetime
// begins when the expression is evaluated and its initial value is the value
// of the expression. Its lifetime ends when the evaluation of the containing
// full expression or full declarator ends. Any attempt to modify an object
// with temporary lifetime results in undefined behavior.
struct X { int a[5]; };
struct X f(void);
union U { int a[10]; double d; };
union U g(void);
void sink(int *);
int func_return(void) {
int *p = f().a; // expected-warning {{temporary whose address is used as value of local variable 'p' will be destroyed at the end of the full-expression}}
p = f().a; // expected-warning {{object backing the pointer 'p' will be destroyed at the end of the full-expression}}
p = g().a; // expected-warning {{object backing the pointer 'p' will be destroyed at the end of the full-expression}}
sink(f().a); // Ok
return *f().a; // Ok
}
int ternary(void) {
int *p = (1 ? (struct X){ 0 } : f()).a; // expected-warning {{temporary whose address is used as value of local variable 'p' will be destroyed at the end of the full-expression}}
int *r = (1 ? (union U){ 0 } : g()).a; // expected-warning {{temporary whose address is used as value of local variable 'r' will be destroyed at the end of the full-expression}}
p = (1 ? (struct X){ 0 } : f()).a; // expected-warning {{object backing the pointer 'p' will be destroyed at the end of the full-expression}}
sink((1 ? (struct X){ 0 } : f()).a); // Ok
// This intentionally gets one diagnostic in C and two in C++. In C, the
// compound literal results in an lvalue, not an rvalue as it does in C++. So
// only one branch results in a temporary in C but both branches do in C++.
int *q = 1 ? (struct X){ 0 }.a : f().a; // expected-warning {{temporary whose address is used as value of local variable 'q' will be destroyed at the end of the full-expression}} \
cpp-warning {{temporary whose address is used as value of local variable 'q' will be destroyed at the end of the full-expression}}
q = 1 ? (struct X){ 0 }.a : f().a; // expected-warning {{object backing the pointer 'q' will be destroyed at the end of the full-expression}} \
cpp-warning {{object backing the pointer 'q' will be destroyed at the end of the full-expression}}
q = 1 ? (struct X){ 0 }.a : g().a; // expected-warning {{object backing the pointer 'q' will be destroyed at the end of the full-expression}} \
cpp-warning {{object backing the pointer 'q' will be destroyed at the end of the full-expression}}
sink(1 ? (struct X){ 0 }.a : f().a); // Ok
return *(1 ? (struct X){ 0 }.a : f().a); // Ok
}
int comma(void) {
struct X x;
int *p = ((void)0, x).a; // c-warning {{temporary whose address is used as value of local variable 'p' will be destroyed at the end of the full-expression}}
p = ((void)0, x).a; // c-warning {{object backing the pointer 'p' will be destroyed at the end of the full-expression}}
sink(((void)0, x).a); // Ok
return *(((void)0, x).a);// Ok
}
int cast(void) {
struct X x;
int *p = ((struct X)x).a; // expected-warning {{temporary whose address is used as value of local variable 'p' will be destroyed at the end of the full-expression}}
p = ((struct X)x).a; // expected-warning {{object backing the pointer 'p' will be destroyed at the end of the full-expression}}
sink(((struct X)x).a); // Ok
return *(((struct X)x).a); // Ok
}
int assign(void) {
struct X x, s;
int *p = (x = s).a; // c-warning {{temporary whose address is used as value of local variable 'p' will be destroyed at the end of the full-expression}}
p = (x = s).a; // c-warning {{object backing the pointer 'p' will be destroyed at the end of the full-expression}}
sink((x = s).a); // Ok
return *((x = s).a); // Ok
}