blob: 103f2aab65754ac38525baf9ca78c39cfbdac4d9 [file]
/* Language-independent attribute handlers shared across front ends.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "target.h"
#include "function.h"
#include "tree.h"
#include "stringpool.h"
#include "attribs.h"
#include "attr-handlers.h"
#include "diagnostic-core.h"
#include "options.h"
#include "cgraph.h"
#include "varasm.h"
#include "common/common-target.h"
#include "tree-pretty-print.h"
/* Helper to define attribute exclusions. */
#define ATTR_EXCL(name, function, type, variable) \
{ name, function, type, variable }
/* Define attributes that are mutually exclusive with one another. */
const struct attribute_spec::exclusions attr_noreturn_exclusions[] =
{
ATTR_EXCL ("alloc_align", true, true, true),
ATTR_EXCL ("alloc_size", true, true, true),
ATTR_EXCL ("const", true, true, true),
ATTR_EXCL ("malloc", true, true, true),
ATTR_EXCL ("pure", true, true, true),
ATTR_EXCL ("returns_twice", true, true, true),
ATTR_EXCL ("warn_unused_result", true, true, true),
ATTR_EXCL (NULL, false, false, false),
};
const struct attribute_spec::exclusions attr_returns_twice_exclusions[] =
{
ATTR_EXCL ("noreturn", true, true, true),
ATTR_EXCL (NULL, false, false, false),
};
/* Exclusions that apply to attribute alloc_align, alloc_size, and malloc. */
const struct attribute_spec::exclusions attr_alloc_exclusions[] =
{
ATTR_EXCL ("const", true, true, true),
ATTR_EXCL ("noreturn", true, true, true),
ATTR_EXCL ("pure", true, true, true),
ATTR_EXCL (NULL, false, false, false),
};
const struct attribute_spec::exclusions attr_const_pure_exclusions[] =
{
ATTR_EXCL ("const", true, true, true),
ATTR_EXCL ("alloc_align", true, true, true),
ATTR_EXCL ("alloc_size", true, true, true),
ATTR_EXCL ("malloc", true, true, true),
ATTR_EXCL ("noreturn", true, true, true),
ATTR_EXCL ("pure", true, true, true),
ATTR_EXCL (NULL, false, false, false)
};
const struct attribute_spec::exclusions attr_always_inline_exclusions[] =
{
ATTR_EXCL ("noinline", true, true, true),
ATTR_EXCL ("target_clones", true, true, true),
ATTR_EXCL (NULL, false, false, false),
};
const struct attribute_spec::exclusions attr_cold_hot_exclusions[] =
{
ATTR_EXCL ("cold", true, true, true),
ATTR_EXCL ("hot", true, true, true),
ATTR_EXCL (NULL, false, false, false)
};
const struct attribute_spec::exclusions attr_noinline_exclusions[] =
{
ATTR_EXCL ("always_inline", true, true, true),
ATTR_EXCL ("gnu_inline", true, true, true),
ATTR_EXCL (NULL, false, false, false),
};
const struct attribute_spec::exclusions attr_target_exclusions[] =
{
ATTR_EXCL ("target_clones", TARGET_HAS_FMV_TARGET_ATTRIBUTE,
TARGET_HAS_FMV_TARGET_ATTRIBUTE, TARGET_HAS_FMV_TARGET_ATTRIBUTE),
ATTR_EXCL (NULL, false, false, false),
};
/* Exclusions that apply to attributes that put declarations in specific
sections. */
const struct attribute_spec::exclusions attr_section_exclusions[] =
{
ATTR_EXCL ("noinit", true, true, true),
ATTR_EXCL ("persistent", true, true, true),
ATTR_EXCL ("section", true, true, true),
ATTR_EXCL (NULL, false, false, false),
};
/* Return the first of DECL or TYPE attributes installed in NODE if it's
a DECL, or TYPE attributes if it's a TYPE, or null otherwise. */
static tree
decl_or_type_attrs (tree node)
{
if (DECL_P (node))
{
if (tree attrs = DECL_ATTRIBUTES (node))
return attrs;
tree type = TREE_TYPE (node);
if (type == error_mark_node)
return NULL_TREE;
return TYPE_ATTRIBUTES (type);
}
if (TYPE_P (node))
return TYPE_ATTRIBUTES (node);
return NULL_TREE;
}
/* Given a pair of NODEs for arbitrary DECLs or TYPEs, validate one or
two integral or string attribute arguments NEWARGS to be applied to
NODE[0] for the absence of conflicts with the same attribute arguments
already applied to NODE[1]. Issue a warning for conflicts and return
false. Otherwise, when no conflicts are found, return true. */
bool
validate_attr_args (tree node[2], tree name, tree newargs[2])
{
/* First validate the arguments against those already applied to
the same declaration (or type). */
tree self[2] = { node[0], node[0] };
if (node[0] != node[1] && !validate_attr_args (self, name, newargs))
return false;
if (!node[1])
return true;
/* Extract the same attribute from the previous declaration or type. */
tree prevattr = decl_or_type_attrs (node[1]);
const char* const namestr = IDENTIFIER_POINTER (name);
prevattr = lookup_attribute (namestr, prevattr);
if (!prevattr)
return true;
/* Extract one or both attribute arguments. */
tree prevargs[2];
prevargs[0] = TREE_VALUE (TREE_VALUE (prevattr));
prevargs[1] = TREE_CHAIN (TREE_VALUE (prevattr));
if (prevargs[1])
prevargs[1] = TREE_VALUE (prevargs[1]);
/* Both arguments must be equal or, for the second pair, neither must
be provided to succeed. */
bool arg1eq, arg2eq;
if (TREE_CODE (newargs[0]) == INTEGER_CST)
{
arg1eq = tree_int_cst_equal (newargs[0], prevargs[0]);
if (newargs[1] && prevargs[1])
arg2eq = tree_int_cst_equal (newargs[1], prevargs[1]);
else
arg2eq = newargs[1] == prevargs[1];
}
else if (TREE_CODE (newargs[0]) == STRING_CST)
{
const char *s0 = TREE_STRING_POINTER (newargs[0]);
const char *s1 = TREE_STRING_POINTER (prevargs[0]);
arg1eq = strcmp (s0, s1) == 0;
if (newargs[1] && prevargs[1])
{
s0 = TREE_STRING_POINTER (newargs[1]);
s1 = TREE_STRING_POINTER (prevargs[1]);
arg2eq = strcmp (s0, s1) == 0;
}
else
arg2eq = newargs[1] == prevargs[1];
}
else
gcc_unreachable ();
if (arg1eq && arg2eq)
return true;
/* If the two locations are different print a note pointing to
the previous one. */
const location_t curloc = input_location;
const location_t prevloc
= DECL_P (node[1]) ? DECL_SOURCE_LOCATION (node[1]) : curloc;
/* Format the attribute specification for convenience. */
char newspec[80], prevspec[80];
if (newargs[1])
snprintf (newspec, sizeof newspec, "%s (%s, %s)", namestr,
print_generic_expr_to_str (newargs[0]),
print_generic_expr_to_str (newargs[1]));
else
snprintf (newspec, sizeof newspec, "%s (%s)", namestr,
print_generic_expr_to_str (newargs[0]));
if (prevargs[1])
snprintf (prevspec, sizeof prevspec, "%s (%s, %s)", namestr,
print_generic_expr_to_str (prevargs[0]),
print_generic_expr_to_str (prevargs[1]));
else
snprintf (prevspec, sizeof prevspec, "%s (%s)", namestr,
print_generic_expr_to_str (prevargs[0]));
if (warning_at (curloc, OPT_Wattributes,
"ignoring attribute %qs because it conflicts "
"with previous %qs",
newspec, prevspec)
&& curloc != prevloc)
inform (prevloc, "previous declaration here");
return false;
}
/* Convenience wrapper for validate_attr_args to validate a single
attribute argument. Used by handlers for attributes that take
just a single argument. */
bool
validate_attr_arg (tree node[2], tree name, tree newarg)
{
tree argarray[2] = { newarg, NULL_TREE };
return validate_attr_args (node, name, argarray);
}
/* Handle an "alias" or "ifunc" attribute; arguments as in
struct attribute_spec.handler, except that IS_ALIAS tells us
whether this is an alias as opposed to ifunc attribute. */
tree
handle_alias_ifunc_attribute (bool is_alias, tree *node, tree name, tree args,
bool *no_add_attrs)
{
tree decl = *node;
if (TREE_CODE (decl) != FUNCTION_DECL
&& (!is_alias || !VAR_P (decl)))
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
else if ((TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl))
|| (TREE_CODE (decl) != FUNCTION_DECL
&& TREE_PUBLIC (decl) && !DECL_EXTERNAL (decl))
/* A static variable declaration is always a tentative definition,
but the alias is a non-tentative definition which overrides. */
|| (TREE_CODE (decl) != FUNCTION_DECL
&& ! TREE_PUBLIC (decl) && DECL_INITIAL (decl)))
{
error ("%q+D defined both normally and as %qE attribute", decl, name);
*no_add_attrs = true;
return NULL_TREE;
}
else if (!is_alias
&& (lookup_attribute ("weak", DECL_ATTRIBUTES (decl))
|| lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))))
{
error ("weak %q+D cannot be defined %qE", decl, name);
*no_add_attrs = true;
return NULL_TREE;
}
/* Note that the very first time we process a nested declaration,
decl_function_context will not be set. Indeed, *would* never
be set except for the DECL_INITIAL/DECL_EXTERNAL frobbery that
we do below. After such frobbery, pushdecl would set the context.
In any case, this is never what we want. */
else if (decl_function_context (decl) == 0 && current_function_decl == NULL)
{
tree id;
id = TREE_VALUE (args);
if (TREE_CODE (id) != STRING_CST)
{
error ("attribute %qE argument not a string", name);
*no_add_attrs = true;
return NULL_TREE;
}
id = get_identifier (TREE_STRING_POINTER (id));
/* This counts as a use of the object pointed to. */
TREE_USED (id) = 1;
if (TREE_CODE (decl) == FUNCTION_DECL)
DECL_INITIAL (decl) = error_mark_node;
else
TREE_STATIC (decl) = 1;
if (!is_alias)
{
/* ifuncs are also aliases, so set that attribute too. */
DECL_ATTRIBUTES (decl)
= tree_cons (get_identifier ("alias"), args,
DECL_ATTRIBUTES (decl));
DECL_ATTRIBUTES (decl) = tree_cons (get_identifier ("ifunc"),
NULL, DECL_ATTRIBUTES (decl));
}
}
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
if (decl_in_symtab_p (*node))
{
struct symtab_node *n = symtab_node::get (decl);
if (n && n->refuse_visibility_changes)
error ("%+qD declared %qs after being used",
decl, is_alias ? "alias" : "ifunc");
}
return NULL_TREE;
}
/* Ignore the given attribute. Used when this attribute may be usefully
overridden by the target, but is not used generically. */
tree
ignore_attribute (tree * ARG_UNUSED (node), tree ARG_UNUSED (name),
tree ARG_UNUSED (args), int ARG_UNUSED (flags),
bool *no_add_attrs)
{
*no_add_attrs = true;
return NULL_TREE;
}
/* Handle a "leaf" attribute; arguments as in
struct attribute_spec.handler. */
tree
handle_leaf_attribute (tree *node, tree name,
tree ARG_UNUSED (args),
int ARG_UNUSED (flags), bool *no_add_attrs)
{
if (TREE_CODE (*node) != FUNCTION_DECL)
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
if (!TREE_PUBLIC (*node))
{
warning (OPT_Wattributes, "%qE attribute has no effect on unit local "
"functions", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "const" attribute; arguments as in
struct attribute_spec.handler. */
tree
handle_const_attribute (tree *node, tree name, tree ARG_UNUSED (args),
int flags, bool *no_add_attrs)
{
tree type = TREE_TYPE (*node);
/* See FIXME comment on noreturn in c_common_attribute_table. */
if (TREE_CODE (*node) == FUNCTION_DECL)
TREE_READONLY (*node) = 1;
else if (TREE_CODE (type) == POINTER_TYPE
&& TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE)
TREE_TYPE (*node)
= (build_qualified_type
(build_pointer_type
(build_type_variant (TREE_TYPE (type), 1,
TREE_THIS_VOLATILE (TREE_TYPE (type)))),
TYPE_QUALS (type)));
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
/* void __builtin_unreachable (void) is const. Accept other such
built-ins but warn on user-defined functions that return void. */
if (!(flags & ATTR_FLAG_BUILT_IN)
&& TREE_CODE (*node) == FUNCTION_DECL
&& VOID_TYPE_P (TREE_TYPE (type)))
warning (OPT_Wattributes, "%qE attribute on function "
"returning %<void%>", name);
return NULL_TREE;
}
/* Handle a "pure" attribute; arguments as in
struct attribute_spec.handler. */
tree
handle_pure_attribute (tree *node, tree name, tree ARG_UNUSED (args),
int ARG_UNUSED (flags), bool *no_add_attrs)
{
if (TREE_CODE (*node) == FUNCTION_DECL)
{
tree type = TREE_TYPE (*node);
if (VOID_TYPE_P (TREE_TYPE (type)))
warning (OPT_Wattributes, "%qE attribute on function "
"returning %<void%>", name);
DECL_PURE_P (*node) = 1;
/* ??? TODO: Support types. */
}
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "no vops" attribute; arguments as in
struct attribute_spec.handler. */
tree
handle_novops_attribute (tree *node, tree ARG_UNUSED (name),
tree ARG_UNUSED (args), int ARG_UNUSED (flags),
bool *ARG_UNUSED (no_add_attrs))
{
gcc_assert (TREE_CODE (*node) == FUNCTION_DECL);
DECL_IS_NOVOPS (*node) = 1;
return NULL_TREE;
}
/* Handle a "nothrow" attribute; arguments as in
struct attribute_spec.handler. */
tree
handle_nothrow_attribute (tree *node, tree name, tree ARG_UNUSED (args),
int ARG_UNUSED (flags), bool *no_add_attrs)
{
if (TREE_CODE (*node) == FUNCTION_DECL)
TREE_NOTHROW (*node) = 1;
/* ??? TODO: Support types. */
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "type_generic" attribute. */
tree
handle_type_generic_attribute (tree *node, tree ARG_UNUSED (name),
tree ARG_UNUSED (args), int ARG_UNUSED (flags),
bool * ARG_UNUSED (no_add_attrs))
{
/* Ensure we have a function type. */
gcc_assert (TREE_CODE (*node) == FUNCTION_TYPE);
/* Ensure we have a variadic function. */
gcc_assert (!prototype_p (*node) || stdarg_p (*node));
return NULL_TREE;
}
/* Handle a "returns_twice" attribute. */
tree
handle_returns_twice_attribute (tree *node, tree name, tree ARG_UNUSED (args),
int ARG_UNUSED (flags), bool *no_add_attrs)
{
if (TREE_CODE (*node) == FUNCTION_DECL)
DECL_IS_RETURNS_TWICE (*node) = 1;
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "fn spec" attribute; arguments as in
struct attribute_spec.handler. */
tree
handle_fnspec_attribute (tree *node ATTRIBUTE_UNUSED, tree ARG_UNUSED (name),
tree args, int ARG_UNUSED (flags),
bool *no_add_attrs ATTRIBUTE_UNUSED)
{
gcc_assert (args
&& TREE_CODE (TREE_VALUE (args)) == STRING_CST
&& !TREE_CHAIN (args));
return NULL_TREE;
}
/* Handle an "visibility" attribute; arguments as in
struct attribute_spec.handler. */
tree
handle_visibility_attribute (tree *node, tree name, tree args,
int ARG_UNUSED (flags),
bool *ARG_UNUSED (no_add_attrs))
{
tree decl = *node;
tree id = TREE_VALUE (args);
enum symbol_visibility vis;
if (TYPE_P (*node))
{
if (TREE_CODE (*node) == ENUMERAL_TYPE)
/* OK. */;
else if (!RECORD_OR_UNION_TYPE_P (*node))
{
warning (OPT_Wattributes, "%qE attribute ignored on non-class types",
name);
return NULL_TREE;
}
else if (TYPE_FIELDS (*node))
{
error ("%qE attribute ignored because %qT is already defined",
name, *node);
return NULL_TREE;
}
}
else if (decl_function_context (decl) != 0 || !TREE_PUBLIC (decl))
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
return NULL_TREE;
}
if (TREE_CODE (id) != STRING_CST)
{
error ("visibility argument not a string");
return NULL_TREE;
}
/* If this is a type, set the visibility on the type decl. */
if (TYPE_P (decl))
{
decl = TYPE_NAME (decl);
if (!decl)
return NULL_TREE;
if (TREE_CODE (decl) == IDENTIFIER_NODE)
{
warning (OPT_Wattributes, "%qE attribute ignored on types",
name);
return NULL_TREE;
}
}
if (strcmp (TREE_STRING_POINTER (id), "default") == 0)
vis = VISIBILITY_DEFAULT;
else if (strcmp (TREE_STRING_POINTER (id), "internal") == 0)
vis = VISIBILITY_INTERNAL;
else if (strcmp (TREE_STRING_POINTER (id), "hidden") == 0)
vis = VISIBILITY_HIDDEN;
else if (strcmp (TREE_STRING_POINTER (id), "protected") == 0)
vis = VISIBILITY_PROTECTED;
else
{
error ("attribute %qE argument must be one of %qs, %qs, %qs, or %qs",
name, "default", "hidden", "protected", "internal");
vis = VISIBILITY_DEFAULT;
}
if (DECL_VISIBILITY_SPECIFIED (decl)
&& vis != DECL_VISIBILITY (decl))
{
tree attributes = (TYPE_P (*node)
? TYPE_ATTRIBUTES (*node)
: DECL_ATTRIBUTES (decl));
if (lookup_attribute ("visibility", attributes))
error ("%qD redeclared with different visibility", decl);
else if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
&& lookup_attribute ("dllimport", attributes))
error ("%qD was declared %qs which implies default visibility",
decl, "dllimport");
else if (TARGET_DLLIMPORT_DECL_ATTRIBUTES
&& lookup_attribute ("dllexport", attributes))
error ("%qD was declared %qs which implies default visibility",
decl, "dllexport");
}
DECL_VISIBILITY (decl) = vis;
DECL_VISIBILITY_SPECIFIED (decl) = 1;
/* Go ahead and attach the attribute to the node as well. This is needed
so we can determine whether we have VISIBILITY_DEFAULT because the
visibility was not specified, or because it was explicitly overridden
from the containing scope. */
return NULL_TREE;
}
/* Handle a "always_inline" attribute; arguments as in
struct attribute_spec.handler. */
tree
handle_always_inline_attribute (tree *node, tree name,
tree ARG_UNUSED (args),
int ARG_UNUSED (flags),
bool *no_add_attrs)
{
if (TREE_CODE (*node) == FUNCTION_DECL)
{
/* Set the attribute and mark it for disregarding inline
limits. */
DECL_DISREGARD_INLINE_LIMITS (*node) = 1;
}
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "noinline" attribute; arguments as in
struct attribute_spec.handler. */
tree
handle_noinline_attribute (tree *node, tree name,
tree ARG_UNUSED (args),
int ARG_UNUSED (flags), bool *no_add_attrs)
{
if (TREE_CODE (*node) == FUNCTION_DECL)
DECL_UNINLINABLE (*node) = 1;
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "weak" attribute; arguments as in
struct attribute_spec.handler. */
tree
handle_weak_attribute (tree *node, tree name,
tree ARG_UNUSED (args),
int ARG_UNUSED (flags),
bool * ARG_UNUSED (no_add_attrs))
{
if (TREE_CODE (*node) == FUNCTION_DECL
&& DECL_DECLARED_INLINE_P (*node))
{
warning (OPT_Wattributes, "inline function %q+D declared weak", *node);
*no_add_attrs = true;
}
else if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (*node)))
{
error ("indirect function %q+D cannot be declared weak", *node);
*no_add_attrs = true;
return NULL_TREE;
}
else if (VAR_OR_FUNCTION_DECL_P (*node))
declare_weak (*node);
else
warning (OPT_Wattributes, "%qE attribute ignored", name);
return NULL_TREE;
}
/* Handle a "target" attribute. */
tree
handle_target_attribute (tree *node, tree name, tree args, int flags,
bool *no_add_attrs)
{
/* Ensure we have a function declaration. */
if (TREE_CODE (*node) != FUNCTION_DECL)
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
else if (! targetm.target_option.valid_attribute_p (*node, name, args,
flags))
*no_add_attrs = true;
/* Check that there's no empty string in values of the attribute. */
for (tree t = args; t != NULL_TREE; t = TREE_CHAIN (t))
{
tree value = TREE_VALUE (t);
if (TREE_CODE (value) == STRING_CST
&& TREE_STRING_LENGTH (value) == 1
&& TREE_STRING_POINTER (value)[0] == '\0')
{
warning (OPT_Wattributes, "empty string in attribute %<target%>");
*no_add_attrs = true;
}
}
return NULL_TREE;
}
/* Handle a "used" attribute; arguments as in
struct attribute_spec.handler. */
tree
handle_used_attribute (tree *pnode, tree name, tree ARG_UNUSED (args),
int ARG_UNUSED (flags), bool *no_add_attrs)
{
tree node = *pnode;
if (TREE_CODE (node) == FUNCTION_DECL
|| (VAR_P (node) && TREE_STATIC (node))
|| (TREE_CODE (node) == TYPE_DECL))
{
TREE_USED (node) = 1;
DECL_PRESERVE_P (node) = 1;
if (VAR_P (node))
DECL_READ_P (node) = 1;
}
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle an "alias" or "ifunc" attribute; arguments as in
struct attribute_spec.handler. */
tree
handle_alias_attribute (tree *node, tree name, tree args,
int ARG_UNUSED (flags), bool *no_add_attrs)
{
return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
}
/* Handle a "section" attribute; arguments as in
struct attribute_spec.handler. */
tree
handle_section_attribute (tree *node, tree name, tree args,
int flags, bool *no_add_attrs)
{
tree decl = *node;
tree res = NULL_TREE;
tree argval = TREE_VALUE (args);
const char* new_section_name;
if (!targetm_common.have_named_sections)
{
error_at (DECL_SOURCE_LOCATION (*node),
"section attributes are not supported for this target");
goto fail;
}
if (!VAR_OR_FUNCTION_DECL_P (decl))
{
error ("section attribute not allowed for %q+D", *node);
goto fail;
}
if (TREE_CODE (argval) != STRING_CST)
{
error ("section attribute argument not a string constant");
goto fail;
}
if (VAR_P (decl)
&& current_function_decl != NULL_TREE
&& !TREE_STATIC (decl))
{
error_at (DECL_SOURCE_LOCATION (decl),
"section attribute cannot be specified for local variables");
goto fail;
}
new_section_name = TREE_STRING_POINTER (argval);
/* The decl may have already been given a section attribute
from a previous declaration. Ensure they match. */
if (const char* const old_section_name = DECL_SECTION_NAME (decl))
if (strcmp (old_section_name, new_section_name) != 0)
{
error ("section of %q+D conflicts with previous declaration",
*node);
goto fail;
}
if (VAR_P (decl)
&& !targetm.have_tls && targetm.emutls.tmpl_section
&& DECL_THREAD_LOCAL_P (decl))
{
error ("section of %q+D cannot be overridden", *node);
goto fail;
}
if (!validate_attr_arg (node, name, argval))
goto fail;
res = targetm.handle_generic_attribute (node, name, args, flags,
no_add_attrs);
/* If the back end confirms the attribute can be added then continue onto
final processing. */
if (!(*no_add_attrs))
{
set_decl_section_name (decl, new_section_name);
return res;
}
fail:
*no_add_attrs = true;
return res;
}
/* Handle a "retain" attribute; arguments as in
struct attribute_spec.handler. */
tree
handle_retain_attribute (tree *pnode, tree name, tree ARG_UNUSED (args),
int ARG_UNUSED (flags), bool *no_add_attrs)
{
tree node = *pnode;
if (SUPPORTS_SHF_GNU_RETAIN
&& (TREE_CODE (node) == FUNCTION_DECL
|| (VAR_P (node) && TREE_STATIC (node))))
;
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Part of the `malloc` attribute handling which is shared by libgccjit and
the C-family front-end. */
tree
handle_malloc_common (tree *node, tree name, tree ARG_UNUSED (args),
int ARG_UNUSED (flags), bool *no_add_attrs)
{
if (TREE_CODE (*node) != FUNCTION_DECL)
{
warning (OPT_Wattributes, "%qE attribute ignored; valid only "
"for functions", name);
*no_add_attrs = true;
return NULL_TREE;
}
tree rettype = TREE_TYPE (TREE_TYPE (*node));
if (!POINTER_TYPE_P (rettype))
{
warning (OPT_Wattributes, "%qE attribute ignored on functions "
"returning %qT; valid only for pointer return types",
name, rettype);
*no_add_attrs = true;
return NULL_TREE;
}
DECL_IS_MALLOC (*node) = 1;
return NULL_TREE;
}