/* For a PARM_DECL, nonzero if it was declared as an array. */
#define C_ARRAY_PARAMETER(NODE) DECL_LANG_FLAG_0 (NODE)
+/* For FUNCTION_DECLs, evaluates true if the decl is a nested
+ function that requires a non-local context. */
+#define C_FUNC_NONLOCAL_CONTEXT(EXP) \
+ DECL_LANG_FLAG_4 (FUNCTION_DECL_CHECK (EXP))
+
/* For FUNCTION_DECLs, evaluates true if the decl is built-in but has
been declared. */
#define C_DECL_DECLARED_BUILTIN(EXP) \
/* Record whether a decl was declared register. This is strictly a
front-end flag, whereas DECL_REGISTER is used for code generation;
they may differ for structures with volatile fields. */
-#define C_DECL_REGISTER(EXP) DECL_LANG_FLAG_4 (EXP)
+#define C_DECL_REGISTER(EXP) \
+ DECL_LANG_FLAG_4 (TREE_NOT_CHECK (EXP, FUNCTION_DECL))
/* Record whether a decl was used in an expression anywhere except an
unevaluated operand of sizeof / typeof / alignof. This is only
extern tree build_omp_array_section (location_t, tree, tree, tree);
extern tree build_external_ref (location_t, tree, bool, tree *);
extern void pop_maybe_used (bool);
+extern void mark_decl_used (tree, bool);
extern struct c_expr c_expr_sizeof_expr (location_t, struct c_expr);
extern struct c_expr c_expr_sizeof_type (location_t, struct c_type_name *);
extern struct c_expr c_expr_countof_expr (location_t, struct c_expr);
static tree find_init_member (tree, struct obstack *);
static void readonly_warning (tree, enum lvalue_use);
static int lvalue_or_else (location_t, const_tree, enum lvalue_use);
-static void record_maybe_used_decl (tree);
+static void record_maybe_used_decl (tree, bool address);
static bool comptypes_internal (const_tree, const_tree,
struct comptypes_data *data);
static bool comptypes_check_for_composite (tree t1, tree t2);
copy_warning (exp, orig_exp);
- return build_unary_op (loc, ADDR_EXPR, exp, false);
+ tree exp2 = build_unary_op (loc, ADDR_EXPR, exp, false);
+
+ /* If the function is defined and known to not to require a non-local
+ context, make sure no trampoline is generated. */
+ if (TREE_CODE (exp) == FUNCTION_DECL
+ && DECL_INITIAL (exp) && !C_FUNC_NONLOCAL_CONTEXT (exp))
+ TREE_NO_TRAMPOLINE (exp2) = 1;
+
+ return exp2;
}
/* Mark EXP as read, not just set, for set but not used -Wunused
return build3_loc (loc, OMP_ARRAY_SECTION, sectype, array, index, length);
}
+
+/* Record that REF is used. This is relevant for undeclared static
+ function and declarations referenced from a non-local context.
+ ADDRESS indicates whether the address is taken. */
+
+void
+mark_decl_used (tree ref, bool address)
+{
+ if (!ref || !DECL_P (ref) || in_alignof)
+ return;
+
+ /* Non-file-scope and non-local reference in nested function. */
+ bool nonloc_p = current_function_decl && DECL_CONTEXT (ref)
+ && DECL_CONTEXT (current_function_decl)
+ && DECL_CONTEXT (ref) != current_function_decl;
+
+ /* An undeclared static function. */
+ bool static_p = TREE_CODE (ref) == FUNCTION_DECL
+ && DECL_INITIAL (ref) == NULL_TREE
+ && DECL_EXTERNAL (ref)
+ && !TREE_PUBLIC (ref);
+
+ if (!static_p && !nonloc_p)
+ return;
+
+ /* If we may be in an unevaluated context, delay the decision. */
+ if (in_sizeof || in_typeof || in_countof)
+ return record_maybe_used_decl (ref, address);
+
+ if (static_p)
+ C_DECL_USED (ref) = 1;
+
+ if (nonloc_p)
+ DECL_NONLOCAL (ref) = 1;
+
+ /* Nothing to do anymore. */
+ if (!nonloc_p || C_FUNC_NONLOCAL_CONTEXT (current_function_decl))
+ return;
+
+ /* Filter out the cases where referencing a non-local variable does not
+ require a non-local context passed via the static chain. */
+ if (!C_TYPE_VARIABLY_MODIFIED (TREE_TYPE (ref)))
+ switch (TREE_CODE (ref))
+ {
+ case FUNCTION_DECL:
+ /* Use of another local function that requires no context is ok. */
+ if (!C_FUNC_NONLOCAL_CONTEXT (ref) && DECL_INITIAL (ref))
+ return;
+ break;
+ case VAR_DECL:
+ /* Static variables and constexpr are ok, but for the later only
+ when the address is not taken. */
+ if (TREE_STATIC (ref) || (C_DECL_DECLARED_CONSTEXPR (ref) && !address))
+ return;
+ break;
+ case TYPE_DECL:
+ /* A typedef is ok when not for a variably-modified type. */
+ return;
+ case CONST_DECL:
+ /* An enumeration constant is ok. */
+ return;
+ case PARM_DECL:
+ break;
+ case LABEL_DECL:
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ /* Mark all parent functions up to the nesting level of the variable as
+ as needing the non-local context. */
+ for (tree cont = current_function_decl; cont; cont = DECL_CONTEXT (cont))
+ {
+ if (cont == DECL_CONTEXT (ref))
+ break;
+
+ /* There should not be any other type of context used for function
+ except TRANSLATION_UNIT_DECL which we should be able to reach. */
+ gcc_checking_assert (TREE_CODE (cont) == FUNCTION_DECL);
+
+ if (TREE_CODE (cont) == FUNCTION_DECL)
+ C_FUNC_NONLOCAL_CONTEXT (cont) = 1;
+ }
+}
+
\f
/* Build an external reference to identifier ID. FUN indicates
whether this will be used for a function call. LOC is the source
TREE_USED (ref) = 1;
}
- if (TREE_CODE (ref) == FUNCTION_DECL && !in_alignof)
- {
- if (!in_sizeof && !in_typeof && !in_countof)
- C_DECL_USED (ref) = 1;
- else if (DECL_INITIAL (ref) == NULL_TREE
- && DECL_EXTERNAL (ref)
- && !TREE_PUBLIC (ref))
- record_maybe_used_decl (ref);
- }
+ mark_decl_used (ref, false);
if (TREE_CODE (ref) == CONST_DECL)
{
ref = DECL_INITIAL (ref);
TREE_CONSTANT (ref) = 1;
}
- else if (current_function_decl != NULL_TREE
- && !DECL_FILE_SCOPE_P (current_function_decl)
- && (VAR_OR_FUNCTION_DECL_P (ref)
- || TREE_CODE (ref) == PARM_DECL))
- {
- tree context = decl_function_context (ref);
-
- if (context != NULL_TREE && context != current_function_decl)
- DECL_NONLOCAL (ref) = 1;
- }
/* C99 6.7.4p3: An inline definition of a function with external
linkage ... shall not contain a reference to an identifier with
internal linkage. */
tree decl;
/* The level seen at (in_sizeof + in_typeof + in_countof). */
int level;
+ /* Seen in address-of. */
+ bool address;
/* The next one at this level or above, or NULL. */
struct maybe_used_decl *next;
};
static struct maybe_used_decl *maybe_used_decls;
-/* Record that DECL, an undefined static function reference seen
- inside sizeof or typeof, might be used if the operand of sizeof is
- a VLA type or the operand of typeof is a variably modified
- type. */
+/* Record that DECL, a reference seen inside sizeof or typeof, might be used
+ if the operand of sizeof is a VLA type or the operand of typeof is a variably
+ modified type. */
static void
-record_maybe_used_decl (tree decl)
+record_maybe_used_decl (tree decl, bool address)
{
struct maybe_used_decl *t = XOBNEW (&parser_obstack, struct maybe_used_decl);
t->decl = decl;
t->level = in_sizeof + in_typeof + in_countof;
+ t->address = address;
t->next = maybe_used_decls;
maybe_used_decls = t;
}
if (used)
{
if (cur_level == 0)
- C_DECL_USED (p->decl) = 1;
+ mark_decl_used (p->decl, p->address);
else
p->level = cur_level;
}
/* FALLTHRU */
case FUNCTION_DECL:
TREE_ADDRESSABLE (x) = 1;
+ mark_decl_used (x, true);
/* FALLTHRU */
default:
return true;
if (!decl)
return NULL_TREE;
TREE_USED (decl) = 1;
+ mark_decl_used (decl, false);
{
add_stmt (build_predict_expr (PRED_GOTO, NOT_TAKEN));
tree t = build1 (GOTO_EXPR, void_type_node, decl);
if (DECL_P (inner)
&& !DECL_EXTERNAL (inner)
- && !TREE_STATIC (inner)
&& DECL_CONTEXT (inner) == current_function_decl
&& POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))))
{
if (TREE_CODE (inner) == LABEL_DECL)
warning_at (loc, OPT_Wreturn_local_addr,
"function returns address of label");
- else
+ else if (TREE_CODE (inner) == FUNCTION_DECL
+ && (C_FUNC_NONLOCAL_CONTEXT (inner)
+ || !DECL_INITIAL (inner)))
+ warning_at (loc, OPT_Wreturn_local_addr,
+ "function returns address of nested function "
+ "referencing local context");
+ else if (TREE_CODE (inner) != FUNCTION_DECL
+ && !TREE_STATIC (inner))
{
warning_at (loc, OPT_Wreturn_local_addr,
"function returns address of local variable");