From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 70764 invoked by alias); 22 May 2019 21:34:14 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Received: (qmail 70756 invoked by uid 89); 22 May 2019 21:34:13 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-20.3 required=5.0 tests=AWL,BAYES_00,FREEMAIL_FROM,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,RCVD_IN_DNSWL_NONE,SPF_PASS autolearn=ham version=3.3.1 spammy=sk:wreturn, sk:Wreturn, dereference, POINTER_TYPE_P X-HELO: mail-qt1-f171.google.com Received: from mail-qt1-f171.google.com (HELO mail-qt1-f171.google.com) (209.85.160.171) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 22 May 2019 21:34:10 +0000 Received: by mail-qt1-f171.google.com with SMTP id o7so4309482qtp.4 for ; Wed, 22 May 2019 14:34:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=to:from:subject:message-id:date:user-agent:mime-version :content-language; bh=vLTBIVfJ9znkiaE1Qj/bv0jVIFGMxUkx4sDMuN2fuQY=; b=ZL+c2hfPyNDwM85lioko4scJ5AuVUpxPvG/c4wICsEDCgQcfurS9QcMJZHfJt2IjYN yaAzisNqTbNO2ujOPTxsLP4dKARRS7GdbQ2zFKzC4vhal1y2xHmJRA60vBe+1P+rbwu6 alxFBdysxSJFu1L6IR8MerWpbD+DR/QVhED90awQutBegCXVXZDS23eb3vVt27jFXPsR Djk+K1QgydakF4ca+rCPibHUcqEa9oyRc5kOZMDJeXoDHSlp7iMlnHP0RiqlubUAJohT CvuuC10FdTg+YyBFNmAJKqaFvfo8LL5OPHFYMViG2Dc+2zHerGeYGwThNmW+2AObIWth zP0g== Return-Path: Received: from [192.168.0.41] (97-118-125-210.hlrn.qwest.net. [97.118.125.210]) by smtp.gmail.com with ESMTPSA id c4sm4426472qkd.24.2019.05.22.14.34.06 for (version=TLS1_3 cipher=AEAD-AES128-GCM-SHA256 bits=128/128); Wed, 22 May 2019 14:34:07 -0700 (PDT) To: gcc-patches From: Martin Sebor Subject: [PATCH] warn on returning alloca and VLA (PR 71924, 90549) Message-ID: Date: Wed, 22 May 2019 21:34:00 -0000 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.3.1 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="------------D6A7B541116BC2F82FE4B7EF" X-IsSubscribed: yes X-SW-Source: 2019-05/txt/msg01525.txt.bz2 This is a multi-part message in MIME format. --------------D6A7B541116BC2F82FE4B7EF Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit Content-length: 853 -Wreturn-local-addr detects a subset of instances of returning the address of a local object from a function but the warning doesn't try to handle alloca or VLAs, or some non-trivial cases of ordinary automatic variables[1]. The attached patch extends the implementation of the warning to detect those. It still doesn't detect instances where the address is the result of a built-in such strcpy[2]. Tested on x86_64-linux. Martin [1] For example, this is only diagnosed with the patch: void* f (int i) { struct S { int a[2]; } s[2]; return &s->a[i]; } [2] The following is not diagnosed even with the patch: void sink (void*); void* f (int i) { char a[6]; char *p = __builtin_strcpy (a, "123"); sink (p); return p; } I would expect detecting to be possible and useful. Maybe as a follow-up. --------------D6A7B541116BC2F82FE4B7EF Content-Type: text/x-patch; name="gcc-71924.diff" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="gcc-71924.diff" Content-length: 18765 PR middle-end/71924 - missing -Wreturn-local-addr returning alloca result PR middle-end/90549 - missing -Wreturn-local-addr maybe returning an address of a local array plus offset gcc/ChangeLog: PR c/71924 * gimple-ssa-isolate-paths.c (is_addr_local): New function. (warn_return_addr_local_phi_arg, warn_return_addr_local): Same. (find_implicit_erroneous_behavior): Call warn_return_addr_local_phi_arg. (find_explicit_erroneous_behavior): Call warn_return_addr_local. gcc/testsuite/ChangeLog: PR c/71924 * gcc.dg/Wreturn-local-addr-2.c: New test. * gcc.dg/Walloca-4.c: Prune expected warnings. * gcc.dg/pr41551.c: Same. * gcc.dg/pr59523.c: Same. * gcc.dg/tree-ssa/pr88775-2.c: Same. * gcc.dg/winline-7.c: Same. diff --git a/gcc/gimple-ssa-isolate-paths.c b/gcc/gimple-ssa-isolate-paths.c index 33fe352bb23..2933ecf502e 100644 --- a/gcc/gimple-ssa-isolate-paths.c +++ b/gcc/gimple-ssa-isolate-paths.c @@ -341,6 +341,135 @@ stmt_uses_0_or_null_in_undefined_way (gimple *stmt) return false; } +/* Return true if EXPR is a expression of pointer type that refers + to the address of a variable with automatic storage duration. + If so, set *PLOC to the location of the object or the call that + allocated it (for alloca and VLAs). When PMAYBE is non-null, + also consider PHI statements and set *PMAYBE when some but not + all arguments of such statements refer to local variables, and + to clear it otherwise. */ + +static bool +is_addr_local (tree exp, location_t *ploc, bool *pmaybe = NULL, + hash_set *visited = NULL) +{ + if (TREE_CODE (exp) == ADDR_EXPR) + { + tree baseaddr = get_base_address (TREE_OPERAND (exp, 0)); + if (TREE_CODE (baseaddr) == MEM_REF) + return is_addr_local (TREE_OPERAND (baseaddr, 0), ploc, pmaybe, visited); + + if ((!VAR_P (baseaddr) + || is_global_var (baseaddr)) + && TREE_CODE (baseaddr) != PARM_DECL) + return false; + + *ploc = DECL_SOURCE_LOCATION (baseaddr); + return true; + } + + if (TREE_CODE (exp) == SSA_NAME) + { + gimple *def_stmt = SSA_NAME_DEF_STMT (exp); + enum gimple_code code = gimple_code (def_stmt); + + if (is_gimple_assign (def_stmt)) + { + tree type = TREE_TYPE (gimple_assign_lhs (def_stmt)); + if (POINTER_TYPE_P (type)) + { + tree ptr = gimple_assign_rhs1 (def_stmt); + return is_addr_local (ptr, ploc, pmaybe, visited); + } + return false; + } + + if (code == GIMPLE_CALL + && gimple_call_builtin_p (def_stmt)) + { + tree fn = gimple_call_fndecl (def_stmt); + int code = DECL_FUNCTION_CODE (fn); + if (code != BUILT_IN_ALLOCA + && code != BUILT_IN_ALLOCA_WITH_ALIGN) + return false; + + *ploc = gimple_location (def_stmt); + return true; + } + + if (code == GIMPLE_PHI && pmaybe) + { + unsigned count = 0; + gphi *phi_stmt = as_a (def_stmt); + + unsigned nargs = gimple_phi_num_args (phi_stmt); + for (unsigned i = 0; i < nargs; ++i) + { + if (!visited->add (phi_stmt)) + { + tree arg = gimple_phi_arg_def (phi_stmt, i); + if (is_addr_local (arg, ploc, pmaybe, visited)) + ++count; + } + } + + *pmaybe = count && count < nargs; + return count != 0; + } + } + + return false; +} + +/* Detect and diagnose returning the address of a local variable in + a PHI result LHS and argument OP and PHI edge E in basic block BB. */ + +static basic_block +warn_return_addr_local_phi_arg (basic_block bb, basic_block duplicate, + tree lhs, tree op, edge e) +{ + location_t origin; + if (!is_addr_local (op, &origin)) + return NULL; + + gimple *use_stmt; + imm_use_iterator iter; + + FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs) + { + greturn *return_stmt = dyn_cast (use_stmt); + if (!return_stmt) + continue; + + if (gimple_return_retval (return_stmt) != lhs) + continue; + + { + auto_diagnostic_group d; + if (warning_at (gimple_location (use_stmt), + OPT_Wreturn_local_addr, + "function may return address " + "of local variable") + && origin != UNKNOWN_LOCATION) + inform (origin, "declared here"); + } + + if ((flag_isolate_erroneous_paths_dereference + || flag_isolate_erroneous_paths_attribute) + && gimple_bb (use_stmt) == bb) + { + duplicate = isolate_path (bb, duplicate, e, + use_stmt, lhs, true); + + /* When we remove an incoming edge, we need to + reprocess the Ith element. */ + cfg_altered = true; + } + } + + return duplicate; +} + /* Look for PHI nodes which feed statements in the same block where the value of the PHI node implies the statement is erroneous. @@ -400,58 +529,19 @@ find_implicit_erroneous_behavior (void) { tree op = gimple_phi_arg_def (phi, i); edge e = gimple_phi_arg_edge (phi, i); - imm_use_iterator iter; - gimple *use_stmt; - next_i = i + 1; - - if (TREE_CODE (op) == ADDR_EXPR) - { - tree valbase = get_base_address (TREE_OPERAND (op, 0)); - if ((VAR_P (valbase) && !is_global_var (valbase)) - || TREE_CODE (valbase) == PARM_DECL) - { - FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs) - { - greturn *return_stmt - = dyn_cast (use_stmt); - if (!return_stmt) - continue; - - if (gimple_return_retval (return_stmt) != lhs) - continue; - - { - auto_diagnostic_group d; - if (warning_at (gimple_location (use_stmt), - OPT_Wreturn_local_addr, - "function may return address " - "of local variable")) - inform (DECL_SOURCE_LOCATION(valbase), - "declared here"); - } - - if ((flag_isolate_erroneous_paths_dereference - || flag_isolate_erroneous_paths_attribute) - && gimple_bb (use_stmt) == bb) - { - duplicate = isolate_path (bb, duplicate, e, - use_stmt, lhs, true); - - /* When we remove an incoming edge, we need to - reprocess the Ith element. */ - next_i = i; - cfg_altered = true; - } - } - } - } + duplicate = warn_return_addr_local_phi_arg (bb, duplicate, + lhs, op, e); + next_i = duplicate ? i : i + 1; if (!integer_zerop (op)) continue; location_t phi_arg_loc = gimple_phi_arg_location (phi, i); + imm_use_iterator iter; + gimple *use_stmt; + /* We've got a NULL PHI argument. Now see if the PHI's result is dereferenced within BB. */ FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs) @@ -482,6 +572,51 @@ find_implicit_erroneous_behavior (void) } } +/* Detect and diagnose returning the address of a local variable + in RETURN_STMT in basic block BB. This only becomes undefined + behavior if the result is used, so we do not insert a trap and + only return NULL instead. */ + +static void +warn_return_addr_local (basic_block bb, greturn *return_stmt) +{ + tree val = gimple_return_retval (return_stmt); + if (!val) + return; + + bool maybe = false; + location_t origin; + hash_set visited_phis; + if (!is_addr_local (val, &origin, &maybe, &visited_phis)) + return; + + /* We only need it for this particular case. */ + calculate_dominance_info (CDI_POST_DOMINATORS); + const char* msg = N_("function returns address of local variable"); + if (maybe + || !dominated_by_p (CDI_POST_DOMINATORS, + single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)), bb)) + msg = N_("function may return address of local variable"); + + { + auto_diagnostic_group d; + if (warning_at (gimple_location (return_stmt), + OPT_Wreturn_local_addr, msg) + && origin != UNKNOWN_LOCATION) + inform (origin, "declared here"); + } + + /* Do not modify code if the user only asked for + warnings. */ + if (flag_isolate_erroneous_paths_dereference + || flag_isolate_erroneous_paths_attribute) + { + tree zero = build_zero_cst (TREE_TYPE (val)); + gimple_return_set_retval (return_stmt, zero); + update_stmt (return_stmt); + } +} + /* Look for statements which exhibit erroneous behavior. For example a NULL pointer dereference. @@ -525,49 +660,10 @@ find_explicit_erroneous_behavior (void) break; } - /* Detect returning the address of a local variable. This only - becomes undefined behavior if the result is used, so we do not - insert a trap and only return NULL instead. */ + /* Look for a return statement that returns the address + of a local variable or the result of alloca. */ if (greturn *return_stmt = dyn_cast (stmt)) - { - tree val = gimple_return_retval (return_stmt); - if (val && TREE_CODE (val) == ADDR_EXPR) - { - tree valbase = get_base_address (TREE_OPERAND (val, 0)); - if ((VAR_P (valbase) && !is_global_var (valbase)) - || TREE_CODE (valbase) == PARM_DECL) - { - /* We only need it for this particular case. */ - calculate_dominance_info (CDI_POST_DOMINATORS); - const char* msg; - bool always_executed = dominated_by_p - (CDI_POST_DOMINATORS, - single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)), bb); - if (always_executed) - msg = N_("function returns address of local variable"); - else - msg = N_("function may return address of " - "local variable"); - { - auto_diagnostic_group d; - if (warning_at (gimple_location (stmt), - OPT_Wreturn_local_addr, msg)) - inform (DECL_SOURCE_LOCATION(valbase), - "declared here"); - } - - /* Do not modify code if the user only asked for - warnings. */ - if (flag_isolate_erroneous_paths_dereference - || flag_isolate_erroneous_paths_attribute) - { - tree zero = build_zero_cst (TREE_TYPE (val)); - gimple_return_set_retval (return_stmt, zero); - update_stmt (stmt); - } - } - } - } + warn_return_addr_local (bb, return_stmt); } } } diff --git a/gcc/testsuite/gcc.dg/Walloca-4.c b/gcc/testsuite/gcc.dg/Walloca-4.c index 85dcb7b9bb9..f888e2db2ed 100644 --- a/gcc/testsuite/gcc.dg/Walloca-4.c +++ b/gcc/testsuite/gcc.dg/Walloca-4.c @@ -7,7 +7,7 @@ { char *src; - _Bool + _Bool use_alloca = (((rear_ptr - w) * sizeof (char)) < 4096U); if (use_alloca) src = (char *) __builtin_alloca ((rear_ptr - w) * sizeof (char)); @@ -15,3 +15,5 @@ src = (char *) __builtin_malloc ((rear_ptr - w) * sizeof (char)); return src; } + +/* { dg-prune-output "-Wreturn-local-addr" } */ diff --git a/gcc/testsuite/gcc.dg/Wreturn-local-addr-2.c b/gcc/testsuite/gcc.dg/Wreturn-local-addr-2.c new file mode 100644 index 00000000000..0e3435c8256 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wreturn-local-addr-2.c @@ -0,0 +1,293 @@ +/* PR c/71924 - missing -Wreturn-local-addr returning alloca result + { dg-do compile } + { dg-options "-O2 -Wall" } */ + +#define ATTR(...) __attribute__ ((__VA_ARGS__)) + +struct A { int a, b, c; }; +struct B { int a, b, c[]; }; + +void sink (void*, ...); + +ATTR (noipa) void* +return_alloca (int n) +{ + void *p = __builtin_alloca (n); + sink (p); + return p; /* { dg-warning "function returns address of local" } */ +} + +ATTR (noipa) void* +return_alloca_index_cst (int n) +{ + int *p = (int*)__builtin_alloca (n); + p = &p[1]; + sink (p); + return p; /* { dg-warning "function returns address of local" } */ +} + +ATTR (noipa) void* +return_alloca_plus_cst (int n) +{ + int *p = (int*)__builtin_alloca (n); + p += 1; + sink (p); + return p; /* { dg-warning "function returns address of local" } */ +} + +ATTR (noipa) void* +return_alloca_plus_var (int n, int i) +{ + char *p = (char*)__builtin_alloca (n); + p += i; + sink (p); + return p; /* { dg-warning "function returns address of local" } */ +} + +ATTR (noipa) void* +return_alloca_member_1 (int n) +{ + struct A *p = (struct A*)__builtin_alloca (n); + sink (&p->a); + return &p->a; /* { dg-warning "function returns address of local" } */ +} + +ATTR (noipa) void* +return_alloca_member_2 (int n) +{ + struct A *p = (struct A*)__builtin_alloca (n); + sink (&p->b); + return &p->b; /* { dg-warning "function returns address of local" } */ +} + +ATTR (noipa) void* +return_alloca_flexarray (int n) +{ + struct B *p = (struct B*)__builtin_alloca (n); + sink (p->c); + return p->c; /* { dg-warning "function returns address of local" } */ +} + + +ATTR (noipa) void* +return_array (void) +{ + int a[32]; + void *p = a; + sink (p); + return p; /* { dg-warning "function returns address of local" } */ +} + +ATTR (noipa) void* +return_array_index_cst (void) +{ + int a[32]; + void *p = &a[2]; + sink (p); + return p; /* { dg-warning "function returns address of local" } */ +} + +ATTR (noipa) void* +return_array_plus_cst (void) +{ + int a[32]; + void *p = a + 2; + sink (p); + return p; /* { dg-warning "function returns address of local" } */ +} + +ATTR (noipa) void* +return_array_plus_var (int i) +{ + int a[32]; + void *p = a + i; + sink (p); + return p; /* { dg-warning "function returns address of local" } */ +} + +ATTR (noipa) void* +return_array_member_1 (void) +{ + struct A a[2]; + int *p = &a[1].a; + sink (a, p); + return p; /* { dg-warning "function returns address of local" } */ +} + +ATTR (noipa) void* +return_array_member_2 (void) +{ + struct A a[32]; + int *p = &a[1].b; + sink (a, p); + return p; /* { dg-warning "function returns address of local" } */ +} + + +ATTR (noipa) void* +return_vla (int n) +{ + char a[n]; + void *p = a; + sink (p); + return p; /* { dg-warning "function returns address of local" } */ +} + +ATTR (noipa) void* +return_vla_index_cst (int n) +{ + char a[n]; + char *p = &a[3]; + sink (p); + return p; /* { dg-warning "function returns address of local" } */ +} + +ATTR (noipa) void* +return_vla_plus_cst (int n) +{ + char a[n]; + char *p = a + 3; + sink (p); + return p; /* { dg-warning "function returns address of local" } */ +} + +ATTR (noipa) void* +return_vla_index_var (int n, int i) +{ + char a[n]; + char *p = &a[i]; + sink (p); + return p; /* { dg-warning "function returns address of local" } */ +} + +ATTR (noipa) void* +return_vla_plus_var (int n, int i) +{ + char a[n]; + char *p = a + i; + sink (p); + return p; /* { dg-warning "function returns address of local" } */ +} + +ATTR (noipa) void* +return_vla_member_1 (int n, int i) +{ + struct A a[n]; + void *p = &a[i].a; + sink (a, p); + return p; /* { dg-warning "function returns address of local" } */ +} + +ATTR (noipa) void* +return_vla_member_2 (int n, int i) +{ + struct A a[n]; + void *p = &a[i].b; + sink (a, p); + return p; /* { dg-warning "function returns address of local" } */ +} + + +ATTR (noipa) void* +return_alloca_or_alloca (int n, int i) +{ + void *p = i ? __builtin_alloca (n * i) : __builtin_alloca (n); + sink (p); + /* The warning here should really be "function returns". */ + return p; /* { dg-warning "function (returns|may return) address of local" } */ +} + +ATTR (noipa) void* +return_alloca_or_alloca_2 (int n, int i) +{ + void *p0 = __builtin_alloca (n); + void *p1 = __builtin_alloca (n * 2); + void *p = i ? p0 : p1; + sink (p0, p1, p); + /* Same as above. */ + return p; /* { dg-warning "function (returns|may return) address of local" } */ +} + +ATTR (noipa) void* +return_array_or_array (int i) +{ + int a[5]; + int b[7]; + void *p = i ? a : b; + sink (a, b, p); + /* The warning here should really be "function returns". */ + return p; /* { dg-warning "function (returns|may return) address of local" } */ +} + +ATTR (noipa) void* +return_array_or_array_plus_var (int i, int j) +{ + int a[5]; + int b[7]; + + void *p0 = a + i; + void *p1 = b + j; + + void *p = i < j ? p0 : p1; + sink (a, b, p0, p1, p); + /* The warning here should really be "function returns". */ + return p; /* { dg-warning "function (returns|may return) address of local" } */ +} + +extern int global[32]; + +ATTR (noipa) void* +may_return_global_or_alloca (int n, int i) +{ + void *p = i ? global : __builtin_alloca (n); + sink (p); + return p; /* { dg-warning "function may return address of local" } */ +} + + +ATTR (noipa) void* +may_return_global_or_alloca_plus_cst (int n, int i) +{ + int *p = i ? global : (int*)__builtin_alloca (n); + p += 7; + sink (p); + return p; /* { dg-warning "function may return address of local" } */ +} + +ATTR (noipa) void* +may_return_global_or_array (int n, int i) +{ + int a[32]; + void *p = i ? global : a; + sink (p); + return p; /* { dg-warning "function may return address of local" } */ +} + +ATTR (noipa) void* +may_return_global_or_array_plus_cst (int n, int i) +{ + int a[32]; + int *p = i ? global : a; + p += 4; + sink (p); + return p; /* { dg-warning "function may return address of local" } */ +} + +ATTR (noipa) void* +may_return_global_or_vla (int n, int i) +{ + int a[n]; + void *p = i ? global : a; + sink (p); + return p; /* { dg-warning "function may return address of local" } */ +} + +ATTR (noipa) void* +may_return_global_or_vla_plus_cst (int n, int i) +{ + int a[n]; + int *p = i ? global : a; + p += 4; + sink (p); + return p; /* { dg-warning "function may return address of local" } */ +} diff --git a/gcc/testsuite/gcc.dg/pr41551.c b/gcc/testsuite/gcc.dg/pr41551.c index 2f2ad2be97e..e1123206cc6 100644 --- a/gcc/testsuite/gcc.dg/pr41551.c +++ b/gcc/testsuite/gcc.dg/pr41551.c @@ -10,3 +10,5 @@ int main(void) int var, *p = &var; return (double)(uintptr_t)(p); } + +/* { dg-prune-output "-Wreturn-local-addr" } */ diff --git a/gcc/testsuite/gcc.dg/pr59523.c b/gcc/testsuite/gcc.dg/pr59523.c index a6c3302a683..49cbe5dd27a 100644 --- a/gcc/testsuite/gcc.dg/pr59523.c +++ b/gcc/testsuite/gcc.dg/pr59523.c @@ -16,3 +16,5 @@ foo (int a, int *b, int *c, int *d) r[i] = 1; return r; } + +/* { dg-prune-output "-Wreturn-local-addr" } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr88775-2.c b/gcc/testsuite/gcc.dg/tree-ssa/pr88775-2.c index 292ce6edefc..ed5df826432 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr88775-2.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr88775-2.c @@ -41,3 +41,5 @@ f5 (void) int c[64] = {}, d[64] = {}; return (__UINTPTR_TYPE__) &c[64] != (__UINTPTR_TYPE__) &d[0]; } + +/* { dg-prune-output "-Wreturn-local-addr" } */ diff --git a/gcc/testsuite/gcc.dg/winline-7.c b/gcc/testsuite/gcc.dg/winline-7.c index 34deca42592..239d748926d 100644 --- a/gcc/testsuite/gcc.dg/winline-7.c +++ b/gcc/testsuite/gcc.dg/winline-7.c @@ -13,3 +13,5 @@ inline void *t (void) { return q (); /* { dg-message "called from here" } */ } + +/* { dg-prune-output "-Wreturn-local-addr" } */ --------------D6A7B541116BC2F82FE4B7EF--