Jason Merrill <[email protected]> writes: >>> I was surprised that no change to standard_conversion was needed, but I >>> guess the change to comp_ptr_ttypes_real covers that. > > (apparently not yet)
I've now fixed this in my version of the patch. > ck_qual vs ck_ptr is a bit of a question, whether these conversions > should fit into the C++ conversion model as a qualification conversion > or pointer conversion. I think ck_qual is the better fit, since they > don't change the pointed-to type. So we want to get ck_qual for > (valid) implicit conversions as well as casts. > > Given that, I think handling this in comp_ptr_ttypes_real still makes > sense, rather than adding an additional case in standard_conversion. > It seems that hunk just isn't right yet. Yes, after experimenting with both approaches, I think this is right in the end. >> How do we (properly) request an >> 'ADDR_SPACE_CONVERT_EXPR' to be generated, be it indeed via >> 'cp_fold_convert' or something else? > > For a ck_qual, convert_like_internal ends up calling cp_convert -> > ocp_convert -> cp_convert_to_pointer. It seems this last function > needs updating to not assume it can express all conversions between > pointers to the same type with build_nop. We actually seem to need to make a far greater change than just a change to this one function. For instance, reinterpret_casts do not go through this mechanism for pointers to void or object types, instead just building a NOP_EXPR directly (due to the TYPE_PTROBV_P (type) && TYPE_PTROBV_P (intype) branch). The FE relies on NOP_EXPR also to represent reinterpret casts at least in templates, AFAICT. This means that any code building pointer casts would also be complicated by processing_template_decl. It seems to be that a cleaner solution is to extend NOP_EXPR to be able to handle address space conversions in the C++ frontend, by lowering such NOP_EXPRs into an ADDR_SPACE_CONVERT_EXPR of a NOP_EXPR during genericization. If you think this is a bad idea, I can go back and fix it up. But, it did allow me to continue prototyping (and will unblock Thomas in getting around to the actual work he needs this feature for), so it's a decent stopgap. Adjusted patch below, for reference. It is definitely not complete, not in the least because of the TODOs left therein. I haven't tested it fully, but it passes the Clang testcase test/SemaCXX/address-space-conversion.cpp (which I am yet to port into dg syntax). It is not a very extensive testcase, as it lacks coverage of any case where there exist a pair of address spaces A and B such that A ⊃ B but not B ⊃ A. I've tried to mitigate this issue with a self-test. I'm also not yet sure whether the template deduction logic here is correct. Nor whether compare_ics behaves as it should. Co-authored-by: Paul IANNETTA <[email protected]> Co-authored-by: Thomas Schwinge <[email protected]> --- gcc/c-family/c-common.cc | 46 +++ gcc/c-family/c-common.h | 6 +- gcc/c/c-decl.cc | 19 - gcc/c/c-typeck.cc | 26 -- gcc/cp/call.cc | 3 +- gcc/cp/cp-gimplify.cc | 36 ++ gcc/cp/cp-lang.cc | 1 + gcc/cp/cp-tree.h | 9 +- gcc/cp/decl.cc | 68 +++- gcc/cp/error.cc | 1 + gcc/cp/mangle.cc | 8 + gcc/cp/parser.cc | 60 ++- gcc/cp/pt.cc | 33 +- gcc/cp/tree.cc | 9 - gcc/cp/typeck.cc | 384 +++++++++++++++++- gcc/doc/extend.texi | 2 +- gcc/testsuite/g++.dg/abi/mangle-addr-space1.C | 9 + gcc/testsuite/g++.dg/abi/mangle-addr-space2.C | 9 + gcc/testsuite/g++.dg/ext/addr-space-decl.C | 5 + gcc/testsuite/g++.dg/ext/addr-space-ops.C | 21 + gcc/testsuite/g++.dg/ext/addr-space-ref.C | 29 ++ gcc/testsuite/g++.dg/parse/addr-space.C | 9 + gcc/testsuite/g++.dg/parse/addr-space1.C | 10 + gcc/testsuite/g++.dg/parse/addr-space2.C | 9 + .../g++.dg/template/addr-space-overload.C | 17 + .../g++.dg/template/addr-space-strip1.C | 18 + .../g++.dg/template/addr-space-strip2.C | 17 + .../g++.dg/template/spec-addr-space.C | 8 + gcc/tree.h | 6 +- 29 files changed, 799 insertions(+), 79 deletions(-) create mode 100644 gcc/testsuite/g++.dg/abi/mangle-addr-space1.C create mode 100644 gcc/testsuite/g++.dg/abi/mangle-addr-space2.C create mode 100644 gcc/testsuite/g++.dg/ext/addr-space-decl.C create mode 100644 gcc/testsuite/g++.dg/ext/addr-space-ops.C create mode 100644 gcc/testsuite/g++.dg/ext/addr-space-ref.C create mode 100644 gcc/testsuite/g++.dg/parse/addr-space.C create mode 100644 gcc/testsuite/g++.dg/parse/addr-space1.C create mode 100644 gcc/testsuite/g++.dg/parse/addr-space2.C create mode 100644 gcc/testsuite/g++.dg/template/addr-space-overload.C create mode 100644 gcc/testsuite/g++.dg/template/addr-space-strip1.C create mode 100644 gcc/testsuite/g++.dg/template/addr-space-strip2.C create mode 100644 gcc/testsuite/g++.dg/template/spec-addr-space.C diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc index a16288f4441..1f131688e94 100644 --- a/gcc/c-family/c-common.cc +++ b/gcc/c-family/c-common.cc @@ -660,6 +660,33 @@ c_addr_space_name (addr_space_t as) return IDENTIFIER_POINTER (ridpointers [rid]); } +/* Return true if between two named address spaces, whether there is a superset + named address space that encompasses both address spaces. If there is a + superset, return which address space is the superset. */ + +bool +addr_space_superset (addr_space_t as1, addr_space_t as2, + addr_space_t * common) +{ + if (as1 == as2) + { + *common = as1; + return true; + } + else if (targetm.addr_space.subset_p (as1, as2)) + { + *common = as2; + return true; + } + else if (targetm.addr_space.subset_p (as2, as1)) + { + *common = as1; + return true; + } + else + return false; +} + /* Push current bindings for the function name VAR_DECLS. */ void @@ -2899,6 +2926,25 @@ c_build_bitfield_integer_type (unsigned HOST_WIDE_INT width, int unsignedp) return build_nonstandard_integer_type (width, unsignedp); } +/* Register reserved keyword WORD as qualifier for address space AS. */ + +void +c_register_addr_space (const char *word, addr_space_t as) +{ + int rid = RID_FIRST_ADDR_SPACE + as; + tree id; + + /* Address space qualifiers are only supported + in C with GNU extensions enabled. */ + if (c_dialect_objc () || flag_no_asm) + return; + + id = get_identifier (word); + C_SET_RID_CODE (id, rid); + TREE_LANG_FLAG_0 (id) = 1; + ridpointers[rid] = id; +} + /* The C version of the register_builtin_type langhook. */ void diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 5711e174049..75ed38f8407 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -843,12 +843,11 @@ extern const struct scoped_attribute_specs c_common_format_attribute_table; extern tree (*make_fname_decl) (location_t, tree, int); -/* In c-decl.cc and cp/tree.cc. FIXME. */ -extern void c_register_addr_space (const char *str, addr_space_t as); - /* In c-common.cc. */ extern bool in_late_binary_op; extern const char *c_addr_space_name (addr_space_t as); +extern const char *c_addr_space_name (addr_space_t as); +extern bool addr_space_superset (addr_space_t, addr_space_t, addr_space_t *); extern tree identifier_global_value (tree); extern tree identifier_global_tag (tree); extern int names_builtin_p (const char *); @@ -999,6 +998,7 @@ extern bool c_common_init (void); extern void c_common_finish (void); extern void c_common_parse_file (void); extern alias_set_type c_common_get_alias_set (tree); +extern void c_register_addr_space (const char *, addr_space_t); extern void c_register_builtin_type (tree, const char*); extern bool c_promoting_integer_type_p (const_tree); extern bool self_promoting_args_p (const_tree); diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc index 1b2f2b4edec..37db0a7e62e 100644 --- a/gcc/c/c-decl.cc +++ b/gcc/c/c-decl.cc @@ -13867,25 +13867,6 @@ c_parse_final_cleanups (void) ext_block = NULL; } -/* Register reserved keyword WORD as qualifier for address space AS. */ - -void -c_register_addr_space (const char *word, addr_space_t as) -{ - int rid = RID_FIRST_ADDR_SPACE + as; - tree id; - - /* Address space qualifiers are only supported - in C with GNU extensions enabled. */ - if (c_dialect_objc () || flag_no_asm) - return; - - id = get_identifier (word); - C_SET_RID_CODE (id, rid); - C_IS_RESERVED_WORD (id) = 1; - ridpointers [rid] = id; -} - /* Return identifier to look up for omp declare reduction. */ tree diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index 6bce8c42754..8918dd6feb8 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -316,32 +316,6 @@ c_type_promotes_to (tree type) return type; } -/* Return true if between two named address spaces, whether there is a superset - named address space that encompasses both address spaces. If there is a - superset, return which address space is the superset. */ - -static bool -addr_space_superset (addr_space_t as1, addr_space_t as2, addr_space_t *common) -{ - if (as1 == as2) - { - *common = as1; - return true; - } - else if (targetm.addr_space.subset_p (as1, as2)) - { - *common = as2; - return true; - } - else if (targetm.addr_space.subset_p (as2, as1)) - { - *common = as1; - return true; - } - else - return false; -} - /* Return a variant of TYPE which has all the type qualifiers of LIKE as well as those of TYPE. */ diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index a071596085a..d81c0559f86 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -1497,7 +1497,8 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p, if (same_type_p (from, to)) /* OK */; - else if (c_cast_p && comp_ptr_ttypes_const (to, from, bounds_either)) + else if (c_cast_p && comp_ptr_ttypes_const (to, from, bounds_either, + /*strict_addr_space=*/false)) /* In a C-style cast, we ignore CV-qualification because we are allowed to perform a static_cast followed by a const_cast. */ diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc index 23a1b7ea2cc..b7d321f4341 100644 --- a/gcc/cp/cp-gimplify.cc +++ b/gcc/cp/cp-gimplify.cc @@ -2424,6 +2424,42 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data) && sanitize_flags_p (SANITIZE_NULL | SANITIZE_ALIGNMENT) && TYPE_REF_P (TREE_TYPE (stmt))) ubsan_maybe_instrument_reference (stmt_p); + + /* Appropriately lower conversions between address spaces. We do this so + late, as the frontend uses NOP_EXPRs to represent all sorts of + casts. */ + if (tree out_type = TREE_TYPE (stmt), + in_type = TREE_TYPE (TREE_OPERAND (stmt, 0)); + INDIRECT_TYPE_P (out_type) && INDIRECT_TYPE_P (in_type)) + { + addr_space_t in_as = TYPE_ADDR_SPACE (TREE_TYPE (in_type)); + addr_space_t out_as = TYPE_ADDR_SPACE (TREE_TYPE (out_type)); + if (in_as != out_as) + { + auto build_indirect_type + = (TYPE_PTR_P (out_type) ? build_pointer_type : + TYPE_REF_P (out_type) ? build_reference_type : + /* No other indirect types exist, for now. */ + (gcc_unreachable (), + /* Placeholder, for typing. */ + build_reference_type)); + + tree pointee = TREE_TYPE (out_type); + /* Remove the address space difference. */ + pointee = (build_qualified_type + (pointee, + TYPE_QUALS_NO_ADDR_SPACE (pointee) + | ENCODE_QUAL_ADDR_SPACE (in_as))); + auto loc = EXPR_LOCATION (stmt); + /* Replace the original conversion. */ + stmt = fold_build1_loc (loc, NOP_EXPR, + build_indirect_type (pointee), + TREE_OPERAND (stmt, 0)); + /* Then, re-add an address space conversion. */ + *stmt_p = fold_build1_loc (loc, ADDR_SPACE_CONVERT_EXPR, + out_type, stmt); + } + } break; case CALL_EXPR: diff --git a/gcc/cp/cp-lang.cc b/gcc/cp/cp-lang.cc index c9d02cc7302..450fe5f43e9 100644 --- a/gcc/cp/cp-lang.cc +++ b/gcc/cp/cp-lang.cc @@ -299,6 +299,7 @@ run_cp_tests (void) /* Additional C++-specific tests. */ cp_pt_cc_tests (); cp_tree_cc_tests (); + cp_typeck_cc_tests (); } } // namespace selftest diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index e65c5326b13..9e7fec3086d 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6964,6 +6964,7 @@ enum cp_decl_spec { ds_constinit, ds_consteval, ds_this, /* Index of last element of decl_spec_names. */ + ds_addr_space, ds_thread, ds_type_spec, ds_redefined_builtin_type_spec, @@ -7000,6 +7001,8 @@ struct cp_decl_specifier_seq { cp_storage_class storage_class; /* For the __intN declspec, this stores the index into the int_n_* arrays. */ int int_n_idx; + /* The address space that the declaration belongs to. */ + addr_space_t address_space; /* True iff TYPE_SPEC defines a class or enum. */ BOOL_BITFIELD type_definition_p : 1; /* True iff multiple types were (erroneously) specified for this @@ -9023,7 +9026,8 @@ extern tree convert_for_initialization (tree, tree, tree, int, impl_conv_rhs, tree, int, tsubst_flags_t); extern int comp_ptr_ttypes (tree, tree); -extern bool comp_ptr_ttypes_const (tree, tree, compare_bounds_t); +extern bool comp_ptr_ttypes_const (tree, tree, compare_bounds_t, + bool addr_space_strict = true); extern bool error_type_p (const_tree); extern bool ptr_reasonably_similar (const_tree, const_tree); extern tree build_ptrmemfunc (tree, tree, int, bool, @@ -9766,6 +9770,9 @@ namespace selftest { /* Declarations for specific families of tests within cp, by source file, in alphabetical order. */ extern void cp_pt_cc_tests (); + extern void cp_cvt_cc_tests (void); + extern void cp_call_cc_tests (void); + extern void cp_typeck_cc_tests (void); extern void cp_tree_cc_tests (void); } // namespace selftest #endif /* #if CHECKING_P */ diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 23f23b39544..2441c8bfd35 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -6277,6 +6277,8 @@ get_type_quals (const cp_decl_specifier_seq *declspecs) type_quals |= TYPE_QUAL_VOLATILE; if (decl_spec_seq_has_spec_p (declspecs, ds_restrict)) type_quals |= TYPE_QUAL_RESTRICT; + if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space)) + type_quals |= ENCODE_QUAL_ADDR_SPACE (declspecs->address_space); return type_quals; } @@ -6399,6 +6401,10 @@ check_tag_decl (cp_decl_specifier_seq *declspecs, error_at (declspecs->locations[ds_restrict], "%<__restrict%> can only be specified for objects and " "functions"); + else if (decl_spec_seq_has_spec_p (declspecs, ds_addr_space)) + error_at (declspecs->locations[ds_addr_space], + "address space can only be specified for objects and " + "functions"); else if (decl_spec_seq_has_spec_p (declspecs, ds_thread)) error_at (declspecs->locations[ds_thread], "%<__thread%> can only be specified for objects " @@ -14692,7 +14698,24 @@ grokdeclarator (const cp_declarator *declarator, && (declarator || type_quals)) type = DECL_ORIGINAL_TYPE (TYPE_NAME (type)); - type_quals |= cp_type_quals (type); + auto old_quals = cp_type_quals (type); + + if (addr_space_t as_old = DECODE_QUAL_ADDR_SPACE (old_quals), + as_new = DECODE_QUAL_ADDR_SPACE (type_quals); + (!ADDR_SPACE_GENERIC_P (as_old) && !ADDR_SPACE_GENERIC_P (as_new) + && as_old != as_new)) + { + /* Must exist, as type_quals has an address space. */ + location_t loc = declspecs->locations[ds_addr_space]; + error_at (loc, "conflicting named address spaces (%s vs %s)", + c_addr_space_name (as_old), c_addr_space_name (as_new)); + /* We'll stick with the new address space. Probably, the user intends + to use the new one, and didn't realize that they have another one + specified. */ + old_quals = CLEAR_QUAL_ADDR_SPACE (old_quals); + } + + type_quals |= old_quals; type = cp_build_qualified_type (type, type_quals, ((((typedef_decl && !DECL_ARTIFICIAL (typedef_decl)) || declspecs->decltype_p) @@ -17141,6 +17164,49 @@ grokdeclarator (const cp_declarator *declarator, && (!VAR_P (decl) || !DECL_TEMPLATE_INSTANTIATED (decl))) cp_apply_type_quals_to_decl (type_quals, decl); + /* Warn about address space used for things other than static memory or + pointers. */ + addr_space_t address_space = DECODE_QUAL_ADDR_SPACE (type_quals); + if (!ADDR_SPACE_GENERIC_P (address_space)) + { + if (VAR_P (decl) && decl_context == NORMAL) + { + duration_kind dk = decl_storage_duration (decl); + switch (dk) + { + case dk_auto: + if (! toplevel_bindings_p ()) + error ("%qs specified for variable %qs with automatic storage", + c_addr_space_name (address_space), name); + break; + case dk_static: + case dk_thread: + case dk_dynamic: + break; + default: + gcc_unreachable (); + } + } + else if (decl_context == PARM && TREE_CODE (type) != ARRAY_TYPE) + { + if (name) + error ("%qs specified for parameter %qs", + c_addr_space_name (address_space), name); + else + error ("%qs specified for unnamed parameter", + c_addr_space_name (address_space)); + } + else if (decl_context == FIELD) + { + if (name) + error ("%qs specified for structure field %qs", + c_addr_space_name (address_space), name); + else + error ("%qs specified for structure field", + c_addr_space_name (address_space)); + } + } + return decl; } } diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc index c2fb6027c52..b6769d0a502 100644 --- a/gcc/cp/error.cc +++ b/gcc/cp/error.cc @@ -2843,6 +2843,7 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags) CASE_CONVERT: case IMPLICIT_CONV_EXPR: case VIEW_CONVERT_EXPR: + case ADDR_SPACE_CONVERT_EXPR: case EXCESS_PRECISION_EXPR: { tree op = TREE_OPERAND (t, 0); diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc index a287cded8ae..48c64c0d776 100644 --- a/gcc/cp/mangle.cc +++ b/gcc/cp/mangle.cc @@ -2894,6 +2894,14 @@ write_CV_qualifiers_for_type (const tree type) array. */ cp_cv_quals quals = TYPE_QUALS (type); + if (addr_space_t as = DECODE_QUAL_ADDR_SPACE (quals)) + { + const char *as_name = c_addr_space_name (as); + write_char ('U'); + write_unsigned_number (strlen (as_name)); + write_string (as_name); + ++num_qualifiers; + } if (quals & TYPE_QUAL_RESTRICT) { write_char ('r'); diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 35f7653a4f1..4aa00eb2c42 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see #define INCLUDE_STRING #include "system.h" #include "coretypes.h" +#include "target.h" #include "cp-tree.h" #include "c-family/c-common.h" #include "timevar.h" @@ -1151,6 +1152,14 @@ cp_lexer_get_preprocessor_token (unsigned flags, cp_token *token) token->type = CPP_KEYWORD; /* Record which keyword. */ token->keyword = C_RID_CODE (token->u.value); + + if (token->keyword >= RID_FIRST_ADDR_SPACE + && token->keyword <= RID_LAST_ADDR_SPACE) + { + addr_space_t as + = (addr_space_t) (token->keyword - RID_FIRST_ADDR_SPACE); + targetm.addr_space.diagnose_usage (as, token->location); + } } else { @@ -8902,6 +8911,15 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, postfix_expression = error_mark_node; break; } + if (type != error_mark_node + && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type)) + && current_function_decl) + { + error + ("compound literal qualified by address-space " + "qualifier"); + type = error_mark_node; + } /* Form the representation of the compound-literal. */ postfix_expression = finish_compound_literal (type, initializer, @@ -22493,6 +22511,14 @@ cp_parser_type_specifier (cp_parser* parser, break; } + if (RID_FIRST_ADDR_SPACE <= keyword + && keyword <= RID_LAST_ADDR_SPACE) + { + ds = ds_addr_space; + if (is_cv_qualifier) + *is_cv_qualifier = true; + } + /* Handle simple keywords. */ if (ds != ds_last) { @@ -27430,6 +27456,7 @@ cp_parser_ptr_operator (cp_parser* parser, GNU Extension: cv-qualifier: + address-space-qualifier __restrict__ Returns a bitmask representing the cv-qualifiers. */ @@ -27466,10 +27493,21 @@ cp_parser_cv_qualifier_seq_opt (cp_parser* parser) break; } + if (RID_FIRST_ADDR_SPACE <= token->keyword + && token->keyword <= RID_LAST_ADDR_SPACE) + cv_qualifier + = ENCODE_QUAL_ADDR_SPACE (token->keyword - RID_FIRST_ADDR_SPACE); + if (!cv_qualifier) break; - if (cv_quals & cv_qualifier) + if (CLEAR_QUAL_ADDR_SPACE (cv_quals & cv_qualifier) + /* We need to special-case address spaces, as they are not simple + bits; specifying duplicate qualifiers for address spaces that do + not share any bits (e.g. for address space 1 and 2) would result + in bit-and above producing zero. */ + || (DECODE_QUAL_ADDR_SPACE (cv_quals) + && DECODE_QUAL_ADDR_SPACE (cv_quals))) { gcc_rich_location richloc (token->location); richloc.add_fixit_remove (); @@ -37650,6 +37688,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs, decl_specs->locations[ds] = location; if (ds == ds_thread) decl_specs->gnu_thread_keyword_p = token_is__thread (token); + else if (ds == ds_addr_space) + decl_specs->address_space = token->keyword - RID_FIRST_ADDR_SPACE; } else { @@ -37682,6 +37722,24 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs, error_at (&richloc, "duplicate %qD", token->u.value); } } + else if (ds == ds_addr_space) + { + addr_space_t as1 = decl_specs->address_space; + addr_space_t as2 = token->keyword - RID_FIRST_ADDR_SPACE; + + gcc_rich_location richloc (location); + richloc.add_fixit_remove (); + if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2) + && as1 != as2) + error_at (&richloc, + "incompatible address space qualifiers %qs and %qs", + c_addr_space_name (as1), c_addr_space_name (as2)); + if (as1 == as2 && !ADDR_SPACE_GENERIC_P (as1)) + error_at (&richloc, "duplicate named address space %qs", + c_addr_space_name (as1)); + + decl_specs->address_space = as2; + } else { /* These correspond to cp-tree.h:cp_decl_spec, diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 0c0b3a2cac5..63df3652375 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -25831,8 +25831,16 @@ template_decl_level (tree decl) static int check_cv_quals_for_unify (int strict, tree arg, tree parm) { - int arg_quals = cp_type_quals (arg); - int parm_quals = cp_type_quals (parm); + int arg_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (arg)); + int parm_quals = CLEAR_QUAL_ADDR_SPACE (cp_type_quals (parm)); + + /* Try to unify ARG's address space into PARM's address space. + If PARM does not have any address space qualifiers (ie., as_parm is 0), + there are no constraints on address spaces for this type. */ + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (cp_type_quals (arg)); + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (cp_type_quals (parm)); + addr_space_t as_common; + addr_space_superset (as_arg, as_parm, &as_common); if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM && !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL)) @@ -25853,6 +25861,9 @@ check_cv_quals_for_unify (int strict, tree arg, tree parm) return 0; } + if (!(as_parm == as_common || as_parm == 0)) + return 0; + if (!(strict & (UNIFY_ALLOW_MORE_CV_QUAL | UNIFY_ALLOW_OUTER_MORE_CV_QUAL)) && (arg_quals & parm_quals) != parm_quals) return 0; @@ -26477,10 +26488,28 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict, arg, parm)) return unify_cv_qual_mismatch (explain_p, parm, arg); + int arg_cv_quals = cp_type_quals (arg); + int parm_cv_quals = cp_type_quals (parm); + + /* If PARM does not contain any address spaces constraints it can + fully match the address space of ARG. However, if PARM contains an + address space constraints, it becomes the upper bound. That is, + AS_ARG may be promoted to AS_PARM but not the converse. If we + ended up here, it means that `check_cv_quals_for_unify' succeeded + and that either AS_PARM is 0 (ie., no constraints) or AS_COMMON == + AS_PARM. */ + addr_space_t as_arg = DECODE_QUAL_ADDR_SPACE (arg_cv_quals); + addr_space_t as_parm = DECODE_QUAL_ADDR_SPACE (parm_cv_quals); + addr_space_t as_common = as_parm ? 0 : as_arg; + /* Consider the case where ARG is `const volatile int' and PARM is `const T'. Then, T should be `volatile int'. */ arg = cp_build_qualified_type (arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none); + int unified_cv = + (CLEAR_QUAL_ADDR_SPACE (arg_cv_quals & ~parm_cv_quals) + | ENCODE_QUAL_ADDR_SPACE (as_common)); + arg = cp_build_qualified_type (arg, unified_cv, tf_none); if (arg == error_mark_node) return unify_invalid (explain_p); diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index c9b29900746..c8da1ad845d 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -6996,15 +6996,6 @@ cp_free_lang_data (tree t) DECL_CHAIN (t) = NULL_TREE; } -/* Stub for c-common. Please keep in sync with c-decl.cc. - FIXME: If address space support is target specific, then this - should be a C target hook. But currently this is not possible, - because this function is called via REGISTER_TARGET_PRAGMAS. */ -void -c_register_addr_space (const char * /*word*/, addr_space_t /*as*/) -{ -} - /* Return the number of operands in T that we care about for things like mangling. */ diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc index 683457ccaf8..6ce65895c36 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -40,6 +40,7 @@ along with GCC; see the file COPYING3. If not see #include "attribs.h" #include "asan.h" #include "gimplify.h" +#include "selftest.h" static tree cp_build_addr_expr_strict (tree, tsubst_flags_t); static tree cp_build_function_call (tree, tree, tsubst_flags_t); @@ -719,8 +720,28 @@ composite_pointer_type_r (const op_location_t &location, return error_mark_node; result_type = void_type_node; } - const int q1 = cp_type_quals (pointee1); - const int q2 = cp_type_quals (pointee2); + + /* If we reach this point, and have different address spaces, that means that + the difference between address spaces is too "deep" to cross (e.g. AS1 T** + vs. AS2 T** - note the double star). */ + auto q1 = cp_type_quals (pointee1); + auto q2 = cp_type_quals (pointee2); + addr_space_t as_t1 = DECODE_QUAL_ADDR_SPACE (q1); + addr_space_t as_t2 = DECODE_QUAL_ADDR_SPACE (q2); + + if (as_t1 != as_t2) + { + if (complain & tf_error) + composite_pointer_error (location, diagnostics::kind::permerror, + t1, t2, operation); + else + return error_mark_node; + + /* Pick an arbitrary AS and stick to it. */ + q1 = SET_QUAL_ADDR_SPACE (q1, as_t1); + q2 = SET_QUAL_ADDR_SPACE (q2, as_t1); + } + const int quals = q1 | q2; result_type = cp_build_qualified_type (result_type, (quals | (*add_const @@ -781,6 +802,45 @@ composite_pointer_type (const op_location_t &location, if (null_ptr_cst_p (arg2)) return t1; + /* First of, as an extension, merge address-spaces of the top pointees. + + We do this first such that QUALIFIERS(T1) | QUALIFIERS(T2) produces a + valid set of qualifiers without a munged address space. */ + if (TYPE_PTR_P (t1) && TYPE_PTR_P (t2)) + { + addr_space_t as1 = TYPE_ADDR_SPACE (TREE_TYPE (t1)); + addr_space_t as2 = TYPE_ADDR_SPACE (TREE_TYPE (t2)); + + if (as1 != as2) + { + addr_space_t as_common; + + /* If the two named address spaces are different, determine the common + superset address space. If there isn't one, raise an error. */ + if (!addr_space_superset (as1, as2, &as_common)) + { + as_common = as1; + if (complain & tf_error) + error_at (location, + "%qT and %qT are in disjoint named address spaces", + t1, t2); + else + return error_mark_node; + } + + auto commonize = [&] (tree &t) + { + auto t_pointee = TREE_TYPE (t); + t_pointee = (build_qualified_type + (t_pointee, + SET_QUAL_ADDR_SPACE (cp_type_quals (t), as_common))); + t = build_pointer_type (t_pointee); + }; + commonize (t1); + commonize (t2); + } + } + /* We have: [expr.type] @@ -2079,7 +2139,11 @@ layout_compatible_type_p (tree type1, tree type2, bool explain/*=false*/) return true; } -/* Returns 1 if TYPE1 is at least as qualified as TYPE2. */ +/* Returns 1 if TYPE1 is at least as qualified as TYPE2, and if its address + space is a superset of that of TYPE2. + + Semantically, this is true if an object with qualifiers of TYPE2 can be + used as an object with qualifiers of TYPE1. */ bool at_least_as_qualified_p (const_tree type1, const_tree type2) @@ -2087,22 +2151,43 @@ at_least_as_qualified_p (const_tree type1, const_tree type2) int q1 = cp_type_quals (type1); int q2 = cp_type_quals (type2); - /* All qualifiers for TYPE2 must also appear in TYPE1. */ - return (q1 & q2) == q2; + addr_space_t as1 = DECODE_QUAL_ADDR_SPACE (q1); + q1 = CLEAR_QUAL_ADDR_SPACE (q1); + addr_space_t as2 = DECODE_QUAL_ADDR_SPACE (q2); + q2 = CLEAR_QUAL_ADDR_SPACE (q2); + + return (q1 & q2) == q2 && targetm.addr_space.subset_p (as2, as1); } -/* Returns 1 if TYPE1 is more cv-qualified than TYPE2, -1 if TYPE2 is - more cv-qualified that TYPE1, and 0 otherwise. */ +/* Returns 1 if TYPE1 is more cv-qualified than TYPE2 (i.e. if a TYPE2 object + can be used safely if qualified by qualifiers of TYPE1), -1 if TYPE2 is more + cv-qualified that TYPE1, and 0 otherwise. */ int comp_cv_qualification (int q1, int q2) { - if (q1 == q2) + addr_space_t as1 = DECODE_QUAL_ADDR_SPACE (q1); + q1 = CLEAR_QUAL_ADDR_SPACE (q1); + addr_space_t as2 = DECODE_QUAL_ADDR_SPACE (q2); + q2 = CLEAR_QUAL_ADDR_SPACE (q2); + + /* Address spaces can either fully overlap (whether distinct or not), in + which case, the two are interchangeable, or be strict super/subsets, or + be disjoint. AS_EQ holds if they are fully overlapped. */ + bool as_eq = (targetm.addr_space.subset_p (as2, as1) + && targetm.addr_space.subset_p (as1, as2)); + + if (q1 == q2 && as_eq) return 0; - if ((q1 & q2) == q2) + /* Here, the address spaces necessarily are either disjoint or in a + super/subset relation. A qualifier set Q1 with associated address space + AS1 is more qualified than Q2 with AS2 iff Q1 contains all the qualifiers + of Q2, and AS1 is a superset of AS2. */ + + if ((q1 & q2) == q2 && targetm.addr_space.subset_p (as2, as1)) return 1; - else if ((q1 & q2) == q1) + else if ((q1 & q2) == q1 && targetm.addr_space.subset_p (as1, as2)) return -1; return 0; @@ -7211,6 +7296,29 @@ pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype, tree restype = ptrdiff_type_node; tree target_type = TREE_TYPE (ptrtype); + /* If the operands point into different address spaces, we need to + explicitly convert them to pointers into the common address space + before we can subtract the numerical address values. */ + if (addr_space_t as0 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op0))), + as1 = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (op1))); + as0 != as1) + { + addr_space_t as_common; + tree common_type; + + if (!addr_space_superset (as0, as1, &as_common)) + { + error_at (loc, "pointers to disjoint address spaces " + "%qT and %qT in subtraction", + TREE_TYPE (op0), TREE_TYPE (op1)); + return error_mark_node; + } + + common_type = common_pointer_type (TREE_TYPE (op0), TREE_TYPE (op1)); + op0 = convert (common_type, op0); + op1 = convert (common_type, op1); + } + if (!complete_type_or_maybe_complain (target_type, NULL_TREE, complain)) return error_mark_node; @@ -8712,6 +8820,41 @@ maybe_warn_about_cast_ignoring_quals (location_t loc, tree type, "type qualifiers ignored on cast result type"); } +/* Warns if the cast cross address-space boundaries. */ +static void +maybe_warn_about_addr_spaces (location_t loc, tree type, + tree expr, tsubst_flags_t complain) +{ + if (TREE_CODE (type) == POINTER_TYPE + && TREE_CODE (TREE_TYPE (expr)) == POINTER_TYPE + && !NULLPTR_TYPE_P (expr) + && (complain & tf_warning)) + { + addr_space_t as_to = TYPE_ADDR_SPACE (TREE_TYPE (type)); + addr_space_t as_from = TYPE_ADDR_SPACE (TREE_TYPE (TREE_TYPE (expr))); + addr_space_t as_common; + + if (!addr_space_superset (as_to, as_from, &as_common)) + { + if (ADDR_SPACE_GENERIC_P (as_from)) + warning_at (loc, 0, "cast to %qs address space pointer " + "from disjoint generic address space pointer", + c_addr_space_name (as_to)); + + else if (ADDR_SPACE_GENERIC_P (as_to)) + warning_at (loc, 0, "cast to generic address space pointer " + "from disjoint %qs address space pointer", + c_addr_space_name (as_from)); + + else + warning_at (loc, 0, "cast to %qs address space pointer " + "from disjoint %qs address space pointer", + c_addr_space_name (as_to), + c_addr_space_name (as_from)); + } + } +} + /* Convert EXPR (an expression with pointer-to-member type) to TYPE (another pointer-to-member type in the same hierarchy) and return the converted expression. If ALLOW_INVERSE_P is permitted, a @@ -9148,6 +9291,7 @@ build_static_cast (location_t loc, tree type, tree oexpr, { maybe_warn_about_useless_cast (loc, type, expr, complain); maybe_warn_about_cast_ignoring_quals (loc, type, complain); + maybe_warn_about_addr_spaces (loc, type, expr, complain); } if (processing_template_decl) goto tmpl; @@ -9471,6 +9615,7 @@ build_reinterpret_cast (location_t loc, tree type, tree expr, { maybe_warn_about_useless_cast (loc, type, expr, complain); maybe_warn_about_cast_ignoring_quals (loc, type, complain); + maybe_warn_about_addr_spaces (loc, type, expr, complain); } protected_set_expr_location (r, loc); return r; @@ -9657,6 +9802,7 @@ build_const_cast (location_t loc, tree type, tree expr, { maybe_warn_about_useless_cast (loc, type, expr, complain); maybe_warn_about_cast_ignoring_quals (loc, type, complain); + maybe_warn_about_addr_spaces (loc, type, expr, complain); } protected_set_expr_location (r, loc); return r; @@ -9766,6 +9912,7 @@ cp_build_c_cast (location_t loc, tree type, tree expr, { maybe_warn_about_useless_cast (loc, type, value, complain); maybe_warn_about_cast_ignoring_quals (loc, type, complain); + maybe_warn_about_addr_spaces (loc, type, expr, complain); } else if (complain & tf_error) build_const_cast_1 (loc, type, value, tf_error, &valid_p); @@ -9790,6 +9937,7 @@ cp_build_c_cast (location_t loc, tree type, tree expr, maybe_warn_about_useless_cast (loc, type, value, complain); maybe_warn_about_cast_ignoring_quals (loc, type, complain); + maybe_warn_about_addr_spaces (loc, type, expr, complain); /* Non-class rvalues always have cv-unqualified type. */ if (!CLASS_TYPE_P (type)) @@ -11941,8 +12089,9 @@ comp_ptr_ttypes_real (tree to, tree from, int constp) { bool to_more_cv_qualified = false; bool is_opaque_pointer = false; + bool is_toplevel = true; - for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from)) + for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from), is_toplevel = false) { if (TREE_CODE (to) != TREE_CODE (from)) return false; @@ -11952,6 +12101,20 @@ comp_ptr_ttypes_real (tree to, tree from, int constp) TYPE_OFFSET_BASETYPE (to))) return false; + /* We want to permit a toplevel address space change, but only into a + subset address space. TODO(arsen): this change has an effect on + compare_ics through comp_cv_qual_signature, but I have no idea what + that effect is yet. */ + if (auto as_to = TYPE_ADDR_SPACE (to), as_from = TYPE_ADDR_SPACE (from); + as_to != as_from) + { + if (!is_toplevel) + return false; + + if (!targetm.addr_space.subset_p (as_from, as_to)) + return false; + } + /* Const and volatile mean something different for function and array types, so the usual checks are not appropriate. We'll check the array type elements in further iterations. */ @@ -11963,6 +12126,7 @@ comp_ptr_ttypes_real (tree to, tree from, int constp) if (!at_least_as_qualified_p (from, to)) { if (constp == 0) + /* I'm not sure what this check is meant to be covering. */ return false; to_more_cv_qualified = true; } @@ -12087,18 +12251,29 @@ ptr_reasonably_similar (const_tree to, const_tree from) /* Return true if TO and FROM (both of which are POINTER_TYPEs or pointer-to-member types) are the same, ignoring cv-qualification at - all levels. CB says how we should behave when comparing array bounds. */ + all levels. CB says how we should behave when comparing array bounds. + + This function permits the address-space of a pointer to vary (i.e. if given + T AS1* and T AS2*, it considers the difference unimportant), unless + STRICT_ADDR_SPACE. */ bool -comp_ptr_ttypes_const (tree to, tree from, compare_bounds_t cb) +comp_ptr_ttypes_const (tree to, tree from, compare_bounds_t cb, + bool strict_addr_space) { bool is_opaque_pointer = false; + int depth = 0; - for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from)) + for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from), depth++) { if (TREE_CODE (to) != TREE_CODE (from)) return false; + /* We want to permit a toplevel address space change. */ + if (auto as_to = TYPE_ADDR_SPACE (to), as_from = TYPE_ADDR_SPACE (from); + (strict_addr_space || depth > 1) && as_to != as_from) + return false; + if (TREE_CODE (from) == OFFSET_TYPE && same_type_p (TYPE_OFFSET_BASETYPE (from), TYPE_OFFSET_BASETYPE (to))) @@ -12529,3 +12704,184 @@ c_decl_implicit (const_tree) { return false; } + +#if CHECKING_P +#include <iterator> + +namespace selftest { + +static void +test_addr_spaces () +{ + /* For testing, we need an address space that is a subset of another. We'll + set up a function where 0 is the superset of all, 1 and 2 are distinct, + and 2 and 3 are overlapping. TODO(arsen): take this out of here, and put + it in selftest.h to be used elsewhere. Also set up other hooks. */ + temp_override subset_p_override + { + targetm.addr_space.subset_p, + [] (addr_space_t sub, addr_space_t super) + { + /* Each set is its own (biggest) subset. */ + if (super == sub) + return true; + /* 0 is the superset of all other address spaces. */ + if (super == 0) + return true; + /* 2 and 3 overlap. */ + if ((super == 3 && sub == 2) + || (super == 2 && sub == 3)) + return true; + return false; + } + }; + + tree int_as[4]; + tree intp_as[std::size(int_as)]; + tree param_intp_as[std::size(int_as)]; + tree intpp_as[std::size(int_as)]; + for (addr_space_t i = 0; i < std::size (intp_as); i++) + { + int_as[i] = build_qualified_type (integer_type_node, + ENCODE_QUAL_ADDR_SPACE (i)); + intp_as[i] = build_pointer_type (int_as[i]); + param_intp_as[i] = (build_decl + (UNKNOWN_LOCATION, PARM_DECL, + get_identifier ("some_parm"), + intp_as[i])); + intpp_as[i] = build_pointer_type (intp_as[i]); + } + + /* 0 is a superset of all, ergo, is as qualified as all. */ + ASSERT_TRUE (at_least_as_qualified_p (int_as[0], int_as[0])); + ASSERT_TRUE (at_least_as_qualified_p (int_as[0], int_as[1])); + ASSERT_TRUE (at_least_as_qualified_p (int_as[0], int_as[2])); + ASSERT_TRUE (at_least_as_qualified_p (int_as[0], int_as[3])); + + /* Conversly, nothing is as qualified as zero (except zero). */ + ASSERT_FALSE (at_least_as_qualified_p (int_as[1], int_as[0])); + ASSERT_FALSE (at_least_as_qualified_p (int_as[2], int_as[0])); + ASSERT_FALSE (at_least_as_qualified_p (int_as[3], int_as[0])); + + /* 1 and 2 are distinct, ergo, neither is as qualified as the other. */ + ASSERT_FALSE (at_least_as_qualified_p (int_as[1], int_as[2])); + ASSERT_FALSE (at_least_as_qualified_p (int_as[2], int_as[1])); + + /* 3 and 2 overlap, ergo, both are as qualified the other. */ + ASSERT_TRUE (at_least_as_qualified_p (int_as[3], int_as[2])); + ASSERT_TRUE (at_least_as_qualified_p (int_as[2], int_as[3])); + + /* The same, with comp_cv_qualification. */ + /* Zero is as qualified as itself. */ + ASSERT_EQ (comp_cv_qualification (int_as[0], int_as[0]), 0); + /* But so are 2 and 3, as they overlap fully. */ + ASSERT_EQ (comp_cv_qualification (int_as[2], int_as[3]), 0); + + /* AS0 is over-qualified in relation to 1-3. */ + ASSERT_EQ (comp_cv_qualification (int_as[0], int_as[1]), 1); + ASSERT_EQ (comp_cv_qualification (int_as[0], int_as[2]), 1); + ASSERT_EQ (comp_cv_qualification (int_as[0], int_as[3]), 1); + + ASSERT_EQ (comp_cv_qualification (int_as[1], int_as[0]), -1); + ASSERT_EQ (comp_cv_qualification (int_as[2], int_as[0]), -1); + ASSERT_EQ (comp_cv_qualification (int_as[3], int_as[0]), -1); + + /* 1 and 2 are distinct, ergo, neither is as qualified as the other. */ + ASSERT_EQ (comp_cv_qualification (int_as[1], int_as[2]), 0); + ASSERT_EQ (comp_cv_qualification (int_as[2], int_as[1]), 0); + + /* Now to try comp_ptr_ttypes_const. */ + /* We can assign pointers in any address space to a pointer in zero. */ + ASSERT_TRUE (comp_ptr_ttypes_const (intp_as[0], intp_as[0], bounds_none, false)); + ASSERT_TRUE (comp_ptr_ttypes_const (intp_as[0], intp_as[1], bounds_none, false)); + ASSERT_TRUE (comp_ptr_ttypes_const (intp_as[0], intp_as[2], bounds_none, false)); + ASSERT_TRUE (comp_ptr_ttypes_const (intp_as[0], intp_as[3], bounds_none, false)); + + /* But not if they're a double-pointer, of course! */ + ASSERT_TRUE (comp_ptr_ttypes_const (intp_as[0], intp_as[0], bounds_none, false)); + ASSERT_FALSE (comp_ptr_ttypes_const (intpp_as[0], intp_as[1], bounds_none, false)); + ASSERT_FALSE (comp_ptr_ttypes_const (intpp_as[0], intp_as[2], bounds_none, false)); + ASSERT_FALSE (comp_ptr_ttypes_const (intpp_as[0], intp_as[3], bounds_none, false)); + + /* ... or strict. */ + ASSERT_TRUE (comp_ptr_ttypes_const (intp_as[0], intp_as[0], bounds_none)); + ASSERT_FALSE (comp_ptr_ttypes_const (intp_as[0], intp_as[1], bounds_none)); + ASSERT_FALSE (comp_ptr_ttypes_const (intp_as[0], intp_as[2], bounds_none)); + ASSERT_FALSE (comp_ptr_ttypes_const (intp_as[0], intp_as[3], bounds_none)); + + /* This function permits swapping the top-level address space. */ + ASSERT_TRUE (comp_ptr_ttypes_const (int_as[2], int_as[3], bounds_none, false)); + ASSERT_TRUE (comp_ptr_ttypes_const (int_as[3], int_as[2], bounds_none, false)); + ASSERT_TRUE (comp_ptr_ttypes_const (int_as[1], int_as[2], bounds_none, false)); + ASSERT_TRUE (comp_ptr_ttypes_const (int_as[2], int_as[1], bounds_none, false)); + + + /* Now to try comp_ptr_ttypes_real. The tests are exactly identical, but + they lose a layer of pointers (see comment above comp_ptr_ttypes). */ + /* We can assign pointers in any address space to a pointer in zero. */ + ASSERT_TRUE (comp_ptr_ttypes (int_as[0], int_as[0])); + ASSERT_TRUE (comp_ptr_ttypes (int_as[0], int_as[1])); + ASSERT_TRUE (comp_ptr_ttypes (int_as[0], int_as[2])); + ASSERT_TRUE (comp_ptr_ttypes (int_as[0], int_as[3])); + + /* But not if they're a double-pointer, of course! */ + ASSERT_TRUE (comp_ptr_ttypes (int_as[0], int_as[0])); + ASSERT_FALSE (comp_ptr_ttypes (intp_as[0], int_as[1])); + ASSERT_FALSE (comp_ptr_ttypes (intp_as[0], int_as[2])); + ASSERT_FALSE (comp_ptr_ttypes (intp_as[0], int_as[3])); + + /* This function permits going from a sub-space into a superspace. */ + ASSERT_TRUE (comp_ptr_ttypes (int_as[2], int_as[3])); + ASSERT_TRUE (comp_ptr_ttypes (int_as[3], int_as[2])); + ASSERT_FALSE (comp_ptr_ttypes (int_as[1], int_as[2])); + ASSERT_FALSE (comp_ptr_ttypes (int_as[2], int_as[1])); + + /* Testing composite_pointer_type. */ + op_location_t oploc (UNKNOWN_LOCATION); + + /* A composite of pointers in two address spaces such that one is a superset + of another is a pointer in the superset address space. */ + auto as0_as1_composite = + composite_pointer_type (oploc, TREE_TYPE (param_intp_as[0]), + TREE_TYPE (param_intp_as[1]), + param_intp_as[0], param_intp_as[1], + CPO_CONDITIONAL_EXPR, + /* We'll allow emitting a diagnostic for the sake + of the developer. */ + tf_warning_or_error); + ASSERT_FALSE (error_type_p (as0_as1_composite)); + ASSERT_TRUE (same_type_p (intp_as[0], as0_as1_composite)); + + /* But if they're unrelated, you get an error. */ + auto as2_as1_composite = + composite_pointer_type (oploc, TREE_TYPE (param_intp_as[2]), + TREE_TYPE (param_intp_as[1]), + param_intp_as[2], param_intp_as[1], + CPO_CONDITIONAL_EXPR, + /* We expect a diagnostic here, so suppress it to + get the corresponding error node. */ + tf_none); + ASSERT_TRUE (error_type_p (as2_as1_composite)); + + /* If they overlap, it doesn't really matter which one we land on. */ + auto as2_as3_composite = + composite_pointer_type (oploc, TREE_TYPE (param_intp_as[2]), + TREE_TYPE (param_intp_as[3]), + param_intp_as[2], param_intp_as[3], + CPO_CONDITIONAL_EXPR, + /* This should pass. */ + tf_warning_or_error); + ASSERT_FALSE (error_type_p (as2_as3_composite)); + /* It doesn't really matter which one we get. Or, it shouldn't matter. */ + ASSERT_TRUE (same_type_p (intp_as[2], as2_as3_composite) + || same_type_p (intp_as[3], as2_as3_composite)); +} + +void +cp_typeck_cc_tests () +{ + test_addr_spaces (); +} + +} +#endif diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 9f821f04853..cebab19e874 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -1375,7 +1375,7 @@ initializer expressions and issues a warning. @section Named Address Spaces @cindex Named Address Spaces -As an extension, GNU C supports named address spaces as +As an extension, GNU C and GNU C++ support named address spaces as defined in the N1275 draft of ISO/IEC DTR 18037. Support for named address spaces in GCC will evolve as the draft technical report changes. Calling conventions for any target might also change. At diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C new file mode 100644 index 00000000000..35701c60637 --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space1.C @@ -0,0 +1,9 @@ +// { dg-do compile { target { i?86-*-* x86_64-*-* } } } +// { dg-final { scan-assembler "_Z1fPU8__seg_fsVi" } } + +int f (int volatile __seg_fs *a) +{ + return *a; +} + +int main () {} diff --git a/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C new file mode 100644 index 00000000000..1daf15a73c0 --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/mangle-addr-space2.C @@ -0,0 +1,9 @@ +// { dg-do compile { target { i?86-*-* x86_64-*-* } } } +// { dg-options "-fabi-version=8 -Wabi -save-temps" } +// { dg-final { scan-assembler "_Z1fIU8__seg_fsiEiPT_" } } + +template <class T> +int f (T *p) { return *p; } +int g (__seg_fs int *p) { return *p; } +__seg_fs int *a; +int main() { f(a); } diff --git a/gcc/testsuite/g++.dg/ext/addr-space-decl.C b/gcc/testsuite/g++.dg/ext/addr-space-decl.C new file mode 100644 index 00000000000..4199bf375b3 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/addr-space-decl.C @@ -0,0 +1,5 @@ +// { dg-do compile { target { i?86-*-* x86_64-*-* } } } +__seg_fs char a, b, c; +__seg_fs const int *p; +static /* gives internal linkage to variable q */ +__seg_fs struct { int a; char b; } * __seg_gs q; diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ops.C b/gcc/testsuite/g++.dg/ext/addr-space-ops.C new file mode 100644 index 00000000000..783e36aabab --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/addr-space-ops.C @@ -0,0 +1,21 @@ +// { dg-do compile { target { i?86-*-* x86_64-*-* } } } +int __seg_fs * fs1; +int __seg_fs * fs2; +int __seg_gs * gs1; +int __seg_gs * gs2; + +int +main () +{ + fs1 + fs2; // { dg-error "invalid operands of types .__seg_fs int.. and .__seg_fs int.. to binary .operator.." } + fs1 - fs2; + fs1 - gs2; // { dg-error "pointers to disjoint address spaces .__seg_fs int.. and .__seg_gs int.. in substraction" } + // { dg-error ".__seg_fs int.. and .__seg_gs int.. are in disjoint named address spaces" "" { target *-*-* } .-1 } + fs1 == fs2; + fs1 != gs2; // { dg-error ".__seg_fs int.. and .__seg_gs int.. are in disjoint named address spaces" } + fs1 = fs2; + fs1 = gs2; // { dg-error "invalid conversion from .__seg_gs int.. to .__seg_fs int.." } + fs1 > fs2; + fs1 < gs2; // { dg-error ".__seg_fs int.. and .__seg_gs int.. are in disjoint named address spaces" } + return 0; +} diff --git a/gcc/testsuite/g++.dg/ext/addr-space-ref.C b/gcc/testsuite/g++.dg/ext/addr-space-ref.C new file mode 100644 index 00000000000..e87649abb5d --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/addr-space-ref.C @@ -0,0 +1,29 @@ +// { dg-do compile { target { i?86-*-* x86_64-*-* } } } +// { dg-prune-output "does not allow .register. storage class specifier" } +int __seg_fs * outer_b; + +struct s { + __seg_fs int * ok; + __seg_gs int ko; // { dg-error ".__seg_gs. specified for structure field .ko." } +}; + +namespace ns_a +{ + int __seg_fs * inner_b; + + template<typename T> + int f (T &a) { return a; } + int g (__seg_fs int a) { return a; } // { dg-error ".__seg_fs. specified for parameter .a." } + int h (__seg_fs int *a) { return *a; } +} + +int +main () +{ + int register __seg_gs reg_gs; // { dg-error ".__seg_gs. specified for variable .reg_gs. with automatic storage" } + static __seg_gs int static_gs; + __seg_fs int auto_fs; // { dg-error ".__seg_fs. specified for variable .auto_fs. with automatic storage" } + __seg_fs int *pa = outer_b; + __seg_fs int& ra = *ns_a::inner_b; + return ns_a::f(ra) + ns_a::f(*pa); +} diff --git a/gcc/testsuite/g++.dg/parse/addr-space.C b/gcc/testsuite/g++.dg/parse/addr-space.C new file mode 100644 index 00000000000..ebb6316054a --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/addr-space.C @@ -0,0 +1,9 @@ +// { dg-do compile { target { i?86-*-* x86_64-*-* } } } + +__seg_fs struct foo; // { dg-error "address space can only be specified for objects and functions" } + +int +main () +{ + return 0; +} diff --git a/gcc/testsuite/g++.dg/parse/addr-space1.C b/gcc/testsuite/g++.dg/parse/addr-space1.C new file mode 100644 index 00000000000..2e8ee32a885 --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/addr-space1.C @@ -0,0 +1,10 @@ +// { dg-do compile { target { i?86-*-* x86_64-*-* } } } +// { dg-options "-std=gnu++98" } + +int +main () +{ + struct foo {int a; char b[2];} structure; + structure = ((__seg_fs struct foo) {1 + 2, 'a', 0}); // { dg-error "compound literal qualified by address-space qualifier" } + return 0; +} diff --git a/gcc/testsuite/g++.dg/parse/addr-space2.C b/gcc/testsuite/g++.dg/parse/addr-space2.C new file mode 100644 index 00000000000..5f64d52885e --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/addr-space2.C @@ -0,0 +1,9 @@ +// { dg-do compile { target { i?86-*-* x86_64-*-* } } } + +__seg_fs __seg_gs int *a; // { dg-error "incompatible address space qualifiers" } + +int +main () +{ + return 0; +} diff --git a/gcc/testsuite/g++.dg/template/addr-space-overload.C b/gcc/testsuite/g++.dg/template/addr-space-overload.C new file mode 100644 index 00000000000..70dfcce53fa --- /dev/null +++ b/gcc/testsuite/g++.dg/template/addr-space-overload.C @@ -0,0 +1,17 @@ +// { dg-do compile { target { i?86-*-* x86_64-*-* } } } + +int __seg_fs * fs1; +int __seg_gs * gs1; + +template<typename T, typename U> +__seg_fs T* f (T __seg_fs * a, U __seg_gs * b) { return a; } +template<typename T, typename U> +__seg_gs T* f (T __seg_gs * a, U __seg_fs * b) { return a; } + +int +main () +{ + f (fs1, gs1); + f (gs1, fs1); + return 0; +} diff --git a/gcc/testsuite/g++.dg/template/addr-space-strip1.C b/gcc/testsuite/g++.dg/template/addr-space-strip1.C new file mode 100644 index 00000000000..36c402f5cd2 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/addr-space-strip1.C @@ -0,0 +1,18 @@ +// { dg-do compile { target { i?86-*-* x86_64-*-* } } } +// { dg-require-effective-target c++11 } +// decltype is only available since c++11 + +int __seg_fs * fs1; +int __seg_gs * gs1; + +template<typename T> struct strip; +template<typename T> struct strip<__seg_fs T *> { typedef T type; }; +template<typename T> struct strip<__seg_gs T *> { typedef T type; }; + +int +main () +{ + *(strip<decltype(fs1)>::type *) fs1 == *(strip<decltype(gs1)>::type *) gs1; + // { dg-warning "cast to generic address space pointer from disjoint" "" { target *-*-* } .-1 } + return 0; +} diff --git a/gcc/testsuite/g++.dg/template/addr-space-strip2.C b/gcc/testsuite/g++.dg/template/addr-space-strip2.C new file mode 100644 index 00000000000..a2ffb532087 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/addr-space-strip2.C @@ -0,0 +1,17 @@ +// { dg-do compile { target { i?86-*-* x86_64-*-* } } } + +int __seg_fs * fs1; +int __seg_gs * gs1; + +template<typename T, typename U> +bool f (T __seg_fs * a, U __seg_gs * b) +{ + return *(T *) a == *(U *) b; + // { dg-warning "cast to generic address space pointer from disjoint" "" { target *-*-* } .-1 } +} + +int +main () +{ + return f (fs1, gs1); +} diff --git a/gcc/testsuite/g++.dg/template/spec-addr-space.C b/gcc/testsuite/g++.dg/template/spec-addr-space.C new file mode 100644 index 00000000000..ae9f4de0e1f --- /dev/null +++ b/gcc/testsuite/g++.dg/template/spec-addr-space.C @@ -0,0 +1,8 @@ +// { dg-do compile { target { i?86-*-* x86_64-*-* } } } + +template <class T> +int f (T __seg_gs *p) { return *p; } // { dg-note "candidate: 'template<class T> int f.__seg_gs T\*." } + // { dg-note "template argument deduction/substitution failed:" "" { target *-*-* } .-1 } +__seg_fs int *a; +int main() { f(a); } // { dg-error "no matching" } +// { dg-note "types .__seg_gs T. and .__seg_fs int. have incompatible cv-qualifiers" "" { target *-*-* } .-1 } diff --git a/gcc/tree.h b/gcc/tree.h index 0774d6bd5a0..98fec64c53b 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -2542,9 +2542,13 @@ extern tree vector_element_bits_tree (const_tree); /* Encode/decode the named memory support as part of the qualifier. If more than 8 qualifiers are added, these macros need to be adjusted. */ -#define ENCODE_QUAL_ADDR_SPACE(NUM) ((NUM & 0xFF) << 8) +#define ENCODE_QUAL_ADDR_SPACE(NUM) (((NUM) & 0xFF) << 8) #define DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF) +/* Replace the address space of QUALS with AS, evaluating to the new quals. */ +#define SET_QUAL_ADDR_SPACE(QUALS, AS) \ + ((CLEAR_QUAL_ADDR_SPACE (QUALS)) | (ENCODE_QUAL_ADDR_SPACE (AS))) + /* Return all qualifiers except for the address space qualifiers. */ #define CLEAR_QUAL_ADDR_SPACE(X) ((X) & ~0xFF00) -- 2.39.5 -- Arsen Arsenović
signature.asc
Description: PGP signature
