blob: 007b928c54b8c40ea15b87830ed7956f07ecf550 [file] [log] [blame]
/* C-family attributes handling.
Copyright (C) 1992-2021 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/>. */
#define INCLUDE_STRING
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "target.h"
#include "function.h"
#include "tree.h"
#include "memmodel.h"
#include "c-common.h"
#include "gimple-expr.h"
#include "tm_p.h"
#include "stringpool.h"
#include "cgraph.h"
#include "diagnostic.h"
#include "intl.h"
#include "stor-layout.h"
#include "calls.h"
#include "attribs.h"
#include "varasm.h"
#include "trans-mem.h"
#include "c-objc.h"
#include "common/common-target.h"
#include "langhooks.h"
#include "tree-inline.h"
#include "toplev.h"
#include "tree-iterator.h"
#include "opts.h"
#include "gimplify.h"
#include "tree-pretty-print.h"
#include "gcc-rich-location.h"
static tree handle_packed_attribute (tree *, tree, tree, int, bool *);
static tree handle_nocommon_attribute (tree *, tree, tree, int, bool *);
static tree handle_common_attribute (tree *, tree, tree, int, bool *);
static tree handle_hot_attribute (tree *, tree, tree, int, bool *);
static tree handle_cold_attribute (tree *, tree, tree, int, bool *);
static tree handle_no_sanitize_attribute (tree *, tree, tree, int, bool *);
static tree handle_no_sanitize_address_attribute (tree *, tree, tree,
int, bool *);
static tree handle_no_sanitize_thread_attribute (tree *, tree, tree,
int, bool *);
static tree handle_no_address_safety_analysis_attribute (tree *, tree, tree,
int, bool *);
static tree handle_no_sanitize_undefined_attribute (tree *, tree, tree, int,
bool *);
static tree handle_no_sanitize_coverage_attribute (tree *, tree, tree, int,
bool *);
static tree handle_asan_odr_indicator_attribute (tree *, tree, tree, int,
bool *);
static tree handle_stack_protect_attribute (tree *, tree, tree, int, bool *);
static tree handle_no_stack_protector_function_attribute (tree *, tree,
tree, int, bool *);
static tree handle_noinline_attribute (tree *, tree, tree, int, bool *);
static tree handle_noclone_attribute (tree *, tree, tree, int, bool *);
static tree handle_nocf_check_attribute (tree *, tree, tree, int, bool *);
static tree handle_symver_attribute (tree *, tree, tree, int, bool *);
static tree handle_noicf_attribute (tree *, tree, tree, int, bool *);
static tree handle_noipa_attribute (tree *, tree, tree, int, bool *);
static tree handle_leaf_attribute (tree *, tree, tree, int, bool *);
static tree handle_always_inline_attribute (tree *, tree, tree, int,
bool *);
static tree handle_gnu_inline_attribute (tree *, tree, tree, int, bool *);
static tree handle_artificial_attribute (tree *, tree, tree, int, bool *);
static tree handle_flatten_attribute (tree *, tree, tree, int, bool *);
static tree handle_error_attribute (tree *, tree, tree, int, bool *);
static tree handle_used_attribute (tree *, tree, tree, int, bool *);
static tree handle_uninitialized_attribute (tree *, tree, tree, int, bool *);
static tree handle_externally_visible_attribute (tree *, tree, tree, int,
bool *);
static tree handle_no_reorder_attribute (tree *, tree, tree, int,
bool *);
static tree handle_const_attribute (tree *, tree, tree, int, bool *);
static tree handle_transparent_union_attribute (tree *, tree, tree,
int, bool *);
static tree handle_scalar_storage_order_attribute (tree *, tree, tree,
int, bool *);
static tree handle_constructor_attribute (tree *, tree, tree, int, bool *);
static tree handle_destructor_attribute (tree *, tree, tree, int, bool *);
static tree handle_mode_attribute (tree *, tree, tree, int, bool *);
static tree handle_section_attribute (tree *, tree, tree, int, bool *);
static tree handle_special_var_sec_attribute (tree *, tree, tree, int, bool *);
static tree handle_aligned_attribute (tree *, tree, tree, int, bool *);
static tree handle_warn_if_not_aligned_attribute (tree *, tree, tree,
int, bool *);
static tree handle_weak_attribute (tree *, tree, tree, int, bool *) ;
static tree handle_noplt_attribute (tree *, tree, tree, int, bool *) ;
static tree handle_alias_ifunc_attribute (bool, tree *, tree, tree, bool *);
static tree handle_ifunc_attribute (tree *, tree, tree, int, bool *);
static tree handle_alias_attribute (tree *, tree, tree, int, bool *);
static tree handle_weakref_attribute (tree *, tree, tree, int, bool *) ;
static tree handle_visibility_attribute (tree *, tree, tree, int,
bool *);
static tree handle_tls_model_attribute (tree *, tree, tree, int,
bool *);
static tree handle_no_instrument_function_attribute (tree *, tree,
tree, int, bool *);
static tree handle_no_profile_instrument_function_attribute (tree *, tree,
tree, int, bool *);
static tree handle_malloc_attribute (tree *, tree, tree, int, bool *);
static tree handle_dealloc_attribute (tree *, tree, tree, int, bool *);
static tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *);
static tree handle_no_limit_stack_attribute (tree *, tree, tree, int,
bool *);
static tree handle_pure_attribute (tree *, tree, tree, int, bool *);
static tree handle_tm_attribute (tree *, tree, tree, int, bool *);
static tree handle_tm_wrap_attribute (tree *, tree, tree, int, bool *);
static tree handle_novops_attribute (tree *, tree, tree, int, bool *);
static tree handle_unavailable_attribute (tree *, tree, tree, int,
bool *);
static tree handle_vector_size_attribute (tree *, tree, tree, int,
bool *) ATTRIBUTE_NONNULL(3);
static tree handle_nonnull_attribute (tree *, tree, tree, int, bool *);
static tree handle_nonstring_attribute (tree *, tree, tree, int, bool *);
static tree handle_nothrow_attribute (tree *, tree, tree, int, bool *);
static tree handle_cleanup_attribute (tree *, tree, tree, int, bool *);
static tree handle_warn_unused_result_attribute (tree *, tree, tree, int,
bool *);
static tree handle_access_attribute (tree *, tree, tree, int, bool *);
static tree handle_sentinel_attribute (tree *, tree, tree, int, bool *);
static tree handle_type_generic_attribute (tree *, tree, tree, int, bool *);
static tree handle_alloc_size_attribute (tree *, tree, tree, int, bool *);
static tree handle_alloc_align_attribute (tree *, tree, tree, int, bool *);
static tree handle_assume_aligned_attribute (tree *, tree, tree, int, bool *);
static tree handle_target_attribute (tree *, tree, tree, int, bool *);
static tree handle_target_clones_attribute (tree *, tree, tree, int, bool *);
static tree handle_optimize_attribute (tree *, tree, tree, int, bool *);
static tree ignore_attribute (tree *, tree, tree, int, bool *);
static tree handle_no_split_stack_attribute (tree *, tree, tree, int, bool *);
static tree handle_zero_call_used_regs_attribute (tree *, tree, tree, int,
bool *);
static tree handle_argspec_attribute (tree *, tree, tree, int, bool *);
static tree handle_fnspec_attribute (tree *, tree, tree, int, bool *);
static tree handle_warn_unused_attribute (tree *, tree, tree, int, bool *);
static tree handle_returns_nonnull_attribute (tree *, tree, tree, int, bool *);
static tree handle_omp_declare_simd_attribute (tree *, tree, tree, int,
bool *);
static tree handle_omp_declare_variant_attribute (tree *, tree, tree, int,
bool *);
static tree handle_simd_attribute (tree *, tree, tree, int, bool *);
static tree handle_omp_declare_target_attribute (tree *, tree, tree, int,
bool *);
static tree handle_designated_init_attribute (tree *, tree, tree, int, bool *);
static tree handle_patchable_function_entry_attribute (tree *, tree, tree,
int, bool *);
static tree handle_copy_attribute (tree *, tree, tree, int, bool *);
static tree handle_nsobject_attribute (tree *, tree, tree, int, bool *);
static tree handle_objc_root_class_attribute (tree *, tree, tree, int, bool *);
static tree handle_objc_nullability_attribute (tree *, tree, tree, int, bool *);
static tree handle_signed_bool_precision_attribute (tree *, tree, tree, int,
bool *);
static tree handle_retain_attribute (tree *, tree, tree, int, bool *);
/* 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. */
static const struct attribute_spec::exclusions attr_aligned_exclusions[] =
{
/* Attribute name exclusion applies to:
function, type, variable */
ATTR_EXCL ("aligned", true, false, false),
ATTR_EXCL ("packed", true, false, false),
ATTR_EXCL (NULL, false, false, false)
};
extern 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)
};
static const struct attribute_spec::exclusions attr_common_exclusions[] =
{
ATTR_EXCL ("common", true, true, true),
ATTR_EXCL ("nocommon", true, true, true),
ATTR_EXCL (NULL, false, false, false),
};
static const struct attribute_spec::exclusions attr_inline_exclusions[] =
{
ATTR_EXCL ("noinline", true, true, true),
ATTR_EXCL (NULL, false, false, false),
};
static 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),
};
extern 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),
};
static const struct attribute_spec::exclusions
attr_warn_unused_result_exclusions[] =
{
ATTR_EXCL ("noreturn", true, true, true),
ATTR_EXCL ("warn_unused_result", true, true, true),
ATTR_EXCL (NULL, false, false, false),
};
static 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. */
static 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),
};
static 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)
};
/* Exclusions that apply to attributes that put declarations in specific
sections. */
static 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),
};
static const struct attribute_spec::exclusions attr_stack_protect_exclusions[] =
{
ATTR_EXCL ("stack_protect", true, false, false),
ATTR_EXCL ("no_stack_protector", true, false, false),
ATTR_EXCL (NULL, false, false, false),
};
/* Table of machine-independent attributes common to all C-like languages.
Current list of processed common attributes: nonnull. */
const struct attribute_spec c_common_attribute_table[] =
{
/* { name, min_len, max_len, decl_req, type_req, fn_type_req,
affects_type_identity, handler, exclude } */
{ "signed_bool_precision", 1, 1, false, true, false, true,
handle_signed_bool_precision_attribute, NULL },
{ "packed", 0, 0, false, false, false, false,
handle_packed_attribute,
attr_aligned_exclusions },
{ "nocommon", 0, 0, true, false, false, false,
handle_nocommon_attribute,
attr_common_exclusions },
{ "common", 0, 0, true, false, false, false,
handle_common_attribute,
attr_common_exclusions },
/* FIXME: logically, noreturn attributes should be listed as
"false, true, true" and apply to function types. But implementing this
would require all the places in the compiler that use TREE_THIS_VOLATILE
on a decl to identify non-returning functions to be located and fixed
to check the function type instead. */
{ "noreturn", 0, 0, true, false, false, false,
handle_noreturn_attribute,
attr_noreturn_exclusions },
{ "volatile", 0, 0, true, false, false, false,
handle_noreturn_attribute, NULL },
{ "stack_protect", 0, 0, true, false, false, false,
handle_stack_protect_attribute,
attr_stack_protect_exclusions },
{ "no_stack_protector", 0, 0, true, false, false, false,
handle_no_stack_protector_function_attribute,
attr_stack_protect_exclusions },
{ "noinline", 0, 0, true, false, false, false,
handle_noinline_attribute,
attr_noinline_exclusions },
{ "noclone", 0, 0, true, false, false, false,
handle_noclone_attribute, NULL },
{ "no_icf", 0, 0, true, false, false, false,
handle_noicf_attribute, NULL },
{ "noipa", 0, 0, true, false, false, false,
handle_noipa_attribute, NULL },
{ "leaf", 0, 0, true, false, false, false,
handle_leaf_attribute, NULL },
{ "always_inline", 0, 0, true, false, false, false,
handle_always_inline_attribute,
attr_inline_exclusions },
{ "gnu_inline", 0, 0, true, false, false, false,
handle_gnu_inline_attribute,
attr_inline_exclusions },
{ "artificial", 0, 0, true, false, false, false,
handle_artificial_attribute, NULL },
{ "flatten", 0, 0, true, false, false, false,
handle_flatten_attribute, NULL },
{ "used", 0, 0, true, false, false, false,
handle_used_attribute, NULL },
{ "unused", 0, 0, false, false, false, false,
handle_unused_attribute, NULL },
{ "uninitialized", 0, 0, true, false, false, false,
handle_uninitialized_attribute, NULL },
{ "retain", 0, 0, true, false, false, false,
handle_retain_attribute, NULL },
{ "externally_visible", 0, 0, true, false, false, false,
handle_externally_visible_attribute, NULL },
{ "no_reorder", 0, 0, true, false, false, false,
handle_no_reorder_attribute, NULL },
/* The same comments as for noreturn attributes apply to const ones. */
{ "const", 0, 0, true, false, false, false,
handle_const_attribute,
attr_const_pure_exclusions },
{ "scalar_storage_order", 1, 1, false, false, false, false,
handle_scalar_storage_order_attribute, NULL },
{ "transparent_union", 0, 0, false, false, false, false,
handle_transparent_union_attribute, NULL },
{ "constructor", 0, 1, true, false, false, false,
handle_constructor_attribute, NULL },
{ "destructor", 0, 1, true, false, false, false,
handle_destructor_attribute, NULL },
{ "mode", 1, 1, false, true, false, false,
handle_mode_attribute, NULL },
{ "section", 1, 1, true, false, false, false,
handle_section_attribute, attr_section_exclusions },
{ "aligned", 0, 1, false, false, false, false,
handle_aligned_attribute,
attr_aligned_exclusions },
{ "warn_if_not_aligned", 0, 1, false, false, false, false,
handle_warn_if_not_aligned_attribute, NULL },
{ "weak", 0, 0, true, false, false, false,
handle_weak_attribute, NULL },
{ "noplt", 0, 0, true, false, false, false,
handle_noplt_attribute, NULL },
{ "ifunc", 1, 1, true, false, false, false,
handle_ifunc_attribute, NULL },
{ "alias", 1, 1, true, false, false, false,
handle_alias_attribute, NULL },
{ "weakref", 0, 1, true, false, false, false,
handle_weakref_attribute, NULL },
{ "no_instrument_function", 0, 0, true, false, false, false,
handle_no_instrument_function_attribute,
NULL },
{ "no_profile_instrument_function", 0, 0, true, false, false, false,
handle_no_profile_instrument_function_attribute,
NULL },
{ "malloc", 0, 2, true, false, false, false,
handle_malloc_attribute, attr_alloc_exclusions },
{ "returns_twice", 0, 0, true, false, false, false,
handle_returns_twice_attribute,
attr_returns_twice_exclusions },
{ "no_stack_limit", 0, 0, true, false, false, false,
handle_no_limit_stack_attribute, NULL },
{ "pure", 0, 0, true, false, false, false,
handle_pure_attribute,
attr_const_pure_exclusions },
{ "transaction_callable", 0, 0, false, true, false, false,
handle_tm_attribute, NULL },
{ "transaction_unsafe", 0, 0, false, true, false, true,
handle_tm_attribute, NULL },
{ "transaction_safe", 0, 0, false, true, false, true,
handle_tm_attribute, NULL },
{ "transaction_safe_dynamic", 0, 0, true, false, false, false,
handle_tm_attribute, NULL },
{ "transaction_may_cancel_outer", 0, 0, false, true, false, false,
handle_tm_attribute, NULL },
/* ??? These two attributes didn't make the transition from the
Intel language document to the multi-vendor language document. */
{ "transaction_pure", 0, 0, false, true, false, false,
handle_tm_attribute, NULL },
{ "transaction_wrap", 1, 1, true, false, false, false,
handle_tm_wrap_attribute, NULL },
/* For internal use (marking of builtins) only. The name contains space
to prevent its usage in source code. */
{ "no vops", 0, 0, true, false, false, false,
handle_novops_attribute, NULL },
{ "deprecated", 0, 1, false, false, false, false,
handle_deprecated_attribute, NULL },
{ "unavailable", 0, 1, false, false, false, false,
handle_unavailable_attribute, NULL },
{ "vector_size", 1, 1, false, true, false, true,
handle_vector_size_attribute, NULL },
{ "visibility", 1, 1, false, false, false, false,
handle_visibility_attribute, NULL },
{ "tls_model", 1, 1, true, false, false, false,
handle_tls_model_attribute, NULL },
{ "nonnull", 0, -1, false, true, true, false,
handle_nonnull_attribute, NULL },
{ "nonstring", 0, 0, true, false, false, false,
handle_nonstring_attribute, NULL },
{ "nothrow", 0, 0, true, false, false, false,
handle_nothrow_attribute, NULL },
{ "may_alias", 0, 0, false, true, false, false, NULL, NULL },
{ "cleanup", 1, 1, true, false, false, false,
handle_cleanup_attribute, NULL },
{ "warn_unused_result", 0, 0, false, true, true, false,
handle_warn_unused_result_attribute,
attr_warn_unused_result_exclusions },
{ "sentinel", 0, 1, false, true, true, false,
handle_sentinel_attribute, NULL },
/* For internal use (marking of builtins) only. The name contains space
to prevent its usage in source code. */
{ "type generic", 0, 0, false, true, true, false,
handle_type_generic_attribute, NULL },
{ "alloc_size", 1, 2, false, true, true, false,
handle_alloc_size_attribute,
attr_alloc_exclusions },
{ "cold", 0, 0, true, false, false, false,
handle_cold_attribute,
attr_cold_hot_exclusions },
{ "hot", 0, 0, true, false, false, false,
handle_hot_attribute,
attr_cold_hot_exclusions },
{ "no_address_safety_analysis",
0, 0, true, false, false, false,
handle_no_address_safety_analysis_attribute,
NULL },
{ "no_sanitize", 1, -1, true, false, false, false,
handle_no_sanitize_attribute, NULL },
{ "no_sanitize_address", 0, 0, true, false, false, false,
handle_no_sanitize_address_attribute, NULL },
{ "no_sanitize_thread", 0, 0, true, false, false, false,
handle_no_sanitize_thread_attribute, NULL },
{ "no_sanitize_undefined", 0, 0, true, false, false, false,
handle_no_sanitize_undefined_attribute, NULL },
{ "no_sanitize_coverage", 0, 0, true, false, false, false,
handle_no_sanitize_coverage_attribute, NULL },
{ "asan odr indicator", 0, 0, true, false, false, false,
handle_asan_odr_indicator_attribute, NULL },
{ "warning", 1, 1, true, false, false, false,
handle_error_attribute, NULL },
{ "error", 1, 1, true, false, false, false,
handle_error_attribute, NULL },
{ "target", 1, -1, true, false, false, false,
handle_target_attribute, NULL },
{ "target_clones", 1, -1, true, false, false, false,
handle_target_clones_attribute, NULL },
{ "optimize", 1, -1, true, false, false, false,
handle_optimize_attribute, NULL },
/* For internal use only. The leading '*' both prevents its usage in
source code and signals that it may be overridden by machine tables. */
{ "*tm regparm", 0, 0, false, true, true, false,
ignore_attribute, NULL },
{ "no_split_stack", 0, 0, true, false, false, false,
handle_no_split_stack_attribute, NULL },
{ "zero_call_used_regs", 1, 1, true, false, false, false,
handle_zero_call_used_regs_attribute, NULL },
/* For internal use only (marking of function arguments).
The name contains a space to prevent its usage in source code. */
{ "arg spec", 1, -1, true, false, false, false,
handle_argspec_attribute, NULL },
/* For internal use (marking of builtins and runtime functions) only.
The name contains space to prevent its usage in source code. */
{ "fn spec", 1, 1, false, true, true, false,
handle_fnspec_attribute, NULL },
{ "warn_unused", 0, 0, false, false, false, false,
handle_warn_unused_attribute, NULL },
{ "returns_nonnull", 0, 0, false, true, true, false,
handle_returns_nonnull_attribute, NULL },
{ "omp declare simd", 0, -1, true, false, false, false,
handle_omp_declare_simd_attribute, NULL },
{ "omp declare variant base", 0, -1, true, false, false, false,
handle_omp_declare_variant_attribute, NULL },
{ "omp declare variant variant", 0, -1, true, false, false, false,
handle_omp_declare_variant_attribute, NULL },
{ "simd", 0, 1, true, false, false, false,
handle_simd_attribute, NULL },
{ "omp declare target", 0, -1, true, false, false, false,
handle_omp_declare_target_attribute, NULL },
{ "omp declare target link", 0, 0, true, false, false, false,
handle_omp_declare_target_attribute, NULL },
{ "omp declare target implicit", 0, 0, true, false, false, false,
handle_omp_declare_target_attribute, NULL },
{ "omp declare target host", 0, 0, true, false, false, false,
handle_omp_declare_target_attribute, NULL },
{ "omp declare target nohost", 0, 0, true, false, false, false,
handle_omp_declare_target_attribute, NULL },
{ "omp declare target block", 0, 0, true, false, false, false,
handle_omp_declare_target_attribute, NULL },
{ "alloc_align", 1, 1, false, true, true, false,
handle_alloc_align_attribute,
attr_alloc_exclusions },
{ "assume_aligned", 1, 2, false, true, true, false,
handle_assume_aligned_attribute, NULL },
{ "designated_init", 0, 0, false, true, false, false,
handle_designated_init_attribute, NULL },
{ "fallthrough", 0, 0, false, false, false, false,
handle_fallthrough_attribute, NULL },
{ "patchable_function_entry", 1, 2, true, false, false, false,
handle_patchable_function_entry_attribute,
NULL },
{ "nocf_check", 0, 0, false, true, true, true,
handle_nocf_check_attribute, NULL },
{ "symver", 1, -1, true, false, false, false,
handle_symver_attribute, NULL},
{ "copy", 1, 1, false, false, false, false,
handle_copy_attribute, NULL },
{ "noinit", 0, 0, true, false, false, false,
handle_special_var_sec_attribute, attr_section_exclusions },
{ "persistent", 0, 0, true, false, false, false,
handle_special_var_sec_attribute, attr_section_exclusions },
{ "access", 1, 3, false, true, true, false,
handle_access_attribute, NULL },
/* Attributes used by Objective-C. */
{ "NSObject", 0, 0, true, false, false, false,
handle_nsobject_attribute, NULL },
{ "objc_root_class", 0, 0, true, false, false, false,
handle_objc_root_class_attribute, NULL },
{ "objc_nullability", 1, 1, true, false, false, false,
handle_objc_nullability_attribute, NULL },
{ "*dealloc", 1, 2, true, false, false, false,
handle_dealloc_attribute, NULL },
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
};
/* Give the specifications for the format attributes, used by C and all
descendants.
Current list of processed format attributes: format, format_arg. */
const struct attribute_spec c_common_format_attribute_table[] =
{
/* { name, min_len, max_len, decl_req, type_req, fn_type_req,
affects_type_identity, handler, exclude } */
{ "format", 3, 3, false, true, true, false,
handle_format_attribute, NULL },
{ "format_arg", 1, 1, false, true, true, false,
handle_format_arg_attribute, NULL },
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
};
/* Returns TRUE iff the attribute indicated by ATTR_ID takes a plain
identifier as an argument, so the front end shouldn't look it up. */
bool
attribute_takes_identifier_p (const_tree attr_id)
{
const struct attribute_spec *spec = lookup_attribute_spec (attr_id);
if (spec == NULL)
/* Unknown attribute that we'll end up ignoring, return true so we
don't complain about an identifier argument. */
return true;
else if (!strcmp ("mode", spec->name)
|| !strcmp ("format", spec->name)
|| !strcmp ("cleanup", spec->name)
|| !strcmp ("access", spec->name))
return true;
else
return targetm.attribute_takes_identifier_p (attr_id);
}
/* Verify that argument value POS at position ARGNO to attribute NAME
applied to function TYPE refers to a function parameter at position
POS and the expected type CODE. Treat CODE == INTEGER_TYPE as
matching all C integral types except bool. If successful, return
POS after default conversions, if any. Otherwise, issue appropriate
warnings and return null. A non-zero 1-based ARGNO should be passed
in by callers only for attributes with more than one argument. */
tree
positional_argument (const_tree fntype, const_tree atname, tree pos,
tree_code code, int argno /* = 0 */,
int flags /* = posargflags () */)
{
if (pos && TREE_CODE (pos) != IDENTIFIER_NODE
&& TREE_CODE (pos) != FUNCTION_DECL)
pos = default_conversion (pos);
tree postype = TREE_TYPE (pos);
if (pos == error_mark_node || !postype)
{
/* Only mention the positional argument number when it's non-zero. */
if (argno < 1)
warning (OPT_Wattributes,
"%qE attribute argument is invalid", atname);
else
warning (OPT_Wattributes,
"%qE attribute argument %i is invalid", atname, argno);
return NULL_TREE;
}
if (!INTEGRAL_TYPE_P (postype))
{
/* Handle this case specially to avoid mentioning the value
of pointer constants in diagnostics. Only mention
the positional argument number when it's non-zero. */
if (argno < 1)
warning (OPT_Wattributes,
"%qE attribute argument has type %qT",
atname, postype);
else
warning (OPT_Wattributes,
"%qE attribute argument %i has type %qT",
atname, argno, postype);
return NULL_TREE;
}
if (TREE_CODE (pos) != INTEGER_CST)
{
/* Only mention the argument number when it's non-zero. */
if (argno < 1)
warning (OPT_Wattributes,
"%qE attribute argument value %qE is not an integer "
"constant",
atname, pos);
else
warning (OPT_Wattributes,
"%qE attribute argument %i value %qE is not an integer "
"constant",
atname, argno, pos);
return NULL_TREE;
}
/* Argument positions are 1-based. */
if (integer_zerop (pos))
{
if (flags & POSARG_ZERO)
/* Zero is explicitly allowed. */
return pos;
if (argno < 1)
warning (OPT_Wattributes,
"%qE attribute argument value %qE does not refer to "
"a function parameter",
atname, pos);
else
warning (OPT_Wattributes,
"%qE attribute argument %i value %qE does not refer to "
"a function parameter",
atname, argno, pos);
return NULL_TREE;
}
if (!prototype_p (fntype))
return pos;
/* Verify that the argument position does not exceed the number
of formal arguments to the function. When POSARG_ELLIPSIS
is set, ARGNO may be beyond the last argument of a vararg
function. */
unsigned nargs = type_num_arguments (fntype);
if (!nargs
|| !tree_fits_uhwi_p (pos)
|| ((flags & POSARG_ELLIPSIS) == 0
&& !IN_RANGE (tree_to_uhwi (pos), 1, nargs)))
{
if (argno < 1)
warning (OPT_Wattributes,
"%qE attribute argument value %qE exceeds the number "
"of function parameters %u",
atname, pos, nargs);
else
warning (OPT_Wattributes,
"%qE attribute argument %i value %qE exceeds the number "
"of function parameters %u",
atname, argno, pos, nargs);
return NULL_TREE;
}
/* Verify that the type of the referenced formal argument matches
the expected type. */
unsigned HOST_WIDE_INT ipos = tree_to_uhwi (pos);
/* Zero was handled above. */
gcc_assert (ipos != 0);
if (tree argtype = type_argument_type (fntype, ipos))
{
if (argtype == error_mark_node)
return NULL_TREE;
if (flags & POSARG_ELLIPSIS)
{
if (argno < 1)
error ("%qE attribute argument value %qE does not refer to "
"a variable argument list",
atname, pos);
else
error ("%qE attribute argument %i value %qE does not refer to "
"a variable argument list",
atname, argno, pos);
return NULL_TREE;
}
/* Where the expected code is STRING_CST accept any pointer
expected by attribute format (this includes possibly qualified
char pointers and, for targets like Darwin, also pointers to
struct CFString). */
bool type_match;
if (code == STRING_CST)
type_match = valid_format_string_type_p (argtype);
else if (code == INTEGER_TYPE)
/* For integers, accept enums, wide characters and other types
that match INTEGRAL_TYPE_P except for bool. */
type_match = (INTEGRAL_TYPE_P (argtype)
&& TREE_CODE (argtype) != BOOLEAN_TYPE);
else
type_match = TREE_CODE (argtype) == code;
if (!type_match)
{
if (code == STRING_CST)
{
/* Reject invalid format strings with an error. */
if (argno < 1)
error ("%qE attribute argument value %qE refers to "
"parameter type %qT",
atname, pos, argtype);
else
error ("%qE attribute argument %i value %qE refers to "
"parameter type %qT",
atname, argno, pos, argtype);
return NULL_TREE;
}
if (argno < 1)
warning (OPT_Wattributes,
"%qE attribute argument value %qE refers to "
"parameter type %qT",
atname, pos, argtype);
else
warning (OPT_Wattributes,
"%qE attribute argument %i value %qE refers to "
"parameter type %qT",
atname, argno, pos, argtype);
return NULL_TREE;
}
}
else if (!(flags & POSARG_ELLIPSIS))
{
if (argno < 1)
warning (OPT_Wattributes,
"%qE attribute argument value %qE refers to "
"a variadic function parameter of unknown type",
atname, pos);
else
warning (OPT_Wattributes,
"%qE attribute argument %i value %qE refers to "
"a variadic function parameter of unknown type",
atname, argno, pos);
return NULL_TREE;
}
return pos;
}
/* 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);
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. */
static 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. */
static bool
validate_attr_arg (tree node[2], tree name, tree newarg)
{
tree argarray[2] = { newarg, NULL_TREE };
return validate_attr_args (node, name, argarray);
}
/* Attribute handlers common to C front ends. */
/* Handle a "signed_bool_precision" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_signed_bool_precision_attribute (tree *node, tree name, tree args,
int, bool *no_add_attrs)
{
*no_add_attrs = true;
if (!flag_gimple)
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
return NULL_TREE;
}
if (!TYPE_P (*node) || TREE_CODE (*node) != BOOLEAN_TYPE)
{
warning (OPT_Wattributes, "%qE attribute only supported on "
"boolean types", name);
return NULL_TREE;
}
unsigned HOST_WIDE_INT prec = HOST_WIDE_INT_M1U;
if (tree_fits_uhwi_p (TREE_VALUE (args)))
prec = tree_to_uhwi (TREE_VALUE (args));
if (prec > MAX_FIXED_MODE_SIZE)
{
warning (OPT_Wattributes, "%qE attribute with unsupported boolean "
"precision", name);
return NULL_TREE;
}
tree new_type = build_nonstandard_boolean_type (prec);
*node = lang_hooks.types.reconstruct_complex_type (*node, new_type);
return NULL_TREE;
}
/* Handle a "packed" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_packed_attribute (tree *node, tree name, tree ARG_UNUSED (args),
int flags, bool *no_add_attrs)
{
if (TYPE_P (*node))
{
if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
{
warning (OPT_Wattributes,
"%qE attribute ignored for type %qT", name, *node);
*no_add_attrs = true;
}
else
TYPE_PACKED (*node) = 1;
}
else if (TREE_CODE (*node) == FIELD_DECL)
{
if (TYPE_ALIGN (TREE_TYPE (*node)) <= BITS_PER_UNIT
/* Still pack bitfields. */
&& ! DECL_C_BIT_FIELD (*node))
warning (OPT_Wattributes,
"%qE attribute ignored for field of type %qT",
name, TREE_TYPE (*node));
else
DECL_PACKED (*node) = 1;
}
/* We can't set DECL_PACKED for a VAR_DECL, because the bit is
used for DECL_REGISTER. It wouldn't mean anything anyway.
We can't set DECL_PACKED on the type of a TYPE_DECL, because
that changes what the typedef is typing. */
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "nocommon" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_nocommon_attribute (tree *node, tree name,
tree ARG_UNUSED (args),
int ARG_UNUSED (flags), bool *no_add_attrs)
{
if (VAR_P (*node))
DECL_COMMON (*node) = 0;
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "common" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_common_attribute (tree *node, tree name, tree ARG_UNUSED (args),
int ARG_UNUSED (flags), bool *no_add_attrs)
{
if (VAR_P (*node))
DECL_COMMON (*node) = 1;
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "noreturn" attribute; arguments as in
struct attribute_spec.handler. */
tree
handle_noreturn_attribute (tree *node, tree name, tree ARG_UNUSED (args),
int ARG_UNUSED (flags), bool *no_add_attrs)
{
tree type = TREE_TYPE (*node);
/* See FIXME comment in c_common_attribute_table. */
if (TREE_CODE (*node) == FUNCTION_DECL
|| objc_method_decl (TREE_CODE (*node)))
TREE_THIS_VOLATILE (*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),
TYPE_READONLY (TREE_TYPE (type)), 1)),
TYPE_QUALS (type)));
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "hot" and attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_hot_attribute (tree *node, tree name, tree ARG_UNUSED (args),
int ARG_UNUSED (flags), bool *no_add_attrs)
{
if (TREE_CODE (*node) == FUNCTION_DECL
|| TREE_CODE (*node) == LABEL_DECL)
{
/* Attribute hot processing is done later with lookup_attribute. */
}
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "cold" and attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_cold_attribute (tree *node, tree name, tree ARG_UNUSED (args),
int ARG_UNUSED (flags), bool *no_add_attrs)
{
if (TREE_CODE (*node) == FUNCTION_DECL
|| TREE_CODE (*node) == LABEL_DECL)
{
/* Attribute cold processing is done later with lookup_attribute. */
}
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Add FLAGS for a function NODE to no_sanitize_flags in DECL_ATTRIBUTES. */
void
add_no_sanitize_value (tree node, unsigned int flags)
{
tree attr = lookup_attribute ("no_sanitize", DECL_ATTRIBUTES (node));
if (attr)
{
unsigned int old_value = tree_to_uhwi (TREE_VALUE (attr));
flags |= old_value;
if (flags == old_value)
return;
TREE_VALUE (attr) = build_int_cst (unsigned_type_node, flags);
}
else
DECL_ATTRIBUTES (node)
= tree_cons (get_identifier ("no_sanitize"),
build_int_cst (unsigned_type_node, flags),
DECL_ATTRIBUTES (node));
}
/* Handle a "no_sanitize" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_no_sanitize_attribute (tree *node, tree name, tree args, int,
bool *no_add_attrs)
{
unsigned int flags = 0;
*no_add_attrs = true;
if (TREE_CODE (*node) != FUNCTION_DECL)
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
return NULL_TREE;
}
for (; args; args = TREE_CHAIN (args))
{
tree id = TREE_VALUE (args);
if (TREE_CODE (id) != STRING_CST)
{
error ("%qE argument not a string", name);
return NULL_TREE;
}
char *string = ASTRDUP (TREE_STRING_POINTER (id));
flags |= parse_no_sanitize_attribute (string);
}
add_no_sanitize_value (*node, flags);
return NULL_TREE;
}
/* Handle a "no_sanitize_address" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_no_sanitize_address_attribute (tree *node, tree name, tree, int,
bool *no_add_attrs)
{
*no_add_attrs = true;
if (TREE_CODE (*node) != FUNCTION_DECL)
warning (OPT_Wattributes, "%qE attribute ignored", name);
else
add_no_sanitize_value (*node, SANITIZE_ADDRESS);
return NULL_TREE;
}
/* Handle a "no_sanitize_thread" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_no_sanitize_thread_attribute (tree *node, tree name, tree, int,
bool *no_add_attrs)
{
*no_add_attrs = true;
if (TREE_CODE (*node) != FUNCTION_DECL)
warning (OPT_Wattributes, "%qE attribute ignored", name);
else
add_no_sanitize_value (*node, SANITIZE_THREAD);
return NULL_TREE;
}
/* Handle a "no_address_safety_analysis" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_no_address_safety_analysis_attribute (tree *node, tree name, tree, int,
bool *no_add_attrs)
{
*no_add_attrs = true;
if (TREE_CODE (*node) != FUNCTION_DECL)
warning (OPT_Wattributes, "%qE attribute ignored", name);
else
add_no_sanitize_value (*node, SANITIZE_ADDRESS);
return NULL_TREE;
}
/* Handle a "no_sanitize_undefined" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_no_sanitize_undefined_attribute (tree *node, tree name, tree, int,
bool *no_add_attrs)
{
*no_add_attrs = true;
if (TREE_CODE (*node) != FUNCTION_DECL)
warning (OPT_Wattributes, "%qE attribute ignored", name);
else
add_no_sanitize_value (*node,
SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT);
return NULL_TREE;
}
/* Handle a "no_sanitize_coverage" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_no_sanitize_coverage_attribute (tree *node, tree name, tree, int,
bool *no_add_attrs)
{
if (TREE_CODE (*node) != FUNCTION_DECL)
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle an "asan odr indicator" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_asan_odr_indicator_attribute (tree *, tree, tree, int, bool *)
{
return NULL_TREE;
}
/* Handle a "stack_protect" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_stack_protect_attribute (tree *node, tree name, tree, int,
bool *no_add_attrs)
{
if (TREE_CODE (*node) != FUNCTION_DECL)
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "no_stack_protector" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_no_stack_protector_function_attribute (tree *node, tree name, tree,
int, bool *no_add_attrs)
{
if (TREE_CODE (*node) != FUNCTION_DECL)
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "noipa" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_noipa_attribute (tree *node, tree name, tree, int, bool *no_add_attrs)
{
if (TREE_CODE (*node) != FUNCTION_DECL)
{
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. */
static 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)
{
if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (*node)))
{
warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
"with attribute %qs", name, "always_inline");
*no_add_attrs = true;
}
else
DECL_UNINLINABLE (*node) = 1;
}
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "noclone" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_noclone_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;
}
return NULL_TREE;
}
/* Handle a "nocf_check" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_nocf_check_attribute (tree *node, tree name,
tree ARG_UNUSED (args),
int ARG_UNUSED (flags), bool *no_add_attrs)
{
if (TREE_CODE (*node) != FUNCTION_TYPE
&& TREE_CODE (*node) != METHOD_TYPE)
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
else if (!(flag_cf_protection & CF_BRANCH))
{
warning (OPT_Wattributes, "%qE attribute ignored. Use "
"%<-fcf-protection%> option to enable it",
name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "no_icf" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_noicf_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;
}
return NULL_TREE;
}
/* Handle a "always_inline" attribute; arguments as in
struct attribute_spec.handler. */
static 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)
{
if (lookup_attribute ("noinline", DECL_ATTRIBUTES (*node)))
{
warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
"with %qs attribute", name, "noinline");
*no_add_attrs = true;
}
else if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (*node)))
{
warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
"with %qs attribute", name, "target_clones");
*no_add_attrs = true;
}
else
/* 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 "gnu_inline" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_gnu_inline_attribute (tree *node, tree name,
tree ARG_UNUSED (args),
int ARG_UNUSED (flags),
bool *no_add_attrs)
{
if (TREE_CODE (*node) == FUNCTION_DECL && DECL_DECLARED_INLINE_P (*node))
{
/* Do nothing else, just set the attribute. We'll get at
it later with lookup_attribute. */
}
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "leaf" attribute; arguments as in
struct attribute_spec.handler. */
static 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 an "artificial" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_artificial_attribute (tree *node, tree name,
tree ARG_UNUSED (args),
int ARG_UNUSED (flags),
bool *no_add_attrs)
{
if (TREE_CODE (*node) == FUNCTION_DECL && DECL_DECLARED_INLINE_P (*node))
{
/* Do nothing else, just set the attribute. We'll get at
it later with lookup_attribute. */
}
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "flatten" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_flatten_attribute (tree *node, tree name,
tree args ATTRIBUTE_UNUSED,
int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
{
if (TREE_CODE (*node) == FUNCTION_DECL)
/* Do nothing else, just set the attribute. We'll get at
it later with lookup_attribute. */
;
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "warning" or "error" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_error_attribute (tree *node, tree name, tree args,
int ARG_UNUSED (flags), bool *no_add_attrs)
{
if (TREE_CODE (*node) == FUNCTION_DECL
&& TREE_CODE (TREE_VALUE (args)) == STRING_CST)
/* Do nothing else, just set the attribute. We'll get at
it later with lookup_attribute. */
;
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "used" attribute; arguments as in
struct attribute_spec.handler. */
static 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 a "unused" attribute; arguments as in
struct attribute_spec.handler. */
tree
handle_unused_attribute (tree *node, tree name, tree ARG_UNUSED (args),
int flags, bool *no_add_attrs)
{
if (DECL_P (*node))
{
tree decl = *node;
if (TREE_CODE (decl) == PARM_DECL
|| VAR_OR_FUNCTION_DECL_P (decl)
|| TREE_CODE (decl) == LABEL_DECL
|| TREE_CODE (decl) == CONST_DECL
|| TREE_CODE (decl) == FIELD_DECL
|| TREE_CODE (decl) == TYPE_DECL)
{
TREE_USED (decl) = 1;
if (VAR_P (decl) || TREE_CODE (decl) == PARM_DECL)
DECL_READ_P (decl) = 1;
}
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
}
else
{
if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
*node = build_variant_type_copy (*node);
TREE_USED (*node) = 1;
}
return NULL_TREE;
}
/* Handle a "retain" attribute; arguments as in
struct attribute_spec.handler. */
static 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;
}
/* Handle an "uninitialized" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_uninitialized_attribute (tree *node, tree name, tree ARG_UNUSED (args),
int ARG_UNUSED (flags), bool *no_add_attrs)
{
tree decl = *node;
if (!VAR_P (decl))
{
warning (OPT_Wattributes, "%qE attribute ignored because %qD "
"is not a variable", name, decl);
*no_add_attrs = true;
}
else if (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
{
warning (OPT_Wattributes, "%qE attribute ignored because %qD "
"is not a local variable", name, decl);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "externally_visible" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_externally_visible_attribute (tree *pnode, tree name,
tree ARG_UNUSED (args),
int ARG_UNUSED (flags),
bool *no_add_attrs)
{
tree node = *pnode;
if (VAR_OR_FUNCTION_DECL_P (node))
{
if ((!TREE_STATIC (node) && TREE_CODE (node) != FUNCTION_DECL
&& !DECL_EXTERNAL (node)) || !TREE_PUBLIC (node))
{
warning (OPT_Wattributes,
"%qE attribute have effect only on public objects", name);
*no_add_attrs = true;
}
}
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle the "no_reorder" attribute. Arguments as in
struct attribute_spec.handler. */
static tree
handle_no_reorder_attribute (tree *pnode,
tree name,
tree,
int,
bool *no_add_attrs)
{
tree node = *pnode;
if (!VAR_OR_FUNCTION_DECL_P (node)
&& !(TREE_STATIC (node) || DECL_EXTERNAL (node)))
{
warning (OPT_Wattributes,
"%qE attribute only affects top level objects",
name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "const" attribute; arguments as in
struct attribute_spec.handler. */
static 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 "scalar_storage_order" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_scalar_storage_order_attribute (tree *node, tree name, tree args,
int flags, bool *no_add_attrs)
{
tree id = TREE_VALUE (args);
tree type;
if (TREE_CODE (*node) == TYPE_DECL
&& ! (flags & ATTR_FLAG_CXX11))
node = &TREE_TYPE (*node);
type = *node;
if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN)
{
error ("%qE attribute is not supported because endianness is not uniform",
name);
return NULL_TREE;
}
if (RECORD_OR_UNION_TYPE_P (type) && !c_dialect_cxx ())
{
bool reverse = false;
if (TREE_CODE (id) == STRING_CST
&& strcmp (TREE_STRING_POINTER (id), "big-endian") == 0)
reverse = !BYTES_BIG_ENDIAN;
else if (TREE_CODE (id) == STRING_CST
&& strcmp (TREE_STRING_POINTER (id), "little-endian") == 0)
reverse = BYTES_BIG_ENDIAN;
else
{
error ("attribute %qE argument must be one of %qs or %qs",
name, "big-endian", "little-endian");
return NULL_TREE;
}
if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
{
if (reverse)
/* A type variant isn't good enough, since we don't want a cast
to such a type to be removed as a no-op. */
*node = type = build_duplicate_type (type);
}
TYPE_REVERSE_STORAGE_ORDER (type) = reverse;
return NULL_TREE;
}
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
return NULL_TREE;
}
/* Handle a "transparent_union" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_transparent_union_attribute (tree *node, tree name,
tree ARG_UNUSED (args), int flags,
bool *no_add_attrs)
{
tree type;
*no_add_attrs = true;
if (TREE_CODE (*node) == TYPE_DECL
&& ! (flags & ATTR_FLAG_CXX11))
node = &TREE_TYPE (*node);
type = *node;
if (TREE_CODE (type) == UNION_TYPE)
{
/* Make sure that the first field will work for a transparent union.
If the type isn't complete yet, leave the check to the code in
finish_struct. */
if (TYPE_SIZE (type))
{
tree first = first_field (type);
if (first == NULL_TREE
|| DECL_ARTIFICIAL (first)
|| TYPE_MODE (type) != DECL_MODE (first))
goto ignored;
}
if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
{
/* If the type isn't complete yet, setting the flag
on a variant wouldn't ever be checked. */
if (!TYPE_SIZE (type))
goto ignored;
/* build_duplicate_type doesn't work for C++. */
if (c_dialect_cxx ())
goto ignored;
/* A type variant isn't good enough, since we don't want a cast
to such a type to be removed as a no-op. */
*node = type = build_duplicate_type (type);
}
for (tree t = TYPE_MAIN_VARIANT (type); t; t = TYPE_NEXT_VARIANT (t))
TYPE_TRANSPARENT_AGGR (t) = 1;
return NULL_TREE;
}
ignored:
warning (OPT_Wattributes, "%qE attribute ignored", name);
return NULL_TREE;
}
/* Subroutine of handle_{con,de}structor_attribute. Evaluate ARGS to
get the requested priority for a constructor or destructor,
possibly issuing diagnostics for invalid or reserved
priorities. */
static priority_type
get_priority (tree args, bool is_destructor)
{
HOST_WIDE_INT pri;
tree arg;
if (!args)
return DEFAULT_INIT_PRIORITY;
if (!SUPPORTS_INIT_PRIORITY)
{
if (is_destructor)
error ("destructor priorities are not supported");
else
error ("constructor priorities are not supported");
return DEFAULT_INIT_PRIORITY;
}
arg = TREE_VALUE (args);
if (TREE_CODE (arg) == IDENTIFIER_NODE)
goto invalid;
if (arg == error_mark_node)
return DEFAULT_INIT_PRIORITY;
arg = default_conversion (arg);
if (!tree_fits_shwi_p (arg)
|| !INTEGRAL_TYPE_P (TREE_TYPE (arg)))
goto invalid;
pri = tree_to_shwi (arg);
if (pri < 0 || pri > MAX_INIT_PRIORITY)
goto invalid;
if (pri <= MAX_RESERVED_INIT_PRIORITY)
{
if (is_destructor)
warning (OPT_Wprio_ctor_dtor,
"destructor priorities from 0 to %d are reserved "
"for the implementation",
MAX_RESERVED_INIT_PRIORITY);
else
warning (OPT_Wprio_ctor_dtor,
"constructor priorities from 0 to %d are reserved "
"for the implementation",
MAX_RESERVED_INIT_PRIORITY);
}
return pri;
invalid:
if (is_destructor)
error ("destructor priorities must be integers from 0 to %d inclusive",
MAX_INIT_PRIORITY);
else
error ("constructor priorities must be integers from 0 to %d inclusive",
MAX_INIT_PRIORITY);
return DEFAULT_INIT_PRIORITY;
}
/* Handle a "constructor" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_constructor_attribute (tree *node, tree name, tree args,
int ARG_UNUSED (flags),
bool *no_add_attrs)
{
tree decl = *node;
tree type = TREE_TYPE (decl);
if (TREE_CODE (decl) == FUNCTION_DECL
&& TREE_CODE (type) == FUNCTION_TYPE
&& decl_function_context (decl) == 0)
{
priority_type priority;
DECL_STATIC_CONSTRUCTOR (decl) = 1;
priority = get_priority (args, /*is_destructor=*/false);
SET_DECL_INIT_PRIORITY (decl, priority);
TREE_USED (decl) = 1;
}
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "destructor" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_destructor_attribute (tree *node, tree name, tree args,
int ARG_UNUSED (flags),
bool *no_add_attrs)
{
tree decl = *node;
tree type = TREE_TYPE (decl);
if (TREE_CODE (decl) == FUNCTION_DECL
&& TREE_CODE (type) == FUNCTION_TYPE
&& decl_function_context (decl) == 0)
{
priority_type priority;
DECL_STATIC_DESTRUCTOR (decl) = 1;
priority = get_priority (args, /*is_destructor=*/true);
SET_DECL_FINI_PRIORITY (decl, priority);
TREE_USED (decl) = 1;
}
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Nonzero if the mode is a valid vector mode for this architecture.
This returns nonzero even if there is no hardware support for the
vector mode, but we can emulate with narrower modes. */
static bool
vector_mode_valid_p (machine_mode mode)
{
enum mode_class mclass = GET_MODE_CLASS (mode);
/* Doh! What's going on? */
if (mclass != MODE_VECTOR_INT
&& mclass != MODE_VECTOR_FLOAT
&& mclass != MODE_VECTOR_FRACT
&& mclass != MODE_VECTOR_UFRACT
&& mclass != MODE_VECTOR_ACCUM
&& mclass != MODE_VECTOR_UACCUM)
return false;
/* Hardware support. Woo hoo! */
if (targetm.vector_mode_supported_p (mode))
return true;
/* We should probably return 1 if requesting V4DI and we have no DI,
but we have V2DI, but this is probably very unlikely. */
/* If we have support for the inner mode, we can safely emulate it.
We may not have V2DI, but me can emulate with a pair of DIs. */
return targetm.scalar_mode_supported_p (GET_MODE_INNER (mode));
}
/* Handle a "mode" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_mode_attribute (tree *node, tree name, tree args,
int ARG_UNUSED (flags), bool *no_add_attrs)
{
tree type = *node;
tree ident = TREE_VALUE (args);
*no_add_attrs = true;
if (TREE_CODE (ident) != IDENTIFIER_NODE)
warning (OPT_Wattributes, "%qE attribute ignored", name);
else
{
int j;
const char *p = IDENTIFIER_POINTER (ident);
int len = strlen (p);
machine_mode mode = VOIDmode;
tree typefm;
bool valid_mode;
if (len > 4 && p[0] == '_' && p[1] == '_'
&& p[len - 1] == '_' && p[len - 2] == '_')
{
char *newp = (char *) alloca (len - 1);
strcpy (newp, &p[2]);
newp[len - 4] = '\0';
p = newp;
}
/* Change this type to have a type with the specified mode.
First check for the special modes. */
if (!strcmp (p, "byte"))
mode = byte_mode;
else if (!strcmp (p, "word"))
mode = word_mode;
else if (!strcmp (p, "pointer"))
mode = ptr_mode;
else if (!strcmp (p, "libgcc_cmp_return"))
mode = targetm.libgcc_cmp_return_mode ();
else if (!strcmp (p, "libgcc_shift_count"))
mode = targetm.libgcc_shift_count_mode ();
else if (!strcmp (p, "unwind_word"))
mode = targetm.unwind_word_mode ();
else
for (j = 0; j < NUM_MACHINE_MODES; j++)
if (!strcmp (p, GET_MODE_NAME (j)))
{
mode = (machine_mode) j;
break;
}
if (mode == VOIDmode)
{
error ("unknown machine mode %qE", ident);
return NULL_TREE;
}
/* Allow the target a chance to translate MODE into something supported.
See PR86324. */
mode = targetm.translate_mode_attribute (mode);
valid_mode = false;
switch (GET_MODE_CLASS (mode))
{
case MODE_INT:
case MODE_PARTIAL_INT:
case MODE_FLOAT:
case MODE_DECIMAL_FLOAT:
case MODE_FRACT:
case MODE_UFRACT:
case MODE_ACCUM:
case MODE_UACCUM:
valid_mode
= targetm.scalar_mode_supported_p (as_a <scalar_mode> (mode));
break;
case MODE_COMPLEX_INT:
case MODE_COMPLEX_FLOAT:
valid_mode = targetm.scalar_mode_supported_p (GET_MODE_INNER (mode));
break;
case MODE_VECTOR_INT:
case MODE_VECTOR_FLOAT:
case MODE_VECTOR_FRACT:
case MODE_VECTOR_UFRACT:
case MODE_VECTOR_ACCUM:
case MODE_VECTOR_UACCUM:
warning (OPT_Wattributes, "specifying vector types with "
"%<__attribute__ ((mode))%> is deprecated");
inform (input_location,
"use %<__attribute__ ((vector_size))%> instead");
valid_mode = vector_mode_valid_p (mode);
break;
default:
break;
}
if (!valid_mode)
{
error ("unable to emulate %qs", p);
return NULL_TREE;
}
if (POINTER_TYPE_P (type))
{
scalar_int_mode addr_mode;
addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (type));
tree (*fn)(tree, machine_mode, bool);
if (!is_a <scalar_int_mode> (mode, &addr_mode)
|| !targetm.addr_space.valid_pointer_mode (addr_mode, as))
{
error ("invalid pointer mode %qs", p);
return NULL_TREE;
}
if (TREE_CODE (type) == POINTER_TYPE)
fn = build_pointer_type_for_mode;
else
fn = build_reference_type_for_mode;
typefm = fn (TREE_TYPE (type), addr_mode, false);
}
else
{
/* For fixed-point modes, we need to test if the signness of type
and the machine mode are consistent. */
if (ALL_FIXED_POINT_MODE_P (mode)
&& TYPE_UNSIGNED (type) != UNSIGNED_FIXED_POINT_MODE_P (mode))
{
error ("signedness of type and machine mode %qs don%'t match", p);
return NULL_TREE;
}
/* For fixed-point modes, we need to pass saturating info. */
typefm = lang_hooks.types.type_for_mode (mode,
ALL_FIXED_POINT_MODE_P (mode) ? TYPE_SATURATING (type)
: TYPE_UNSIGNED (type));
}
if (typefm == NULL_TREE)
{
error ("no data type for mode %qs", p);
return NULL_TREE;
}
else if (TREE_CODE (type) == ENUMERAL_TYPE)
{
/* For enumeral types, copy the precision from the integer
type returned above. If not an INTEGER_TYPE, we can't use
this mode for this type. */
if (TREE_CODE (typefm) != INTEGER_TYPE)
{
error ("cannot use mode %qs for enumerated types", p);
return NULL_TREE;
}
if (flags & ATTR_FLAG_TYPE_IN_PLACE)
{
TYPE_PRECISION (type) = TYPE_PRECISION (typefm);
typefm = type;
}
else
{
/* We cannot build a type variant, as there's code that assumes
that TYPE_MAIN_VARIANT has the same mode. This includes the
debug generators. Instead, create a subrange type. This
results in all of the enumeral values being emitted only once
in the original, and the subtype gets them by reference. */
if (TYPE_UNSIGNED (type))
typefm = make_unsigned_type (TYPE_PRECISION (typefm));
else
typefm = make_signed_type (TYPE_PRECISION (typefm));
TREE_TYPE (typefm) = type;
}
*no_add_attrs = false;
}
else if (VECTOR_MODE_P (mode)
? TREE_CODE (type) != TREE_CODE (TREE_TYPE (typefm))
: TREE_CODE (type) != TREE_CODE (typefm))
{
error ("mode %qs applied to inappropriate type", p);
return NULL_TREE;
}
*node = build_qualified_type (typefm, TYPE_QUALS (type));
}
return NULL_TREE;
}
/* Handle a "section" attribute; arguments as in
struct attribute_spec.handler. */
static 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;
}
/* Common codes shared by handle_warn_if_not_aligned_attribute and
handle_aligned_attribute. */
static tree
common_handle_aligned_attribute (tree *node, tree name, tree args, int flags,
bool *no_add_attrs,
bool warn_if_not_aligned_p)
{
tree decl = NULL_TREE;
tree *type = NULL;
bool is_type = false;
tree align_expr;
/* The last (already pushed) declaration with all validated attributes
merged in or the current about-to-be-pushed one if one hasn't been
yet. */
tree last_decl = node[1] ? node[1] : *node;
if (args)
{
align_expr = TREE_VALUE (args);
if (align_expr && TREE_CODE (align_expr) != IDENTIFIER_NODE
&& TREE_CODE (align_expr) != FUNCTION_DECL)
align_expr = default_conversion (align_expr);
}
else
align_expr = size_int (ATTRIBUTE_ALIGNED_VALUE / BITS_PER_UNIT);
if (DECL_P (*node))
{
decl = *node;
type = &TREE_TYPE (decl);
is_type = TREE_CODE (*node) == TYPE_DECL;
}
else if (TYPE_P (*node))
type = node, is_type = true;
/* True to consider invalid alignments greater than MAX_OFILE_ALIGNMENT. */
bool objfile = (TREE_CODE (*node) == FUNCTION_DECL
|| (VAR_P (*node) && TREE_STATIC (*node)));
/* Log2 of specified alignment. */
int pow2align = check_user_alignment (align_expr, objfile,
/* warn_zero = */ true);
if (pow2align == -1)
{
*no_add_attrs = true;
return NULL_TREE;
}
/* The alignment in bits corresponding to the specified alignment. */
unsigned bitalign = (1U << pow2align) * BITS_PER_UNIT;
/* The alignment of the current declaration and that of the last
pushed declaration, determined on demand below. */
unsigned curalign = 0;
unsigned lastalign = 0;
/* True when SET_DECL_ALIGN() should be called for the decl when
*NO_ADD_ATTRS is false. */
bool set_align = true;
if (is_type)
{
if ((flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
/* OK, modify the type in place. */;
/* If we have a TYPE_DECL, then copy the type, so that we
don't accidentally modify a builtin type. See pushdecl. */
else if (decl && TREE_TYPE (decl) != error_mark_node
&& DECL_ORIGINAL_TYPE (decl) == NULL_TREE)
{
tree tt = TREE_TYPE (decl);
*type = build_variant_type_copy (*type);
DECL_ORIGINAL_TYPE (decl) = tt;
TYPE_NAME (*type) = decl;
TREE_USED (*type) = TREE_USED (decl);
TREE_TYPE (decl) = *type;
}
else
*type = build_variant_type_copy (*type);
if (warn_if_not_aligned_p)
{
SET_TYPE_WARN_IF_NOT_ALIGN (*type, bitalign);
warn_if_not_aligned_p = false;
}
else
{
SET_TYPE_ALIGN (*type, bitalign);
TYPE_USER_ALIGN (*type) = 1;
}
}
else if (! VAR_OR_FUNCTION_DECL_P (decl)
&& TREE_CODE (decl) != FIELD_DECL)
{
error ("alignment may not be specified for %q+D", decl);
*no_add_attrs = true;
}
else if (TREE_CODE (decl) == FUNCTION_DECL
&& (((curalign = DECL_ALIGN (decl)) > bitalign)
| ((lastalign = DECL_ALIGN (last_decl)) > bitalign)))
{
/* Either a prior attribute on the same declaration or one
on a prior declaration of the same function specifies
stricter alignment than this attribute. */
bool note = (lastalign > curalign
|| (lastalign == curalign
&& (DECL_USER_ALIGN (last_decl)
> DECL_USER_ALIGN (decl))));
if (note)
curalign = lastalign;
curalign /= BITS_PER_UNIT;
unsigned newalign = bitalign / BITS_PER_UNIT;
auto_diagnostic_group d;
if ((DECL_USER_ALIGN (decl)
|| DECL_USER_ALIGN (last_decl)))
{
if (warning (OPT_Wattributes,
"ignoring attribute %<%E (%u)%> because it conflicts "
"with attribute %<%E (%u)%>",
name, newalign, name, curalign)
&& note)
inform (DECL_SOURCE_LOCATION (last_decl),
"previous declaration here");
/* Only reject attempts to relax/override an alignment
explicitly specified previously and accept declarations
that appear to relax the implicit function alignment for
the target. Both increasing and increasing the alignment
set by -falign-functions setting is permitted. */
*no_add_attrs = true;
}
else if (!warn_if_not_aligned_p)
{
/* Do not fail for attribute warn_if_not_aligned. Otherwise,
silently avoid applying the alignment to the declaration
because it's implicitly satisfied by the target. Apply
the attribute nevertheless so it can be retrieved by
__builtin_has_attribute. */
set_align = false;
}
}
else if (DECL_USER_ALIGN (decl)
&& DECL_ALIGN (decl) > bitalign)
/* C++-11 [dcl.align/4]:
When multiple alignment-specifiers are specified for an
entity, the alignment requirement shall be set to the
strictest specified alignment.
This formally comes from the c++11 specification but we are
doing it for the GNU attribute syntax as well. */
*no_add_attrs = true;
else if (warn_if_not_aligned_p
&& TREE_CODE (decl) == FIELD_DECL
&& !DECL_C_BIT_FIELD (decl))
{
SET_DECL_WARN_IF_NOT_ALIGN (decl, bitalign);
warn_if_not_aligned_p = false;
set_align = false;
}
if (warn_if_not_aligned_p)
{
error ("%<warn_if_not_aligned%> may not be specified for %q+D",
decl);
*no_add_attrs = true;
}
else if (!is_type && !*no_add_attrs && set_align)
{
SET_DECL_ALIGN (decl, bitalign);
DECL_USER_ALIGN (decl) = 1;
}
return NULL_TREE;
}
/* Handle a "aligned" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_aligned_attribute (tree *node, tree name, tree args,
int flags, bool *no_add_attrs)
{
return common_handle_aligned_attribute (node, name, args, flags,
no_add_attrs, false);
}
/* Handle a "warn_if_not_aligned" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_warn_if_not_aligned_attribute (tree *node, tree name,
tree args, int flags,
bool *no_add_attrs)
{
return common_handle_aligned_attribute (node, name, args, flags,
no_add_attrs, true);
}
/* Handle a "weak" attribute; arguments as in
struct attribute_spec.handler. */
static 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 "noinit" or "persistent" attribute; arguments as in
struct attribute_spec.handler.
This generic handler is used for "special variable sections" that allow the
section name to be set using a dedicated attribute. Additional validation
is performed for the specific properties of the section corresponding to the
attribute.
The ".noinit" section *is not* loaded by the program loader, and is not
initialized by the runtime startup code.
The ".persistent" section *is* loaded by the program loader, but is not
initialized by the runtime startup code. */
static tree
handle_special_var_sec_attribute (tree *node, tree name, tree args,
int flags, bool *no_add_attrs)
{
tree decl = *node;
tree res = NULL_TREE;
/* First perform generic validation common to "noinit" and "persistent"
attributes. */
if (!targetm_common.have_named_sections)
{
error_at (DECL_SOURCE_LOCATION (decl),
"section attributes are not supported for this target");
goto fail;
}
if (!VAR_P (decl))
{
warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
"ignoring %qE attribute not set on a variable",
name);
goto fail;
}
if (VAR_P (decl)
&& current_function_decl != NULL_TREE
&& !TREE_STATIC (decl))
{
error_at (DECL_SOURCE_LOCATION (decl),
"%qE attribute cannot be specified for local variables",
name);
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", decl);
goto fail;
}
if (!targetm.have_switchable_bss_sections)
{
error ("%qE attribute is specific to ELF targets", name);
goto fail;
}
if (TREE_READONLY (decl))
{
warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
"ignoring %qE attribute set on const variable",
name);
goto fail;
}
/* Now validate noinit/persistent individually. */
if (strcmp (IDENTIFIER_POINTER (name), "noinit") == 0)
{
if (DECL_INITIAL (decl))
{
warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
"ignoring %qE attribute set on initialized variable",
name);
goto fail;
}
/* If this var is thought to be common, then change this. "noinit"
variables must be placed in an explicit ".noinit" section. */
DECL_COMMON (decl) = 0;
}
else if (strcmp (IDENTIFIER_POINTER (name), "persistent") == 0)
{
if (DECL_COMMON (decl) || DECL_INITIAL (decl) == NULL_TREE)
{
warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
"ignoring %qE attribute set on uninitialized variable",
name);
goto fail;
}
}
else
gcc_unreachable ();
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))
return res;
fail:
*no_add_attrs = true;
return res;
}
/* Handle a "noplt" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_noplt_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)
{
warning (OPT_Wattributes,
"%qE attribute is only applicable on functions", name);
*no_add_attrs = true;
return NULL_TREE;
}
return NULL_TREE;
}
/* Handle a "symver" attribute. */
static tree
handle_symver_attribute (tree *node, tree ARG_UNUSED (name), tree args,
int ARG_UNUSED (flags), bool *no_add_attrs)
{
tree symver;
const char *symver_str;
if (TREE_CODE (*node) != FUNCTION_DECL && TREE_CODE (*node) != VAR_DECL)
{
warning (OPT_Wattributes,
"%<symver%> attribute only applies to functions and variables");
*no_add_attrs = true;
return NULL_TREE;
}
if (!decl_in_symtab_p (*node))
{
warning (OPT_Wattributes,
"%<symver%> attribute is only applicable to symbols");
*no_add_attrs = true;
return NULL_TREE;
}
for (; args; args = TREE_CHAIN (args))
{
symver = TREE_VALUE (args);
if (TREE_CODE (symver) != STRING_CST)
{
error ("%<symver%> attribute argument not a string constant");
*no_add_attrs = true;
return NULL_TREE;
}
symver_str = TREE_STRING_POINTER (symver);
int ats = 0;
for (int n = 0; (int)n < TREE_STRING_LENGTH (symver); n++)
if (symver_str[n] == '@')
ats++;
if (ats != 1 && ats != 2)
{
error ("symver attribute argument must have format %<name@nodename%>");
error ("%<symver%> attribute argument %qs must contain one or two "
"%<@%>", symver_str);
*no_add_attrs = true;
return NULL_TREE;
}
}
return NULL_TREE;
}
/* 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. */
static 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;
}
/* Handle an "alias" or "ifunc" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_ifunc_attribute (tree *node, tree name, tree args,
int ARG_UNUSED (flags), bool *no_add_attrs)
{
return handle_alias_ifunc_attribute (false, node, name, args, no_add_attrs);
}
/* Handle an "alias" or "ifunc" attribute; arguments as in
struct attribute_spec.handler. */
static 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 the "copy" attribute NAME by copying the set of attributes
from the symbol referenced by ARGS to the declaration of *NODE. */
static tree
handle_copy_attribute (tree *node, tree name, tree args,
int flags, bool *no_add_attrs)
{
/* Do not apply the copy attribute itself. It serves no purpose
other than to copy other attributes. */
*no_add_attrs = true;
tree decl = *node;
tree ref = TREE_VALUE (args);
if (ref == error_mark_node)
return NULL_TREE;
if (TREE_CODE (ref) == STRING_CST)
{
/* Explicitly handle this case since using a string literal
as an argument is a likely mistake. */
error_at (DECL_SOURCE_LOCATION (decl),
"%qE attribute argument cannot be a string",
name);
return NULL_TREE;
}
if (CONSTANT_CLASS_P (ref)
&& (INTEGRAL_TYPE_P (TREE_TYPE (ref))
|| FLOAT_TYPE_P (TREE_TYPE (ref))))
{
/* Similar to the string case, since some function attributes
accept literal numbers as arguments (e.g., alloc_size or
nonnull) using one here is a likely mistake. */
error_at (DECL_SOURCE_LOCATION (decl),
"%qE attribute argument cannot be a constant arithmetic "
"expression",
name);
return NULL_TREE;
}
if (ref == node[1])
{
/* Another possible mistake (but indirect self-references aren't
and diagnosed and shouldn't be). */
if (warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wattributes,
"%qE attribute ignored on a redeclaration "
"of the referenced symbol",
name))
inform (DECL_SOURCE_LOCATION (node[1]),
"previous declaration here");
return NULL_TREE;
}
/* Consider address-of expressions in the attribute argument
as requests to copy from the referenced entity. */
if (TREE_CODE (ref) == ADDR_EXPR)
ref = TREE_OPERAND (ref, 0);
do
{
/* Drill down into references to find the referenced decl. */
tree_code refcode = TREE_CODE (ref);
if (refcode == ARRAY_REF
|| refcode == INDIRECT_REF)
ref = TREE_OPERAND (ref, 0);
else if (refcode == COMPONENT_REF)
ref = TREE_OPERAND (ref, 1);
else
break;
} while (!DECL_P (ref));
/* For object pointer expressions, consider those to be requests
to copy from their type, such as in:
struct __attribute__ (copy ((struct T *)0)) U { ... };
which copies type attributes from struct T to the declaration
of struct U. */
if ((CONSTANT_CLASS_P (ref) || EXPR_P (ref))
&& POINTER_TYPE_P (TREE_TYPE (ref))
&& !FUNCTION_POINTER_TYPE_P (TREE_TYPE (ref)))
ref = TREE_TYPE (ref);
tree reftype = TYPE_P (ref) ? ref : TREE_TYPE (ref);
if (DECL_P (decl))
{
if ((VAR_P (decl)
&& (TREE_CODE (ref) == FUNCTION_DECL
|| (EXPR_P (ref)
&& POINTER_TYPE_P (reftype)
&& FUNC_OR_METHOD_TYPE_P (TREE_TYPE (reftype)))))
|| (TREE_CODE (decl) == FUNCTION_DECL
&& (VAR_P (ref)
|| (EXPR_P (ref)
&& !FUNC_OR_METHOD_TYPE_P (reftype)
&& (!POINTER_TYPE_P (reftype)
|| !FUNC_OR_METHOD_TYPE_P (TREE_TYPE (reftype)))))))
{
/* It makes no sense to try to copy function attributes
to a variable, or variable attributes to a function. */
if (warning (OPT_Wattributes,
"%qE attribute ignored on a declaration of "
"a different kind than referenced symbol",
name)
&& DECL_P (ref))
inform (DECL_SOURCE_LOCATION (ref),
"symbol %qD referenced by %qD declared here", ref, decl);
return NULL_TREE;
}
tree attrs = NULL_TREE;
if (DECL_P (ref))
attrs = DECL_ATTRIBUTES (ref);
else if (TYPE_P (ref))
attrs = TYPE_ATTRIBUTES (ref);
/* Copy decl attributes from REF to DECL. */
for (tree at = attrs; at; at = TREE_CHAIN (at))
{
/* Avoid copying attributes that affect a symbol linkage,
inlining, or visibility since those in all likelihood
only apply to the target.
FIXME: make it possible to specify which attributes to
copy or not to copy in the copy attribute itself. */
tree atname = get_attribute_name (at);
if (is_attribute_p ("alias", atname)
|| is_attribute_p ("always_inline", atname)
|| is_attribute_p ("gnu_inline", atname)
|| is_attribute_p ("ifunc", atname)
|| is_attribute_p ("noinline", atname)
|| is_attribute_p ("visibility", atname)
|| is_attribute_p ("weak", atname)
|| is_attribute_p ("weakref", atname)
|| is_attribute_p ("target_clones", atname))
continue;
/* Attribute leaf only applies to extern functions.
Avoid copying it to static ones. */
if (!TREE_PUBLIC (decl)
&& is_attribute_p ("leaf", atname))
continue;
tree atargs = TREE_VALUE (at);
/* Create a copy of just the one attribute ar AT, including
its argumentsm and add it to DECL. */
tree attr = tree_cons (atname, copy_list (atargs), NULL_TREE);
decl_attributes (node, attr, flags, EXPR_P (ref) ? NULL_TREE : ref);
}
/* Proceed to copy type attributes below. */
}
else if (!TYPE_P (decl))
{
error_at (DECL_SOURCE_LOCATION (decl),
"%qE attribute must apply to a declaration",
name);
return NULL_TREE;
}
/* A function declared with attribute nothrow has the attribute
attached to it, but a C++ throw() function does not. */
if (TREE_NOTHROW (ref))
TREE_NOTHROW (decl) = true;
/* Similarly, a function declared with attribute noreturn has it
attached on to it, but a C11 _Noreturn function does not. */
if (DECL_P (ref)
&& TREE_THIS_VOLATILE (ref)
&& FUNC_OR_METHOD_TYPE_P (reftype))
TREE_THIS_VOLATILE (decl) = true;
if (POINTER_TYPE_P (reftype))
reftype = TREE_TYPE (reftype);
if (!TYPE_P (reftype))
return NULL_TREE;
tree attrs = TYPE_ATTRIBUTES (reftype);
/* Copy type attributes from REF to DECL. Pass in REF if it's a DECL
or a type but not if it's an expression. Set ATTR_FLAG_INTERNAL
since the attributes' arguments may be in their internal form. */
for (tree at = attrs; at; at = TREE_CHAIN (at))
decl_attributes (node, at, flags | ATTR_FLAG_INTERNAL,
EXPR_P (ref) ? NULL_TREE : ref);
return NULL_TREE;
}
/* Handle a "weakref" attribute; arguments as in struct
attribute_spec.handler. */
static tree
handle_weakref_attribute (tree *node, tree name, tree args,
int flags, bool *no_add_attrs)
{
tree attr = NULL_TREE;
/* We must ignore the attribute when it is associated with
local-scoped decls, since attribute alias is ignored and many
such symbols do not even have a DECL_WEAK field. */
if (decl_function_context (*node)
|| current_function_decl
|| !VAR_OR_FUNCTION_DECL_P (*node))
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
return NULL_TREE;
}
if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (*node)))
{
error ("indirect function %q+D cannot be declared %qE",
*node, name);
*no_add_attrs = true;
return NULL_TREE;
}
/* The idea here is that `weakref("name")' mutates into `weakref,
alias("name")', and weakref without arguments, in turn,
implicitly adds weak. */
if (args)
{
attr = tree_cons (get_identifier ("alias"), args, attr);
attr = tree_cons (get_identifier ("weakref"), NULL_TREE, attr);
*no_add_attrs = true;
decl_attributes (node, attr, flags);
}
else
{
if (lookup_attribute ("alias", DECL_ATTRIBUTES (*node)))
error_at (DECL_SOURCE_LOCATION (*node),
"%qE attribute must appear before %qs attribute",
name, "alias");
/* Can't call declare_weak because it wants this to be TREE_PUBLIC,
and that isn't supported; and because it wants to add it to
the list of weak decls, which isn't helpful. */
DECL_WEAK (*node) = 1;
}
if (decl_in_symtab_p (*node))
{
struct symtab_node *n = symtab_node::get (*node);
if (n && n->refuse_visibility_changes)
error ("%+qD declared %qE after being used", *node, name);
}
return NULL_TREE;
}
/* Handle an "visibility" attribute; arguments as in
struct attribute_spec.handler. */
static 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 an "tls_model" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_tls_model_attribute (tree *node, tree name, tree args,
int ARG_UNUSED (flags),
bool *ARG_UNUSED (no_add_attrs))
{
tree id;
tree decl = *node;
enum tls_model kind;
if (!VAR_P (decl))
{
warning (OPT_Wattributes, "%qE attribute ignored because %qD "
"is not a variable",
name, decl);
return NULL_TREE;
}
if (!DECL_THREAD_LOCAL_P (decl))
{
warning (OPT_Wattributes, "%qE attribute ignored because %qD does "
"not have thread storage duration", name, decl);
return NULL_TREE;
}
kind = DECL_TLS_MODEL (decl);
id = TREE_VALUE (args);
if (TREE_CODE (id) != STRING_CST)
{
error ("%qE argument not a string", name);
return NULL_TREE;
}
if (!strcmp (TREE_STRING_POINTER (id), "local-exec"))
kind = TLS_MODEL_LOCAL_EXEC;
else if (!strcmp (TREE_STRING_POINTER (id), "initial-exec"))
kind = TLS_MODEL_INITIAL_EXEC;
else if (!strcmp (TREE_STRING_POINTER (id), "local-dynamic"))
kind = optimize ? TLS_MODEL_LOCAL_DYNAMIC : TLS_MODEL_GLOBAL_DYNAMIC;
else if (!strcmp (TREE_STRING_POINTER (id), "global-dynamic"))
kind = TLS_MODEL_GLOBAL_DYNAMIC;
else
error ("%qE argument must be one of %qs, %qs, %qs, or %qs",
name,
"local-exec", "initial-exec", "local-dynamic", "global-dynamic");
set_decl_tls_model (decl, kind);
return NULL_TREE;
}
/* Handle a "no_instrument_function" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_no_instrument_function_attribute (tree *node, tree name,
tree ARG_UNUSED (args),
int ARG_UNUSED (flags),
bool *no_add_attrs)
{
tree decl = *node;
if (TREE_CODE (decl) != FUNCTION_DECL)
{
error_at (DECL_SOURCE_LOCATION (decl),
"%qE attribute applies only to functions", name);
*no_add_attrs = true;
}
else
DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl) = 1;
return NULL_TREE;
}
/* Handle a "no_profile_instrument_function" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_no_profile_instrument_function_attribute (tree *node, tree name, tree,
int, bool *no_add_attrs)
{
if (TREE_CODE (*node) != FUNCTION_DECL)
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* If ALLOC_DECL and DEALLOC_DECL are a pair of user-defined functions,
if they are declared inline issue warnings and return null. Otherwise
create attribute noinline, install it in ALLOC_DECL, and return it.
Otherwise return null. */
static tree
maybe_add_noinline (tree name, tree alloc_decl, tree dealloc_decl,
bool *no_add_attrs)
{
if (fndecl_built_in_p (alloc_decl) || fndecl_built_in_p (dealloc_decl))
return NULL_TREE;
/* When inlining (or optimization) is enabled and the allocator and
deallocator are not built-in functions, ignore the attribute on
functions declared inline since it could lead to false positives
when inlining one or the other call would wind up calling
a mismatched allocator or deallocator. */
if ((optimize && DECL_DECLARED_INLINE_P (alloc_decl))
|| lookup_attribute ("always_inline", DECL_ATTRIBUTES (alloc_decl)))
{
warning (OPT_Wattributes,
"%<%E (%E)%> attribute ignored on functions "
"declared %qs", name, DECL_NAME (dealloc_decl), "inline");
*no_add_attrs = true;
return NULL_TREE;
}
if ((optimize && DECL_DECLARED_INLINE_P (dealloc_decl))
|| lookup_attribute ("always_inline", DECL_ATTRIBUTES (dealloc_decl)))
{
warning (OPT_Wattributes,
"%<%E (%E)%> attribute ignored with deallocation "
"functions declared %qs",
name, DECL_NAME (dealloc_decl), "inline");
inform (DECL_SOURCE_LOCATION (dealloc_decl),
"deallocation function declared here" );
*no_add_attrs = true;
return NULL_TREE;
}
/* Disable inlining for non-standard deallocators to avoid false
positives due to mismatches between the inlined implementation
of one and not the other pair of functions. */
tree attr = tree_cons (get_identifier ("noinline"), NULL_TREE, NULL_TREE);
decl_attributes (&alloc_decl, attr, 0);
return attr;
}
/* Handle the "malloc" attribute. */
static tree
handle_malloc_attribute (tree *node, tree name, tree args, int flags,
bool *no_add_attrs)
{
if (flags & ATTR_FLAG_INTERNAL)
/* Recursive call. */
return NULL_TREE;
tree fndecl = *node;
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;
}
if (!args)
{
/* Only the form of the attribute with no arguments declares
a function malloc-like. */
DECL_IS_MALLOC (*node) = 1;
return NULL_TREE;
}
tree dealloc = TREE_VALUE (args);
if (error_operand_p (dealloc))
{
/* If the argument is in error it will have already been diagnosed.
Avoid issuing redundant errors here. */
*no_add_attrs = true;
return NULL_TREE;
}
STRIP_NOPS (dealloc);
if (TREE_CODE (dealloc) == ADDR_EXPR)
{
/* In C++ the argument may be wrapped in a cast to disambiguate
one of a number of overloads (such as operator delete). To
make things interesting, the cast looks different between
different C++ versions. Strip it and install the attribute
with the disambiguated function. */
dealloc = TREE_OPERAND (dealloc, 0);
*no_add_attrs = true;
tree attr = tree_cons (NULL_TREE, dealloc, TREE_CHAIN (args));
attr = build_tree_list (name, attr);
return decl_attributes (node, attr, 0);
}
if (TREE_CODE (dealloc) != FUNCTION_DECL)
{
if (TREE_CODE (dealloc) == OVERLOAD)
{
/* Handle specially the common case of specifying one of a number
of overloads, such as operator delete. */
error ("%qE attribute argument 1 is ambiguous", name);
inform (input_location,
"use a cast to the expected type to disambiguate");
*no_add_attrs = true;
return NULL_TREE;
}
error ("%qE attribute argument 1 does not name a function", name);
if (DECL_P (dealloc))
inform (DECL_SOURCE_LOCATION (dealloc),
"argument references a symbol declared here");
*no_add_attrs = true;
return NULL_TREE;
}
/* Mentioning the deallocation function qualifies as its use. */
TREE_USED (dealloc) = 1;
tree fntype = TREE_TYPE (dealloc);
tree argpos = TREE_CHAIN (args) ? TREE_VALUE (TREE_CHAIN (args)) : NULL_TREE;
if (!argpos)
{
tree argtypes = TYPE_ARG_TYPES (fntype);
if (!argtypes)
{
/* Reject functions without a prototype. */
error ("%qE attribute argument 1 must take a pointer "
"type as its first argument", name);
inform (DECL_SOURCE_LOCATION (dealloc),
"referenced symbol declared here");
*no_add_attrs = true;
return NULL_TREE;
}
tree argtype = TREE_VALUE (argtypes);
if (TREE_CODE (argtype) != POINTER_TYPE)
{
/* Reject functions that don't take a pointer as their first
argument. */
error ("%qE attribute argument 1 must take a pointer type "
"as its first argument; have %qT", name, argtype);
inform (DECL_SOURCE_LOCATION (dealloc),
"referenced symbol declared here");
*no_add_attrs = true;
return NULL_TREE;
}
/* Disable inlining for non-standard deallocators to avoid false
positives (or warn if either function is explicitly inline). */
tree at_noinline =
maybe_add_noinline (name, fndecl, dealloc, no_add_attrs);
if (*no_add_attrs)
return NULL_TREE;
/* Add attribute *dealloc to the deallocator function associating
it with this one. Ideally, the attribute would reference
the DECL of the deallocator but since that changes for each
redeclaration, use DECL_NAME instead. (DECL_ASSEMBLER_NAME
need not be set set this point and setting it here is too early. */
tree attrs = build_tree_list (NULL_TREE, DECL_NAME (fndecl));
attrs = tree_cons (get_identifier ("*dealloc"), attrs, at_noinline);
decl_attributes (&dealloc, attrs, 0);
return NULL_TREE;
}
/* Validate the positional argument. */
argpos = positional_argument (fntype, name, argpos, POINTER_TYPE);
if (!argpos)
{
*no_add_attrs = true;
return NULL_TREE;
}
/* As above, disable inlining for non-standard deallocators to avoid
false positives (or warn). */
tree at_noinline =
maybe_add_noinline (name, fndecl, dealloc, no_add_attrs);
if (*no_add_attrs)
return NULL_TREE;
/* It's valid to declare the same function with multiple instances
of attribute malloc, each naming the same or different deallocator
functions, and each referencing either the same or a different
positional argument. */
tree attrs = tree_cons (NULL_TREE, argpos, NULL_TREE);
attrs = tree_cons (NULL_TREE, DECL_NAME (fndecl), attrs);
attrs = tree_cons (get_identifier ("*dealloc"), attrs, at_noinline);
decl_attributes (&dealloc, attrs, 0);
return NULL_TREE;
}
/* Handle the internal "*dealloc" attribute added for functions declared
with the one- and two-argument forms of attribute malloc. Add it
to *NODE unless it's already there with the same arguments. */
static tree
handle_dealloc_attribute (tree *node, tree name, tree args, int,
bool *no_add_attrs)
{
tree fndecl = *node;
tree attrs = DECL_ATTRIBUTES (fndecl);
if (!attrs)
return NULL_TREE;
tree arg = TREE_VALUE (args);
args = TREE_CHAIN (args);
tree arg_pos = args ? TREE_VALUE (args) : integer_zero_node;
gcc_checking_assert ((DECL_P (arg)
&& fndecl_built_in_p (arg, BUILT_IN_NORMAL))
|| TREE_CODE (arg) == IDENTIFIER_NODE);
const char* const namestr = IDENTIFIER_POINTER (name);
for (tree at = attrs; (at = lookup_attribute (namestr, at));
at = TREE_CHAIN (at))
{
tree alloc = TREE_VALUE (at);
if (!alloc)
continue;
tree pos = TREE_CHAIN (alloc);
alloc = TREE_VALUE (alloc);
pos = pos ? TREE_VALUE (pos) : integer_zero_node;
gcc_checking_assert ((DECL_P (alloc)
&& fndecl_built_in_p (alloc, BUILT_IN_NORMAL))
|| TREE_CODE (alloc) == IDENTIFIER_NODE);
if (alloc == arg && tree_int_cst_equal (pos, arg_pos))
{
/* The function already has the attribute either without any
arguments or with the same arguments as the attribute that's
being added. Return without adding another copy. */
*no_add_attrs = true;
return NULL_TREE;
}
}
return NULL_TREE;
}
/* Handle the "alloc_size (argpos1 [, argpos2])" function type attribute.
*NODE is the type of the function the attribute is being applied to. */
static tree
handle_alloc_size_attribute (tree *node, tree name, tree args,
int ARG_UNUSED (flags), bool *no_add_attrs)
{
tree fntype = *node;
tree rettype = TREE_TYPE (fntype);
if (!POINTER_TYPE_P (rettype))
{
warning (OPT_Wattributes,
"%qE attribute ignored on a function returning %qT",
name, rettype);
*no_add_attrs = true;
return NULL_TREE;
}
tree newargs[2] = { NULL_TREE, NULL_TREE };
for (int i = 1; args; ++i)
{
tree pos = TREE_VALUE (args);
/* NEXT is null when the attribute includes just one argument.
That's used to tell positional_argument to avoid mentioning
the argument number in diagnostics (since there's just one
mentioning it is unnecessary and coule be confusing). */
tree next = TREE_CHAIN (args);
if (tree val = positional_argument (fntype, name, pos, INTEGER_TYPE,
next || i > 1 ? i : 0))
{
TREE_VALUE (args) = val;
newargs[i - 1] = val;
}
else
{
*no_add_attrs = true;
return NULL_TREE;
}
args = next;
}
if (!validate_attr_args (node, name, newargs))
*no_add_attrs = true;
return NULL_TREE;
}
/* Handle an "alloc_align (argpos)" attribute. */
static tree
handle_alloc_align_attribute (tree *node, tree name, tree args, int,
bool *no_add_attrs)
{
tree fntype = *node;
tree rettype = TREE_TYPE (fntype);
if (!POINTER_TYPE_P (rettype))
{
warning (OPT_Wattributes,
"%qE attribute ignored on a function returning %qT",
name, rettype);
*no_add_attrs = true;
return NULL_TREE;
}
if (tree val = positional_argument (*node, name, TREE_VALUE (args),
INTEGER_TYPE))
if (validate_attr_arg (node, name, val))
return NULL_TREE;
*no_add_attrs = true;
return NULL_TREE;
}
/* Handle a "assume_aligned" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_assume_aligned_attribute (tree *node, tree name, tree args, int,
bool *no_add_attrs)
{
tree decl = *node;
tree rettype = TREE_TYPE (decl);
if (TREE_CODE (rettype) != POINTER_TYPE)
{
warning (OPT_Wattributes,
"%qE attribute ignored on a function returning %qT",
name, rettype);
*no_add_attrs = true;
return NULL_TREE;
}
/* The alignment specified by the first argument. */
tree align = NULL_TREE;
for (; args; args = TREE_CHAIN (args))
{
tree val = TREE_VALUE (args);
if (val && TREE_CODE (val) != IDENTIFIER_NODE
&& TREE_CODE (val) != FUNCTION_DECL)
val = default_conversion (val);
if (!tree_fits_shwi_p (val))
{
warning (OPT_Wattributes,
"%qE attribute argument %E is not an integer constant",
name, val);
*no_add_attrs = true;
return NULL_TREE;
}
else if (tree_int_cst_sgn (val) < 0)
{
warning (OPT_Wattributes,
"%qE attribute argument %E is not positive", name, val);
*no_add_attrs = true;
return NULL_TREE;
}
if (!align)
{
/* Validate and save the alignment. */
if (!integer_pow2p (val))
{
warning (OPT_Wattributes,
"%qE attribute argument %E is not a power of 2",
name, val);
*no_add_attrs = true;
return NULL_TREE;
}
align = val;
}
else if (tree_int_cst_le (align, val))
{
/* The misalignment specified by the second argument
must be non-negative and less than the alignment. */
warning (OPT_Wattributes,
"%qE attribute argument %E is not in the range [0, %wu]",
name, val, tree_to_uhwi (align) - 1);
*no_add_attrs = true;
return NULL_TREE;
}
}
return NULL_TREE;
}
/* Handle the internal-only "arg spec" attribute. */
static tree
handle_argspec_attribute (tree *, tree, tree args, int, bool *)
{
/* Verify the attribute has one or two arguments and their kind. */
gcc_assert (args && TREE_CODE (TREE_VALUE (args)) == STRING_CST);
for (tree next = TREE_CHAIN (args); next; next = TREE_CHAIN (next))
{
tree val = TREE_VALUE (next);
gcc_assert (DECL_P (val) || EXPR_P (val));
}
return NULL_TREE;
}
/* Handle the internal-only "fn spec" attribute. */
static 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 a "warn_unused" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_warn_unused_attribute (tree *node, tree name,
tree args ATTRIBUTE_UNUSED,
int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
{
if (TYPE_P (*node))
/* Do nothing else, just set the attribute. We'll get at
it later with lookup_attribute. */
;
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle an "omp declare simd" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_omp_declare_simd_attribute (tree *, tree, tree, int, bool *)
{
return NULL_TREE;
}
/* Handle an "omp declare variant {base,variant}" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_omp_declare_variant_attribute (tree *, tree, tree, int, bool *)
{
return NULL_TREE;
}
/* Handle a "simd" attribute. */
static tree
handle_simd_attribute (tree *node, tree name, tree args, int, bool *no_add_attrs)
{
if (TREE_CODE (*node) == FUNCTION_DECL)
{
tree t = get_identifier ("omp declare simd");
tree attr = NULL_TREE;
if (args)
{
tree 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;
}
if (strcmp (TREE_STRING_POINTER (id), "notinbranch") == 0)
attr = build_omp_clause (DECL_SOURCE_LOCATION (*node),
OMP_CLAUSE_NOTINBRANCH);
else if (strcmp (TREE_STRING_POINTER (id), "inbranch") == 0)
attr = build_omp_clause (DECL_SOURCE_LOCATION (*node),
OMP_CLAUSE_INBRANCH);
else
{
error ("only %<inbranch%> and %<notinbranch%> flags are "
"allowed for %<__simd__%> attribute");
*no_add_attrs = true;
return NULL_TREE;
}
}
DECL_ATTRIBUTES (*node)
= tree_cons (t, build_tree_list (NULL_TREE, attr),
DECL_ATTRIBUTES (*node));
}
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle an "omp declare target" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_omp_declare_target_attribute (tree *, tree, tree, int, bool *)
{
return NULL_TREE;
}
/* Handle a "returns_twice" attribute; arguments as in
struct attribute_spec.handler. */
static 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 "no_limit_stack" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_no_limit_stack_attribute (tree *node, tree name,
tree ARG_UNUSED (args),
int ARG_UNUSED (flags),
bool *no_add_attrs)
{
tree decl = *node;
if (TREE_CODE (decl) != FUNCTION_DECL)
{
error_at (DECL_SOURCE_LOCATION (decl),
"%qE attribute applies only to functions", name);
*no_add_attrs = true;
}
else if (DECL_INITIAL (decl))
{
error_at (DECL_SOURCE_LOCATION (decl),
"cannot set %qE attribute after definition", name);
*no_add_attrs = true;
}
else
DECL_NO_LIMIT_STACK (decl) = 1;
return NULL_TREE;
}
/* Handle a "pure" attribute; arguments as in
struct attribute_spec.handler. */
static 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;
}
/* Digest an attribute list destined for a transactional memory statement.
ALLOWED is the set of attributes that are allowed for this statement;
return the attribute we parsed. Multiple attributes are never allowed. */
int
parse_tm_stmt_attr (tree attrs, int allowed)
{
tree a_seen = NULL;
int m_seen = 0;
for ( ; attrs ; attrs = TREE_CHAIN (attrs))
{
tree a = get_attribute_name (attrs);
tree ns = get_attribute_namespace (attrs);
int m = 0;
if (is_attribute_p ("outer", a)
&& (ns == NULL_TREE || strcmp (IDENTIFIER_POINTER (ns), "gnu") == 0))
m = TM_STMT_ATTR_OUTER;
if ((m & allowed) == 0)
{
warning (OPT_Wattributes, "%qE attribute directive ignored", a);
continue;
}
if (m_seen == 0)
{
a_seen = a;
m_seen = m;
}
else if (m_seen == m)
warning (OPT_Wattributes, "%qE attribute duplicated", a);
else
warning (OPT_Wattributes, "%qE attribute follows %qE", a, a_seen);
}
return m_seen;
}
/* Transform a TM attribute name into a maskable integer and back.
Note that NULL (i.e. no attribute) is mapped to UNKNOWN, corresponding
to how the lack of an attribute is treated. */
int
tm_attr_to_mask (tree attr)
{
if (attr == NULL)
return 0;
if (is_attribute_p ("transaction_safe", attr))
return TM_ATTR_SAFE;
if (is_attribute_p ("transaction_callable", attr))
return TM_ATTR_CALLABLE;
if (is_attribute_p ("transaction_pure", attr))
return TM_ATTR_PURE;
if (is_attribute_p ("transaction_unsafe", attr))
return TM_ATTR_IRREVOCABLE;
if (is_attribute_p ("transaction_may_cancel_outer", attr))
return TM_ATTR_MAY_CANCEL_OUTER;
return 0;
}
tree
tm_mask_to_attr (int mask)
{
const char *str;
switch (mask)
{
case TM_ATTR_SAFE:
str = "transaction_safe";
break;
case TM_ATTR_CALLABLE:
str = "transaction_callable";
break;
case TM_ATTR_PURE:
str = "transaction_pure";
break;
case TM_ATTR_IRREVOCABLE:
str = "transaction_unsafe";
break;
case TM_ATTR_MAY_CANCEL_OUTER:
str = "transaction_may_cancel_outer";
break;
default:
gcc_unreachable ();
}
return get_identifier (str);
}
/* Return the first TM attribute seen in LIST. */
tree
find_tm_attribute (tree list)
{
for (; list ; list = TREE_CHAIN (list))
{
tree name = get_attribute_name (list);
if (tm_attr_to_mask (name) != 0)
return name;
}
return NULL_TREE;
}
/* Handle the TM attributes; arguments as in struct attribute_spec.handler.
Here we accept only function types, and verify that none of the other
function TM attributes are also applied. */
/* ??? We need to accept class types for C++, but not C. This greatly
complicates this function, since we can no longer rely on the extra
processing given by function_type_required. */
static tree
handle_tm_attribute (tree *node, tree name, tree args,
int flags, bool *no_add_attrs)
{
/* Only one path adds the attribute; others don't. */
*no_add_attrs = true;
switch (TREE_CODE (*node))
{
case RECORD_TYPE:
case UNION_TYPE:
/* Only tm_callable and tm_safe apply to classes. */
if (tm_attr_to_mask (name) & ~(TM_ATTR_SAFE | TM_ATTR_CALLABLE))
goto ignored;
/* FALLTHRU */
case FUNCTION_TYPE:
case METHOD_TYPE:
{
tree old_name = find_tm_attribute (TYPE_ATTRIBUTES (*node));
if (old_name == name)
;
else if (old_name != NULL_TREE)
error ("type was previously declared %qE", old_name);
else
*no_add_attrs = false;
}
break;
case FUNCTION_DECL:
{
/* transaction_safe_dynamic goes on the FUNCTION_DECL, but we also
want to set transaction_safe on the type. */
gcc_assert (is_attribute_p ("transaction_safe_dynamic", name));
if (!TYPE_P (DECL_CONTEXT (*node)))
error_at (DECL_SOURCE_LOCATION (*node),
"%<transaction_safe_dynamic%> may only be specified for "
"a virtual function");
*no_add_attrs = false;
decl_attributes (&TREE_TYPE (*node),
build_tree_list (get_identifier ("transaction_safe"),
NULL_TREE),
0);
break;
}
case POINTER_TYPE:
{
enum tree_code subcode = TREE_CODE (TREE_TYPE (*node));
if (subcode == FUNCTION_TYPE || subcode == METHOD_TYPE)
{
tree fn_tmp = TREE_TYPE (*node);
decl_attributes (&fn_tmp, tree_cons (name, args, NULL), 0);
*node = build_pointer_type (fn_tmp);
break;
}
}
/* FALLTHRU */
default:
/* If a function is next, pass it on to be tried next. */
if (flags & (int) ATTR_FLAG_FUNCTION_NEXT)
return tree_cons (name, args, NULL);
ignored:
warning (OPT_Wattributes, "%qE attribute ignored", name);
break;
}
return NULL_TREE;
}
/* Handle the TM_WRAP attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_tm_wrap_attribute (tree *node, tree name, tree args,
int ARG_UNUSED (flags), bool *no_add_attrs)
{
tree decl = *node;
/* We don't need the attribute even on success, since we
record the entry in an external table. */
*no_add_attrs = true;
if (TREE_CODE (decl) != FUNCTION_DECL)
warning (OPT_Wattributes, "%qE attribute ignored", name);
else
{
tree wrap_decl = TREE_VALUE (args);
if (error_operand_p (wrap_decl))
;
else if (TREE_CODE (wrap_decl) != IDENTIFIER_NODE
&& !VAR_OR_FUNCTION_DECL_P (wrap_decl))
error ("%qE argument not an identifier", name);
else
{
if (TREE_CODE (wrap_decl) == IDENTIFIER_NODE)
wrap_decl = lookup_name (wrap_decl);
if (wrap_decl && TREE_CODE (wrap_decl) == FUNCTION_DECL)
{
if (lang_hooks.types_compatible_p (TREE_TYPE (decl),
TREE_TYPE (wrap_decl)))
record_tm_replacement (wrap_decl, decl);
else
error ("%qD is not compatible with %qD", wrap_decl, decl);
}
else
error ("%qE argument is not a function", name);
}
}
return NULL_TREE;
}
/* Ignore the given attribute. Used when this attribute may be usefully
overridden by the target, but is not used generically. */
static 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 "no vops" attribute; arguments as in
struct attribute_spec.handler. */
static 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 "deprecated" attribute; arguments as in
struct attribute_spec.handler. */
tree
handle_deprecated_attribute (tree *node, tree name,
tree args, int flags,
bool *no_add_attrs)
{
tree type = NULL_TREE;
int warn = 0;
tree what = NULL_TREE;
if (!args)
*no_add_attrs = true;
else if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
{
error ("deprecated message is not a string");
*no_add_attrs = true;
}
if (DECL_P (*node))
{
tree decl = *node;
type = TREE_TYPE (decl);
if (TREE_CODE (decl) == TYPE_DECL
|| TREE_CODE (decl) == PARM_DECL
|| VAR_OR_FUNCTION_DECL_P (decl)
|| TREE_CODE (decl) == FIELD_DECL
|| TREE_CODE (decl) == CONST_DECL
|| objc_method_decl (TREE_CODE (decl)))
TREE_DEPRECATED (decl) = 1;
else
warn = 1;
}
else if (TYPE_P (*node))
{
if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
*node = build_variant_type_copy (*node);
TREE_DEPRECATED (*node) = 1;
type = *node;
}
else
warn = 1;
if (warn)
{
*no_add_attrs = true;
if (type && TYPE_NAME (type))
{
if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
what = TYPE_NAME (*node);
else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
&& DECL_NAME (TYPE_NAME (type)))
what = DECL_NAME (TYPE_NAME (type));
}
if (what)
warning (OPT_Wattributes, "%qE attribute ignored for %qE", name, what);
else
warning (OPT_Wattributes, "%qE attribute ignored", name);
}
return NULL_TREE;
}
/* Handle a "unavailable" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_unavailable_attribute (tree *node, tree name,
tree args, int flags,
bool *no_add_attrs)
{
tree type = NULL_TREE;
int warn = 0;
tree what = NULL_TREE;
if (!args)
*no_add_attrs = true;
else if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
{
error ("the message attached to %<unavailable%> is not a string");
*no_add_attrs = true;
}
if (DECL_P (*node))
{
tree decl = *node;
type = TREE_TYPE (decl);
if (TREE_CODE (decl) == TYPE_DECL
|| TREE_CODE (decl) == PARM_DECL
|| VAR_OR_FUNCTION_DECL_P (decl)
|| TREE_CODE (decl) == FIELD_DECL
|| TREE_CODE (decl) == CONST_DECL
|| objc_method_decl (TREE_CODE (decl)))
TREE_UNAVAILABLE (decl) = 1;
else
warn = 1;
}
else if (TYPE_P (*node))
{
if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
*node = build_variant_type_copy (*node);
TREE_UNAVAILABLE (*node) = 1;
type = *node;
}
else
warn = 1;
if (warn)
{
*no_add_attrs = true;
if (type && TYPE_NAME (type))
{
if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
what = TYPE_NAME (*node);
else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
&& DECL_NAME (TYPE_NAME (type)))
what = DECL_NAME (TYPE_NAME (type));
}
if (what)
warning (OPT_Wattributes, "%qE attribute ignored for %qE", name, what);
else
warning (OPT_Wattributes, "%qE attribute ignored", name);
}
return NULL_TREE;
}
/* Return the "base" type from TYPE that is suitable to apply attribute
vector_size to by stripping arrays, function types, etc. */
static tree
type_for_vector_size (tree type)
{
/* We need to provide for vector pointers, vector arrays, and
functions returning vectors. For example:
__attribute__((vector_size(16))) short *foo;
In this case, the mode is SI, but the type being modified is
HI, so we need to look further. */
while (POINTER_TYPE_P (type)
|| TREE_CODE (type) == FUNCTION_TYPE
|| TREE_CODE (type) == METHOD_TYPE
|| TREE_CODE (type) == ARRAY_TYPE
|| TREE_CODE (type) == OFFSET_TYPE)
type = TREE_TYPE (type);
return type;
}
/* Given TYPE, return the base type to which the vector_size attribute
ATNAME with ARGS, when non-null, can be applied, if one exists.
On success and when both ARGS and PTRNUNITS are non-null, set
*PTRNUNINTS to the number of vector units. When PTRNUNITS is not
null, issue a warning when the attribute argument is not constant
and an error if there is no such type. Otherwise issue a warning
in the latter case and return null. */
static tree
type_valid_for_vector_size (tree type, tree atname, tree args,
unsigned HOST_WIDE_INT *ptrnunits)
{
bool error_p = ptrnunits != NULL;
/* Get the mode of the type being modified. */
machine_mode orig_mode = TYPE_MODE (type);
if ((!INTEGRAL_TYPE_P (type)
&& !SCALAR_FLOAT_TYPE_P (type)
&& !FIXED_POINT_TYPE_P (type))
|| (!SCALAR_FLOAT_MODE_P (orig_mode)
&& GET_MODE_CLASS (orig_mode) != MODE_INT
&& !ALL_SCALAR_FIXED_POINT_MODE_P (orig_mode))
|| !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type))
|| TREE_CODE (type) == BOOLEAN_TYPE)
{
if (error_p)
error ("invalid vector type for attribute %qE", atname);
else
warning (OPT_Wattributes, "invalid vector type for attribute %qE",
atname);
return NULL_TREE;
}
/* When no argument has been provided this is just a request to validate
the type above. Return TYPE to indicate success. */
if (!args)
return type;
tree size = TREE_VALUE (args);
/* Erroneous arguments have already been diagnosed. */
if (size == error_mark_node)
return NULL_TREE;
if (size && TREE_CODE (size) != IDENTIFIER_NODE
&& TREE_CODE (size) != FUNCTION_DECL)
size = default_conversion (size);
if (TREE_CODE (size) != INTEGER_CST)
{
if (error_p)
error ("%qE attribute argument value %qE is not an integer constant",
atname, size);
else
warning (OPT_Wattributes,
"%qE attribute argument value %qE is not an integer constant",
atname, size);
return NULL_TREE;
}
if (!TYPE_UNSIGNED (TREE_TYPE (size))
&& tree_int_cst_sgn (size) < 0)
{
if (error_p)
error ("%qE attribute argument value %qE is negative",
atname, size);
else
warning (OPT_Wattributes,
"%qE attribute argument value %qE is negative",
atname, size);
return NULL_TREE;
}
/* The attribute argument value is constrained by the maximum bit
alignment representable in unsigned int on the host. */
unsigned HOST_WIDE_INT vecsize;
unsigned HOST_WIDE_INT maxsize = tree_to_uhwi (max_object_size ());
if (!tree_fits_uhwi_p (size)
|| (vecsize = tree_to_uhwi (size)) > maxsize)
{
if (error_p)
error ("%qE attribute argument value %qE exceeds %wu",
atname, size, maxsize);
else
warning (OPT_Wattributes,
"%qE attribute argument value %qE exceeds %wu",
atname, size, maxsize);
return NULL_TREE;
}
if (vecsize % tree_to_uhwi (TYPE_SIZE_UNIT (type)))
{
if (error_p)
error ("vector size not an integral multiple of component size");
return NULL_TREE;
}
if (vecsize == 0)
{
error ("zero vector size");
return NULL;
}
/* Calculate how many units fit in the vector. */
unsigned HOST_WIDE_INT nunits = vecsize / tree_to_uhwi (TYPE_SIZE_UNIT (type));
if (nunits & (nunits - 1))
{
if (error_p)
error ("number of vector components %wu not a power of two", nunits);
else
warning (OPT_Wattributes,
"number of vector components %wu not a power of two", nunits);
return NULL_TREE;
}
if (nunits >= (unsigned HOST_WIDE_INT)INT_MAX)
{
if (error_p)
error ("number of vector components %wu exceeds %d",
nunits, INT_MAX - 1);
else
warning (OPT_Wattributes,
"number of vector components %wu exceeds %d",
nunits, INT_MAX - 1);
return NULL_TREE;
}
if (ptrnunits)
*ptrnunits = nunits;
return type;
}
/* Handle a "vector_size" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_vector_size_attribute (tree *node, tree name, tree args,
int ARG_UNUSED (flags),
bool *no_add_attrs)
{
*no_add_attrs = true;
/* Determine the "base" type to apply the attribute to. */
tree type = type_for_vector_size (*node);
/* Get the vector size (in bytes) and let the function compute
the number of vector units. */
unsigned HOST_WIDE_INT nunits;
type = type_valid_for_vector_size (type, name, args, &nunits);
if (!type)
return NULL_TREE;
gcc_checking_assert (args != NULL);
tree new_type = build_vector_type (type, nunits);
/* Build back pointers if needed. */
*node = lang_hooks.types.reconstruct_complex_type (*node, new_type);
return NULL_TREE;
}
/* Handle the "nonnull" attribute. */
static tree
handle_nonnull_attribute (tree *node, tree name,
tree args, int ARG_UNUSED (flags),
bool *no_add_attrs)
{
tree type = *node;
/* If no arguments are specified, all pointer arguments should be
non-null. Verify a full prototype is given so that the arguments
will have the correct types when we actually check them later.
Avoid diagnosing type-generic built-ins since those have no
prototype. */
if (!args)
{
if (!prototype_p (type)
&& (!TYPE_ATTRIBUTES (type)
|| !lookup_attribute ("type generic", TYPE_ATTRIBUTES (type))))
{
error ("%qE attribute without arguments on a non-prototype",
name);
*no_add_attrs = true;
}
return NULL_TREE;
}
for (int i = 1; args; ++i)
{
tree pos = TREE_VALUE (args);
/* NEXT is null when the attribute includes just one argument.
That's used to tell positional_argument to avoid mentioning
the argument number in diagnostics (since there's just one
mentioning it is unnecessary and coule be confusing). */
tree next = TREE_CHAIN (args);
if (tree val = positional_argument (type, name, pos, POINTER_TYPE,
next || i > 1 ? i : 0))
TREE_VALUE (args) = val;
else
{
*no_add_attrs = true;
break;
}
args = next;
}
return NULL_TREE;
}
/* Handle the "nonstring" variable attribute. */
static tree
handle_nonstring_attribute (tree *node, tree name, tree ARG_UNUSED (args),
int ARG_UNUSED (flags), bool *no_add_attrs)
{
gcc_assert (!args);
tree_code code = TREE_CODE (*node);
if (VAR_P (*node)
|| code == FIELD_DECL
|| code == PARM_DECL)
{
tree type = TREE_TYPE (*node);
if (POINTER_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE)
{
/* Accept the attribute on arrays and pointers to all three
narrow character types. */
tree eltype = TREE_TYPE (type);
eltype = TYPE_MAIN_VARIANT (eltype);
if (eltype == char_type_node
|| eltype == signed_char_type_node
|| eltype == unsigned_char_type_node)
return NULL_TREE;
}
warning (OPT_Wattributes,
"%qE attribute ignored on objects of type %qT",
name, type);
*no_add_attrs = true;
return NULL_TREE;
}
if (code == FUNCTION_DECL)
warning (OPT_Wattributes,
"%qE attribute does not apply to functions", name);
else if (code == TYPE_DECL)
warning (OPT_Wattributes,
"%qE attribute does not apply to types", name);
else
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
return NULL_TREE;
}
/* Given a function type FUNCTYPE, returns the type of the parameter
ARGNO or null if ARGNO exceeds the number of parameters. On failure
set *NARGS to the number of function parameters. */
static tree
get_argument_type (tree functype, unsigned argno, unsigned *nargs)
{
function_args_iterator iter;
function_args_iter_init (&iter, functype);
unsigned count = 0;
for ( ; iter.next; ++count, function_args_iter_next (&iter))
{
if (count + 1 == argno)
{
tree argtype = function_args_iter_cond (&iter);
if (VOID_TYPE_P (argtype))
break;
if (argtype != error_mark_node)
return argtype;
}
}
*nargs = count;
return NULL_TREE;
}
/* Given a function FNDECL return the function argument at the zero-
based position ARGNO or null if it can't be found. */
static tree
get_argument (tree fndecl, unsigned argno)
{
if (!DECL_P (fndecl))
return NULL_TREE;
unsigned i = 0;
for (tree arg = DECL_ARGUMENTS (fndecl); arg; arg = TREE_CHAIN (arg))
if (i++ == argno)
return arg;
return NULL_TREE;
}
/* Attempt to append attribute access specification ATTRSPEC, optionally
described by the human-readable string ATTRSTR, for type T, to one in
ATTRS. VBLIST is an optional list of bounds of variable length array
parameters described by ATTRSTR.
Issue warning for conflicts and return null if any are found.
Return the concatenated access string on success. */
static tree
append_access_attr (tree node[3], tree attrs, const char *attrstr,
const char *attrspec, tree vblist = NULL_TREE)
{
tree argstr = build_string (strlen (attrspec) + 1, attrspec);
tree ataccess = tree_cons (NULL_TREE, argstr, vblist);
ataccess = tree_cons (get_identifier ("access"), ataccess, NULL_TREE);
/* The access specification being applied. This may be an implicit
access spec synthesized for array (or VLA) parameters even for
a declaration with an explicit access spec already applied, if
this call corresponds to the first declaration of the function. */
rdwr_map new_idxs;
init_attr_rdwr_indices (&new_idxs, ataccess);
/* The current access specification alrady applied. */
rdwr_map cur_idxs;
init_attr_rdwr_indices (&cur_idxs, attrs);
std::string spec;
for (auto it = new_idxs.begin (); it != new_idxs.end (); ++it)
{
const auto &newaxsref = *it;
/* The map has two equal entries for each pointer argument that
has an associated size argument. Process just the entry for
the former. */
if ((unsigned)newaxsref.first != newaxsref.second.ptrarg)
continue;
const attr_access* const cura = cur_idxs.get (newaxsref.first);
if (!cura)
{
/* The new attribute needs to be added. */
tree str = newaxsref.second.to_internal_string ();
spec += TREE_STRING_POINTER (str);
continue;
}
/* The new access spec refers to an array/pointer argument for
which an access spec already exists. Check and diagnose any
conflicts. If no conflicts are found, merge the two. */
const attr_access* const newa = &newaxsref.second;
if (!attrstr)
{
tree str = NULL_TREE;
if (newa->mode != access_deferred)
str = newa->to_external_string ();
else if (cura->mode != access_deferred)
str = cura->to_external_string ();
if (str)
attrstr = TREE_STRING_POINTER (str);
}
location_t curloc = input_location;
if (node[2] && DECL_P (node[2]))
curloc = DECL_SOURCE_LOCATION (node[2]);
location_t prevloc = UNKNOWN_LOCATION;
if (node[1] && DECL_P (node[1]))
prevloc = DECL_SOURCE_LOCATION (node[1]);
if (newa->mode != cura->mode
&& newa->mode != access_deferred
&& cura->mode != access_deferred
&& newa->internal_p == cura->internal_p)
{
/* Mismatch in access mode. */
auto_diagnostic_group d;
if (warning_at (curloc, OPT_Wattributes,
"attribute %qs mismatch with mode %qs",
attrstr, cura->mode_names[cura->mode])
&& prevloc != UNKNOWN_LOCATION)
inform (prevloc, "previous declaration here");
continue;
}
/* Set if PTRARG refers to a VLA with an unspecified bound (T[*]).
Be prepared for either CURA or NEWA to refer to it, depending
on which happens to come first in the declaration. */
const bool cur_vla_ub = (cura->internal_p
&& cura->sizarg == UINT_MAX
&& cura->minsize == HOST_WIDE_INT_M1U);
const bool new_vla_ub = (newa->internal_p
&& newa->sizarg == UINT_MAX
&& newa->minsize == HOST_WIDE_INT_M1U);
if (newa->sizarg != cura->sizarg
&& attrstr
&& (!(cur_vla_ub ^ new_vla_ub)
|| (!cura->internal_p && !newa->internal_p)))
{
/* Avoid diagnosing redeclarations of functions with no explicit
attribute access that add one. */
if (newa->mode == access_deferred
&& cura->mode != access_deferred
&& newa->sizarg == UINT_MAX
&& cura->sizarg != UINT_MAX)
continue;
if (cura->mode == access_deferred
&& newa->mode != access_deferred
&& cura->sizarg == UINT_MAX
&& newa->sizarg != UINT_MAX)
continue;
/* The two specs designate different size arguments. It's okay
for the explicit spec to specify a size where none is provided
by the implicit (VLA) one, as in:
__attribute__ ((access (read_write, 1, 2)))
void f (int*, int);
but not for two explicit access attributes to do that. */
bool warned = false;
auto_diagnostic_group d;
if (newa->sizarg == UINT_MAX)
/* Mismatch in the presence of the size argument. */
warned = warning_at (curloc, OPT_Wattributes,
"attribute %qs missing positional argument 2 "
"provided in previous designation by argument "
"%u", attrstr, cura->sizarg + 1);
else if (cura->sizarg == UINT_MAX)
/* Mismatch in the presence of the size argument. */
warned = warning_at (curloc, OPT_Wattributes,
"attribute %qs positional argument 2 "
"missing in previous designation",
attrstr);
else if (newa->internal_p || cura->internal_p)
/* Mismatch in the value of the size argument and a VLA bound. */
warned = warning_at (curloc, OPT_Wattributes,
"attribute %qs positional argument 2 "
"conflicts with previous designation "
"by argument %u",
attrstr, cura->sizarg + 1);
else
/* Mismatch in the value of the size argument between two
explicit access attributes. */
warned = warning_at (curloc, OPT_Wattributes,
"attribute %qs mismatched positional argument "
"values %i and %i",
attrstr, newa->sizarg + 1, cura->sizarg + 1);
if (warned)
{
/* If the previous declaration is a function (as opposed
to a typedef of one), find the location of the array
or pointer argument that uses the conflicting VLA bound
and point to it in the note. */
const attr_access* const pa = cura->size ? cura : newa;
tree size = pa->size ? TREE_VALUE (pa->size) : NULL_TREE;
if (size && DECL_P (size))
{
location_t argloc = UNKNOWN_LOCATION;
if (tree arg = get_argument (node[2], pa->ptrarg))
argloc = DECL_SOURCE_LOCATION (arg);
gcc_rich_location richloc (DECL_SOURCE_LOCATION (size));
if (argloc != UNKNOWN_LOCATION)
richloc.add_range (argloc);
inform (&richloc, "designating the bound of variable "
"length array argument %u",
pa->ptrarg + 1);
}
else if (prevloc != UNKNOWN_LOCATION)
inform (prevloc, "previous declaration here");
}
continue;
}
if (newa->internal_p == cura->internal_p)
continue;
/* Merge the CURA and NEWA. */
attr_access merged = newaxsref.second;
/* VLA seen in a declaration takes precedence. */
if (cura->minsize == HOST_WIDE_INT_M1U)
merged.minsize = HOST_WIDE_INT_M1U;
/* Use the explicitly specified size positional argument. */
if (cura->sizarg != UINT_MAX)
merged.sizarg = cura->sizarg;
/* Use the explicitly specified mode. */
if (merged.mode == access_deferred)
merged.mode = cura->mode;
tree str = merged.to_internal_string ();
spec += TREE_STRING_POINTER (str);
}
if (!spec.length ())
return NULL_TREE;
return build_string (spec.length (), spec.c_str ());
}
/* Convenience wrapper for the above. */
tree
append_access_attr (tree node[3], tree attrs, const char *attrstr,
char code, HOST_WIDE_INT idxs[2])
{
char attrspec[80];
int n = sprintf (attrspec, "%c%u", code, (unsigned) idxs[0] - 1);
if (idxs[1])
n += sprintf (attrspec + n, ",%u", (unsigned) idxs[1] - 1);
return append_access_attr (node, attrs, attrstr, attrspec);
}
/* Handle the access attribute for function type NODE[0], with the function
DECL optionally in NODE[1]. The handler is called both in response to
an explict attribute access on a declaration with a mode and one or two
positional arguments, and for internally synthesized access specifications
with a string argument optionally followd by a DECL or expression
representing a VLA bound. To speed up parsing, the handler transforms
the attribute and its arguments into a string. */
static tree
handle_access_attribute (tree node[3], tree name, tree args, int flags,
bool *no_add_attrs)
{
tree attrs = TYPE_ATTRIBUTES (*node);
tree type = *node;
if (POINTER_TYPE_P (type))
{
tree ptype = TREE_TYPE (type);
if (FUNC_OR_METHOD_TYPE_P (ptype))
type = ptype;
}
*no_add_attrs = true;
/* Verify a full prototype is provided so that the argument types
can be validated. Avoid diagnosing type-generic built-ins since
those have no prototype. */
if (!args
&& !prototype_p (type)
&& (!attrs || !lookup_attribute ("type generic", attrs)))
{
error ("attribute %qE without arguments on a non-prototype", name);
return NULL_TREE;
}
tree access_mode = TREE_VALUE (args);
if (TREE_CODE (access_mode) == STRING_CST)
{
const char* const str = TREE_STRING_POINTER (access_mode);
if (*str == '+')
{
/* This is a request to merge an internal specification for
a function declaration involving arrays but no explicit
attribute access. */
tree vblist = TREE_CHAIN (args);
tree axstr = append_access_attr (node, attrs, NULL, str + 1,
vblist);
if (!axstr)
return NULL_TREE;
/* Replace any existing access attribute specification with
the concatenation above. */
tree axsat = tree_cons (NULL_TREE, axstr, vblist);
axsat = tree_cons (name, axsat, NULL_TREE);
/* Recursively call self to "replace" the documented/external
form of the attribute with the condensend internal form. */
decl_attributes (node, axsat, flags | ATTR_FLAG_INTERNAL);
return NULL_TREE;
}
if (flags & ATTR_FLAG_INTERNAL)
{
/* This is a recursive call to handle the condensed internal
form of the attribute (see below). Since all validation
has been done simply return here, accepting the attribute
as is. */
*no_add_attrs = false;
return NULL_TREE;
}
}
/* Set to true when the access mode has the form of a function call
as in 'attribute (read_only (1, 2))'. That's an easy mistake to
make and so worth a special diagnostic. */
bool funcall = false;
if (TREE_CODE (access_mode) == CALL_EXPR)
{
access_mode = CALL_EXPR_FN (access_mode);
if (TREE_CODE (access_mode) != ADDR_EXPR)
{
error ("attribute %qE invalid mode", name);
return NULL_TREE;
}
access_mode = TREE_OPERAND (access_mode, 0);
access_mode = DECL_NAME (access_mode);
funcall = true;
}
else if (TREE_CODE (access_mode) != IDENTIFIER_NODE)
{
error ("attribute %qE mode %qE is not an identifier; expected one of "
"%qs, %qs, %qs, or %qs", name, access_mode,
"read_only", "read_write", "write_only", "none");
return NULL_TREE;
}
const char* const access_str = IDENTIFIER_POINTER (access_mode);
const char *ps = access_str;
if (ps[0] == '_' && ps[1] == '_')
{
size_t len = strlen (ps);
if (ps[len - 1] == '_' && ps[len - 2] == '_')
ps += 2;
}
int imode;
{
const int nmodes =
sizeof attr_access::mode_names / sizeof *attr_access::mode_names;
for (imode = 0; imode != nmodes; ++imode)
if (!strncmp (ps, attr_access::mode_names[imode],
strlen (attr_access::mode_names[imode])))
break;
if (imode == nmodes)
{
error ("attribute %qE invalid mode %qs; expected one of "
"%qs, %qs, %qs, or %qs", name, access_str,
"read_only", "read_write", "write_only", "none");
return NULL_TREE;
}
}
const ::access_mode mode = static_cast<::access_mode>(imode);
if (funcall)
{
error ("attribute %qE unexpected %<(%> after mode %qs; expected "
"a positional argument or %<)%>",
name, access_str);
return NULL_TREE;
}
args = TREE_CHAIN (args);
if (!args)
{
/* The first positional argument is required. It may be worth
dropping the requirement at some point and having read_only
apply to all const-qualified pointers and read_write or
write_only to the rest. */
error ("attribute %<%E(%s)%> missing an argument",
name, access_str);
return NULL_TREE;
}
/* One or more positional arguments have been specified. Validate
them. */
tree idxnodes[2] = { NULL_TREE, NULL_TREE };
tree argtypes[2] = { NULL_TREE, NULL_TREE };
/* 1-based attribute positional arguments or zero if not specified.
Invalid negative or excessive values are also stored but used
only in diagnostics. */
HOST_WIDE_INT idxs[2] = { 0, 0 };
/* Number of function formal arguments (used in diagnostics). */
unsigned nfuncargs = 0;
/* Number of (optional) attribute positional arguments. */
unsigned nattrargs = 0;
for (unsigned i = 0; i != 2; ++i, args = TREE_CHAIN (args), ++nattrargs)
{
if (!args)
break;
idxnodes[i] = TREE_VALUE (args);
if (TREE_CODE (idxnodes[i]) != IDENTIFIER_NODE
&& TREE_CODE (idxnodes[i]) != FUNCTION_DECL)
idxnodes[i] = default_conversion (idxnodes[i]);
if (tree_fits_shwi_p (idxnodes[i]))
{
idxs[i] = tree_to_shwi (idxnodes[i]);
argtypes[i] = get_argument_type (type, idxs[i], &nfuncargs);
}
}
if ((nattrargs == 1 && !idxs[0])
|| (nattrargs == 2 && (!idxs[0] || !idxs[1])))
{
if (idxnodes[1])
error ("attribute %<%E(%s, %E, %E)%> invalid positional argument %i",
name, access_str, idxnodes[0], idxnodes[1], idxs[0] ? 2 : 1);
else
error ("attribute %<%E(%s, %E)%> invalid positional argument %i",
name, access_str, idxnodes[0], idxs[0] ? 2 : 1);
return NULL_TREE;
}
/* Format the attribute specification to include in diagnostics. */
char attrstr[80];
if (idxnodes[1])
snprintf (attrstr, sizeof attrstr, "%s(%s, %lli, %lli)",
IDENTIFIER_POINTER (name), access_str,
(long long) idxs[0], (long long) idxs[1]);
else if (idxnodes[0])
snprintf (attrstr, sizeof attrstr, "%s(%s, %lli)",
IDENTIFIER_POINTER (name), access_str,
(long long) idxs[0]);
else
snprintf (attrstr, sizeof attrstr, "%s(%s)",
IDENTIFIER_POINTER (name), access_str);
/* Verify the positional argument values are in range. */
if (!argtypes[0] || (idxnodes[1] && !argtypes[1]))
{
if (idxnodes[0])
{
if (idxs[0] < 0 || idxs[1] < 0)
error ("attribute %qs positional argument %i invalid value %wi",
attrstr, idxs[0] < 0 ? 1 : 2,
idxs[0] < 0 ? idxs[0] : idxs[1]);
else
error ("attribute %qs positional argument %i value %wi exceeds "
"number of function arguments %u",
attrstr, idxs[0] ? 1 : 2,
idxs[0] ? idxs[0] : idxs[1],
nfuncargs);
}
else
error ("attribute %qs invalid positional argument", attrstr);
return NULL_TREE;
}
if (!POINTER_TYPE_P (argtypes[0]))
{
/* The first argument must have a pointer or reference type. */
error ("attribute %qs positional argument 1 references "
"non-pointer argument type %qT",
attrstr, argtypes[0]);
return NULL_TREE;
}
{
/* Pointers to functions are not allowed. */
tree ptrtype = TREE_TYPE (argtypes[0]);
if (FUNC_OR_METHOD_TYPE_P (ptrtype))
{
error ("attribute %qs positional argument 1 references "
"argument of function type %qT",
attrstr, ptrtype);
return NULL_TREE;
}
}
if (mode == access_read_write || mode == access_write_only)
{
/* Read_write and write_only modes must reference non-const
arguments. */
if (TYPE_READONLY (TREE_TYPE (argtypes[0])))
{
error ("attribute %qs positional argument 1 references "
"%qs-qualified argument type %qT",
attrstr, "const", argtypes[0]);
return NULL_TREE;
}
}
else if (!TYPE_READONLY (TREE_TYPE (argtypes[0])))
{
/* A read_only mode should ideally reference const-qualified
arguments but it's not diagnosed error if one doesn't.
This makes it possible to annotate legacy, const-incorrect
APIs. It might be worth a diagnostic along the lines of
-Wsuggest-const. */
;
}
if (argtypes[1] && !INTEGRAL_TYPE_P (argtypes[1]))
{
error ("attribute %qs positional argument 2 references "
"non-integer argument type %qT",
attrstr, argtypes[1]);
return NULL_TREE;
}
/* Verify that the new attribute doesn't conflict with any existing
attributes specified on previous declarations of the same type
and if not, concatenate the two. */
const char code = attr_access::mode_chars[mode];
tree new_attrs = append_access_attr (node, attrs, attrstr, code, idxs);
if (!new_attrs)
return NULL_TREE;
/* Replace any existing access attribute specification with
the concatenation above. */
new_attrs = tree_cons (NULL_TREE, new_attrs, NULL_TREE);
new_attrs = tree_cons (name, new_attrs, NULL_TREE);
if (node[1])
{
/* Repeat for the previously declared type. */
attrs = TYPE_ATTRIBUTES (TREE_TYPE (node[1]));
new_attrs = append_access_attr (node, attrs, attrstr, code, idxs);
if (!new_attrs)
return NULL_TREE;
new_attrs = tree_cons (NULL_TREE, new_attrs, NULL_TREE);
new_attrs = tree_cons (name, new_attrs, NULL_TREE);
}
/* Recursively call self to "replace" the documented/external form
of the attribute with the condensed internal form. */
decl_attributes (node, new_attrs, flags | ATTR_FLAG_INTERNAL);
return NULL_TREE;
}
/* Extract attribute "arg spec" from each FNDECL argument that has it,
build a single attribute access corresponding to all the arguments,
and return the result. SKIP_VOIDPTR set to ignore void* parameters
(used for user-defined functions for which, unlike in for built-ins,
void* cannot be relied on to determine anything about the access
through it or whether it even takes place).
For example, the parameters in the declaration:
void f (int x, int y, char [x][1][y][3], char [y][2][y][5]);
result in the following attribute access:
value: "+^2[*],$0$1^3[*],$1$1"
list: < <0, x> <1, y> >
where the list has a single value which itself is is a list each
of whose <node>s corresponds to one VLA bound for each of the two
parameters. */
tree
build_attr_access_from_parms (tree parms, bool skip_voidptr)
{
/* Maps each named integral argument DECL seen so far to its position
in the argument list; used to associate VLA sizes with arguments. */
hash_map<tree, unsigned> arg2pos;
/* The string representation of the access specification for all
arguments. */
std::string spec;
unsigned argpos = 0;
/* A TREE_LIST of VLA bounds. */
tree vblist = NULL_TREE;
for (tree arg = parms; arg; arg = TREE_CHAIN (arg), ++argpos)
{
if (!DECL_P (arg))
continue;
tree argtype = TREE_TYPE (arg);
if (DECL_NAME (arg) && INTEGRAL_TYPE_P (argtype))
arg2pos.put (arg, argpos);
tree argspec = DECL_ATTRIBUTES (arg);
if (!argspec)
continue;
if (POINTER_TYPE_P (argtype))
{
/* void* arguments in user-defined functions could point to
anything; skip them. */
tree reftype = TREE_TYPE (argtype);
if (skip_voidptr && VOID_TYPE_P (reftype))
continue;
}
/* Each parameter should have at most one "arg spec" attribute. */
argspec = lookup_attribute ("arg spec", argspec);
if (!argspec)
continue;
/* Attribute arg spec should have one or two arguments. */
argspec = TREE_VALUE (argspec);
/* The attribute arg spec string. */
tree str = TREE_VALUE (argspec);
const char *s = TREE_STRING_POINTER (str);
/* Create the attribute access string from the arg spec string,
optionally followed by position of the VLA bound argument if
it is one. */
{
size_t specend = spec.length ();
if (!specend)
{
spec = '+';
specend = 1;
}
/* Format the access string in place. */
int len = snprintf (NULL, 0, "%c%u%s",
attr_access::mode_chars[access_deferred],
argpos, s);
spec.resize (specend + len + 1);
sprintf (&spec[specend], "%c%u%s",
attr_access::mode_chars[access_deferred],
argpos, s);
/* Trim the trailing NUL. */
spec.resize (specend + len);
}
/* The (optional) list of expressions denoting the VLA bounds
N in ARGTYPE <arg>[Ni]...[Nj]...[Nk]. */
tree argvbs = TREE_CHAIN (argspec);
if (argvbs)
{
spec += ',';
/* Add ARGVBS to the list. Their presence is indicated by
appending a comma followed by the dollar sign and, when
it corresponds to a function parameter, the position of
each bound Ni, so it can be distinguished from
an unspecified bound (as in T[*]). The list is in reverse
order of arguments and needs to be reversed to access in
order. */
vblist = tree_cons (NULL_TREE, argvbs, vblist);
unsigned nelts = 0;
for (tree vb = argvbs; vb; vb = TREE_CHAIN (vb), ++nelts)
{
tree bound = TREE_VALUE (vb);
if (const unsigned *psizpos = arg2pos.get (bound))
{
/* BOUND previously seen in the parameter list. */
TREE_PURPOSE (vb) = size_int (*psizpos);
/* Format the position string in place. */
int len = snprintf (NULL, 0, "$%u", *psizpos);
size_t specend = spec.length ();
spec.resize (specend + len + 1);
sprintf (&spec[specend], "$%u", *psizpos);
/* Trim the trailing NUL. */
spec.resize (specend + len);
}
else
{
/* BOUND doesn't name a parameter (it could be a global
variable or an expression such as a function call). */
spec += '$';
}
}
}
}
if (!spec.length ())
return NULL_TREE;
/* Attribute access takes a two or three arguments. Wrap VBLIST in
another list in case it has more nodes than would otherwise fit. */
vblist = build_tree_list (NULL_TREE, vblist);
/* Build a single attribute access with the string describing all
array arguments and an optional list of any non-parameter VLA
bounds in order. */
tree str = build_string (spec.length (), spec.c_str ());
tree attrargs = tree_cons (NULL_TREE, str, vblist);
tree name = get_identifier ("access");
return build_tree_list (name, attrargs);
}
/* Handle a "nothrow" attribute; arguments as in
struct attribute_spec.handler. */
static 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 "cleanup" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_cleanup_attribute (tree *node, tree name, tree args,
int ARG_UNUSED (flags), bool *no_add_attrs)
{
tree decl = *node;
tree cleanup_id, cleanup_decl;
/* ??? Could perhaps support cleanups on TREE_STATIC, much like we do
for global destructors in C++. This requires infrastructure that
we don't have generically at the moment. It's also not a feature
we'd be missing too much, since we do have attribute constructor. */
if (!VAR_P (decl) || TREE_STATIC (decl))
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
return NULL_TREE;
}
/* Verify that the argument is a function in scope. */
/* ??? We could support pointers to functions here as well, if
that was considered desirable. */
cleanup_id = TREE_VALUE (args);
if (TREE_CODE (cleanup_id) != IDENTIFIER_NODE)
{
error ("cleanup argument not an identifier");
*no_add_attrs = true;
return NULL_TREE;
}
cleanup_decl = lookup_name (cleanup_id);
if (!cleanup_decl || TREE_CODE (cleanup_decl) != FUNCTION_DECL)
{
error ("cleanup argument not a function");
*no_add_attrs = true;
return NULL_TREE;
}
/* That the function has proper type is checked with the
eventual call to build_function_call. */
return NULL_TREE;
}
/* Handle a "warn_unused_result" attribute. No special handling. */
static tree
handle_warn_unused_result_attribute (tree *node, tree name,
tree ARG_UNUSED (args),
int ARG_UNUSED (flags), bool *no_add_attrs)
{
/* Ignore the attribute for functions not returning any value. */
if (VOID_TYPE_P (TREE_TYPE (*node)))
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "sentinel" attribute. */
static tree
handle_sentinel_attribute (tree *node, tree name, tree args,
int ARG_UNUSED (flags), bool *no_add_attrs)
{
if (!prototype_p (*node))
{
warning (OPT_Wattributes,
"%qE attribute requires prototypes with named arguments", name);
*no_add_attrs = true;
}
else
{
if (!stdarg_p (*node))
{
warning (OPT_Wattributes,
"%qE attribute only applies to variadic functions", name);
*no_add_attrs = true;
}
}
if (args)
{
tree position = TREE_VALUE (args);
if (position && TREE_CODE (position) != IDENTIFIER_NODE
&& TREE_CODE (position) != FUNCTION_DECL)
position = default_conversion (position);
if (TREE_CODE (position) != INTEGER_CST
|| !INTEGRAL_TYPE_P (TREE_TYPE (position)))
{
warning (OPT_Wattributes,
"requested position is not an integer constant");
*no_add_attrs = true;
}
else
{
if (tree_int_cst_lt (position, integer_zero_node))
{
warning (OPT_Wattributes,
"requested position is less than zero");
*no_add_attrs = true;
}
}
}
return NULL_TREE;
}
/* Handle a "type_generic" attribute. */
static 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 "target" attribute. */
static tree
handle_target_attribute (tree *node, tree name, tree args, int flags,
bool *no_add_attrs)
{
/* Ensure we have a function type. */
if (TREE_CODE (*node) != FUNCTION_DECL)
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
else if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (*node)))
{
warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
"with %qs attribute", name, "target_clones");
*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 "target_clones" attribute. */
static tree
handle_target_clones_attribute (tree *node, tree name, tree ARG_UNUSED (args),
int ARG_UNUSED (flags), bool *no_add_attrs)
{
/* Ensure we have a function type. */
if (TREE_CODE (*node) == FUNCTION_DECL)
{
if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
{
error ("%qE attribute argument not a string constant", name);
*no_add_attrs = true;
}
else if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (*node)))
{
warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
"with %qs attribute", name, "always_inline");
*no_add_attrs = true;
}
else if (lookup_attribute ("target", DECL_ATTRIBUTES (*node)))
{
warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
"with %qs attribute", name, "target");
*no_add_attrs = true;
}
else
/* Do not inline functions with multiple clone targets. */
DECL_UNINLINABLE (*node) = 1;
}
else
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* For handling "optimize" attribute. arguments as in
struct attribute_spec.handler. */
static tree
handle_optimize_attribute (tree *node, tree name, tree args,
int ARG_UNUSED (flags), bool *no_add_attrs)
{
/* Ensure we have a function type. */
if (TREE_CODE (*node) != FUNCTION_DECL)
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
else
{
struct cl_optimization cur_opts;
tree old_opts = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (*node);
/* Save current options. */
cl_optimization_save (&cur_opts, &global_options, &global_options_set);
tree prev_target_node = build_target_option_node (&global_options,
&global_options_set);
/* If we previously had some optimization options, use them as the
default. */
gcc_options *saved_global_options = NULL;
/* When #pragma GCC optimize pragma is used, it modifies global_options
without calling targetm.override_options_after_change. That can leave
target flags inconsistent for comparison. */
if (flag_checking && optimization_current_node == optimization_default_node)
{
saved_global_options = XNEW (gcc_options);
*saved_global_options = global_options;
}
if (old_opts)
cl_optimization_restore (&global_options, &global_options_set,
TREE_OPTIMIZATION (old_opts));
/* Parse options, and update the vector. */
parse_optimize_options (args, true);
DECL_FUNCTION_SPECIFIC_OPTIMIZATION (*node)
= build_optimization_node (&global_options, &global_options_set);
tree target_node = build_target_option_node (&global_options,
&global_options_set);
if (prev_target_node != target_node)
DECL_FUNCTION_SPECIFIC_TARGET (*node) = target_node;
/* Restore current options. */
cl_optimization_restore (&global_options, &global_options_set,
&cur_opts);
cl_target_option_restore (&global_options, &global_options_set,
TREE_TARGET_OPTION (prev_target_node));
if (saved_global_options != NULL)
{
cl_optimization_compare (saved_global_options, &global_options);
free (saved_global_options);
}
}
return NULL_TREE;
}
/* Handle a "no_split_stack" attribute. */
static tree
handle_no_split_stack_attribute (tree *node, tree name,
tree ARG_UNUSED (args),
int ARG_UNUSED (flags),
bool *no_add_attrs)
{
tree decl = *node;
if (TREE_CODE (decl) != FUNCTION_DECL)
{
error_at (DECL_SOURCE_LOCATION (decl),
"%qE attribute applies only to functions", name);
*no_add_attrs = true;
}
else if (DECL_INITIAL (decl))
{
error_at (DECL_SOURCE_LOCATION (decl),
"cannot set %qE attribute after definition", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "zero_call_used_regs" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_zero_call_used_regs_attribute (tree *node, tree name, tree args,
int ARG_UNUSED (flags),
bool *no_add_attrs)
{
tree decl = *node;
tree id = TREE_VALUE (args);
if (TREE_CODE (decl) != FUNCTION_DECL)
{
error_at (DECL_SOURCE_LOCATION (decl),
"%qE attribute applies only to functions", name);
*no_add_attrs = true;
return NULL_TREE;
}
if (TREE_CODE (id) != STRING_CST)
{
error_at (DECL_SOURCE_LOCATION (decl),
"%qE argument not a string", name);
*no_add_attrs = true;
return NULL_TREE;
}
bool found = false;
for (unsigned int i = 0; zero_call_used_regs_opts[i].name != NULL; ++i)
if (strcmp (TREE_STRING_POINTER (id),
zero_call_used_regs_opts[i].name) == 0)
{
found = true;
break;
}
if (!found)
{
error_at (DECL_SOURCE_LOCATION (decl),
"unrecognized %qE attribute argument %qs",
name, TREE_STRING_POINTER (id));
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "returns_nonnull" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_returns_nonnull_attribute (tree *node, tree name, tree, int,
bool *no_add_attrs)
{
// Even without a prototype we still have a return type we can check.
if (TREE_CODE (TREE_TYPE (*node)) != POINTER_TYPE)
{
error ("%qE attribute on a function not returning a pointer", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "designated_init" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_designated_init_attribute (tree *node, tree name, tree, int,
bool *no_add_attrs)
{
if (TREE_CODE (*node) != RECORD_TYPE)
{
error ("%qE attribute is only valid on %<struct%> type", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
/* Handle a "fallthrough" attribute; arguments as in struct
attribute_spec.handler. */
tree
handle_fallthrough_attribute (tree *, tree name, tree, int,
bool *no_add_attrs)
{
pedwarn (input_location, OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
return NULL_TREE;
}
/* Handle a "patchable_function_entry" attributes; arguments as in
struct attribute_spec.handler. */
static tree
handle_patchable_function_entry_attribute (tree *, tree name, tree args,
int, bool *no_add_attrs)
{
for (; args; args = TREE_CHAIN (args))
{
tree val = TREE_VALUE (args);
if (val && TREE_CODE (val) != IDENTIFIER_NODE
&& TREE_CODE (val) != FUNCTION_DECL)
val = default_conversion (val);
if (!tree_fits_uhwi_p (val))
{
warning (OPT_Wattributes,
"%qE attribute argument %qE is not an integer constant",
name, val);
*no_add_attrs = true;
return NULL_TREE;
}
if (tree_to_uhwi (val) > USHRT_MAX)
{
warning (OPT_Wattributes,
"%qE attribute argument %qE exceeds %u",
name, val, USHRT_MAX);
*no_add_attrs = true;
return NULL_TREE;
}
}
return NULL_TREE;
}
/* Handle a "NSObject" attributes; arguments as in
struct attribute_spec.handler. */
static tree
handle_nsobject_attribute (tree *node, tree name, tree args,
int /*flags*/, bool *no_add_attrs)
{
*no_add_attrs = true;
/* This attribute only applies to typedefs (or field decls for properties),
we drop it otherwise - but warn about this if enabled. */
if (TREE_CODE (*node) != TYPE_DECL && TREE_CODE (*node) != FIELD_DECL)
{
warning (OPT_WNSObject_attribute, "%qE attribute may be put on a"
" typedef only; attribute is ignored", name);
return NULL_TREE;
}
/* The original implementation only allowed pointers to records, however
recent implementations also allow void *. */
tree type = TREE_TYPE (*node);
if (!type || !POINTER_TYPE_P (type)
|| (TREE_CODE (TREE_TYPE (type)) != RECORD_TYPE
&& !VOID_TYPE_P (TREE_TYPE (type))))
{
error ("%qE attribute is for pointer types only", name);
return NULL_TREE;
}
tree t = tree_cons (name, args, TYPE_ATTRIBUTES (type));
TREE_TYPE (*node) = build_type_attribute_variant (type, t);
return NULL_TREE;
}
/* Handle a "objc_root_class" attributes; arguments as in
struct attribute_spec.handler. */
static tree
handle_objc_root_class_attribute (tree */*node*/, tree name, tree /*args*/,
int /*flags*/, bool *no_add_attrs)
{
/* This has no meaning outside Objective-C. */
if (!c_dialect_objc())
warning (OPT_Wattributes, "%qE is only applicable to Objective-C"
" class interfaces, attribute ignored", name);
*no_add_attrs = true;
return NULL_TREE;
}
/* Handle an "objc_nullability" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_objc_nullability_attribute (tree *node, tree name, tree args,
int /*flags*/,
bool *no_add_attrs)
{
*no_add_attrs = true;
tree type = TREE_TYPE (*node);
if (TREE_CODE (*node) == FUNCTION_DECL)
type = TREE_TYPE (type);
if (type && !POINTER_TYPE_P (type))
{
error ("%qE cannot be applied to non-pointer type %qT", name, type);
return NULL_TREE;
}
/* We accept objc_nullability() with a single argument.
string: "unspecified", "nullable", "nonnull" or "resettable"
integer: 0 and 3 where the values have the same meaning as
the strings. */
tree val = TREE_VALUE (args);
if (TREE_CODE (val) == INTEGER_CST)
{
val = default_conversion (val);
if (!tree_fits_uhwi_p (val) || tree_to_uhwi (val) > 3)
error ("%qE attribute argument %qE is not an integer constant"
" between 0 and 3", name, val);
else
*no_add_attrs = false; /* OK */
}
else if (TREE_CODE (val) == STRING_CST
&& (strcmp (TREE_STRING_POINTER (val), "nullable") == 0
|| strcmp (TREE_STRING_POINTER (val), "nonnull") == 0
|| strcmp (TREE_STRING_POINTER (val), "unspecified") == 0
|| strcmp (TREE_STRING_POINTER (val), "resettable") == 0))
*no_add_attrs = false; /* OK */
else if (val != error_mark_node)
error ("%qE attribute argument %qE is not recognised", name, val);
return NULL_TREE;
}
/* Attempt to partially validate a single attribute ATTR as if
it were to be applied to an entity OPER. */
static bool
validate_attribute (location_t atloc, tree oper, tree attr)
{
/* Determine whether the name of the attribute is valid
and fail with an error if not. */
tree atname = get_attribute_name (attr);
if (!lookup_attribute_spec (atname))
{
if (atloc != UNKNOWN_LOCATION)
error_at (atloc, "unknown attribute %qE", atname);
return false;
}
tree args = TREE_VALUE (attr);
if (!args)
return true;
/* FIXME: Do some validation. */
const char *atstr = IDENTIFIER_POINTER (atname);
if (!strcmp (atstr, "format"))
return true;
/* Only when attribute arguments have been provided try to validate
the whole thing. decl_attributes doesn't return an indication of
success or failure so proceed regardless. */
const char tmpname[] = "__builtin_has_attribute_tmp.";
tree tmpid = get_identifier (tmpname);
tree tmpdecl;
if (!strcmp (atstr, "vector_size"))
{
tree type = TYPE_P (oper) ? oper : TREE_TYPE (oper);
/* Check for function type here since type_for_vector_size
strips it while looking for a function's return type. */
if (FUNC_OR_METHOD_TYPE_P (type))
{
warning_at (atloc, OPT_Wattributes,
"invalid operand type %qT for %qs", type, atstr);
return false;
}
type = type_for_vector_size (type);
if (VECTOR_TYPE_P (type))
type = TREE_TYPE (type);
/* Avoid trying to apply attribute vector_size to OPER since
it's overly restrictive. Simply make sure it has the right
type. */
return type_valid_for_vector_size (type, atname, args, NULL);
}
if (TYPE_P (oper))
tmpdecl = build_decl (atloc, TYPE_DECL, tmpid, oper);
else if (DECL_P (oper))
tmpdecl = build_decl (atloc, TREE_CODE (oper), tmpid, TREE_TYPE (oper));
else if (EXPR_P (oper))
tmpdecl = build_decl (atloc, TYPE_DECL, tmpid, TREE_TYPE (oper));
else
return false;
/* Temporarily clear CURRENT_FUNCTION_DECL to make decl_attributes
believe the DECL declared above is at file scope. (See bug 87526.) */
tree save_curfunc = current_function_decl;
current_function_decl = NULL_TREE;
if (DECL_P (tmpdecl))
{
if (DECL_P (oper))
/* An alias cannot be a definition so declare the symbol extern. */
DECL_EXTERNAL (tmpdecl) = true;
/* Attribute visibility only applies to symbols visible from other
translation units so make it "public." */
TREE_PUBLIC (tmpdecl) = TREE_PUBLIC (oper);
}
decl_attributes (&tmpdecl, attr, 0);
current_function_decl = save_curfunc;
/* FIXME: Change decl_attributes to indicate success or failure (and
parameterize it to avoid failing with errors). */
return true;
}
/* Return true if the DECL, EXPR, or TYPE t has been declared with
attribute ATTR. For DECL, consider also its type. For EXPR,
consider just its type. */
bool
has_attribute (location_t atloc, tree t, tree attr, tree (*convert)(tree))
{
if (!attr || !t || t == error_mark_node)
return false;
if (!validate_attribute (atloc, t, attr))
return false;
tree type = NULL_TREE;
tree expr = NULL_TREE;
if (TYPE_P (t))
type = t;
else
{
do
{
/* Determine the array element/member declaration from
a COMPONENT_REF and an INDIRECT_REF involving a refeence. */
STRIP_NOPS (t);
tree_code code = TREE_CODE (t);
if (code == INDIRECT_REF)
{
tree op0 = TREE_OPERAND (t, 0);
if (TREE_CODE (TREE_TYPE (op0)) == REFERENCE_TYPE)
t = op0;
else
break;
}
else if (code == COMPONENT_REF)
t = TREE_OPERAND (t, 1);
else
break;
} while (true);
expr = t;
}
/* Set to true when an attribute is found in the referenced entity
that matches the specified attribute. */
bool found_match = false;
tree atname = get_attribute_name (attr);
const char *namestr = IDENTIFIER_POINTER (atname);
/* Iterate once for a type and twice for a function or variable
declaration: once for the DECL and the second time for its
TYPE. */
for (bool done = false; !found_match && !done; )
{
tree atlist;
if (type)
{
if (type == error_mark_node)
{
/* This could be a label. FIXME: add support for labels. */
warning_at (atloc, OPT_Wattributes,
(TYPE_P (t)
? G_("%qs attribute not supported for %qT "
"in %<__builtin_has_attribute%>")
: G_("%qs attribute not supported for %qE "
"in %<__builtin_has_attribute%>")),
namestr, t);
return false;
}
/* Clear EXPR to prevent considering it again below. */
atlist = TYPE_ATTRIBUTES (type);
expr = NULL_TREE;
done = true;
}
else if (DECL_P (expr))
{
/* Set TYPE to the DECL's type to process it on the next
iteration. */
atlist = DECL_ATTRIBUTES (expr);
type = TREE_TYPE (expr);
}
else
{
type = TREE_TYPE (expr);
atlist = TYPE_ATTRIBUTES (type);
done = true;
}
/* True when an attribute with the sought name (though not necessarily
with the sought attributes) has been found on the attribute chain. */
bool found_attr = false;
/* When clear, the first mismatched attribute argument results
in failure. Otherwise, the first matched attribute argument
results in success. */
bool attr_nonnull = !strcmp ("nonnull", namestr);
bool ignore_mismatches = attr_nonnull;
/* Iterate over the instances of the sought attribute on the DECL or
TYPE (there may be multiple instances with different arguments). */
for (; (atlist = lookup_attribute (namestr, atlist));
found_attr = true, atlist = TREE_CHAIN (atlist))
{
/* If there are no arguments to match the result is true except
for nonnull where the attribute with no arguments must match. */
if (!TREE_VALUE (attr))
return attr_nonnull ? !TREE_VALUE (atlist) : true;
/* Attribute nonnull with no arguments subsumes all values of
the attribute. FIXME: This is overly broad since it only
applies to pointer arguments, but querying non-pointer
arguments is diagnosed. */
if (!TREE_VALUE (atlist) && attr_nonnull)
return true;
/* Iterate over the DECL or TYPE attribute argument's values. */
for (tree val = TREE_VALUE (atlist); val; val = TREE_CHAIN (val))
{
/* Iterate over the arguments in the sought attribute comparing
their values to those specified for the DECL or TYPE. */
for (tree arg = TREE_VALUE (attr); arg; arg = TREE_CHAIN (arg))
{
tree v1 = TREE_VALUE (val);
tree v2 = TREE_VALUE (arg);
if (v1 == v2)
return true;
if (!v1 || !v2)
break;
if (TREE_CODE (v1) == IDENTIFIER_NODE
|| TREE_CODE (v2) == IDENTIFIER_NODE)
/* Two identifiers are the same if their values are
equal (that's handled above). Otherwise ther are
either not the same or oneis not an identifier. */
return false;
/* Convert to make them equality-comparable. */
v1 = convert (v1);
v2 = convert (v2);
/* A positive value indicates equality, negative means
"don't know." */
if (simple_cst_equal (v1, v2) == 1)
return true;
if (!ignore_mismatches)
break;
}
}
}
if (!found_attr)
{
/* Some attributes are encoded directly in the tree node. */
if (!strcmp ("aligned", namestr))
{
if (tree arg = TREE_VALUE (attr))
{
arg = convert (TREE_VALUE (arg));
if (!tree_fits_uhwi_p (arg))
/* Invalid argument. */;
else if (expr && DECL_P (expr)
&& DECL_USER_ALIGN (expr))
found_match = DECL_ALIGN_UNIT (expr) == tree_to_uhwi (arg);
else if (type && TYPE_USER_ALIGN (type))
found_match = TYPE_ALIGN_UNIT (type) == tree_to_uhwi (arg);
}
else if (expr && DECL_P (expr))
found_match = DECL_USER_ALIGN (expr);
else if (type)
found_match = TYPE_USER_ALIGN (type);
}
else if (!strcmp ("const", namestr))
{
if (expr && DECL_P (expr))
found_match = TREE_READONLY (expr);
}
else if (!strcmp ("noreturn", namestr))
{
/* C11 _Noreturn sets the volatile bit without attaching
an attribute to the decl. */
if (expr
&& DECL_P (expr)
&& FUNC_OR_METHOD_TYPE_P (TREE_TYPE (expr)))
found_match = TREE_THIS_VOLATILE (expr);
}
else if (!strcmp ("pure", namestr))
{
if (expr && DECL_P (expr))
found_match = DECL_PURE_P (expr);
}
else if (!strcmp ("deprecated", namestr))
{
found_match = TREE_DEPRECATED (expr ? expr : type);
if (found_match)
return true;
}
else if (!strcmp ("vector_size", namestr))
{
if (!type || !VECTOR_TYPE_P (type))
return false;
if (tree arg = TREE_VALUE (attr))
{
/* Compare the vector size argument for equality. */
arg = convert (TREE_VALUE (arg));
return tree_int_cst_equal (arg, TYPE_SIZE_UNIT (type)) == 1;
}
else
return true;
}
else if (!strcmp ("warn_if_not_aligned", namestr))
{
if (tree arg = TREE_VALUE (attr))
{
arg = convert (TREE_VALUE (arg));
if (expr && DECL_P (expr))
found_match = (DECL_WARN_IF_NOT_ALIGN (expr)
== tree_to_uhwi (arg) * BITS_PER_UNIT);
else if (type)
found_match = (TYPE_WARN_IF_NOT_ALIGN (type)
== tree_to_uhwi (arg) * BITS_PER_UNIT);
}
else if (expr && DECL_P (expr))
found_match = DECL_WARN_IF_NOT_ALIGN (expr);
else if (type)
found_match = TYPE_WARN_IF_NOT_ALIGN (type);
}
else if (!strcmp ("transparent_union", namestr))
{
if (type)
found_match = TYPE_TRANSPARENT_AGGR (type) != 0;
}
else if (!strcmp ("mode", namestr))
{
/* Finally issue a warning for attributes that cannot
be supported in this context. Attribute mode is not
added to a symbol and cannot be determined from it. */
warning_at (atloc, OPT_Wattributes,
"%qs attribute not supported in "
"%<__builtin_has_attribute%>", namestr);
break;
}
}
}
return found_match;
}