/* C-family attributes handling.
- Copyright (C) 1992-2017 Free Software Foundation, Inc.
+ Copyright (C) 1992-2019 Free Software Foundation, Inc.
This file is part of GCC.
#include "tree-iterator.h"
#include "opts.h"
#include "gimplify.h"
+#include "tree-pretty-print.h"
static tree handle_packed_attribute (tree *, tree, tree, int, bool *);
static tree handle_nocommon_attribute (tree *, tree, tree, int, bool *);
static tree handle_stack_protect_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_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_mode_attribute (tree *, tree, tree, int, bool *);
static tree handle_section_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_vector_size_attribute (tree *, tree, tree, int,
bool *);
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,
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_bnd_variable_size_attribute (tree *, tree, tree, int, bool *);
-static tree handle_bnd_legacy (tree *, tree, tree, int, bool *);
-static tree handle_bnd_instrument (tree *, tree, tree, int, bool *);
static tree handle_fallthrough_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 *);
+
+/* 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),
+};
+
+static 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)
+};
/* Table of machine-independent attributes common to all C-like languages.
- All attributes referencing arguments should be additionally processed
- in chkp_copy_function_type_adding_bounds for correct instrumentation
- by Pointer Bounds Checker.
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, handler,
- affects_type_identity } */
- { "packed", 0, 0, false, false, false,
- handle_packed_attribute , false},
- { "nocommon", 0, 0, true, false, false,
- handle_nocommon_attribute, false},
- { "common", 0, 0, true, false, false,
- handle_common_attribute, false },
+ /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
+ affects_type_identity, handler, exclude } */
+ { "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,
- handle_noreturn_attribute, false },
- { "volatile", 0, 0, true, false, false,
- handle_noreturn_attribute, false },
- { "stack_protect", 0, 0, true, false, false,
- handle_stack_protect_attribute, false },
- { "noinline", 0, 0, true, false, false,
- handle_noinline_attribute, false },
- { "noclone", 0, 0, true, false, false,
- handle_noclone_attribute, false },
- { "no_icf", 0, 0, true, false, false,
- handle_noicf_attribute, false },
- { "leaf", 0, 0, true, false, false,
- handle_leaf_attribute, false },
- { "always_inline", 0, 0, true, false, false,
- handle_always_inline_attribute, false },
- { "gnu_inline", 0, 0, true, false, false,
- handle_gnu_inline_attribute, false },
- { "artificial", 0, 0, true, false, false,
- handle_artificial_attribute, false },
- { "flatten", 0, 0, true, false, false,
- handle_flatten_attribute, false },
- { "used", 0, 0, true, false, false,
- handle_used_attribute, false },
- { "unused", 0, 0, false, false, false,
- handle_unused_attribute, false },
- { "externally_visible", 0, 0, true, false, false,
- handle_externally_visible_attribute, false },
- { "no_reorder", 0, 0, true, false, false,
- handle_no_reorder_attribute, false },
+ { "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, NULL },
+ { "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 },
+ { "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,
- handle_const_attribute, false },
- { "scalar_storage_order", 1, 1, false, false, false,
- handle_scalar_storage_order_attribute, false },
- { "transparent_union", 0, 0, false, false, false,
- handle_transparent_union_attribute, false },
- { "constructor", 0, 1, true, false, false,
- handle_constructor_attribute, false },
- { "destructor", 0, 1, true, false, false,
- handle_destructor_attribute, false },
- { "mode", 1, 1, false, true, false,
- handle_mode_attribute, false },
- { "section", 1, 1, true, false, false,
- handle_section_attribute, false },
- { "aligned", 0, 1, false, false, false,
- handle_aligned_attribute, false },
- { "weak", 0, 0, true, false, false,
- handle_weak_attribute, false },
- { "noplt", 0, 0, true, false, false,
- handle_noplt_attribute, false },
- { "ifunc", 1, 1, true, false, false,
- handle_ifunc_attribute, false },
- { "alias", 1, 1, true, false, false,
- handle_alias_attribute, false },
- { "weakref", 0, 1, true, false, false,
- handle_weakref_attribute, false },
- { "no_instrument_function", 0, 0, true, false, false,
+ { "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, NULL },
+ { "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,
- false },
- { "no_profile_instrument_function", 0, 0, true, false, false,
+ NULL },
+ { "no_profile_instrument_function", 0, 0, true, false, false, false,
handle_no_profile_instrument_function_attribute,
- false },
- { "malloc", 0, 0, true, false, false,
- handle_malloc_attribute, false },
- { "returns_twice", 0, 0, true, false, false,
- handle_returns_twice_attribute, false },
- { "no_stack_limit", 0, 0, true, false, false,
- handle_no_limit_stack_attribute, false },
- { "pure", 0, 0, true, false, false,
- handle_pure_attribute, false },
- { "transaction_callable", 0, 0, false, true, false,
- handle_tm_attribute, false },
- { "transaction_unsafe", 0, 0, false, true, false,
- handle_tm_attribute, true },
- { "transaction_safe", 0, 0, false, true, false,
- handle_tm_attribute, true },
- { "transaction_safe_dynamic", 0, 0, true, false, false,
- handle_tm_attribute, false },
- { "transaction_may_cancel_outer", 0, 0, false, true, false,
- handle_tm_attribute, false },
+ NULL },
+ { "malloc", 0, 0, 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,
- handle_tm_attribute, false },
- { "transaction_wrap", 1, 1, true, false, false,
- handle_tm_wrap_attribute, false },
+ { "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,
- handle_novops_attribute, false },
- { "deprecated", 0, 1, false, false, false,
- handle_deprecated_attribute, false },
- { "vector_size", 1, 1, false, true, false,
- handle_vector_size_attribute, true },
- { "visibility", 1, 1, false, false, false,
- handle_visibility_attribute, false },
- { "tls_model", 1, 1, true, false, false,
- handle_tls_model_attribute, false },
- { "nonnull", 0, -1, false, true, true,
- handle_nonnull_attribute, false },
- { "nothrow", 0, 0, true, false, false,
- handle_nothrow_attribute, false },
- { "may_alias", 0, 0, false, true, false, NULL, false },
- { "cleanup", 1, 1, true, false, false,
- handle_cleanup_attribute, false },
- { "warn_unused_result", 0, 0, false, true, true,
- handle_warn_unused_result_attribute, false },
- { "sentinel", 0, 1, false, true, true,
- handle_sentinel_attribute, false },
+ { "no vops", 0, 0, true, false, false, false,
+ handle_novops_attribute, NULL },
+ { "deprecated", 0, 1, false, false, false, false,
+ handle_deprecated_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,
- handle_type_generic_attribute, false },
- { "alloc_size", 1, 2, false, true, true,
- handle_alloc_size_attribute, false },
- { "cold", 0, 0, true, false, false,
- handle_cold_attribute, false },
- { "hot", 0, 0, true, false, false,
- handle_hot_attribute, false },
+ { "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,
+ 0, 0, true, false, false, false,
handle_no_address_safety_analysis_attribute,
- false },
- { "no_sanitize", 1, 1, true, false, false,
- handle_no_sanitize_attribute,
- false },
- { "no_sanitize_address", 0, 0, true, false, false,
- handle_no_sanitize_address_attribute,
- false },
- { "no_sanitize_thread", 0, 0, true, false, false,
- handle_no_sanitize_thread_attribute,
- false },
- { "no_sanitize_undefined", 0, 0, true, false, false,
- handle_no_sanitize_undefined_attribute,
- false },
- { "asan odr indicator", 0, 0, true, false, false,
- handle_asan_odr_indicator_attribute,
- false },
- { "warning", 1, 1, true, false, false,
- handle_error_attribute, false },
- { "error", 1, 1, true, false, false,
- handle_error_attribute, false },
- { "target", 1, -1, true, false, false,
- handle_target_attribute, false },
- { "target_clones", 1, -1, true, false, false,
- handle_target_clones_attribute, false },
- { "optimize", 1, -1, true, false, false,
- handle_optimize_attribute, false },
+ 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 },
+ { "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,
- ignore_attribute, false },
- { "no_split_stack", 0, 0, true, false, false,
- handle_no_split_stack_attribute, false },
+ { "*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 },
/* 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,
- handle_fnspec_attribute, false },
- { "warn_unused", 0, 0, false, false, false,
- handle_warn_unused_attribute, false },
- { "returns_nonnull", 0, 0, false, true, true,
- handle_returns_nonnull_attribute, false },
- { "omp declare simd", 0, -1, true, false, false,
- handle_omp_declare_simd_attribute, false },
- { "cilk simd function", 0, -1, true, false, false,
- handle_omp_declare_simd_attribute, false },
- { "simd", 0, 1, true, false, false,
- handle_simd_attribute, false },
- { "omp declare target", 0, 0, true, false, false,
- handle_omp_declare_target_attribute, false },
- { "omp declare target link", 0, 0, true, false, false,
- handle_omp_declare_target_attribute, false },
- { "alloc_align", 1, 1, false, true, true,
- handle_alloc_align_attribute, false },
- { "assume_aligned", 1, 2, false, true, true,
- handle_assume_aligned_attribute, false },
- { "designated_init", 0, 0, false, true, false,
- handle_designated_init_attribute, false },
- { "bnd_variable_size", 0, 0, true, false, false,
- handle_bnd_variable_size_attribute, false },
- { "bnd_legacy", 0, 0, true, false, false,
- handle_bnd_legacy, false },
- { "bnd_instrument", 0, 0, true, false, false,
- handle_bnd_instrument, false },
- { "fallthrough", 0, 0, false, false, false,
- handle_fallthrough_attribute, false },
- { "patchable_function_entry", 1, 2, true, false, false,
+ { "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 },
+ { "simd", 0, 1, true, false, false, false,
+ handle_simd_attribute, NULL },
+ { "omp declare target", 0, 0, 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 },
+ { "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,
- false },
- { NULL, 0, 0, false, false, false, NULL, false }
+ NULL },
+ { "nocf_check", 0, 0, false, true, true, true,
+ handle_nocf_check_attribute, NULL },
+ { "copy", 1, 1, false, false, false, false,
+ handle_copy_attribute, NULL },
+ { NULL, 0, 0, false, false, false, false, NULL, NULL }
};
/* Give the specifications for the format attributes, used by C and all
descendants.
- All attributes referencing arguments should be additionally processed
- in chkp_copy_function_type_adding_bounds for correct instrumentation
- by Pointer Bounds Checker.
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, handler,
- affects_type_identity } */
- { "format", 3, 3, false, true, true,
- handle_format_attribute, false },
- { "format_arg", 1, 1, false, true, true,
- handle_format_arg_attribute, false },
- { NULL, 0, 0, false, false, false, NULL, false }
+ /* { 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
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 (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;
+}
+
+
/* Attribute handlers common to C front ends. */
/* Handle a "packed" attribute; arguments as in
if (TYPE_P (*node))
{
if (!(flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
- *node = build_variant_type_copy (*node);
- TYPE_PACKED (*node) = 1;
+ {
+ 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_INITIAL (*node))
+ && ! DECL_C_BIT_FIELD (*node))
warning (OPT_Wattributes,
"%qE attribute ignored for field of type %qT",
name, TREE_TYPE (*node));
if (TREE_CODE (*node) == FUNCTION_DECL
|| TREE_CODE (*node) == LABEL_DECL)
{
- if (lookup_attribute ("cold", DECL_ATTRIBUTES (*node)) != NULL)
- {
- warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
- "with attribute %qs", name, "cold");
- *no_add_attrs = true;
- }
- /* Most of the rest of the hot processing is done later with
- lookup_attribute. */
+ /* Attribute hot processing is done later with lookup_attribute. */
}
else
{
if (TREE_CODE (*node) == FUNCTION_DECL
|| TREE_CODE (*node) == LABEL_DECL)
{
- if (lookup_attribute ("hot", DECL_ATTRIBUTES (*node)) != NULL)
- {
- warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
- "with attribute %qs", name, "hot");
- *no_add_attrs = true;
- }
- /* Most of the rest of the cold processing is done later with
- lookup_attribute. */
+ /* Attribute cold processing is done later with lookup_attribute. */
}
else
{
handle_no_sanitize_attribute (tree *node, tree name, tree args, int,
bool *no_add_attrs)
{
+ unsigned int flags = 0;
*no_add_attrs = true;
- tree id = TREE_VALUE (args);
if (TREE_CODE (*node) != FUNCTION_DECL)
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
return NULL_TREE;
}
- if (TREE_CODE (id) != STRING_CST)
+ for (; args; args = TREE_CHAIN (args))
{
- error ("no_sanitize argument not a string");
- return NULL_TREE;
- }
-
- char *error_value = NULL;
- char *string = ASTRDUP (TREE_STRING_POINTER (id));
- unsigned int flags = parse_no_sanitize_attribute (string, &error_value);
+ tree id = TREE_VALUE (args);
+ if (TREE_CODE (id) != STRING_CST)
+ {
+ error ("no_sanitize argument not a string");
+ return NULL_TREE;
+ }
- if (error_value)
- {
- error ("wrong argument: \"%s\"", error_value);
- return NULL_TREE;
+ char *string = ASTRDUP (TREE_STRING_POINTER (id));
+ flags |= parse_no_sanitize_attribute (string);
}
add_no_sanitize_value (*node, flags);
warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
}
- else
- DECL_ATTRIBUTES (*node)
- = tree_cons (get_identifier ("stack_protect"),
- NULL_TREE, DECL_ATTRIBUTES (*node));
+
+ 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;
}
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_const_attribute (tree *node, tree name, tree ARG_UNUSED (args),
- int ARG_UNUSED (flags), bool *no_add_attrs)
+ int flags, bool *no_add_attrs)
{
tree type = TREE_TYPE (*node);
*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;
}
if (pri <= MAX_RESERVED_INIT_PRIORITY)
{
if (is_destructor)
- warning (0,
+ warning (OPT_Wprio_ctor_dtor,
"destructor priorities from 0 to %d are reserved "
"for the implementation",
MAX_RESERVED_INIT_PRIORITY);
else
- warning (0,
+ warning (OPT_Wprio_ctor_dtor,
"constructor priorities from 0 to %d are reserved "
"for the implementation",
MAX_RESERVED_INIT_PRIORITY);
vector_mode_valid_p (machine_mode mode)
{
enum mode_class mclass = GET_MODE_CLASS (mode);
- machine_mode innermode;
/* Doh! What's going on? */
if (mclass != MODE_VECTOR_INT
if (targetm.vector_mode_supported_p (mode))
return true;
- innermode = GET_MODE_INNER (mode);
-
/* 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 (innermode);
+ return targetm.scalar_mode_supported_p (GET_MODE_INNER (mode));
}
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_UFRACT:
case MODE_ACCUM:
case MODE_UACCUM:
- valid_mode = targetm.scalar_mode_supported_p (mode);
+ valid_mode
+ = targetm.scalar_mode_supported_p (as_a <scalar_mode> (mode));
break;
case MODE_COMPLEX_INT:
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 (!targetm.addr_space.valid_pointer_mode (mode, as))
+ 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;
fn = build_pointer_type_for_mode;
else
fn = build_reference_type_for_mode;
- typefm = fn (TREE_TYPE (type), mode, false);
+ typefm = fn (TREE_TYPE (type), addr_mode, false);
}
else
{
return !alignment_too_large_p;
}
-/* Handle a "aligned" attribute; arguments as in
- struct attribute_spec.handler. */
+/* Common codes shared by handle_warn_if_not_aligned_attribute and
+ handle_aligned_attribute. */
static tree
-handle_aligned_attribute (tree *node, tree ARG_UNUSED (name), tree args,
- int flags, bool *no_add_attrs)
+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;
- int is_type = 0;
+ bool is_type = false;
tree align_expr;
- int i;
+
+ /* 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)
{
is_type = TREE_CODE (*node) == TYPE_DECL;
}
else if (TYPE_P (*node))
- type = node, is_type = 1;
+ type = node, is_type = true;
- if ((i = check_user_alignment (align_expr, true)) == -1
- || !check_cxx_fundamental_alignment_constraints (*node, i, flags))
- *no_add_attrs = true;
- else if (is_type)
+ /* 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
+ || !check_cxx_fundamental_alignment_constraints (*node, pow2align, flags))
+ {
+ *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. */;
else
*type = build_variant_type_copy (*type);
- SET_TYPE_ALIGN (*type, (1U << i) * BITS_PER_UNIT);
- TYPE_USER_ALIGN (*type) = 1;
+ 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 != 0;
+ if (lastalign)
+ 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) > (1U << i) * BITS_PER_UNIT)
+ && DECL_ALIGN (decl) > bitalign)
/* C++-11 [dcl.align/4]:
When multiple alignment-specifiers are specified for an
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 (TREE_CODE (decl) == FUNCTION_DECL
- && DECL_ALIGN (decl) > (1U << i) * BITS_PER_UNIT)
+ else if (!warn_if_not_aligned_p
+ && TREE_CODE (decl) == FUNCTION_DECL
+ && DECL_ALIGN (decl) > bitalign)
{
+ /* Don't warn for function alignment here if warn_if_not_aligned_p
+ is true. It will be warned about later. */
if (DECL_USER_ALIGN (decl))
- error ("alignment for %q+D was previously specified as %d "
- "and may not be decreased", decl,
- DECL_ALIGN (decl) / BITS_PER_UNIT);
- else
- error ("alignment for %q+D must be at least %d", decl,
- DECL_ALIGN (decl) / BITS_PER_UNIT);
+ {
+ /* 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. */
+ error ("alignment for %q+D was previously specified as %d "
+ "and may not be decreased", decl,
+ DECL_ALIGN (decl) / BITS_PER_UNIT);
+ *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
+ else if (!is_type && !*no_add_attrs && set_align)
{
- SET_DECL_ALIGN (decl, (1U << i) * BITS_PER_UNIT);
+ 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. */
return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
}
-/* Handle a "weakref" attribute; arguments as in struct
- attribute_spec.handler. */
+/* 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_weakref_attribute (tree *node, tree ARG_UNUSED (name), tree args,
- int flags, bool *no_add_attrs)
+handle_copy_attribute (tree *node, tree name, tree args,
+ int flags, bool *no_add_attrs)
{
- tree attr = NULL_TREE;
+ /* Do not apply the copy attribute itself. It serves no purpose
+ other than to copy other attributes. */
+ *no_add_attrs = true;
- /* 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))
+ tree decl = *node;
+
+ tree ref = TREE_VALUE (args);
+ if (ref == error_mark_node)
+ return NULL_TREE;
+
+ if (TREE_CODE (ref) == STRING_CST)
{
- warning (OPT_Wattributes, "%qE attribute ignored", name);
- *no_add_attrs = true;
+ /* 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 (lookup_attribute ("ifunc", DECL_ATTRIBUTES (*node)))
+ if (CONSTANT_CLASS_P (ref)
+ && (INTEGRAL_TYPE_P (TREE_TYPE (ref))
+ || FLOAT_TYPE_P (TREE_TYPE (ref))))
{
- error ("indirect function %q+D cannot be declared weakref", *node);
- *no_add_attrs = true;
+ /* 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;
}
- /* The idea here is that `weakref("name")' mutates into `weakref,
- alias("name")', and weakref without arguments, in turn,
- implicitly adds weak. */
-
- if (args)
+ if (ref == node[1])
{
- 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);
+ /* 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. For constant
+ 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 (TREE_CODE (ref) == ADDR_EXPR)
+ ref = TREE_OPERAND (ref, 0);
+ else if (CONSTANT_CLASS_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 (TREE_TYPE (ref))
+ && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (TREE_TYPE (ref))))))
+ || (TREE_CODE (decl) == FUNCTION_DECL
+ && (VAR_P (ref)
+ || (EXPR_P (ref)
+ && !FUNC_OR_METHOD_TYPE_P (TREE_TYPE (ref))))))
+ {
+ /* 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))
+ 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, 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. */
+ tree reftype = ref;
+ if (DECL_P (ref)
+ && TREE_THIS_VOLATILE (ref)
+ && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (reftype)))
+ TREE_THIS_VOLATILE (decl) = true;
+
+ if (DECL_P (ref) || EXPR_P (ref))
+ reftype = TREE_TYPE (ref);
+
+ 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. */
+ for (tree at = attrs; at; at = TREE_CHAIN (at))
+ decl_attributes (node, at, flags, ref);
+
+ return NULL_TREE;
+}
+
+/* Handle a "weakref" attribute; arguments as in struct
+ attribute_spec.handler. */
+
+static tree
+handle_weakref_attribute (tree *node, tree ARG_UNUSED (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 weakref", *node);
+ *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
{
static tree
handle_tls_model_attribute (tree *node, tree name, tree args,
- int ARG_UNUSED (flags), bool *no_add_attrs)
+ int ARG_UNUSED (flags),
+ bool *ARG_UNUSED (no_add_attrs))
{
tree id;
tree decl = *node;
enum tls_model kind;
- *no_add_attrs = true;
+ if (!VAR_P (decl))
+ {
+ warning (OPT_Wattributes, "%qE attribute ignored because %qD "
+ "is not a variable",
+ name, decl);
+ return NULL_TREE;
+ }
- if (!VAR_P (decl) || !DECL_THREAD_LOCAL_P (decl))
+ if (!DECL_THREAD_LOCAL_P (decl))
{
- warning (OPT_Wattributes, "%qE attribute ignored", name);
+ warning (OPT_Wattributes, "%qE attribute ignored because %qD does "
+ "not have thread storage duration", name, decl);
return NULL_TREE;
}
id = TREE_VALUE (args);
if (TREE_CODE (id) != STRING_CST)
{
- error ("tls_model argument not a string");
+ error ("%qE argument not a string", name);
return NULL_TREE;
}
else if (!strcmp (TREE_STRING_POINTER (id), "global-dynamic"))
kind = TLS_MODEL_GLOBAL_DYNAMIC;
else
- error ("tls_model argument must be one of \"local-exec\", \"initial-exec\", \"local-dynamic\" or \"global-dynamic\"");
+ 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;
struct attribute_spec.handler. */
static tree
-handle_alloc_size_attribute (tree *node, tree ARG_UNUSED (name), tree args,
+handle_alloc_size_attribute (tree *node, tree name, tree args,
int ARG_UNUSED (flags), bool *no_add_attrs)
{
- unsigned arg_count = type_num_arguments (*node);
- for (; args; args = TREE_CHAIN (args))
+ tree decl = *node;
+ tree rettype = TREE_TYPE (decl);
+ if (!POINTER_TYPE_P (rettype))
{
- tree position = TREE_VALUE (args);
- if (position && TREE_CODE (position) != IDENTIFIER_NODE
- && TREE_CODE (position) != FUNCTION_DECL)
- position = default_conversion (position);
+ warning (OPT_Wattributes,
+ "%qE attribute ignored on a function returning %qT",
+ name, rettype);
+ *no_add_attrs = true;
+ return NULL_TREE;
+ }
- if (!tree_fits_uhwi_p (position)
- || !arg_count
- || !IN_RANGE (tree_to_uhwi (position), 1, arg_count))
+ 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 (decl, name, pos, INTEGER_TYPE,
+ next || i > 1 ? i : 0))
+ TREE_VALUE (args) = val;
+ else
{
- warning (OPT_Wattributes,
- "alloc_size parameter outside range");
*no_add_attrs = true;
- return NULL_TREE;
+ break;
}
+
+ args = next;
}
+
return NULL_TREE;
}
struct attribute_spec.handler. */
static tree
-handle_alloc_align_attribute (tree *node, tree, tree args, int,
+handle_alloc_align_attribute (tree *node, tree name, tree args, int,
bool *no_add_attrs)
{
- unsigned arg_count = type_num_arguments (*node);
- tree position = TREE_VALUE (args);
- if (position && TREE_CODE (position) != IDENTIFIER_NODE
- && TREE_CODE (position) != FUNCTION_DECL)
- position = default_conversion (position);
-
- if (!tree_fits_uhwi_p (position)
- || !arg_count
- || !IN_RANGE (tree_to_uhwi (position), 1, arg_count))
+ tree decl = *node;
+ tree rettype = TREE_TYPE (decl);
+ if (!POINTER_TYPE_P (rettype))
{
warning (OPT_Wattributes,
- "alloc_align parameter outside range");
+ "%qE attribute ignored on a function returning %qT",
+ name, rettype);
*no_add_attrs = true;
return NULL_TREE;
}
+
+ if (!positional_argument (*node, name, TREE_VALUE (args), INTEGER_TYPE))
+ *no_add_attrs = true;
+
return NULL_TREE;
}
struct attribute_spec.handler. */
static tree
-handle_assume_aligned_attribute (tree *, tree, tree args, int,
+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 position = TREE_VALUE (args);
- if (position && TREE_CODE (position) != IDENTIFIER_NODE
- && TREE_CODE (position) != FUNCTION_DECL)
- position = default_conversion (position);
+ tree val = TREE_VALUE (args);
+ if (val && TREE_CODE (val) != IDENTIFIER_NODE
+ && TREE_CODE (val) != FUNCTION_DECL)
+ val = default_conversion (val);
- if (TREE_CODE (position) != INTEGER_CST)
+ if (!tree_fits_shwi_p (val))
{
warning (OPT_Wattributes,
- "assume_aligned parameter not integer constant");
+ "%qE attribute %E is not an integer constant",
+ 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_sgn (val) < 0 || 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, %E)",
+ name, val, align);
*no_add_attrs = true;
return NULL_TREE;
}
return NULL_TREE;
}
-/* Handle a "bnd_variable_size" attribute; arguments as in
- struct attribute_spec.handler. */
-
-static tree
-handle_bnd_variable_size_attribute (tree *node, tree name, tree ARG_UNUSED (args),
- int ARG_UNUSED (flags), bool *no_add_attrs)
-{
- if (TREE_CODE (*node) != FIELD_DECL)
- {
- warning (OPT_Wattributes, "%qE attribute ignored", name);
- *no_add_attrs = true;
- }
-
- return NULL_TREE;
-}
-
-/* Handle a "bnd_legacy" attribute; arguments as in
- struct attribute_spec.handler. */
-
-static tree
-handle_bnd_legacy (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 "bnd_instrument" attribute; arguments as in
- struct attribute_spec.handler. */
-
-static tree
-handle_bnd_instrument (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 "warn_unused" attribute; arguments as in
struct attribute_spec.handler. */
{
if (TREE_CODE (*node) == FUNCTION_DECL)
{
- if (lookup_attribute ("cilk simd function",
- DECL_ATTRIBUTES (*node)) != NULL)
- {
- error_at (DECL_SOURCE_LOCATION (*node),
- "%<__simd__%> attribute cannot be used in the same "
- "function marked as a Cilk Plus SIMD-enabled function");
- *no_add_attrs = true;
- }
- else
+ tree t = get_identifier ("omp declare simd");
+ tree attr = NULL_TREE;
+ if (args)
{
- 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;
- }
+ tree id = TREE_VALUE (args);
- 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;
- }
+ if (TREE_CODE (id) != STRING_CST)
+ {
+ error ("attribute %qE argument not a string", name);
+ *no_add_attrs = true;
+ return NULL_TREE;
}
- DECL_ATTRIBUTES (*node) = tree_cons (t,
- build_tree_list (NULL_TREE,
- attr),
- DECL_ATTRIBUTES (*node));
+ 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
{
int ARG_UNUSED (flags), bool *no_add_attrs)
{
if (TREE_CODE (*node) == FUNCTION_DECL)
- DECL_PURE_P (*node) = 1;
- /* ??? TODO: Support types. */
+ {
+ 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);
return NULL_TREE;
}
-/* Handle a "vector_size" attribute; arguments as in
- struct attribute_spec.handler. */
-
+/* Return the "base" type from TYPE that is suitable to apply attribute
+ vector_size to by stripping arrays, function types, etc. */
static tree
-handle_vector_size_attribute (tree *node, tree name, tree args,
- int ARG_UNUSED (flags),
- bool *no_add_attrs)
+type_for_vector_size (tree type)
{
- unsigned HOST_WIDE_INT vecsize, nunits;
- machine_mode orig_mode;
- tree type = *node, new_type, size;
-
- *no_add_attrs = true;
-
- size = TREE_VALUE (args);
- if (size && TREE_CODE (size) != IDENTIFIER_NODE
- && TREE_CODE (size) != FUNCTION_DECL)
- size = default_conversion (size);
-
- if (!tree_fits_uhwi_p (size))
- {
- warning (OPT_Wattributes, "%qE attribute ignored", name);
- return NULL_TREE;
- }
-
- /* Get the vector size (in bytes). */
- vecsize = tree_to_uhwi (size);
-
/* We need to provide for vector pointers, vector arrays, and
functions returning vectors. For example:
|| 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. */
- orig_mode = TYPE_MODE (type);
+ machine_mode orig_mode = TYPE_MODE (type);
if ((!INTEGRAL_TYPE_P (type)
&& !SCALAR_FLOAT_TYPE_P (type)
|| !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type))
|| TREE_CODE (type) == BOOLEAN_TYPE)
{
- error ("invalid vector type for attribute %qE", name);
+ 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);
+ if (size && TREE_CODE (size) != IDENTIFIER_NODE
+ && TREE_CODE (size) != FUNCTION_DECL)
+ size = default_conversion (size);
+
+ if (!tree_fits_uhwi_p (size))
+ {
+ /* FIXME: make the error message more informative. */
+ if (error_p)
+ warning (OPT_Wattributes, "%qE attribute ignored", atname);
+ return NULL_TREE;
+ }
+
+ unsigned HOST_WIDE_INT vecsize = tree_to_uhwi (size);
if (vecsize % tree_to_uhwi (TYPE_SIZE_UNIT (type)))
{
- error ("vector size not an integral multiple of component size");
- return NULL;
+ if (error_p)
+ error ("vector size not an integral multiple of component size");
+ return NULL_TREE;
}
if (vecsize == 0)
}
/* Calculate how many units fit in the vector. */
- nunits = vecsize / tree_to_uhwi (TYPE_SIZE_UNIT (type));
+ unsigned HOST_WIDE_INT nunits = vecsize / tree_to_uhwi (TYPE_SIZE_UNIT (type));
if (nunits & (nunits - 1))
{
- error ("number of components of the vector not a power of two");
+ if (error_p)
+ error ("number of components of the vector not a power of two");
+ else
+ warning (OPT_Wattributes,
+ "number of components of the vector not a power of two");
return NULL_TREE;
}
- new_type = build_vector_type (type, nunits);
+ 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;
+
+ tree new_type = build_vector_type (type, nunits);
/* Build back pointers if needed. */
*node = lang_hooks.types.reconstruct_complex_type (*node, new_type);
/* Handle the "nonnull" attribute. */
static tree
-handle_nonnull_attribute (tree *node, tree ARG_UNUSED (name),
+handle_nonnull_attribute (tree *node, tree name,
tree args, int ARG_UNUSED (flags),
bool *no_add_attrs)
{
tree type = *node;
- unsigned HOST_WIDE_INT attr_arg_num;
/* If no arguments are specified, all pointer arguments should be
non-null. Verify a full prototype is given so that the arguments
return NULL_TREE;
}
- /* Argument list specified. Verify that each argument number references
- a pointer argument. */
- for (attr_arg_num = 1; args; attr_arg_num++, args = TREE_CHAIN (args))
+ for (int i = 1; args; ++i)
{
- unsigned HOST_WIDE_INT arg_num = 0, ck_num;
-
- tree arg = TREE_VALUE (args);
- if (arg && TREE_CODE (arg) != IDENTIFIER_NODE
- && TREE_CODE (arg) != FUNCTION_DECL)
- TREE_VALUE (args) = arg = default_conversion (arg);
-
- if (!get_nonnull_operand (arg, &arg_num))
+ 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
{
- error ("nonnull argument has invalid operand number (argument %lu)",
- (unsigned long) attr_arg_num);
*no_add_attrs = true;
- return NULL_TREE;
+ break;
}
+ args = next;
+ }
- if (prototype_p (type))
- {
- function_args_iterator iter;
- tree argument;
+ return NULL_TREE;
+}
- function_args_iter_init (&iter, type);
- for (ck_num = 1; ; ck_num++, function_args_iter_next (&iter))
- {
- argument = function_args_iter_cond (&iter);
- if (argument == NULL_TREE || ck_num == arg_num)
- break;
- }
+/* Handle the "nonstring" variable attribute. */
- if (!argument
- || TREE_CODE (argument) == VOID_TYPE)
- {
- error ("nonnull argument with out-of-range operand number "
- "(argument %lu, operand %lu)",
- (unsigned long) attr_arg_num, (unsigned long) arg_num);
- *no_add_attrs = true;
- return NULL_TREE;
- }
+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 (TREE_CODE (argument) != POINTER_TYPE)
- {
- error ("nonnull argument references non-pointer operand "
- "(argument %lu, operand %lu)",
- (unsigned long) attr_arg_num, (unsigned long) arg_num);
- *no_add_attrs = true;
- return NULL_TREE;
- }
+ 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;
}
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;
}
/* Nothing to be done here. */
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
+ tmpdecl = build_decl (atloc, TREE_CODE (oper), tmpid, TREE_TYPE (oper));
+
+ /* 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 defintion 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
+ an ARRAY/COMPONENT_REF. */
+ STRIP_NOPS (t);
+ tree_code code = TREE_CODE (t);
+ if (code == ARRAY_REF)
+ t = TREE_OPERAND (t, 0);
+ 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
+ {
+ atlist = TYPE_ATTRIBUTES (TREE_TYPE (expr));
+ 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 (expr && DECL_P (expr)
+ && DECL_USER_ALIGN (expr)
+ && tree_fits_uhwi_p (arg))
+ 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)
+ continue;
+
+ /* Determine the base type from arrays, pointers, and such.
+ Fail if the base type is not a vector. */
+ type = type_for_vector_size (type);
+ if (!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;
+}