Re: [PATCH gcc/* 2/2] gcc: stop using 'int' to represent sets of qualifiers
"Jose E. Marchesi" <jemarch-mXXj517/[email protected]> Sun, 05 Jul 2026 23:31:45 +0200
| Newsgroups | gmane.comp.gcc.jit,gmane.comp.gcc.patches,gmane.comp.gcc.fortran,gmane.comp.gcc.algol68,gmane.comp.gcc.rust |
|---|---|
| Message-ID | <[email protected]> |
[Adding David Faust in CC, for the BTF bits and dwarf2out in general.]
> The set of all qualifiers present on a type consists of the const,
> volatile, restrict and atomic qualification, which may be either present
> or absent, and the address space qualifier, which may be one of many
> values (one of which is the "generic" address space, present on all
> platforms, used in absence of another address space; it is also the
> address space qualifier on which all standard library routines operate).
>
> For the former four, 'int' serves us okay; using ints as sets is a
> well-understood pattern (but it leads to problems like those described
> here when those ints cease to be sets). Since they're either present or
> absent, treating an int as a bit-set is convenient and simple. But,
> with the introduction of address space qualifiers into the mix, the
> semantics of these operators becomes incorrect.
>
> Take, for instance, the following line of code:
>
> quals_union = quals1 | quals2;
>
> ... (where quals1, quals2 are TYPE_QUALS of some types)
>
> Only in cases where DECODE_QUAL_ADDR_SPACE (quals1) ==
> DECODE_QUAL_ADDR_SPACE (quals2), or where one of those address spaces is
> generic, and the other a super/subset of the generic address space, does
> this yields what the author intended. In all other cases, the operation
> above yields subtly incorrect results, while the compiler is (naturally)
> silent about it.
>
> This issue is not theoretical either. In the implementation of the C++
> Named Address Spaces support currently being worked on and discussed on
> the mailing list[2], the following broken testcase arose (for the GCN
> target; __flat address space is AS1, __lds is AS2, and __gds is AS3;
> __flat is a superset address space to __lds):
>
> template<typename T>
> __flat T *
> foo ();
>
> void
> bar ()
> { **foo<__lds int> (); }
>
> The above testcase produces the diagnostic:
>
> <stdin>:1:52: error: invalid type argument of unary '*' (have '__gds int')
>
> It is obvious how this came about: the frontend used '|' to merge two
> sets of qualifiers, presuming that such a merge can never fail, because
> the union of two sets is a valid and complete function, and that the
> bit-OR of these two bitsets is the union of the corresponding two sets
> of qualifiers.
>
> However, this is not so: with the introduction of address spaces, the
> union of two sets of qualifiers ceases to coincide with the bit-OR
> operator, and the union becomes a partial function.
>
> To demonstrate the former, we can use the testcase above. In it, the
> C++ frontend was trying to take the union of the qualifier sets {__lds}
> and {__flat}. These were previously represented as 0x0100 and 0x0200.
> Ergo, the result of the bit-op was 0x0300.
>
> 0x0300 corresponds to the qualifier set {__gds}. This is how we got the
> bad diagnostic above. This is an instance of the "bit-OR no longer
> coincides with set union (except by accident)" problem; the union of
> those two sets (in most cases, anyway) is {__flat}.
>
> But, even if we were to fix this initial problem, we still have the
> problem of the union of qualifier sets becoming a partial function.
>
> This one can be demonstrated in C, as the C frontend already deals with
> this problem in an ad-hoc fashion.
>
> In the following case, the __lds and __global address spaces are
> distinct (though they are both subsets of the __flat address space):
>
> typedef __lds int lds_int;
> void foo (__global lds_int *x);
>
> ... the C frontend issues the diagnostic:
>
> <stdin>:2:31: error: conflicting named address spaces (__global vs __lds)
>
> It had to do so because the union of the qualifier sets {__lds} and
> {__global} for the purposes of this declaration does not exist.[1]
>
> The way the C FE handles this is ad-hoc: in 'grokdeclarator', it has a
> specific check that covers this case:
>
> if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2) && as1 != as2)
> error_at (loc, "conflicting named address spaces (%s vs %s)",
> c_addr_space_name (as1), c_addr_space_name (as2));
>
> Thus, it is quite easy for developers to forget this check.
>
> What's worse, there is a lot of existing code that presumes that the set
> of qualifiers is actually the set of the four "simple" present/absent
> qualifier (CVRA). If that presumption changes, there's no way to
> diagnose all sites where this presumption is now broken (as it is in the
> C++ NAS support patch).
>
> The C++ type system is completely capable of encoding the restrictions
> above, and ergo diagnosing misuse. Making use of that is the goal of
> this patch.
>
> First, we make a distinction between cv_qualifier and qualifier_set, as
> many frontends do not care about anything but the cv-qualifiers, and
> because the former have operations not applicable to the latter.
>
> The former models the CVRA qualifiers, which are either present or
> absent.
>
> In this revision, I left it as an unscoped enum, but fixed its
> underlying type as 'unsigned char' (so that its size is known to be less
> than that of qualifier_set). It may be desirable to make it an enum
> class, to forbid the usage of operators like + on it. This is a lower
> priority since there's no existing code that does so.
>
> The latter (qualifier_set) models the set of all qualifiers, i.e. CVRA +
> the address space qualifier (at the moment).
>
> The interesting bits of the patch are in tree-core.h and tree.h. These
> two provide the new types and matching helper functions. It may be
> worth breaking them out into bits-style headers, though. I've tried to
> curb the growth of those headers too much, but the constexpr operators
> and functions were actually needed a few times.
>
> For cv_qualifiers, I've provided binary bitwise operations, in order to
> inhibit integer promotion. This makes it so that manual casting isn't
> necessary when using cv_qualifier values.
>
> For qualifier_set, tree.h lost operations that were made redundant/wrong
> by the change. In their place, I've provided functions for modifying
> and reading qualifier_set values.
>
> Note, however, the decision to drop operator& for qualifier_sets. As it
> turns out, many places in the codebase used patterns like 'q & ~p' to
> remove qualifier P from set Q, but this became incorrect, as it loses
> the address space qualifier also.
>
> This operation was also often used for simple presence checks, by simply
> checking 'q & p', so I initially made operator& return 'bool', but this
> turned out to silently change the meaning of some existing code, where
> a pattern like 'int cqual = q & TYPE_QUALS_CONST' appeared.
>
> Hence, I decided it is better not to provide this operator as it opens
> the possibility for easy misuse, and because 'without', 'intercept' and
> 'has' are quite short anyway.
>
> Qualifier sets may be decomposed into (currently) a pair, that may be
> destructured via std::tie. This was provided as such to allow inducing
> errors should a new component ever appear on qualifiers sets, even
> though this is quite an unlikely eventuality. In essence, should
> qualifier sets grow to contain one more member, all the places that do:
>
> std::tie (cvquals, addrspace) = quals.split ();
>
> ... would yell, letting us know what to fix.
>
> The qualifier set type is 16 bits, and trivially copyable and
> destructible, and so, fits into registers on most machines. Most of the
> operations on the qualifier set type are also provided as constexpr
> functions, and so, should be very easy for the compiler to optimize
> away.
>
> The two union operations provided for qualifier sets now are merge and
> join. These differ in that the former is apt for finding qualification
> that can be used in common for two objects, and that the latter can be
> used to add qualification to an existing qualifier set
> "syntactically" (i.e. as if the keywords were just added to the original
> source code from which the qualifier set was constructed). These two
> operations were most common in the C++ frontend, especially the latter.
>
> This version of the patch does not extensively refactor the C frontend
> to utilize the new operations; since it is a blocker for the C++ Named
> Address Spaces support, I didn't prioritize that.
>
> Reg-strapped on x86_64-linux-gnu, powerpc64le-linux-gnu, and
> (currently being) tested on amdgcn-amdhsa and s390x-ibm-linux-gnu.
> Build-tested for rl78-elf.
>
> No functional changes intended.
>
> [1] Note that there's actually a few distinct union operations that
> could exist, depending on the context. For instance, one would
> expect '__AS1 some_typedef', where 'some_typedef' is in the generic
> address space, to always be acceptable in a declaration, but, for
> two values __AS1 T* p1 and T* pG, the expression cond ? p1 : pG is
> not acceptable if __AS1 is not subset of the generic address space
> or vice-versa, despite both performing something that can be
> described as a qualifier-set union of {__AS1} and {}.
> [2] https://inbox.sourceware.org/gcc-patches/[email protected]/
>
> gcc/ada/ChangeLog:
>
> * gcc-interface/decl.cc (gnat_to_gnu_entity): Update uses of
> qualifiers not to use 'int'.
> (gnat_to_gnu_component_type): Ditto.
> * gcc-interface/gigi.h (ada_type_quals): New. Helper function
> returning qualifiers for a type relevant to Ada.
> (change_qualified_type): Update not to use 'int' for qualifiers.
> * gcc-interface/utils.cc (update_pointer_to): Use ada_type_quals
> instead of TYPE_QUALS.
>
> gcc/ChangeLog:
>
> * attribs.cc (decl_attributes): Update to use qualifier_sets
> instead of ints for qualifiers.
> (build_type_attribute_qual_variant): Ditto.
> (attr_access::array_as_string): Ditto.
> * attribs.h (build_type_attribute_qual_variant): Ditto.
> * config/gcn/gcn-tree.cc (gcn_goacc_get_worker_red_decl): Update
> not to use 'int' for qualifiers, and to use new qualifier_set
> APIs.
> (gcn_goacc_adjust_private_decl): Ditto.
> (gcn_goacc_create_worker_broadcast_record): Ditto.
> * config/i386/i386-builtins.cc (ix86_get_builtin_type): Use
> cv_qualifier instead of 'int' when dealing with qualifiers.
> * config/i386/i386.cc (ix86_stack_protect_guard): Update
> not to use 'int' for qualifiers, and to use new qualifier_set
> APIs.
> * config/rl78/rl78.cc (rl78_insert_attributes): Update
> not to use 'int' for qualifiers, and to use new qualifier_set
> APIs.
> * config/rs6000/rs6000-c.cc (altivec_resolve_overloaded_builtin):
> Update not to use 'int' for qualifiers, and to use new
> qualifier_set APIs.
> * config/rs6000/rs6000.cc (rs6000_handle_altivec_attribute):
> Update not to use 'int' for qualifiers, and to use new
> qualifier_set APIs.
> * config/s390/s390-c.cc (s390_fn_types_compatible): Update to
> use new qualifier APIs.
> * coretypes.h (ADDR_SPACE_GENERIC): Make sure the constant is of
> type addr_space_t.
> * dwarf2out.cc (decl_quals): Use cv_qualifier instead of 'int'
> for qualifiers.
> (modified_type_die): Ditto.
> (add_type_attribute): Ditto.
> (subrange_type_die): Ditto.
> (get_nearest_type_subqualifiers): Ditto.
> (struct dwarf_qual_info_t): Ditto.
> (qualified_die_p): Ditto.
> (override_type_for_decl_p): Ditto.
> * fold-const.cc (fold_unary_loc): Use new qualifier_set APIs.
> * gimple-lower-bitint.cc (bitint_large_huge::limb_access): Use
> new qualifier_set APIs.
> (bitint_large_huge::build_bit_field_ref): Ditto.
> (bitint_large_huge::lower_stmt): Ditto.
> * gimplify.cc: Use new qualifier_set APIs.
> * ipa-free-lang-data.cc (free_lang_data_in_type): Use new
> qualifier_set APIs.
> * langhooks.h (struct qualifier_set): Forward-declare.
> (struct lang_hooks_for_tree_dump): Make TYPE_QUALS return
> qualifier_set.
> * langhooks-def.h (lhd_tree_dump_type_quals): Update signature
> to match above change.
> * langhooks.cc (lhd_tree_dump_dump_tree): Ditto.
> * omp-low.cc (install_var_field): Update to use new
> qualifier_set APIs.
> * omp-oacc-neuter-broadcast.cc (install_var_field): Update to
> use new qualifier_set APIs.
> * omp-offload.cc (oacc_rewrite_var_decl): Update to use new
> qualifier_set APIs.
> * tree-core.h (enum cv_qualifier): Set underlying type to
> 'unsigned char'.
> (TYPE_QUAL_ALL): New. Mask of all elements of cv_qualifier.
> (operator|): New. Returns union of two CV-qualifier sets.
> (operator|=): New. As above, but mutates LHS.
> (operator&): New. Returns intersection of two CV-qualifier
> sets.
> (operator&=): New. As above, but mutates LHS.
> (operator^): New. Returns symmetric difference of two
> CV-qualifier sets.
> (operator^=): New. As above, but mutates LHS.
> (operator~): New. Flips state of all CV-qualifiers from present
> to absent in a CV-qualifier set and vice-versa, returning a new
> CV-qualifier set.
> * tree-dump.cc (dequeue_and_dump): Update to use new qualifier
> set APIs, and handle address spaces.
> * tree-inline.cc (remap_type_1): Update to use new qualifier_set
> APIs.
> * tree-pretty-print.cc (dump_generic_node): Update to use new
> qualifier set APIs.
> * tree-profile.cc (tree_profiling): Update to use new qualifier
> set APIs.
> * tree-sra.cc (build_ref_for_offset): Update to use new
> qualifier set APIs.
> * tree-ssa-address.cc (move_hint_to_base): Update to use new
> qualifier set APIs.
> * tree-switch-conversion.cc (switch_conversion::build_one_array):
> Update to use new qualifier set APIs.
> * tree-vect-stmts.cc (get_related_vectype_for_scalar_type):
> Update to use new qualifier set APIs.
> * tree.cc (set_type_quals): Update to use qualifier_set instead
> of 'int'.
> (qualifier_set::merge): New.
> (qualifier_set::join): New.
> (qualifier_set::can_qualify): New.
> (qualifier_set::debug): New.
> (check_base_type): Update to use new qualifier_set APIs.
> (check_qualified_type): Ditto.
> (get_qualified_type): Ditto.
> (build_qualified_type): Ditto.
> (make_vector_type): Ditto.
> (build_atomic_base): Ditto.
> * tree.h (ENCODE_QUAL_ADDR_SPACE): Drop.
> (DECODE_QUAL_ADDR_SPACE): Drop.
> (CLEAR_QUAL_ADDR_SPACE): Drop.
> (KEEP_QUAL_ADDR_SPACE): Drop.
> (struct qualifier_set): New. Provides a representation for a
> set of CV-qualifiers and an address space qualifier, as well as
> various operations that pertain to such sets.
> (TYPE_QUALS): Update to produce a qualifier_set.
> (operator|): New. Convenience operator for adding CV-qualifiers
> to a qualifier_set.
> (operator|=): New. Like the above, but mutates LHS.
> (operator^): New. Convenience operator for taking the symmetric
> difference between a qualifier_set and a CV-qualifier set.
> (operator^=): New. Like the above, but mutates LHS.
> (TYPE_QUALS_NO_ADDR_SPACE): Update to produce cv_qualifier.
> (TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC): Ditto.
> (check_qualified_type): Update signature to use qualifier_set
> instead of ints.
> (get_qualified_type): Ditto.
> (build_qualified_type): Ditto.
> (build_type_variant): Ditto.
> (qualifier_set::qualifier_set): New. Constructs a qualifier set
> from a TREE_NODE. Used where new expansion of TYPE_QUALS (which
> contains a comma) breaks other macros. :-(
> * ubsan.cc (instrument_bool_enum_load): Update to use new
> qualifier set APIs.
> * vtable-verify.cc (vtbl_map_get_node): Update to use new
> qualifier set APIs.
> (find_or_create_vtbl_map_node): Ditto.
>
> gcc/c-family/ChangeLog:
>
> * c-ada-spec.cc (dump_ada_node): Update to use new qualifier set
> APIs.
> * c-common.cc (c_apply_type_quals_to_decl): Update to receive
> cv_qualifier.
> (complete_array_type): Use qualifier_set instead of 'int' for
> sets of qualifiers.
> (get_atomic_generic_size): Update to use new qualifier set APIs.
> * c-common.h (c_apply_type_quals_to_decl): Update to receive
> cv_qualifier. Provide an overload that takes a qualifier_set as
> convenience.
> (c_build_qualified_type): Update to receive qualifier_set
> instead of an int.
> * c-format.cc (deref_n_times): Use TYPE_UNQUALIFIED instead of
> constant zero.
> * c-pretty-print.cc (pp_c_cv_qualifiers): Update to receive
> cv_qualifier.
> (pp_c_type_qualifier_list): Stop using 'int' for CV-qualifier
> sets.
> (c_pretty_printer::direct_abstract_declarator): Ditto.
> * c-pretty-print.h (pp_c_cv_qualifiers): Update to receive
> cv_qualifier.
>
> gcc/c/ChangeLog:
>
> * c-aux-info.cc (gen_type): Update to use new qualifier_set
> APIs.
> * c-decl.cc (diagnose_mismatched_decls): Ditto.
> (quals_from_declspecs): Update to return qualifier sets.
> (build_array_declarator): Use qualifier_set instead of ints.
> (diagnose_uninitialized_cst_member): Update to use new
> qualifier_set APIs.
> (grokdeclarator): Ditto.
> (get_parm_info): Update to use new qualifier_set APIs.
> (c_update_type_canonical): Ditto.
> (finish_struct): Ditto.
> (make_pointer_declarator): Use qualifier_set instead of ints.
> * c-objc-common.cc (c_tree_printer): Convert %v arg to
> cv_qualifier.
> * c-parser.cc (c_parser_declspecs): Update to use qualifier_set
> APIs.
> (c_parser_typeof_specifier): Ditto.
> (c_parser_generic_selection): Ditto.
> (c_parser_postfix_expression_after_paren_type): Ditto.
> * c-tree.h (struct c_declarator): Update to use qualifier_set
> instead of ints.
> (quals_from_declspecs): Update to use qualifier_set.
> * c-typeck.cc (null_pointer_constant_p): Update to use
> qualifier_set.
> (qualify_type): Update to use new qualifier_set APIs.
> (c_build_array_type): Ditto.
> (c_build_type_attribute_qual_variant): Update to use
> qualifier_set.
> (composite_type_internal): Ditto.
> (common_pointer_type): Ditto.
> (c_common_type): Ditto.
> (function_types_compatible_p): Ditto.
> (convert_lvalue_to_rvalue): Ditto.
> (build_component_ref): Ditto.
> (build_function_call_vec): Ditto.
> (build_unary_op): Ditto.
> (build_conditional_expr): Ditto.
> (handle_warn_cast_qual): Ditto.
> (convert_for_assignment): Ditto.
> (build_binary_op): Ditto.
> (c_build_qualified_type): Ditto.
>
> gcc/cp/ChangeLog:
>
> * call.cc (strip_top_quals): Use TYPE_UNQUALIFIED instead of 0.
> (standard_conversion): Update to use qualifier_set APIs.
> * class.cc (build_simple_base_path): Update to use qualifier_set
> instead of int.
> * cp-objcp-common.h (cp_type_quals_as_set): Add wrapper, to
> allow converting cv_qualifier to qualifier_set.
> (LANG_HOOKS_TREE_DUMP_TYPE_QUALS_FN): Use said wrapper.
> * cp-tree.h (cp_cv_quals): Change to be cv_qualifier.
> (build_stub_type): Update to receive cv_qualifier.
> (cp_build_qualified_type): Ditto.
> (cp_type_quals): Update to return cv_qualifier.
> (type_memfn_quals): Ditto.
> (cp_apply_type_quals_to_decl): Update to take cv_qualifier.
> * decl.cc (get_type_quals): Update to use and return
> cv_qualifier.
> (cp_finish_decomp): Replace usages of TYPE_QUALS with
> cp_type_quals.
> (grokdeclarator): Update to use cv_qualifier.
> (grokparms): Use TYPE_UNQUALIFIED instead of literal zero.
> * error.cc (dump_lambda_function): Update to use qualifier_set
> APIs.
> * mangle.cc (write_CV_qualifiers_for_type): Ditto.
> * method.cc (do_build_copy_constructor): Update to use
> cv_qualifier instead of int.
> (do_build_copy_assign): Ditto.
> (build_stub_type): Update to receive cv_qualifier.
> (get_copy_ctor): Update to use cv_qualifier instead of int.
> (get_copy_assign): Ditto.
> (walk_field_subobs): Update to receive cv_qualifier.
> (synthesized_method_base_walk): Ditto.
> (synthesized_method_walk): Ditto.
> (implicitly_declare_fn): Update to use cv_qualifier.
> * module.cc (trees_in::tree_node): Cast read-in qualifiers to
> cv_qualifier.
> * pt.cc (tsubst): Update to use cv_qualifier instead of int.
> (resolve_typename_type): Ditto.
> * reflect.cc (type_of): Ditto.
> (eval_remove_volatile): Ditto.
> (eval_add_const): Ditto.
> (eval_add_volatile): Ditto.
> (eval_add_cv): Ditto.
> (eval_make_signed): Ditto.
> * semantics.cc (finish_non_static_data_member): Ditto.
> (finish_decltype_type): Ditto.
> * tree.cc (c_build_qualified_type): Update to receive
> qualifier_set, in line with original.
> (cp_build_qualified_type): Update to receive qualifier_set.
> (cp_check_qualified_type): Update to receive cv_qualifier.
> (cv_unqualified): Update to use cv_qualifier.
> (build_cp_fntype_variant): Ditto.
> (maybe_dummy_object): Ditto.
> * typeck.cc (original_type): Ditto.
> (composite_pointer_type_r): Ditto.
> (merge_types): Ditto.
> (build_class_member_access_expr): Ditto.
> (cp_type_quals): Update to return cv_qualifier.
> (type_memfn_quals): Ditto.
> (cp_apply_type_quals_to_decl): Update to receive cv_qualifier.
> (casts_away_constness_r): Use cv_qualifier instead of int for
> representing CV-qualification.
>
> gcc/d/ChangeLog:
>
> * d-codegen.cc (build_vthis_function): Update to use new
> qualifier_set APIs.
> * types.cc (insert_type_modifiers): Use cv_qualifier instead
> of int.
>
> gcc/fortran/ChangeLog:
>
> * trans-openmp.cc (gfc_omp_finish_clause): Update to use new
> qualifier_set APIs.
> * trans-types.cc (gfc_nonrestricted_type): Ditto.
>
> gcc/jit/ChangeLog:
>
> * dummy-frontend.cc (tree_type_to_jit_type):Update to use new
> qualifier_set APIs.
>
> gcc/objc/ChangeLog:
>
> * objc-act.cc (objc_push_parm): Use TYPE_QUALS instead of
> inlining its definition.
>
> gcc/rust/ChangeLog:
>
> * backend/rust-tree.cc (rs_type_quals): Update to use and return
> cv_qualifier.
> (type_memfn_quals): Ditto.
> (rs_build_qualified_type_real): Update to receive and use
> cv_qualifiers.
> (cv_unqualified): Update to use cv_qualifier.
> (strip_top_quals): Replace literal zero with TYPE_UNQUALIFIED.
> * backend/rust-tree.h (rs_type_quals): Update to return
> cv_qualifier.
> (type_memfn_quals): Ditto.
> (rs_build_qualified_type_real): Update to receive
> cv_qualifier. Remove duplicate declaration.
> (rs_build_qualified_type): Remove duplicate definition.
>
> libcc1/ChangeLog:
>
> * libcc1plugin.cc (plugin_build_qualified_type): Update to use
> qualifier_set.
> * libcp1plugin.cc (plugin_build_method_type): Fix up
> initialization of cp_cv_quals.
> (plugin_build_qualified_type): Ditto.
> ---
> gcc/ada/gcc-interface/decl.cc | 15 +-
> gcc/ada/gcc-interface/gigi.h | 14 +-
> gcc/ada/gcc-interface/utils.cc | 3 +-
> gcc/attribs.cc | 9 +-
> gcc/attribs.h | 2 +-
> gcc/c-family/c-ada-spec.cc | 6 +-
> gcc/c-family/c-common.cc | 18 +-
> gcc/c-family/c-common.h | 8 +-
> gcc/c-family/c-format.cc | 2 +-
> gcc/c-family/c-pretty-print.cc | 9 +-
> gcc/c-family/c-pretty-print.h | 2 +-
> gcc/c/c-aux-info.cc | 2 +-
> gcc/c/c-decl.cc | 144 +++++++-------
> gcc/c/c-objc-common.cc | 4 +-
> gcc/c/c-parser.cc | 16 +-
> gcc/c/c-tree.h | 6 +-
> gcc/c/c-typeck.cc | 108 ++++++-----
> gcc/config/gcn/gcn-tree.cc | 11 +-
> gcc/config/i386/i386-builtins.cc | 2 +-
> gcc/config/i386/i386.cc | 2 +-
> gcc/config/rl78/rl78.cc | 2 +-
> gcc/config/rs6000/rs6000-c.cc | 6 +-
> gcc/config/rs6000/rs6000.cc | 2 +-
> gcc/config/s390/s390-c.cc | 6 +-
> gcc/coretypes.h | 2 +-
> gcc/cp/call.cc | 8 +-
> gcc/cp/class.cc | 2 +-
> gcc/cp/cp-objcp-common.h | 5 +-
> gcc/cp/cp-tree.h | 12 +-
> gcc/cp/decl.cc | 12 +-
> gcc/cp/error.cc | 2 +-
> gcc/cp/mangle.cc | 7 +-
> gcc/cp/method.cc | 32 +--
> gcc/cp/module.cc | 3 +-
> gcc/cp/pt.cc | 11 +-
> gcc/cp/reflect.cc | 12 +-
> gcc/cp/semantics.cc | 4 +-
> gcc/cp/tree.cc | 34 ++--
> gcc/cp/typeck.cc | 42 ++--
> gcc/d/d-codegen.cc | 2 +-
> gcc/d/types.cc | 2 +-
> gcc/dwarf2out.cc | 46 ++---
> gcc/fold-const.cc | 2 +-
> gcc/fortran/trans-openmp.cc | 2 +-
> gcc/fortran/trans-types.cc | 3 +-
> gcc/gimple-lower-bitint.cc | 21 +-
> gcc/gimplify.cc | 13 +-
> gcc/ipa-free-lang-data.cc | 5 +-
> gcc/jit/dummy-frontend.cc | 2 +-
> gcc/langhooks-def.h | 2 +-
> gcc/langhooks.cc | 2 +-
> gcc/langhooks.h | 5 +-
> gcc/objc/objc-act.cc | 6 +-
> gcc/omp-low.cc | 4 +-
> gcc/omp-oacc-neuter-broadcast.cc | 4 +-
> gcc/omp-offload.cc | 21 +-
> gcc/rust/backend/rust-tree.cc | 35 ++--
> gcc/rust/backend/rust-tree.h | 10 +-
> gcc/tree-core.h | 68 ++++++-
> gcc/tree-dump.cc | 7 +-
> gcc/tree-inline.cc | 4 +-
> gcc/tree-pretty-print.cc | 14 +-
> gcc/tree-profile.cc | 2 +-
> gcc/tree-sra.cc | 3 +-
> gcc/tree-ssa-address.cc | 3 +-
> gcc/tree-switch-conversion.cc | 3 +-
> gcc/tree-vect-stmts.cc | 3 +-
> gcc/tree.cc | 149 ++++++++++++--
> gcc/tree.h | 322 ++++++++++++++++++++++++++++---
> gcc/ubsan.cc | 4 +-
> gcc/vtable-verify.cc | 10 +-
> libcc1/libcc1plugin.cc | 2 +-
> libcc1/libcp1plugin.cc | 4 +-
> 73 files changed, 951 insertions(+), 421 deletions(-)
>
> diff --git a/gcc/ada/gcc-interface/decl.cc b/gcc/ada/gcc-interface/decl.cc
> index e569eb1bddf1..0f5ea69207bb 100644
> --- a/gcc/ada/gcc-interface/decl.cc
> +++ b/gcc/ada/gcc-interface/decl.cc
> @@ -3041,9 +3041,10 @@ gnat_to_gnu_entity (Entity_Id gnat_entity, tree gnu_expr, bool definition)
> process_attributes (&gnu_type, &attr_list, false, gnat_entity);
> if (Treat_As_Volatile (gnat_entity))
> {
> - const int quals
> + const auto quals
> = TYPE_QUAL_VOLATILE
> - | (Is_Full_Access (gnat_entity) ? TYPE_QUAL_ATOMIC : 0);
> + | (Is_Full_Access (gnat_entity)
> + ? TYPE_QUAL_ATOMIC : TYPE_UNQUALIFIED);
> gnu_type = change_qualified_type (gnu_type, quals);
> }
> /* Make it artificial only if the base type was artificial too.
> @@ -4992,9 +4993,10 @@ gnat_to_gnu_entity (Entity_Id gnat_entity, tree gnu_expr, bool definition)
> if (Treat_As_Volatile (gnat_entity)
> && !Is_Packed_Array_Impl_Type (gnat_entity))
> {
> - const int quals
> + const auto quals
> = TYPE_QUAL_VOLATILE
> - | (Is_Full_Access (gnat_entity) ? TYPE_QUAL_ATOMIC : 0);
> + | (Is_Full_Access (gnat_entity)
> + ? TYPE_QUAL_ATOMIC : TYPE_UNQUALIFIED);
> /* This is required by free_lang_data_in_type to disable the ODR. */
> if (TREE_CODE (gnu_type) == ENUMERAL_TYPE)
> TYPE_STUB_DECL (gnu_type)
> @@ -5666,9 +5668,10 @@ gnat_to_gnu_component_type (Entity_Id gnat_array, bool definition,
>
> if (Has_Volatile_Components (gnat_array))
> {
> - const int quals
> + const auto quals
> = TYPE_QUAL_VOLATILE
> - | (Has_Atomic_Components (gnat_array) ? TYPE_QUAL_ATOMIC : 0);
> + | (Has_Atomic_Components (gnat_array)
> + ? TYPE_QUAL_ATOMIC : TYPE_UNQUALIFIED);
> gnu_type = change_qualified_type (gnu_type, quals);
> }
>
> diff --git a/gcc/ada/gcc-interface/gigi.h b/gcc/ada/gcc-interface/gigi.h
> index f4d791195187..3fd85debd9be 100644
> --- a/gcc/ada/gcc-interface/gigi.h
> +++ b/gcc/ada/gcc-interface/gigi.h
> @@ -1248,11 +1248,23 @@ gnat_signed_type_for (tree type_node)
> return gnat_signed_or_unsigned_type_for (0, type_node);
> }
>
> +/* Like TYPE_QUALS, but doesn't return qualifiers that GNAT doesn't use. */
> +static inline cv_qualifier
> +ada_type_quals (const_tree type)
> +{
> + addr_space_t as;
> + cv_qualifier cv;
> + std::tie (cv, as) = TYPE_QUALS (type).split ();
> + /* Ada does not support address spaces (at the moment). */
> + gcc_assert (ADDR_SPACE_GENERIC_P (as));
> + return cv;
> +}
> +
> /* Like build_qualified_type, but TYPE_QUALS is added to the existing
> qualifiers on TYPE. */
>
> static inline tree
> -change_qualified_type (tree type, int type_quals)
> +change_qualified_type (tree type, cv_qualifier type_quals)
> {
> /* Qualifiers must be put on the associated array type. */
> if (TREE_CODE (type) == UNCONSTRAINED_ARRAY_TYPE)
> diff --git a/gcc/ada/gcc-interface/utils.cc b/gcc/ada/gcc-interface/utils.cc
> index f74b6361b404..81d53baa390b 100644
> --- a/gcc/ada/gcc-interface/utils.cc
> +++ b/gcc/ada/gcc-interface/utils.cc
> @@ -4656,7 +4656,8 @@ update_pointer_to (tree old_type, tree new_type)
> initial set, and will often end up with OLD_TYPE == NEW_TYPE then. */
> new_type
> = build_qualified_type (new_type,
> - TYPE_QUALS (old_type) | TYPE_QUALS (new_type));
> + ada_type_quals (old_type)
> + | ada_type_quals (new_type));
>
> /* If old type and new type are identical, there is nothing to do. */
> if (old_type == new_type)
> diff --git a/gcc/attribs.cc b/gcc/attribs.cc
> index cd4be1bd6c5f..a7bc7895dc46 100644
> --- a/gcc/attribs.cc
> +++ b/gcc/attribs.cc
> @@ -726,7 +726,7 @@ decl_attributes (tree *node, tree attributes, int flags,
> tree *anode = node;
> const struct attribute_spec *spec
> = lookup_scoped_attribute_spec (ns, name);
> - int fn_ptr_quals = 0;
> + qualifier_set fn_ptr_quals {};
> tree fn_ptr_tmp = NULL_TREE;
> const bool cxx11_attr_p = cxx11_attribute_p (attr);
>
> @@ -1012,7 +1012,7 @@ decl_attributes (tree *node, tree attributes, int flags,
> /* Rebuild the function pointer type and put it in the
> appropriate place. */
> fn_ptr_tmp = build_pointer_type (fn_ptr_tmp);
> - if (fn_ptr_quals)
> + if (fn_ptr_quals != qualifier_set {})
> fn_ptr_tmp = build_qualified_type (fn_ptr_tmp, fn_ptr_quals);
> if (DECL_P (*node))
> TREE_TYPE (*node) = fn_ptr_tmp;
> @@ -1307,7 +1307,8 @@ build_decl_attribute_variant (tree ddecl, tree attribute)
> Record such modified types already made so we don't make duplicates. */
>
> tree
> -build_type_attribute_qual_variant (tree otype, tree attribute, int quals)
> +build_type_attribute_qual_variant (tree otype, tree attribute,
> + qualifier_set quals)
> {
> tree ttype = otype;
> if (! attribute_list_equal (TYPE_ATTRIBUTES (ttype), attribute))
> @@ -2712,7 +2713,7 @@ attr_access::array_as_string (tree type) const
> arat = build_tree_list (get_identifier ("array "), flag);
> }
>
> - const int quals = TYPE_QUALS (type);
> + const auto quals = TYPE_QUALS (type);
> type = build_array_type (eltype, index_type);
> type = build_type_attribute_qual_variant (type, arat, quals);
> }
> diff --git a/gcc/attribs.h b/gcc/attribs.h
> index 9f9abc5e4c32..b3881d9a91e2 100644
> --- a/gcc/attribs.h
> +++ b/gcc/attribs.h
> @@ -67,7 +67,7 @@ extern void handle_ignored_attributes_option (vec<char *> *);
>
> extern tree build_type_attribute_variant (tree, tree);
> extern tree build_decl_attribute_variant (tree, tree);
> -extern tree build_type_attribute_qual_variant (tree, tree, int);
> +extern tree build_type_attribute_qual_variant (tree, tree, qualifier_set);
>
> extern bool simple_cst_list_equal (const_tree, const_tree);
> extern bool attribute_value_equal (const_tree, const_tree);
> diff --git a/gcc/c-family/c-ada-spec.cc b/gcc/c-family/c-ada-spec.cc
> index b06f78742a0b..857c3c8711a3 100644
> --- a/gcc/c-family/c-ada-spec.cc
> +++ b/gcc/c-family/c-ada-spec.cc
> @@ -2313,7 +2313,7 @@ dump_ada_node (pretty_printer *pp, tree node, tree type, int spc,
> else
> {
> tree ref_type = TREE_TYPE (node);
> - const unsigned int quals = TYPE_QUALS (ref_type);
> + const auto quals = TYPE_QUALS (ref_type);
> bool is_access;
>
> if (VOID_TYPE_P (ref_type))
> @@ -2377,12 +2377,12 @@ dump_ada_node (pretty_printer *pp, tree node, tree type, int spc,
> is_access = true;
> pp_string (pp, "access ");
>
> - if (quals & TYPE_QUAL_CONST)
> + if (quals.has (TYPE_QUAL_CONST))
> pp_string (pp, "constant ");
> else if (!name_only)
> pp_string (pp, "all ");
> }
> - else if (quals & TYPE_QUAL_CONST)
> + else if (quals.has (TYPE_QUAL_CONST))
> {
> is_access = false;
> pp_string (pp, "in ");
> diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
> index a16288f4441c..87111a8ac4a4 100644
> --- a/gcc/c-family/c-common.cc
> +++ b/gcc/c-family/c-common.cc
> @@ -3875,7 +3875,7 @@ static void def_builtin_1 (enum built_in_function fncode,
> /* Apply the TYPE_QUALS to the new DECL. */
>
> void
> -c_apply_type_quals_to_decl (int type_quals, tree decl)
> +c_apply_type_quals_to_decl (cv_qualifier type_quals, tree decl)
> {
> tree type = TREE_TYPE (decl);
>
> @@ -7353,7 +7353,8 @@ int
> complete_array_type (tree *ptype, tree initial_value, bool do_default)
> {
> tree maxindex, type, main_type, elt, unqual_elt;
> - int failure = 0, quals;
> + int failure = 0;
> + qualifier_set quals;
> bool overflow_p = false;
>
> maxindex = size_zero_node;
> @@ -7447,10 +7448,11 @@ complete_array_type (tree *ptype, tree initial_value, bool do_default)
> type = *ptype;
> elt = TREE_TYPE (type);
> quals = TYPE_QUALS (strip_array_types (elt));
> - if (quals == 0)
> + if (quals == qualifier_set {})
> unqual_elt = elt;
> else
> - unqual_elt = c_build_qualified_type (elt, KEEP_QUAL_ADDR_SPACE (quals));
> + unqual_elt = c_build_qualified_type (elt,
> + quals.without (TYPE_QUAL_ALL));
>
> /* Using build_distinct_type_copy and modifying things afterward instead
> of using build_array_type to create a new type preserves all of the
> @@ -7486,7 +7488,7 @@ complete_array_type (tree *ptype, tree initial_value, bool do_default)
> TYPE_CANONICAL (TYPE_DOMAIN (main_type)),
> TYPE_TYPELESS_STORAGE (main_type));
>
> - if (quals == 0)
> + if (quals == qualifier_set {})
> type = main_type;
> else
> type = c_build_qualified_type (main_type, quals);
> @@ -8004,9 +8006,9 @@ get_atomic_generic_size (location_t loc, tree function,
>
> {
> auto_diagnostic_group d;
> - int quals = TYPE_QUALS (TREE_TYPE (type));
> + auto quals = TYPE_QUALS (TREE_TYPE (type));
> /* Must not write to an argument of a const-qualified type. */
> - if (outputs & (1 << x) && quals & TYPE_QUAL_CONST)
> + if (outputs & (1 << x) && quals.has (TYPE_QUAL_CONST))
> {
> if (c_dialect_cxx ())
> {
> @@ -8023,7 +8025,7 @@ get_atomic_generic_size (location_t loc, tree function,
> function);
> }
> /* Only the first argument is allowed to be volatile. */
> - if (x > 0 && quals & TYPE_QUAL_VOLATILE)
> + if (x > 0 && quals.has (TYPE_QUAL_VOLATILE))
> {
> if (c_dialect_cxx ())
> {
> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
> index 5711e1740498..fe933e5ca976 100644
> --- a/gcc/c-family/c-common.h
> +++ b/gcc/c-family/c-common.h
> @@ -897,7 +897,10 @@ extern bool decl_with_nonnull_addr_p (const_tree);
> extern tree c_fully_fold (tree, bool, bool *, bool = false);
> extern tree c_wrap_maybe_const (tree, bool);
> extern tree c_common_truthvalue_conversion (location_t, tree);
> -extern void c_apply_type_quals_to_decl (int, tree);
> +extern void c_apply_type_quals_to_decl (cv_qualifier, tree);
> +inline void
> +c_apply_type_quals_to_decl (qualifier_set qs, tree decl)
> +{ c_apply_type_quals_to_decl (qs.cv_quals (), decl); }
> extern tree c_sizeof_or_alignof_type (location_t, tree, bool, bool, int);
> extern tree c_alignof_expr (location_t, tree);
> extern tree c_countof_type (location_t, tree);
> @@ -976,7 +979,8 @@ extern tree pointer_int_sum (location_t, enum tree_code, tree, tree,
> bool = true);
>
> /* Add qualifiers to a type, in the fashion for C. */
> -extern tree c_build_qualified_type (tree, int, tree = NULL_TREE, size_t = 0);
> +extern tree c_build_qualified_type (tree, qualifier_set, tree = NULL_TREE,
> + size_t = 0);
>
> /* Build tree nodes and builtin functions common to both C and C++ language
> frontends. */
> diff --git a/gcc/c-family/c-format.cc b/gcc/c-family/c-format.cc
> index 1eb8f90747d5..baae9a3bc693 100644
> --- a/gcc/c-family/c-format.cc
> +++ b/gcc/c-family/c-format.cc
> @@ -4391,7 +4391,7 @@ deref_n_times (tree type, int n)
> type = TREE_TYPE (type);
> }
> /* Strip off any "const" etc. */
> - return build_qualified_type (type, 0);
> + return build_qualified_type (type, TYPE_UNQUALIFIED);
> }
>
> /* Lookup the format code for FORMAT_LEN within FLI,
> diff --git a/gcc/c-family/c-pretty-print.cc b/gcc/c-family/c-pretty-print.cc
> index b084163e6a35..429134f68d02 100644
> --- a/gcc/c-family/c-pretty-print.cc
> +++ b/gcc/c-family/c-pretty-print.cc
> @@ -168,7 +168,8 @@ pp_c_exclamation (c_pretty_printer *pp)
> /* Print out the external representation of QUALIFIERS. */
>
> void
> -pp_c_cv_qualifiers (c_pretty_printer *pp, int qualifiers, bool func_type)
> +pp_c_cv_qualifiers (c_pretty_printer *pp, cv_qualifier qualifiers,
> + bool func_type)
> {
> const char *p = pp_last_position_in_text (pp);
>
> @@ -242,7 +243,7 @@ pp_c_space_for_pointer_operator (c_pretty_printer *pp, tree t)
> void
> pp_c_type_qualifier_list (c_pretty_printer *pp, tree t)
> {
> - int qualifiers;
> + cv_qualifier qualifiers;
>
> if (!t || t == error_mark_node)
> return;
> @@ -252,7 +253,7 @@ pp_c_type_qualifier_list (c_pretty_printer *pp, tree t)
>
> if (TREE_CODE (t) != ARRAY_TYPE)
> {
> - qualifiers = TYPE_QUALS (t);
> + qualifiers = TYPE_QUALS_NO_ADDR_SPACE (t);
> pp_c_cv_qualifiers (pp, qualifiers,
> TREE_CODE (t) == FUNCTION_TYPE);
> }
> @@ -627,7 +628,7 @@ c_pretty_printer::direct_abstract_declarator (tree t)
> case ARRAY_TYPE:
> pp_c_left_bracket (this);
>
> - if (int quals = TYPE_QUALS (t))
> + if (auto quals = TYPE_QUALS_NO_ADDR_SPACE (t))
> {
> /* Print the array qualifiers such as in "T[const restrict 3]". */
> pp_c_cv_qualifiers (this, quals, false);
> diff --git a/gcc/c-family/c-pretty-print.h b/gcc/c-family/c-pretty-print.h
> index 90ae3d033eed..de1dfccb8cbc 100644
> --- a/gcc/c-family/c-pretty-print.h
> +++ b/gcc/c-family/c-pretty-print.h
> @@ -128,7 +128,7 @@ void pp_c_space_for_pointer_operator (c_pretty_printer *, tree);
> void pp_c_tree_decl_identifier (c_pretty_printer *, tree);
> void pp_c_function_definition (c_pretty_printer *, tree);
> void pp_c_attributes_display (c_pretty_printer *, tree);
> -void pp_c_cv_qualifiers (c_pretty_printer *pp, int qualifiers, bool func_type);
> +void pp_c_cv_qualifiers (c_pretty_printer *pp, cv_qualifier, bool func_type);
> void pp_c_type_qualifier_list (c_pretty_printer *, tree);
> void pp_c_parameter_type_list (c_pretty_printer *, tree);
> void pp_c_specifier_qualifier_list (c_pretty_printer *, tree);
> diff --git a/gcc/c/c-aux-info.cc b/gcc/c/c-aux-info.cc
> index fc2c97b51479..311ea5caef39 100644
> --- a/gcc/c/c-aux-info.cc
> +++ b/gcc/c/c-aux-info.cc
> @@ -405,7 +405,7 @@ gen_type (const char *ret_val, tree t, formals_style style)
> data_type = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (t)));
> /* Normally, `unsigned' is part of the deal. Not so if it comes
> with a type qualifier. */
> - if (TYPE_UNSIGNED (t) && TYPE_QUALS (t))
> + if (TYPE_UNSIGNED (t) && TYPE_QUALS (t) != qualifier_set {})
> data_type = concat ("unsigned ", data_type, NULL);
> break;
>
> diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> index 4dc1e94394bc..8e35fc53ed94 100644
> --- a/gcc/c/c-decl.cc
> +++ b/gcc/c/c-decl.cc
> @@ -2320,13 +2320,15 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl,
> }
> else
> {
> - int new_quals = TYPE_QUALS (newtype);
> - int old_quals = TYPE_QUALS (oldtype);
> + auto new_quals = TYPE_QUALS (newtype);
> + auto old_quals = TYPE_QUALS (oldtype);
>
> if (new_quals != old_quals)
> {
> - addr_space_t new_addr = DECODE_QUAL_ADDR_SPACE (new_quals);
> - addr_space_t old_addr = DECODE_QUAL_ADDR_SPACE (old_quals);
> + addr_space_t new_addr, old_addr;
> + cv_qualifier new_cv, old_cv;
> + std::tie (new_cv, new_addr) = new_quals.split ();
> + std::tie (old_cv, old_addr) = old_quals.split ();
> if (new_addr != old_addr)
> {
> if (ADDR_SPACE_GENERIC_P (new_addr))
> @@ -2345,8 +2347,7 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl,
> newdecl);
> }
>
> - if (CLEAR_QUAL_ADDR_SPACE (new_quals)
> - != CLEAR_QUAL_ADDR_SPACE (old_quals))
> + if (new_cv != old_cv)
> error ("conflicting type qualifiers for %q+D", newdecl);
> }
> else
> @@ -5440,14 +5441,13 @@ shadow_tag_warned (const struct c_declspecs *declspecs, int warned)
> bits. SPECS represents declaration specifiers that the grammar
> only permits to contain type qualifiers and attributes. */
>
> -int
> +qualifier_set
> quals_from_declspecs (const struct c_declspecs *specs)
> {
> - int quals = ((specs->const_p ? TYPE_QUAL_CONST : 0)
> - | (specs->volatile_p ? TYPE_QUAL_VOLATILE : 0)
> - | (specs->restrict_p ? TYPE_QUAL_RESTRICT : 0)
> - | (specs->atomic_p ? TYPE_QUAL_ATOMIC : 0)
> - | (ENCODE_QUAL_ADDR_SPACE (specs->address_space)));
> + auto cv_quals = cv_qualifier ((specs->const_p ? TYPE_QUAL_CONST : 0)
> + | (specs->volatile_p ? TYPE_QUAL_VOLATILE : 0)
> + | (specs->restrict_p ? TYPE_QUAL_RESTRICT : 0)
> + | (specs->atomic_p ? TYPE_QUAL_ATOMIC : 0));
> gcc_assert (!specs->type
> && !specs->decl_attr
> && specs->typespec_word == cts_none
> @@ -5465,7 +5465,7 @@ quals_from_declspecs (const struct c_declspecs *specs)
> && !specs->inline_p
> && !specs->noreturn_p
> && !specs->thread_p);
> - return quals;
> + return {cv_quals, specs->address_space};
> }
>
> /* Construct an array declarator. LOC is the location of the
> @@ -5497,7 +5497,7 @@ build_array_declarator (location_t loc,
> else
> {
> declarator->u.array.attrs = NULL_TREE;
> - declarator->u.array.quals = 0;
> + declarator->u.array.quals = qualifier_set {};
> }
> declarator->u.array.static_p = static_p;
> declarator->u.array.vla_unspec_p = vla_unspec_p;
> @@ -5984,7 +5984,7 @@ diagnose_uninitialized_cst_member (tree decl, tree type)
> continue;
> field_type = strip_array_types (TREE_TYPE (field));
>
> - if (TYPE_QUALS (field_type) & TYPE_QUAL_CONST)
> + if (TYPE_QUALS (field_type).has (TYPE_QUAL_CONST))
> {
> auto_diagnostic_group d;
> if (warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wc___compat,
> @@ -6880,14 +6880,14 @@ grokdeclarator (const struct c_declarator *declarator,
> int restrictp;
> int volatilep;
> int atomicp;
> - int type_quals = TYPE_UNQUALIFIED;
> + qualifier_set type_quals {};
> tree name = NULL_TREE;
> bool funcdef_flag = false;
> bool funcdef_syntax = false;
> bool size_varies = false;
> bool size_error = false;
> tree decl_attr = declspecs->decl_attr;
> - int array_ptr_quals = TYPE_UNQUALIFIED;
> + qualifier_set array_ptr_quals {};
> tree array_ptr_attrs = NULL_TREE;
> bool array_parm_static = false;
> bool array_parm_vla_unspec_p = false;
> @@ -7094,16 +7094,17 @@ grokdeclarator (const struct c_declarator *declarator,
>
> if ((TREE_CODE (type) == ARRAY_TYPE
> || first_non_attr_kind == cdk_array)
> - && TYPE_QUALS (element_type))
> + && TYPE_QUALS (element_type) != qualifier_set {})
> {
> orig_qual_type = type;
> type = c_build_qualified_type (type, TYPE_UNQUALIFIED);
> }
> - type_quals = ((constp ? TYPE_QUAL_CONST : 0)
> - | (restrictp ? TYPE_QUAL_RESTRICT : 0)
> - | (volatilep ? TYPE_QUAL_VOLATILE : 0)
> - | (atomicp ? TYPE_QUAL_ATOMIC : 0)
> - | ENCODE_QUAL_ADDR_SPACE (address_space));
> + type_quals =
> + {(constp ? TYPE_QUAL_CONST : TYPE_UNQUALIFIED)
> + | (restrictp ? TYPE_QUAL_RESTRICT : TYPE_UNQUALIFIED)
> + | (volatilep ? TYPE_QUAL_VOLATILE : TYPE_UNQUALIFIED)
> + | (atomicp ? TYPE_QUAL_ATOMIC : TYPE_UNQUALIFIED),
> + address_space};
> if (type_quals != TYPE_QUALS (element_type))
> orig_qual_type = NULL_TREE;
>
> @@ -7252,7 +7253,7 @@ grokdeclarator (const struct c_declarator *declarator,
> array or function or pointer, and DECLARATOR has had its
> outermost layer removed. */
>
> - if (array_ptr_quals != TYPE_UNQUALIFIED
> + if (array_ptr_quals != qualifier_set {}
> || array_ptr_attrs != NULL_TREE
> || array_parm_static)
> {
> @@ -7260,7 +7261,7 @@ grokdeclarator (const struct c_declarator *declarator,
> array type which is converted to pointer type)
> may have static or type qualifiers. */
> error_at (loc, "static or type qualifiers in non-parameter array declarator");
> - array_ptr_quals = TYPE_UNQUALIFIED;
> + array_ptr_quals = qualifier_set {};
> array_ptr_attrs = NULL_TREE;
> array_parm_static = false;
> }
> @@ -7581,10 +7582,10 @@ grokdeclarator (const struct c_declarator *declarator,
> modify the shared type, so we gcc_assert (itype)
> below. */
> {
> - addr_space_t as = DECODE_QUAL_ADDR_SPACE (type_quals);
> + addr_space_t as = type_quals.addr_space ();
> if (!ADDR_SPACE_GENERIC_P (as) && as != TYPE_ADDR_SPACE (type))
> type = c_build_qualified_type (type,
> - ENCODE_QUAL_ADDR_SPACE (as));
> + {TYPE_UNQUALIFIED, as});
> if (array_parm_vla_unspec_p)
> type = c_build_array_type_unspecified (type);
> else
> @@ -7626,13 +7627,13 @@ grokdeclarator (const struct c_declarator *declarator,
> }
>
> if (decl_context != PARM
> - && (array_ptr_quals != TYPE_UNQUALIFIED
> + && (array_ptr_quals != qualifier_set {}
> || array_ptr_attrs != NULL_TREE
> || array_parm_static))
> {
> error_at (loc, "static or type qualifiers in non-parameter "
> "array declarator");
> - array_ptr_quals = TYPE_UNQUALIFIED;
> + array_ptr_quals = qualifier_set {};
> array_ptr_attrs = NULL_TREE;
> array_parm_static = false;
> }
> @@ -7691,7 +7692,7 @@ grokdeclarator (const struct c_declarator *declarator,
>
> /* Type qualifiers before the return type of the function
> qualify the return type, not the function type. */
> - if (type_quals)
> + if (type_quals != qualifier_set {})
> {
> const enum c_declspec_word ignored_quals_list[] =
> {
> @@ -7716,12 +7717,14 @@ grokdeclarator (const struct c_declarator *declarator,
> actually removed from the return type when
> determining the function type. For C23, _Atomic is
> removed as well. */
> - int quals_used = type_quals;
> + auto quals_used = type_quals;
> if (flag_isoc23)
> - quals_used = 0;
> + quals_used = qualifier_set {};
> else if (flag_isoc11)
> - quals_used &= TYPE_QUAL_ATOMIC;
> - if (quals_used && VOID_TYPE_P (type) && really_funcdef)
> + quals_used = quals_used.without (~TYPE_QUAL_ATOMIC);
> + if (quals_used != qualifier_set {}
> + && VOID_TYPE_P (type)
> + && really_funcdef)
> pedwarn (specs_loc, 0,
> "function definition has qualified void "
> "return type");
> @@ -7734,13 +7737,13 @@ grokdeclarator (const struct c_declarator *declarator,
> DR#423 resolution is not entirely clear about
> this. */
> if (flag_isoc11
> - && (type_quals & TYPE_QUAL_RESTRICT)
> + && (type_quals.has (TYPE_QUAL_RESTRICT))
> && (!POINTER_TYPE_P (type)
> || !C_TYPE_OBJECT_OR_INCOMPLETE_P (TREE_TYPE (type))))
> error_at (loc, "invalid use of %<restrict%>");
> type = c_build_qualified_type (type, quals_used);
> }
> - type_quals = TYPE_UNQUALIFIED;
> + type_quals = qualifier_set {};
>
> type = c_build_function_type (type, arg_types,
> arg_info->no_named_args_stdarg_p);
> @@ -7762,18 +7765,18 @@ grokdeclarator (const struct c_declarator *declarator,
> {
> /* Merge any constancy or volatility into the target type
> for the pointer. */
> - if ((type_quals & TYPE_QUAL_ATOMIC)
> + if (type_quals.has (TYPE_QUAL_ATOMIC)
> && TREE_CODE (type) == FUNCTION_TYPE)
> {
> error_at (loc,
> "%<_Atomic%>-qualified function type");
> - type_quals &= ~TYPE_QUAL_ATOMIC;
> + type_quals = type_quals.without (TYPE_QUAL_ATOMIC);
> }
> else if (pedantic && TREE_CODE (type) == FUNCTION_TYPE
> - && type_quals)
> + && type_quals != qualifier_set {})
> pedwarn (loc, OPT_Wpedantic,
> "ISO C forbids qualified function types");
> - if (type_quals)
> + if (type_quals != qualifier_set {})
> type = c_build_qualified_type (type, type_quals, orig_qual_type,
> orig_qual_indirect);
> orig_qual_type = NULL_TREE;
> @@ -7817,7 +7820,7 @@ grokdeclarator (const struct c_declarator *declarator,
>
> /* Warn about address space used for things other than static memory or
> pointers. */
> - address_space = DECODE_QUAL_ADDR_SPACE (type_quals);
> + address_space = type_quals.addr_space ();
> if (!ADDR_SPACE_GENERIC_P (address_space))
> {
> if (decl_context == NORMAL)
> @@ -7875,13 +7878,13 @@ grokdeclarator (const struct c_declarator *declarator,
> /* C11 makes it implementation-defined (6.7.2.1#5) whether
> atomic types are permitted for bit-fields; we have no code to
> make bit-field accesses atomic, so disallow them. */
> - if (type_quals & TYPE_QUAL_ATOMIC)
> + if (type_quals.has (TYPE_QUAL_ATOMIC))
> {
> if (name)
> error_at (loc, "bit-field %qE has atomic type", name);
> else
> error_at (loc, "bit-field has atomic type");
> - type_quals &= ~TYPE_QUAL_ATOMIC;
> + type_quals = type_quals.without (TYPE_QUAL_ATOMIC);
> }
> }
>
> @@ -7930,18 +7933,18 @@ grokdeclarator (const struct c_declarator *declarator,
> if (storage_class == csc_typedef)
> {
> tree decl;
> - if ((type_quals & TYPE_QUAL_ATOMIC)
> + if ((type_quals.has (TYPE_QUAL_ATOMIC))
> && TREE_CODE (type) == FUNCTION_TYPE)
> {
> error_at (loc,
> "%<_Atomic%>-qualified function type");
> - type_quals &= ~TYPE_QUAL_ATOMIC;
> + type_quals = type_quals.without (TYPE_QUAL_ATOMIC);
> }
> else if (pedantic && TREE_CODE (type) == FUNCTION_TYPE
> - && type_quals)
> + && type_quals != qualifier_set {})
> pedwarn (loc, OPT_Wpedantic,
> "ISO C forbids qualified function types");
> - if (type_quals)
> + if (type_quals != qualifier_set {})
> type = c_build_qualified_type (type, type_quals, orig_qual_type,
> orig_qual_indirect);
> decl = build_decl (declarator->id_loc,
> @@ -7984,18 +7987,18 @@ grokdeclarator (const struct c_declarator *declarator,
> and fields. */
> gcc_assert (storage_class == csc_none && !threadp
> && !declspecs->inline_p && !declspecs->noreturn_p);
> - if ((type_quals & TYPE_QUAL_ATOMIC)
> + if (type_quals.has (TYPE_QUAL_ATOMIC)
> && TREE_CODE (type) == FUNCTION_TYPE)
> {
> error_at (loc,
> "%<_Atomic%>-qualified function type");
> - type_quals &= ~TYPE_QUAL_ATOMIC;
> + type_quals = type_quals.without (TYPE_QUAL_ATOMIC);
> }
> else if (pedantic && TREE_CODE (type) == FUNCTION_TYPE
> - && type_quals)
> + && type_quals != qualifier_set {})
> pedwarn (loc, OPT_Wpedantic,
> "ISO C forbids const or volatile function types");
> - if (type_quals)
> + if (type_quals != qualifier_set {})
> type = c_build_qualified_type (type, type_quals, orig_qual_type,
> orig_qual_indirect);
> return type;
> @@ -8055,7 +8058,7 @@ grokdeclarator (const struct c_declarator *declarator,
> else
> orig_qual_indirect--;
> }
> - if (type_quals)
> + if (type_quals != qualifier_set {})
> type = c_build_qualified_type (type, type_quals, orig_qual_type,
> orig_qual_indirect);
>
> @@ -8070,7 +8073,7 @@ grokdeclarator (const struct c_declarator *declarator,
>
> type = c_build_pointer_type (type);
> type_quals = array_ptr_quals;
> - if (type_quals)
> + if (type_quals != qualifier_set {})
> type = c_build_qualified_type (type, type_quals);
>
> /* We don't yet implement attributes in this context. */
> @@ -8083,21 +8086,21 @@ grokdeclarator (const struct c_declarator *declarator,
> }
> else if (TREE_CODE (type) == FUNCTION_TYPE)
> {
> - if (type_quals & TYPE_QUAL_ATOMIC)
> + if (type_quals.has (TYPE_QUAL_ATOMIC))
> {
> error_at (loc,
> "%<_Atomic%>-qualified function type");
> - type_quals &= ~TYPE_QUAL_ATOMIC;
> + type_quals = type_quals.without (TYPE_QUAL_ATOMIC);
> }
> - else if (type_quals)
> + else if (type_quals != qualifier_set {})
> pedwarn (loc, OPT_Wpedantic,
> "ISO C forbids qualified function types");
> - if (type_quals)
> + if (type_quals != qualifier_set {})
> type = c_build_qualified_type (type, type_quals);
> type = c_build_pointer_type (type);
> - type_quals = TYPE_UNQUALIFIED;
> + type_quals = qualifier_set {};
> }
> - else if (type_quals)
> + else if (type_quals != qualifier_set {})
> type = c_build_qualified_type (type, type_quals);
>
> decl = build_decl (declarator->id_loc,
> @@ -8204,13 +8207,15 @@ grokdeclarator (const struct c_declarator *declarator,
> FUNCTION_DECL, declarator->u.id.id, type);
> decl = build_decl_attribute_variant (decl, decl_attr);
>
> - if (type_quals & TYPE_QUAL_ATOMIC)
> + if (type_quals.has (TYPE_QUAL_ATOMIC))
> {
> error_at (loc,
> "%<_Atomic%>-qualified function type");
> - type_quals &= ~TYPE_QUAL_ATOMIC;
> + type_quals = type_quals.without (TYPE_QUAL_ATOMIC);
> }
> - else if (pedantic && type_quals && !DECL_IN_SYSTEM_HEADER (decl))
> + else if (pedantic
> + && type_quals != qualifier_set {}
> + && !DECL_IN_SYSTEM_HEADER (decl))
> pedwarn (loc, OPT_Wpedantic,
> "ISO C forbids qualified function types");
>
> @@ -8307,8 +8312,8 @@ grokdeclarator (const struct c_declarator *declarator,
> if (c_type_variably_modified_p (type))
> error_at (loc, "%<constexpr%> object has variably modified "
> "type");
> - if (type_quals
> - & (TYPE_QUAL_VOLATILE | TYPE_QUAL_RESTRICT | TYPE_QUAL_ATOMIC))
> + if (type_quals.has
> + (TYPE_QUAL_VOLATILE | TYPE_QUAL_RESTRICT | TYPE_QUAL_ATOMIC))
> error_at (loc, "invalid qualifiers for %<constexpr%> object");
> else
> {
> @@ -8645,7 +8650,7 @@ get_parm_info (bool ellipsis, tree expr)
> && !DECL_NAME (b->decl) /* anonymous */
> && VOID_TYPE_P (TREE_TYPE (b->decl))) /* of void type */
> {
> - if (TYPE_QUALS (TREE_TYPE (b->decl)) != TYPE_UNQUALIFIED
> + if (TYPE_QUALS (TREE_TYPE (b->decl)) != qualifier_set {}
> || C_DECL_REGISTER (b->decl))
> error_at (b->locus, "%<void%> as only parameter may not be qualified");
>
> @@ -9453,12 +9458,13 @@ is_flexible_array_member_p (bool is_last_field,
> static void
> c_update_type_canonical (tree t)
> {
> - gcc_checking_assert (TYPE_MAIN_VARIANT (t) == t && !TYPE_QUALS (t));
> + gcc_checking_assert (TYPE_MAIN_VARIANT (t) == t
> + && qualifier_set {t} == qualifier_set {});
> for (tree x = t, l = NULL_TREE; x; l = x, x = TYPE_NEXT_VARIANT (x))
> {
> if (x != t && TYPE_STRUCTURAL_EQUALITY_P (x))
> {
> - if (!TYPE_QUALS (x))
> + if (TYPE_QUALS (x) == qualifier_set {})
> TYPE_CANONICAL (x) = TYPE_CANONICAL (t);
> else
> {
> @@ -9716,7 +9722,7 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
> /* Any field that is volatile, restrict-qualified or atomic
> means the type cannot be used for a constexpr object. */
> if (TYPE_QUALS (t1)
> - & (TYPE_QUAL_VOLATILE | TYPE_QUAL_RESTRICT | TYPE_QUAL_ATOMIC))
> + .has (TYPE_QUAL_VOLATILE | TYPE_QUAL_RESTRICT | TYPE_QUAL_ATOMIC))
> C_TYPE_FIELDS_NON_CONSTEXPR (t) = 1;
> else if (RECORD_OR_UNION_TYPE_P (t1) && C_TYPE_FIELDS_NON_CONSTEXPR (t1))
> C_TYPE_FIELDS_NON_CONSTEXPR (t) = 1;
> @@ -12051,7 +12057,7 @@ make_pointer_declarator (struct c_declspecs *type_quals_attrs,
> struct c_declarator *target)
> {
> tree attrs;
> - int quals = 0;
> + qualifier_set quals {};
> struct c_declarator *itarget = target;
> struct c_declarator *ret = XOBNEW (&parser_obstack, struct c_declarator);
> if (type_quals_attrs)
> diff --git a/gcc/c/c-objc-common.cc b/gcc/c/c-objc-common.cc
> index 15f981bfc525..ea21f1cc7c72 100644
> --- a/gcc/c/c-objc-common.cc
> +++ b/gcc/c/c-objc-common.cc
> @@ -385,7 +385,9 @@ c_tree_printer (pretty_printer *pp, text_info *text, const char *spec,
> return true;
>
> case 'v':
> - pp_c_cv_qualifiers (cpp, va_arg (*text->m_args_ptr, int), hash);
> + pp_c_cv_qualifiers (cpp,
> + cv_qualifier (va_arg (*text->m_args_ptr, int)),
> + hash);
> return true;
>
> default:
> diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
> index 4eba29997273..0aec48c36629 100644
> --- a/gcc/c/c-parser.cc
> +++ b/gcc/c/c-parser.cc
> @@ -3984,7 +3984,7 @@ c_parser_declspecs (c_parser *parser, struct c_declspecs *specs,
> error_at (loc, "%<_Atomic%>-qualified array type");
> else if (TREE_CODE (t.spec) == FUNCTION_TYPE)
> error_at (loc, "%<_Atomic%>-qualified function type");
> - else if (TYPE_QUALS (t.spec) != TYPE_UNQUALIFIED)
> + else if (TYPE_QUALS (t.spec) != qualifier_set {})
> error_at (loc, "%<_Atomic%> applied to a qualified type");
> else
> t.spec = c_build_qualified_type (t.spec, TYPE_QUAL_ATOMIC);
> @@ -4833,12 +4833,12 @@ c_parser_typeof_specifier (c_parser *parser)
> if (is_unqual)
> {
> bool is_array = TREE_CODE (ret.spec) == ARRAY_TYPE;
> - int quals = TYPE_QUALS (strip_array_types (ret.spec));
> - if ((is_array ? quals & ~TYPE_QUAL_ATOMIC : quals)
> + auto quals = TYPE_QUALS (strip_array_types (ret.spec));
> + if ((is_array ? quals.without (TYPE_QUAL_ATOMIC) : quals)
> != TYPE_UNQUALIFIED)
> {
> ret.spec = TYPE_MAIN_VARIANT (ret.spec);
> - if (quals & TYPE_QUAL_ATOMIC && is_array)
> + if (quals.has (TYPE_QUAL_ATOMIC) && is_array)
> ret.spec = c_build_qualified_type (ret.spec,
> TYPE_QUAL_ATOMIC);
> }
> @@ -4849,10 +4849,10 @@ c_parser_typeof_specifier (c_parser *parser)
> expressions such as &abort, but in GCC it is represented
> internally as a type qualifier. */
> if (TREE_CODE (ret.spec) == FUNCTION_TYPE
> - && TYPE_QUALS (ret.spec) != TYPE_UNQUALIFIED)
> + && TYPE_QUALS (ret.spec) != qualifier_set {})
> ret.spec = TYPE_MAIN_VARIANT (ret.spec);
> else if (FUNCTION_POINTER_TYPE_P (ret.spec)
> - && TYPE_QUALS (TREE_TYPE (ret.spec)) != TYPE_UNQUALIFIED)
> + && TYPE_QUALS (TREE_TYPE (ret.spec)) != qualifier_set {})
> ret.spec
> = c_build_pointer_type (TYPE_MAIN_VARIANT (TREE_TYPE (ret.spec)));
> }
> @@ -11424,7 +11424,7 @@ c_parser_generic_selection (c_parser *parser)
> such as &abort, but in GCC it is represented internally as a type
> qualifier. */
> if (FUNCTION_POINTER_TYPE_P (selector_type)
> - && TYPE_QUALS (TREE_TYPE (selector_type)) != TYPE_UNQUALIFIED)
> + && TYPE_QUALS (TREE_TYPE (selector_type)) != qualifier_set {})
> selector_type
> = c_build_pointer_type (TYPE_MAIN_VARIANT (TREE_TYPE (selector_type)));
> }
> @@ -13818,7 +13818,7 @@ c_parser_postfix_expression_after_paren_type (c_parser *parser,
> restrict qualified or have a member with such a qualifier.
> const qualification is implicitly added. */
> if (TYPE_QUALS (type_no_array)
> - & (TYPE_QUAL_VOLATILE | TYPE_QUAL_RESTRICT | TYPE_QUAL_ATOMIC))
> + .has (TYPE_QUAL_VOLATILE | TYPE_QUAL_RESTRICT | TYPE_QUAL_ATOMIC))
> error_at (type_loc, "invalid qualifiers for %<constexpr%> object");
> else if (RECORD_OR_UNION_TYPE_P (type_no_array)
> && C_TYPE_FIELDS_NON_CONSTEXPR (type_no_array))
> diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
> index 511b155d46dc..87a6c64b1680 100644
> --- a/gcc/c/c-tree.h
> +++ b/gcc/c/c-tree.h
> @@ -567,7 +567,7 @@ struct c_declarator {
> /* The array dimension, or NULL for [] and [*]. */
> tree dimen;
> /* The qualifiers inside []. */
> - int quals;
> + qualifier_set quals;
> /* The attributes (currently ignored) inside []. */
> tree attrs;
> /* Whether [static] was used. */
> @@ -576,7 +576,7 @@ struct c_declarator {
> bool vla_unspec_p : 1;
> } array;
> /* For pointers, the qualifiers on the pointer type. */
> - int pointer_quals;
> + qualifier_set pointer_quals;
> /* For attributes. */
> tree attrs;
> } u;
> @@ -685,7 +685,7 @@ extern void record_inline_static (location_t, tree, tree,
> enum c_inline_static_type);
> extern void c_init_decl_processing (void);
> extern void c_print_identifier (FILE *, tree, int);
> -extern int quals_from_declspecs (const struct c_declspecs *);
> +extern qualifier_set quals_from_declspecs (const struct c_declspecs *);
> extern struct c_declarator *build_array_declarator (location_t, tree,
> struct c_declspecs *,
> bool, bool);
> diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
> index 643035b50a08..9c3d0cfbeb45 100644
> --- a/gcc/c/c-typeck.cc
> +++ b/gcc/c/c-typeck.cc
> @@ -165,7 +165,7 @@ null_pointer_constant_p (const_tree expr)
> && (INTEGRAL_TYPE_P (type)
> || (TREE_CODE (type) == POINTER_TYPE
> && VOID_TYPE_P (TREE_TYPE (type))
> - && TYPE_QUALS (TREE_TYPE (type)) == TYPE_UNQUALIFIED)));
> + && TYPE_QUALS (TREE_TYPE (type)) == qualifier_set {})));
> }
>
> /* EXPR may appear in an unevaluated part of an integer constant
> @@ -362,9 +362,9 @@ qualify_type (tree type, tree like)
> }
>
> return c_build_qualified_type (type,
> - TYPE_QUALS_NO_ADDR_SPACE (type)
> - | TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (like)
> - | ENCODE_QUAL_ADDR_SPACE (as_common));
> + {TYPE_QUALS_NO_ADDR_SPACE (type)
> + | TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (like),
> + as_common});
> }
>
>
> @@ -489,11 +489,11 @@ c_build_function_type (tree type, tree args, bool no)
> tree
> c_build_array_type (tree type, tree domain)
> {
> - int type_quals = TYPE_QUALS (type);
> + auto type_quals = TYPE_QUALS (type);
>
> /* Identify typeless storage as introduced in C2Y
> and supported also in earlier language modes. */
> - bool typeless = (char_type_p (type) && !(type_quals & TYPE_QUAL_ATOMIC))
> + bool typeless = (char_type_p (type) && !(type_quals.has (TYPE_QUAL_ATOMIC)))
> || (AGGREGATE_TYPE_P (type) && TYPE_TYPELESS_STORAGE (type));
>
> tree ret = build_array_type (type, domain, typeless);
> @@ -518,8 +518,8 @@ c_build_array_type_unspecified (tree type)
> }
>
>
> -tree
> -c_build_type_attribute_qual_variant (tree type, tree attrs, int quals)
> +static tree
> +c_build_type_attribute_qual_variant (tree type, tree attrs, qualifier_set quals)
> {
> tree ret = build_type_attribute_qual_variant (type, attrs, quals);
> return c_set_type_bits (ret, type);
> @@ -807,7 +807,7 @@ composite_type_internal (tree t1, tree t2, tree cond,
> case POINTER_TYPE:
> /* For two pointers, do this recursively on the target type. */
> {
> - gcc_checking_assert (TYPE_QUALS (t1) == TYPE_QUALS (t2));
> + gcc_checking_assert (qualifier_set {t1} == qualifier_set {t2});
> tree target = composite_type_internal (TREE_TYPE (t1), TREE_TYPE (t2),
> cond, cache);
> tree n = c_build_pointer_type_for_mode (target, TYPE_MODE (t1), false);
> @@ -886,7 +886,7 @@ composite_type_internal (tree t1, tree t2, tree cond,
> up TYPE_MAIN_VARIANT correctly, we need to form the
> composite of the unqualified types and add the qualifiers
> back at the end. */
> - int quals = TYPE_QUALS (strip_array_types (elt));
> + auto quals = TYPE_QUALS (strip_array_types (elt));
> tree unqual_elt = c_build_qualified_type (elt, TYPE_UNQUALIFIED);
>
> t1 = c_build_array_type (unqual_elt, td);
> @@ -1108,9 +1108,9 @@ static tree
> common_pointer_type (tree t1, tree t2, tree cond)
> {
> tree attributes;
> - unsigned target_quals;
> + qualifier_set target_quals;
> addr_space_t as1, as2, as_common;
> - int quals1, quals2;
> + cv_qualifier quals1, quals2;
>
> /* Save time if the two types are the same. */
>
> @@ -1143,9 +1143,9 @@ common_pointer_type (tree t1, tree t2, tree cond)
> if used inconsistently. The middle-end uses these to mark const
> and noreturn functions. */
> if (TREE_CODE (pointed_to_1) == FUNCTION_TYPE)
> - target_quals = (quals1 & quals2);
> + target_quals = quals1 & quals2;
> else
> - target_quals = (quals1 | quals2);
> + target_quals = quals1 | quals2;
>
> /* If the two named address spaces are different, determine the common
> superset address space. This is guaranteed to exist due to the
> @@ -1155,7 +1155,7 @@ common_pointer_type (tree t1, tree t2, tree cond)
> if (!addr_space_superset (as1, as2, &as_common))
> gcc_unreachable ();
>
> - target_quals |= ENCODE_QUAL_ADDR_SPACE (as_common);
> + target_quals.set_as (as_common);
>
> t1 = c_build_pointer_type (c_build_qualified_type (target, target_quals));
> return c_build_type_attribute_variant (t1, attributes);
> @@ -1181,10 +1181,10 @@ c_common_type (tree t1, tree t2)
> if (t2 == error_mark_node)
> return t1;
>
> - if (TYPE_QUALS (t1) != TYPE_UNQUALIFIED)
> + if (TYPE_QUALS (t1) != qualifier_set {})
> t1 = TYPE_MAIN_VARIANT (t1);
>
> - if (TYPE_QUALS (t2) != TYPE_UNQUALIFIED)
> + if (TYPE_QUALS (t2) != qualifier_set {})
> t2 = TYPE_MAIN_VARIANT (t2);
>
> if (TYPE_ATTRIBUTES (t1) != NULL_TREE)
> @@ -2108,10 +2108,10 @@ function_types_compatible_p (const_tree f1, const_tree f2,
> pedwarn (input_location, 0, "function return types not compatible due to %<volatile%>");
> if (TYPE_VOLATILE (ret1))
> ret1 = build_qualified_type (TYPE_MAIN_VARIANT (ret1),
> - TYPE_QUALS (ret1) & ~TYPE_QUAL_VOLATILE);
> + TYPE_QUALS (ret1).without (TYPE_QUAL_VOLATILE));
> if (TYPE_VOLATILE (ret2))
> ret2 = build_qualified_type (TYPE_MAIN_VARIANT (ret2),
> - TYPE_QUALS (ret2) & ~TYPE_QUAL_VOLATILE);
> + TYPE_QUALS (ret2).without (TYPE_QUAL_VOLATILE));
>
> bool ignore_pargs = data->ignore_promoting_args;
> data->ignore_promoting_args = false;
> @@ -2638,7 +2638,7 @@ convert_lvalue_to_rvalue (location_t loc, struct c_expr exp,
> exp = default_function_array_conversion (loc, exp);
> if (!VOID_TYPE_P (TREE_TYPE (exp.value))
> || (flag_isoc2y
> - && TYPE_QUALS (TREE_TYPE (exp.value)) != TYPE_UNQUALIFIED))
> + && TYPE_QUALS (TREE_TYPE (exp.value)) != qualifier_set {}))
> exp.value = require_complete_type (loc, exp.value);
> if (for_init || !RECORD_OR_UNION_TYPE_P (TREE_TYPE (exp.value)))
> {
> @@ -3377,7 +3377,7 @@ build_component_ref (location_t loc, tree datum, tree component,
> do
> {
> tree subdatum = TREE_VALUE (field);
> - int quals;
> + qualifier_set quals;
> tree subtype;
> bool use_datum_quals;
>
> @@ -3393,7 +3393,17 @@ build_component_ref (location_t loc, tree datum, tree component,
>
> quals = TYPE_QUALS (strip_array_types (TREE_TYPE (subdatum)));
> if (use_datum_quals)
> - quals |= TYPE_QUALS (TREE_TYPE (datum));
> + {
> + /* SUBDATUM refers to a field, which lack their own address
> + space. */
> + gcc_assert (ADDR_SPACE_GENERIC_P (quals.addr_space ()));
> + addr_space_t datum_as;
> + cv_qualifier datum_cv;
> + std::tie (datum_cv, datum_as)
> + = TYPE_QUALS (TREE_TYPE (datum)).split();
> + quals |= datum_cv;
> + quals.set_as(datum_as);
> + }
> subtype = c_build_qualified_type (TREE_TYPE (subdatum), quals);
>
> ref = build3 (COMPONENT_REF, subtype, datum, subdatum,
> @@ -4388,7 +4398,7 @@ build_function_call_vec (location_t loc, vec<location_t> arg_loc,
> warning_at (loc, 0, "function called through a non-compatible type");
>
> if (VOID_TYPE_P (return_type)
> - && TYPE_QUALS (return_type) != TYPE_UNQUALIFIED)
> + && TYPE_QUALS (return_type) != qualifier_set {})
> pedwarn (loc, 0,
> "function with qualified void return type called");
> }
> @@ -4407,7 +4417,7 @@ build_function_call_vec (location_t loc, vec<location_t> arg_loc,
> nargs, argarray, &arg_loc,
> comptypes);
>
> - if (TYPE_QUALS (return_type) != TYPE_UNQUALIFIED
> + if (TYPE_QUALS (return_type) != qualifier_set {}
> && !VOID_TYPE_P (return_type))
> return_type = c_build_qualified_type (return_type, TYPE_UNQUALIFIED);
> if (name != NULL_TREE
> @@ -4444,7 +4454,7 @@ build_function_call_vec (location_t loc, vec<location_t> arg_loc,
>
> if (VOID_TYPE_P (TREE_TYPE (result)))
> {
> - if (TYPE_QUALS (TREE_TYPE (result)) != TYPE_UNQUALIFIED)
> + if (TYPE_QUALS (TREE_TYPE (result)) != qualifier_set {})
> pedwarn (loc, 0,
> "function with qualified void return type called");
> return result;
> @@ -6206,7 +6216,7 @@ build_unary_op (location_t location, enum tree_code code, tree xarg,
> else
> val = build2 (code, TREE_TYPE (arg), arg, inc);
> TREE_SIDE_EFFECTS (val) = 1;
> - if (TYPE_QUALS (TREE_TYPE (val)) != TYPE_UNQUALIFIED)
> + if (TYPE_QUALS (TREE_TYPE (val)) != qualifier_set {})
> TREE_TYPE (val) = c_build_qualified_type (TREE_TYPE (val),
> TYPE_UNQUALIFIED);
> ret = val;
> @@ -6220,7 +6230,7 @@ build_unary_op (location_t location, enum tree_code code, tree xarg,
> expressions of type void), or, in C99, the result of a [] or
> unary '*' operator. */
> if (VOID_TYPE_P (TREE_TYPE (arg))
> - && TYPE_QUALS (TREE_TYPE (arg)) == TYPE_UNQUALIFIED
> + && TYPE_QUALS (TREE_TYPE (arg)) == qualifier_set {}
> && (!INDIRECT_REF_P (arg) || !flag_isoc99))
> pedwarn (location, 0, "taking address of expression of type %<void%>");
>
> @@ -6266,8 +6276,7 @@ build_unary_op (location_t location, enum tree_code code, tree xarg,
> && (TREE_READONLY (arg) || TREE_THIS_VOLATILE (arg))
> && TREE_CODE (argtype) == FUNCTION_TYPE)
> {
> - int orig_quals = TYPE_QUALS (strip_array_types (argtype));
> - int quals = orig_quals;
> + auto quals = TYPE_QUALS (strip_array_types (argtype));
>
> if (TREE_READONLY (arg))
> quals |= TYPE_QUAL_CONST;
> @@ -6890,7 +6899,7 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp,
> }
> tree t2_stripped = strip_array_types (t2);
> if ((TREE_CODE (t2) == ARRAY_TYPE)
> - && (TYPE_QUALS (t2_stripped) & ~TYPE_QUALS (t1)))
> + && !TYPE_QUALS (t1).can_qualify (TYPE_QUALS (t2_stripped)))
> {
> if (!flag_isoc23)
> warning_at (colon_loc, OPT_Wdiscarded_array_qualifiers,
> @@ -6915,7 +6924,7 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp,
> result_type = objc_common_type (type1, type2);
> else
> {
> - int qual = ENCODE_QUAL_ADDR_SPACE (as_common);
> + qualifier_set qual {TYPE_UNQUALIFIED, as_common};
> enum diagnostics::kind kind = diagnostics::kind::permerror;
> if (!flag_isoc99)
> /* This downgrade to a warning ensures that -std=gnu89
> @@ -7310,7 +7319,7 @@ handle_warn_cast_qual (location_t loc, tree type, tree otype)
> {
> in_type = TREE_TYPE (in_type);
> in_otype = TREE_TYPE (in_otype);
> - if ((TYPE_QUALS (in_type) &~ TYPE_QUALS (in_otype)) != 0
> + if (!TYPE_QUALS (in_otype).can_qualify (TYPE_QUALS (in_type))
> && !is_const)
> {
> warning_at (loc, OPT_Wcast_qual,
> @@ -8700,14 +8709,14 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
> || (VOID_TYPE_P (ttr) && !TYPE_ATOMIC (ttr))
> || comp_target_types (location, memb_type, rhstype))
> {
> - int lquals = TYPE_QUALS (ttl) & ~TYPE_QUAL_ATOMIC;
> - int rquals = TYPE_QUALS (ttr) & ~TYPE_QUAL_ATOMIC;
> + auto lquals = TYPE_QUALS (ttl).without (TYPE_QUAL_ATOMIC);
> + auto rquals = TYPE_QUALS (ttr).without (TYPE_QUAL_ATOMIC);
> /* If this type won't generate any warnings, use it. */
> if (lquals == rquals
> || ((TREE_CODE (ttr) == FUNCTION_TYPE
> && TREE_CODE (ttl) == FUNCTION_TYPE)
> - ? ((lquals | rquals) == rquals)
> - : ((lquals | rquals) == lquals)))
> + ? (rquals.can_qualify (lquals))
> + : (lquals.can_qualify (rquals))))
> break;
>
> /* Keep looking for a better type, but remember this one. */
> @@ -8758,7 +8767,8 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
> "unqualified"),
> G_("return makes %q#v qualified function "
> "pointer from unqualified"),
> - TYPE_QUALS (ttl) & ~TYPE_QUALS (ttr));
> + TYPE_QUALS_NO_ADDR_SPACE (ttl)
> + & ~TYPE_QUALS_NO_ADDR_SPACE (ttr));
> }
> else if (TYPE_QUALS_NO_ADDR_SPACE (ttr)
> & ~TYPE_QUALS_NO_ADDR_SPACE (ttl))
> @@ -8772,7 +8782,8 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
> "from pointer target type"),
> G_("return discards %qv qualifier from "
> "pointer target type"),
> - TYPE_QUALS (ttr) & ~TYPE_QUALS (ttl));
> + TYPE_QUALS_NO_ADDR_SPACE (ttr)
> + & ~TYPE_QUALS_NO_ADDR_SPACE (ttl));
>
> memb = marginal_memb;
> }
> @@ -9035,7 +9046,8 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
> "from pointer target type"),
> G_("return discards %qv qualifier from "
> "pointer target type"),
> - TYPE_QUALS (ttr) & ~TYPE_QUALS (ttl));
> + TYPE_QUALS_NO_ADDR_SPACE (ttr)
> + & ~TYPE_QUALS_NO_ADDR_SPACE (ttl));
> }
> else if (pedantic
> && ((VOID_TYPE_P (ttl) && TREE_CODE (ttr) == FUNCTION_TYPE)
> @@ -9078,7 +9090,8 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
> "from pointer target type"),
> G_("return discards %qv qualifier from "
> "pointer target type"),
> - TYPE_QUALS (ttr) & ~TYPE_QUALS (ttl));
> + TYPE_QUALS_NO_ADDR_SPACE (ttr)
> + & ~TYPE_QUALS_NO_ADDR_SPACE (ttl));
> else if (warn_quals_ped)
> pedwarn_c11 (location, OPT_Wc11_c23_compat,
> "array with qualifier on the element is not qualified before C23");
> @@ -9146,7 +9159,8 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
> "function pointer from unqualified"),
> G_("return makes %q#v qualified function "
> "pointer from unqualified"),
> - TYPE_QUALS (ttl) & ~TYPE_QUALS (ttr));
> + TYPE_QUALS_NO_ADDR_SPACE (ttl)
> + & ~TYPE_QUALS_NO_ADDR_SPACE (ttr));
> }
> }
> /* Avoid warning about the volatile ObjC EH puts on decls. */
> @@ -14941,7 +14955,7 @@ build_binary_op (location_t location, enum tree_code code,
>
> if (result_type == NULL_TREE)
> {
> - int qual = ENCODE_QUAL_ADDR_SPACE (as_common);
> + qualifier_set qual {TYPE_UNQUALIFIED, as_common};
> result_type = c_build_pointer_type
> (c_build_qualified_type (void_type_node, qual));
> }
> @@ -15075,7 +15089,7 @@ build_binary_op (location_t location, enum tree_code code,
> }
> else
> {
> - int qual = ENCODE_QUAL_ADDR_SPACE (as_common);
> + qualifier_set qual {TYPE_UNQUALIFIED, as_common};
> result_type = c_build_pointer_type
> (c_build_qualified_type (void_type_node, qual));
> pedwarn (location, OPT_Wcompare_distinct_pointer_types,
> @@ -18656,8 +18670,8 @@ c_finish_transaction (location_t loc, tree block, int flags)
> type was derived). */
>
> tree
> -c_build_qualified_type (tree type, int type_quals, tree orig_qual_type,
> - size_t orig_qual_indirect)
> +c_build_qualified_type (tree type, qualifier_set type_quals,
> + tree orig_qual_type, size_t orig_qual_indirect)
> {
> if (type == error_mark_node)
> return type;
> @@ -18718,12 +18732,12 @@ c_build_qualified_type (tree type, int type_quals, tree orig_qual_type,
> /* A restrict-qualified pointer type must be a pointer to object or
> incomplete type. Note that the use of POINTER_TYPE_P also allows
> REFERENCE_TYPEs, which is appropriate for C++. */
> - if ((type_quals & TYPE_QUAL_RESTRICT)
> + if ((type_quals.has (TYPE_QUAL_RESTRICT))
> && (!POINTER_TYPE_P (type)
> || !C_TYPE_OBJECT_OR_INCOMPLETE_P (TREE_TYPE (type))))
> {
> error ("invalid use of %<restrict%>");
> - type_quals &= ~TYPE_QUAL_RESTRICT;
> + type_quals = type_quals.without (TYPE_QUAL_RESTRICT);
> }
>
> tree var_type = (orig_qual_type && orig_qual_indirect == 0
> diff --git a/gcc/config/gcn/gcn-tree.cc b/gcc/config/gcn/gcn-tree.cc
> index cfad5cbda46f..4512634c340f 100644
> --- a/gcc/config/gcn/gcn-tree.cc
> +++ b/gcc/config/gcn/gcn-tree.cc
> @@ -315,8 +315,7 @@ gcn_goacc_get_worker_red_decl (tree type, unsigned offset)
>
> tree var_type
> = build_qualified_type (type,
> - (TYPE_QUALS (type)
> - | ENCODE_QUAL_ADDR_SPACE (ADDR_SPACE_LDS)));
> + (TYPE_QUALS (type).with_as (ADDR_SPACE_LDS)));
>
> gcc_assert (offset
> < (machfun->reduction_limit - machfun->reduction_base));
> @@ -536,8 +535,8 @@ gcn_goacc_adjust_private_decl (location_t, tree var, int level)
>
> tree type = TREE_TYPE (var);
> tree lds_type = build_qualified_type (type,
> - TYPE_QUALS_NO_ADDR_SPACE (type)
> - | ENCODE_QUAL_ADDR_SPACE (ADDR_SPACE_LDS));
> + TYPE_QUALS (type)
> + .with_as (ADDR_SPACE_LDS));
> machine_function *machfun = cfun->machine;
>
> TREE_TYPE (var) = lds_type;
> @@ -565,8 +564,8 @@ gcn_goacc_create_worker_broadcast_record (tree record_type, bool sender,
> unsigned HOST_WIDE_INT offset)
> {
> tree type = build_qualified_type (record_type,
> - TYPE_QUALS_NO_ADDR_SPACE (record_type)
> - | ENCODE_QUAL_ADDR_SPACE (ADDR_SPACE_LDS));
> + TYPE_QUALS (record_type)
> + .with_as (ADDR_SPACE_LDS));
>
> if (!sender)
> {
> diff --git a/gcc/config/i386/i386-builtins.cc b/gcc/config/i386/i386-builtins.cc
> index 6837efd8da5e..c25f6d67d97c 100644
> --- a/gcc/config/i386/i386-builtins.cc
> +++ b/gcc/config/i386/i386-builtins.cc
> @@ -157,7 +157,7 @@ ix86_get_builtin_type (enum ix86_builtin_type tcode)
> }
> else
> {
> - int quals;
> + cv_qualifier quals;
>
> index = tcode - IX86_BT_LAST_VECT - 1;
> if (tcode <= IX86_BT_LAST_PTR)
> diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
> index e66958db7acb..7f79b4f4f7fe 100644
> --- a/gcc/config/i386/i386.cc
> +++ b/gcc/config/i386/i386.cc
> @@ -25503,7 +25503,7 @@ ix86_stack_protect_guard (void)
> if (TARGET_SSP_TLS_GUARD)
> {
> tree type_node = lang_hooks.types.type_for_mode (ptr_mode, 1);
> - int qual = ENCODE_QUAL_ADDR_SPACE (ix86_stack_protector_guard_reg);
> + qualifier_set qual {TYPE_UNQUALIFIED, ix86_stack_protector_guard_reg};
> tree type = build_qualified_type (type_node, qual);
> tree t;
>
> diff --git a/gcc/config/rl78/rl78.cc b/gcc/config/rl78/rl78.cc
> index 193a5fa080b2..3fc111f36595 100644
> --- a/gcc/config/rl78/rl78.cc
> +++ b/gcc/config/rl78/rl78.cc
> @@ -4732,7 +4732,7 @@ rl78_insert_attributes (tree decl, tree *attributes ATTRIBUTE_UNUSED)
> {
> tree type = TREE_TYPE (decl);
> tree attr = TYPE_ATTRIBUTES (type);
> - int q = TYPE_QUALS_NO_ADDR_SPACE (type) | ENCODE_QUAL_ADDR_SPACE (ADDR_SPACE_FAR);
> + auto q = TYPE_QUALS (type).with_as (ADDR_SPACE_FAR);
>
> TREE_TYPE (decl) = build_type_attribute_qual_variant (type, attr, q);
> }
> diff --git a/gcc/config/rs6000/rs6000-c.cc b/gcc/config/rs6000/rs6000-c.cc
> index 3cbdb6fb2ba1..2fc984ddc040 100644
> --- a/gcc/config/rs6000/rs6000-c.cc
> +++ b/gcc/config/rs6000/rs6000-c.cc
> @@ -1927,14 +1927,14 @@ altivec_resolve_overloaded_builtin (location_t loc, tree fndecl,
> matching further down. */
> if (POINTER_TYPE_P (decl_type)
> && POINTER_TYPE_P (type)
> - && TYPE_QUALS (TREE_TYPE (type)) != 0)
> + && TYPE_QUALS (TREE_TYPE (type)).nonempty_p ())
> {
> if (TYPE_READONLY (TREE_TYPE (type))
> && !TYPE_READONLY (TREE_TYPE (decl_type)))
> warning (0, "passing argument %d of %qE discards %qs "
> "qualifier from pointer target type", n + 1, fndecl,
> "const");
> - type = build_qualified_type (TREE_TYPE (type), 0);
> + type = build_qualified_type (TREE_TYPE (type), TYPE_UNQUALIFIED);
> type = build_pointer_type (type);
> arg = c_fold_convert (type, arg);
> }
> @@ -1945,7 +1945,7 @@ altivec_resolve_overloaded_builtin (location_t loc, tree fndecl,
> && POINTER_TYPE_P (type)
> && TYPE_READONLY (TREE_TYPE (type)))
> {
> - type = build_qualified_type (TREE_TYPE (type), 0);
> + type = build_qualified_type (TREE_TYPE (type), TYPE_UNQUALIFIED);
> type = build_pointer_type (type);
> arg = c_fold_convert (type, arg);
> }
> diff --git a/gcc/config/rs6000/rs6000.cc b/gcc/config/rs6000/rs6000.cc
> index d8669d9ffce4..b38840a28d38 100644
> --- a/gcc/config/rs6000/rs6000.cc
> +++ b/gcc/config/rs6000/rs6000.cc
> @@ -20592,7 +20592,7 @@ rs6000_handle_altivec_attribute (tree *node,
>
> /* Propagate qualifiers attached to the element type
> onto the vector type. */
> - if (result && result != type && TYPE_QUALS (type))
> + if (result && result != type && TYPE_QUALS (type).nonempty_p ())
> result = build_qualified_type (result, TYPE_QUALS (type));
>
> *no_add_attrs = true; /* No need to hang on to the attribute. */
> diff --git a/gcc/config/s390/s390-c.cc b/gcc/config/s390/s390-c.cc
> index db9a88ee8054..579dacde3b0a 100644
> --- a/gcc/config/s390/s390-c.cc
> +++ b/gcc/config/s390/s390-c.cc
> @@ -847,10 +847,10 @@ s390_fn_types_compatible (enum s390_builtin_ov_type_index typeindex,
> /* If the incoming pointer argument has more qualifiers than the
> argument type it can still be an imperfect match. */
> if (POINTER_TYPE_P (b_arg_type) && POINTER_TYPE_P (in_type)
> - && !(TYPE_QUALS (TREE_TYPE (in_type))
> - & ~TYPE_QUALS (TREE_TYPE (b_arg_type)))
> && (TYPE_QUALS (TREE_TYPE (b_arg_type))
> - & ~TYPE_QUALS (TREE_TYPE (in_type))))
> + .can_qualify (TYPE_QUALS (TREE_TYPE (in_type))))
> + && (TYPE_QUALS (TREE_TYPE (b_arg_type))
> + != TYPE_QUALS (TREE_TYPE (in_type))))
> {
> tree qual_in_type =
> build_qualified_type (TREE_TYPE (in_type),
> diff --git a/gcc/coretypes.h b/gcc/coretypes.h
> index 5cc602ed7e5d..d0d61d5f0451 100644
> --- a/gcc/coretypes.h
> +++ b/gcc/coretypes.h
> @@ -191,7 +191,7 @@ class bitmap_view;
> typedef unsigned char addr_space_t;
>
> /* The value of addr_space_t that represents the generic address space. */
> -#define ADDR_SPACE_GENERIC 0
> +#define ADDR_SPACE_GENERIC ((addr_space_t) 0)
> #define ADDR_SPACE_GENERIC_P(AS) ((AS) == ADDR_SPACE_GENERIC)
>
> /* The major intermediate representations of GCC. */
> diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
> index 143e85760b3b..17de392743f2 100644
> --- a/gcc/cp/call.cc
> +++ b/gcc/cp/call.cc
> @@ -1277,7 +1277,7 @@ strip_top_quals (tree t)
> {
> if (TREE_CODE (t) == ARRAY_TYPE)
> return t;
> - return cp_build_qualified_type (t, 0);
> + return cp_build_qualified_type (t, TYPE_UNQUALIFIED);
> }
>
> /* Returns the standard conversion path (see [conv]) from type FROM to type
> @@ -1428,10 +1428,10 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
> qualifiers, so any TYPE_QUALS must be for attributes const or
> noreturn. Strip them. */
> if (TREE_CODE (to_pointee) == FUNCTION_TYPE
> - && TYPE_QUALS (to_pointee))
> + && TYPE_QUALS (to_pointee).nonempty_p ())
> to_pointee = build_qualified_type (to_pointee, TYPE_UNQUALIFIED);
> if (TREE_CODE (from_pointee) == FUNCTION_TYPE
> - && TYPE_QUALS (from_pointee))
> + && TYPE_QUALS (from_pointee).nonempty_p ())
> from_pointee = build_qualified_type (from_pointee, TYPE_UNQUALIFIED);
> }
> else
> @@ -1450,7 +1450,7 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
> {
> tree nfrom = TREE_TYPE (from);
> /* Don't try to apply restrict to void. */
> - int quals = cp_type_quals (nfrom) & ~TYPE_QUAL_RESTRICT;
> + auto quals = cp_type_quals (nfrom) & ~TYPE_QUAL_RESTRICT;
> from_pointee = cp_build_qualified_type (void_type_node, quals);
> from = build_pointer_type (from_pointee);
> conv = build_conv (ck_ptr, from, conv);
> diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
> index 2fcaa6cd81bd..93fa2f6864d3 100644
> --- a/gcc/cp/class.cc
> +++ b/gcc/cp/class.cc
> @@ -593,7 +593,7 @@ build_simple_base_path (tree expr, tree binfo)
> /* We don't use build_class_member_access_expr here, as that
> has unnecessary checks, and more importantly results in
> recursive calls to dfs_walk_once. */
> - int type_quals = cp_type_quals (TREE_TYPE (expr));
> + auto type_quals = cp_type_quals (TREE_TYPE (expr));
>
> expr = build3 (COMPONENT_REF,
> cp_build_qualified_type (type, type_quals),
> diff --git a/gcc/cp/cp-objcp-common.h b/gcc/cp/cp-objcp-common.h
> index ed29e65e4f3e..e66b8f7329d9 100644
> --- a/gcc/cp/cp-objcp-common.h
> +++ b/gcc/cp/cp-objcp-common.h
> @@ -142,7 +142,10 @@ static const scoped_attribute_specs *const cp_objcp_attribute_table[] =
> #undef LANG_HOOKS_TREE_DUMP_DUMP_TREE_FN
> #define LANG_HOOKS_TREE_DUMP_DUMP_TREE_FN cp_dump_tree
> #undef LANG_HOOKS_TREE_DUMP_TYPE_QUALS_FN
> -#define LANG_HOOKS_TREE_DUMP_TYPE_QUALS_FN cp_type_quals
> +inline qualifier_set
> +cp_type_quals_as_set (const_tree type)
> +{ return { cp_type_quals (type) }; }
> +#define LANG_HOOKS_TREE_DUMP_TYPE_QUALS_FN cp_type_quals_as_set
>
> #undef LANG_HOOKS_MAKE_TYPE
> #define LANG_HOOKS_MAKE_TYPE cxx_make_type_hook
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index a1b95b6f569b..e9505fe3aabf 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -6890,7 +6890,7 @@ inline tree ovl_op_identifier (tree_code code) { return ovl_op_identifier (false
> /* A type-qualifier, or bitmask therefore, using the TYPE_QUAL
> constants. */
>
> -typedef int cp_cv_quals;
> +typedef cv_qualifier cp_cv_quals;
>
> /* Non-static member functions have an optional virt-specifier-seq.
> There is a VIRT_SPEC value for each virt-specifier.
> @@ -8006,7 +8006,7 @@ extern tree get_copy_ctor (tree, tsubst_flags_t);
> extern tree get_copy_assign (tree);
> extern tree get_default_ctor (tree);
> extern tree get_dtor (tree, tsubst_flags_t);
> -extern tree build_stub_type (tree, int, bool);
> +extern tree build_stub_type (tree, cv_qualifier, bool);
> extern tree build_stub_object (tree);
> extern bool is_stub_object (tree);
> extern tree build_invoke (tree, const_tree,
> @@ -8866,7 +8866,7 @@ extern tree make_ptrmem_cst (tree, tree);
> extern tree cp_build_type_attribute_variant (tree, tree);
> extern tree cp_build_reference_type (tree, bool);
> extern tree move (tree);
> -extern tree cp_build_qualified_type (tree, int,
> +extern tree cp_build_qualified_type (tree, cv_qualifier,
> tsubst_flags_t = tf_warning_or_error);
> extern tree cp_build_function_type (tree, tree);
> extern bool cv_qualified_p (const_tree);
> @@ -9029,14 +9029,14 @@ extern bool error_type_p (const_tree);
> extern bool ptr_reasonably_similar (const_tree, const_tree);
> extern tree build_ptrmemfunc (tree, tree, int, bool,
> tsubst_flags_t);
> -extern int cp_type_quals (const_tree);
> -extern int type_memfn_quals (const_tree);
> +extern cv_qualifier cp_type_quals (const_tree);
> +extern cv_qualifier type_memfn_quals (const_tree);
> extern cp_ref_qualifier type_memfn_rqual (const_tree);
> extern tree apply_memfn_quals (tree, cp_cv_quals,
> cp_ref_qualifier = REF_QUAL_NONE);
> extern bool cp_has_mutable_p (const_tree);
> extern bool at_least_as_qualified_p (const_tree, const_tree);
> -extern void cp_apply_type_quals_to_decl (int, tree);
> +extern void cp_apply_type_quals_to_decl (cv_qualifier, tree);
> extern tree build_ptrmemfunc1 (tree, tree, tree);
> extern void expand_ptrmemfunc_cst (tree, tree *, tree *);
> extern tree type_after_usual_arithmetic_conversions (tree, tree);
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index c2a75c669c7b..aed28b0c92c6 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -6266,10 +6266,10 @@ warn_misplaced_attr_for_class_type (location_t location,
> /* Returns the cv-qualifiers that apply to the type specified
> by the DECLSPECS. */
>
> -static int
> +static cv_qualifier
> get_type_quals (const cp_decl_specifier_seq *declspecs)
> {
> - int type_quals = TYPE_UNQUALIFIED;
> + auto type_quals = TYPE_UNQUALIFIED;
>
> if (decl_spec_seq_has_spec_p (declspecs, ds_const))
> type_quals |= TYPE_QUAL_CONST;
> @@ -10859,7 +10859,7 @@ cp_finish_decomp (tree decl, cp_decomp *decomp, bool test_p)
> eltscnt = 2;
> if (pack != -1 ? count - 1 > eltscnt : count != eltscnt)
> goto cnt_mismatch;
> - eltype = cp_build_qualified_type (TREE_TYPE (type), TYPE_QUALS (type));
> + eltype = cp_build_qualified_type (TREE_TYPE (type), cp_type_quals (type));
> for (unsigned int i = 0; i < count; i++)
> {
> if ((unsigned) pack == i)
> @@ -10905,7 +10905,7 @@ cp_finish_decomp (tree decl, cp_decomp *decomp, bool test_p)
> }
> if (pack != -1 ? count - 1 > eltscnt : count != eltscnt)
> goto cnt_mismatch;
> - eltype = cp_build_qualified_type (TREE_TYPE (type), TYPE_QUALS (type));
> + eltype = cp_build_qualified_type (TREE_TYPE (type), cp_type_quals (type));
> for (unsigned int i = 0; i < count; i++)
> {
> if ((unsigned) pack == i)
> @@ -14019,7 +14019,7 @@ grokdeclarator (const cp_declarator *declarator,
> a member function. */
> cp_ref_qualifier rqual = REF_QUAL_NONE;
> /* cv-qualifiers that apply to the type specified by the DECLSPECS. */
> - int type_quals = get_type_quals (declspecs);
> + auto type_quals = get_type_quals (declspecs);
> tree raises = NULL_TREE;
> int template_count = 0;
> tree returned_attrs = NULL_TREE;
> @@ -17442,7 +17442,7 @@ grokparms (tree parmlist, tree *parms)
>
> /* Top-level qualifiers on the parameters are
> ignored for function types. */
> - type = cp_build_qualified_type (type, 0);
> + type = cp_build_qualified_type (type, TYPE_UNQUALIFIED);
> if (TREE_CODE (type) == METHOD_TYPE)
> {
> error ("parameter %qD invalidly declared method type", decl);
> diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
> index c2fb6027c521..a322ba54855e 100644
> --- a/gcc/cp/error.cc
> +++ b/gcc/cp/error.cc
> @@ -1913,7 +1913,7 @@ dump_lambda_function (cxx_pretty_printer *pp,
> pp_c_ws_string (pp, "static");
> }
> else if (!(TYPE_QUALS (class_of_this_parm (TREE_TYPE (fn)))
> - & TYPE_QUAL_CONST))
> + .has (TYPE_QUAL_CONST)))
> {
> pp->set_padding (pp_before);
> pp_c_ws_string (pp, "mutable");
> diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
> index d368359dccd6..e88e61f85184 100644
> --- a/gcc/cp/mangle.cc
> +++ b/gcc/cp/mangle.cc
> @@ -2892,7 +2892,12 @@ write_CV_qualifiers_for_type (const tree type)
> /* Note that we do not use cp_type_quals below; given "const
> int[3]", the "const" is emitted with the "int", not with the
> array. */
> - cp_cv_quals quals = TYPE_QUALS (type);
> + cv_qualifier quals;
> + addr_space_t as;
> + std::tie (quals, as) = TYPE_QUALS (type).split ();
> +
> + /* No address space support yet. */
> + gcc_checking_assert (ADDR_SPACE_GENERIC_P (as));
>
> if (quals & TYPE_QUAL_RESTRICT)
> {
> diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
> index 4c432efb56ae..215edb86a20b 100644
> --- a/gcc/cp/method.cc
> +++ b/gcc/cp/method.cc
> @@ -736,7 +736,7 @@ do_build_copy_constructor (tree fndecl)
>
> if (!inh)
> {
> - int cvquals = cp_type_quals (TREE_TYPE (parm));
> + auto cvquals = cp_type_quals (TREE_TYPE (parm));
>
> for (tree fields = TYPE_FIELDS (current_class_type);
> fields; fields = DECL_CHAIN (fields))
> @@ -767,7 +767,7 @@ do_build_copy_constructor (tree fndecl)
> types.) */
> if (!TYPE_REF_P (expr_type))
> {
> - int quals = cvquals;
> + auto quals = cvquals;
>
> if (DECL_MUTABLE_P (field))
> quals &= ~TYPE_QUAL_CONST;
> @@ -820,7 +820,7 @@ do_build_copy_assign (tree fndecl)
> else
> {
> tree fields;
> - int cvquals = cp_type_quals (TREE_TYPE (parm));
> + auto cvquals = cp_type_quals (TREE_TYPE (parm));
> int i;
> tree binfo, base_binfo;
>
> @@ -856,7 +856,7 @@ do_build_copy_assign (tree fndecl)
> tree init = parm;
> tree field = fields;
> tree expr_type;
> - int quals;
> + cv_qualifier quals;
>
> if (TREE_CODE (field) != FIELD_DECL || DECL_ARTIFICIAL (field))
> continue;
> @@ -1900,7 +1900,7 @@ maybe_synthesize_method (tree fndecl)
> rvalue if RVALUE is true. */
>
> tree
> -build_stub_type (tree type, int quals, bool rvalue)
> +build_stub_type (tree type, cv_qualifier quals, bool rvalue)
> {
> tree argtype
> = cp_build_qualified_type (type, quals,
> @@ -2196,8 +2196,8 @@ get_default_ctor (tree type)
> tree
> get_copy_ctor (tree type, tsubst_flags_t complain)
> {
> - int quals = (TYPE_HAS_CONST_COPY_CTOR (type)
> - ? TYPE_QUAL_CONST : TYPE_UNQUALIFIED);
> + auto quals = (TYPE_HAS_CONST_COPY_CTOR (type)
> + ? TYPE_QUAL_CONST : TYPE_UNQUALIFIED);
> tree argtype = build_stub_type (type, quals, false);
> tree fn = locate_fn_flags (type, complete_ctor_identifier, argtype,
> LOOKUP_NORMAL, complain);
> @@ -2211,8 +2211,8 @@ get_copy_ctor (tree type, tsubst_flags_t complain)
> tree
> get_copy_assign (tree type)
> {
> - int quals = (TYPE_HAS_CONST_COPY_ASSIGN (type)
> - ? TYPE_QUAL_CONST : TYPE_UNQUALIFIED);
> + auto quals = (TYPE_HAS_CONST_COPY_ASSIGN (type)
> + ? TYPE_QUAL_CONST : TYPE_UNQUALIFIED);
> tree argtype = build_stub_type (type, quals, false);
> tree fn = locate_fn_flags (type, assign_op_identifier, argtype,
> LOOKUP_NORMAL, tf_warning_or_error);
> @@ -2679,7 +2679,7 @@ process_subob_fn (tree fn, special_function_kind sfk, tree *spec_p,
>
> static void
> walk_field_subobs (tree fields, special_function_kind sfk, tree fnname,
> - int quals, tree *spec_p, bool *trivial_p,
> + cv_qualifier quals, tree *spec_p, bool *trivial_p,
> bool *deleted_p, bool *constexpr_p,
> bool diag, int flags, tsubst_flags_t complain,
> bool dtor_from_ctor)
> @@ -2845,7 +2845,7 @@ walk_field_subobs (tree fields, special_function_kind sfk, tree fnname,
>
> if (SFK_COPY_P (sfk) || SFK_MOVE_P (sfk))
> {
> - int mem_quals = cp_type_quals (mem_type) | quals;
> + auto mem_quals = cp_type_quals (mem_type) | quals;
> if (DECL_MUTABLE_P (field))
> mem_quals &= ~TYPE_QUAL_CONST;
> argtype = build_stub_type (mem_type, mem_quals, SFK_MOVE_P (sfk));
> @@ -2926,9 +2926,9 @@ walk_field_subobs (tree fields, special_function_kind sfk, tree fnname,
>
> static tree
> synthesized_method_base_walk (tree binfo, tree base_binfo,
> - special_function_kind sfk, tree fnname, int quals,
> - tree *inheriting_ctor, tree inherited_parms,
> - int flags, bool diag,
> + special_function_kind sfk, tree fnname,
> + cv_qualifier quals, tree *inheriting_ctor,
> + tree inherited_parms, int flags, bool diag,
> tree *spec_p, bool *trivial_p,
> bool *deleted_p, bool *constexpr_p)
> {
> @@ -3118,7 +3118,7 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
> /* We're in get_defaulted_eh_spec; we don't actually want any walking
> diagnostics, we just want complain set. */
> diag = false;
> - int quals = const_p ? TYPE_QUAL_CONST : TYPE_UNQUALIFIED;
> + auto quals = const_p ? TYPE_QUAL_CONST : TYPE_UNQUALIFIED;
>
> for (binfo = TYPE_BINFO (ctype), i = 0;
> BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i)
> @@ -3519,7 +3519,7 @@ implicitly_declare_fn (special_function_kind kind, tree type,
> else
> return_type = void_type_node;
>
> - int this_quals = TYPE_UNQUALIFIED;
> + auto this_quals = TYPE_UNQUALIFIED;
> switch (kind)
> {
> case sfk_destructor:
> diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
> index f250dc3e9dfe..9bde899e5594 100644
> --- a/gcc/cp/module.cc
> +++ b/gcc/cp/module.cc
> @@ -10793,7 +10793,8 @@ trees_in::tree_node (bool is_use)
>
> int quals = i ();
> if (quals >= 0 && !get_overrun ())
> - res = cp_build_qualified_type (res, quals);
> + res = cp_build_qualified_type (res,
> + static_cast<cv_qualifier> (quals));
>
> int tag = i ();
> if (!tag)
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index f7aa10226801..3e7f22c0fd75 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -17236,7 +17236,7 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
> else
> {
> /* We don't have an instantiation yet, so drop the typedef. */
> - int quals = cp_type_quals (t);
> + auto quals = cp_type_quals (t);
> t = DECL_ORIGINAL_TYPE (decl);
> t = cp_build_qualified_type (t, quals,
> complain | tf_ignore_bad_quals);
> @@ -17418,11 +17418,9 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>
> if (code == TEMPLATE_TYPE_PARM)
> {
> - int quals;
> -
> gcc_assert (TYPE_P (arg));
>
> - quals = cp_type_quals (arg) | cp_type_quals (t);
> + auto quals = cp_type_quals (arg) | cp_type_quals (t);
>
> return cp_build_qualified_type
> (arg, quals, complain | tf_ignore_bad_quals);
> @@ -17515,7 +17513,7 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
> /* If we get here, we must have been looking at a parm for a
> more deeply nested template. Make a new version of this
> template parameter, but with a lower level. */
> - int quals;
> + cv_qualifier quals;
> switch (code)
> {
> case TEMPLATE_TYPE_PARM:
> @@ -31076,7 +31074,6 @@ resolve_typename_type (tree type, bool only_current_p)
> tree scope;
> tree name;
> tree decl;
> - int quals;
> tree pushed_scope;
> tree result;
>
> @@ -31198,7 +31195,7 @@ resolve_typename_type (tree type, bool only_current_p)
> }
>
> /* Qualify the resulting type. */
> - quals = cp_type_quals (type);
> + auto quals = cp_type_quals (type);
> if (quals)
> result = cp_build_qualified_type (result, cp_type_quals (result) | quals);
>
> diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
> index 96f5de8481d5..07bdcd19b55f 100644
> --- a/gcc/cp/reflect.cc
> +++ b/gcc/cp/reflect.cc
> @@ -2611,7 +2611,7 @@ type_of (tree r, reflect_kind kind)
> r = TREE_TYPE (TREE_VALUE (TREE_VALUE (r)));
> if (CLASS_TYPE_P (r))
> {
> - int quals = cp_type_quals (r);
> + auto quals = cp_type_quals (r);
> quals |= TYPE_QUAL_CONST;
> r = cp_build_qualified_type (r, quals);
> }
> @@ -5234,7 +5234,7 @@ static tree
> eval_remove_volatile (location_t loc, tree type)
> {
> type = strip_typedefs (type);
> - int quals = cp_type_quals (type);
> + auto quals = cp_type_quals (type);
> quals &= ~TYPE_QUAL_VOLATILE;
> type = cp_build_qualified_type (type, quals);
> return get_reflection_raw (loc, type);
> @@ -5264,7 +5264,7 @@ eval_add_const (location_t loc, tree type)
> type = strip_typedefs (type);
> if (!TYPE_REF_P (type) && !FUNC_OR_METHOD_TYPE_P (type))
> {
> - int quals = cp_type_quals (type);
> + auto quals = cp_type_quals (type);
> quals |= TYPE_QUAL_CONST;
> type = cp_build_qualified_type (type, quals);
> }
> @@ -5282,7 +5282,7 @@ eval_add_volatile (location_t loc, tree type)
> type = strip_typedefs (type);
> if (!TYPE_REF_P (type) && !FUNC_OR_METHOD_TYPE_P (type))
> {
> - int quals = cp_type_quals (type);
> + auto quals = cp_type_quals (type);
> quals |= TYPE_QUAL_VOLATILE;
> type = cp_build_qualified_type (type, quals);
> }
> @@ -5300,7 +5300,7 @@ eval_add_cv (location_t loc, tree type)
> type = strip_typedefs (type);
> if (!TYPE_REF_P (type) && !FUNC_OR_METHOD_TYPE_P (type))
> {
> - int quals = cp_type_quals (type);
> + auto quals = cp_type_quals (type);
> quals |= (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE);
> type = cp_build_qualified_type (type, quals);
> }
> @@ -5391,7 +5391,7 @@ eval_make_signed (location_t loc, const constexpr_ctx *ctx, tree type,
> ret = c_common_signed_or_unsigned_type (unsignedp, type);
> if (ret != type)
> {
> - int quals = cp_type_quals (type);
> + auto quals = cp_type_quals (type);
> quals &= (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE);
> ret = cp_build_qualified_type (ret, quals);
> }
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index 3a7e19c190a4..ad0bff79d705 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -2808,7 +2808,7 @@ finish_non_static_data_member (tree decl, tree object, tree qualifying_scope,
> else
> {
> /* Set the cv qualifiers. */
> - int quals = cp_type_quals (TREE_TYPE (object));
> + auto quals = cp_type_quals (TREE_TYPE (object));
>
> if (DECL_MUTABLE_P (decl))
> quals &= ~TYPE_QUAL_CONST;
> @@ -13472,7 +13472,7 @@ finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
>
> if (type && !TYPE_REF_P (type))
> {
> - int quals;
> + cv_qualifier quals;
> if (current_function_decl
> && LAMBDA_FUNCTION_P (current_function_decl)
> && DECL_XOBJ_MEMBER_FUNCTION_P (current_function_decl))
> diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
> index e543cf145195..befdec3ae0de 100644
> --- a/gcc/cp/tree.cc
> +++ b/gcc/cp/tree.cc
> @@ -1469,10 +1469,16 @@ move (tree expr)
> the C version of this function does not properly maintain canonical
> types (which are not used in C). */
> tree
> -c_build_qualified_type (tree type, int type_quals, tree /* orig_qual_type */,
> +c_build_qualified_type (tree type, qualifier_set type_quals,
> + tree /* orig_qual_type */,
> size_t /* orig_qual_indirect */)
> {
> - return cp_build_qualified_type (type, type_quals);
> + cv_qualifier cv_quals;
> + addr_space_t as;
> + std::tie (cv_quals, as) = type_quals.split ();
> + /* No address space support yet. */
> + gcc_assert (ADDR_SPACE_GENERIC_P (as));
> + return cp_build_qualified_type (type, cv_quals);
> }
>
>
> @@ -1497,11 +1503,11 @@ c_build_qualified_type (tree type, int type_quals, tree /* orig_qual_type */,
> in a similar manner for restricting non-pointer types. */
>
> tree
> -cp_build_qualified_type (tree type, int type_quals,
> +cp_build_qualified_type (tree type, cv_qualifier type_quals,
> tsubst_flags_t complain /* = tf_warning_or_error */)
> {
> tree result;
> - int bad_quals = TYPE_UNQUALIFIED;
> + auto bad_quals = TYPE_UNQUALIFIED;
>
> if (type == error_mark_node)
> return type;
> @@ -1640,12 +1646,10 @@ cp_build_function_type (tree value_type, tree arg_types)
> tree
> cv_unqualified (tree type)
> {
> - int quals;
> -
> if (type == error_mark_node)
> return type;
>
> - quals = cp_type_quals (type);
> + auto quals = cp_type_quals (type);
> quals &= ~(TYPE_QUAL_CONST|TYPE_QUAL_VOLATILE);
> return cp_build_qualified_type (type, quals);
> }
> @@ -2448,8 +2452,9 @@ build_qualified_name (tree type, tree scope, tree name, bool template_p)
> parameters. */
>
> static bool
> -cp_check_qualified_type (const_tree cand, const_tree base, int type_quals,
> - cp_ref_qualifier rqual, tree raises, bool late)
> +cp_check_qualified_type (const_tree cand, const_tree base,
> + cv_qualifier type_quals, cp_ref_qualifier rqual,
> + tree raises, bool late)
> {
> return (TYPE_QUALS (cand) == type_quals
> && check_base_type (cand, base)
> @@ -2937,7 +2942,14 @@ tree
> build_cp_fntype_variant (tree type, cp_ref_qualifier rqual,
> tree raises, bool late)
> {
> - cp_cv_quals type_quals = TYPE_QUALS (type);
> + cv_qualifier type_quals;
> + addr_space_t as;
> + std::tie (type_quals, as) = TYPE_QUALS (type).split ();
> + /* AS here is the address space of the method or function. For the latter,
> + it won't ever make sense. For the former, it may, one day, if we support
> + address space qualification on non-static member functions. But not
> + today. */
> + gcc_assert (ADDR_SPACE_GENERIC_P (as));
>
> if (cp_check_qualified_type (type, type, type_quals, rqual, raises, late))
> return type;
> @@ -4659,7 +4671,7 @@ maybe_dummy_object (tree type, tree* binfop)
> non-lambda) 'this' if available. */
> if (ctype)
> {
> - int quals = TYPE_UNQUALIFIED;
> + auto quals = TYPE_UNQUALIFIED;
> if (tree lambda = CLASSTYPE_LAMBDA_EXPR (ctype))
> {
> if (tree cap = lambda_expr_this_capture (lambda, false))
> diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
> index 683457ccaf8c..61a8111b2ad7 100644
> --- a/gcc/cp/typeck.cc
> +++ b/gcc/cp/typeck.cc
> @@ -241,7 +241,7 @@ commonparms (tree p1, tree p2)
> static tree
> original_type (tree t)
> {
> - int quals = cp_type_quals (t);
> + auto quals = cp_type_quals (t);
> while (t != error_mark_node
> && TYPE_NAME (t) != NULL_TREE)
> {
> @@ -719,9 +719,9 @@ 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);
> - const int quals = q1 | q2;
> + const auto q1 = cp_type_quals (pointee1);
> + const auto q2 = cp_type_quals (pointee2);
> + const auto quals = q1 | q2;
> result_type = cp_build_qualified_type (result_type,
> (quals | (*add_const
> ? TYPE_QUAL_CONST
> @@ -987,7 +987,7 @@ merge_types (tree t1, tree t2)
> /* For two pointers, do this recursively on the target type. */
> {
> tree target = merge_types (TREE_TYPE (t1), TREE_TYPE (t2));
> - int quals = cp_type_quals (t1);
> + auto quals = cp_type_quals (t1);
>
> if (code1 == POINTER_TYPE)
> {
> @@ -1005,9 +1005,8 @@ merge_types (tree t1, tree t2)
>
> case OFFSET_TYPE:
> {
> - int quals;
> tree pointee;
> - quals = cp_type_quals (t1);
> + auto quals = cp_type_quals (t1);
> pointee = merge_types (TYPE_PTRMEM_POINTED_TO_TYPE (t1),
> TYPE_PTRMEM_POINTED_TO_TYPE (t2));
> t1 = build_ptrmem_type (TYPE_PTRMEM_CLASS_TYPE (t1),
> @@ -3047,7 +3046,7 @@ build_class_member_access_expr (cp_expr object, tree member,
> {
> /* A non-static data member. */
> bool null_object_p;
> - int type_quals;
> + cv_qualifier type_quals;
> tree member_type;
>
> if (INDIRECT_REF_P (object))
> @@ -12123,10 +12122,11 @@ comp_ptr_ttypes_const (tree to, tree from, compare_bounds_t cb)
> /* Returns the type qualifiers for this type, including the qualifiers on the
> elements for an array type. */
>
> -int
> +cv_qualifier
> cp_type_quals (const_tree type)
> {
> - int quals;
> + cv_qualifier quals;
> + addr_space_t as;
> /* This CONST_CAST is okay because strip_array_types returns its
> argument unmodified and we assign it to a const_tree. */
> type = strip_array_types (const_cast<tree> (type));
> @@ -12134,7 +12134,10 @@ cp_type_quals (const_tree type)
> /* Quals on a FUNCTION_TYPE are memfn quals. */
> || TREE_CODE (type) == FUNCTION_TYPE)
> return TYPE_UNQUALIFIED;
> - quals = TYPE_QUALS (type);
> +
> + std::tie (quals, as) = TYPE_QUALS (type).split ();
> + /* No address space support yet. */
> + gcc_assert (ADDR_SPACE_GENERIC_P (as));
> /* METHOD and REFERENCE_TYPEs should never have quals. */
> gcc_assert ((TREE_CODE (type) != METHOD_TYPE
> && !TYPE_REF_P (type))
> @@ -12161,11 +12164,18 @@ type_memfn_rqual (const_tree type)
> /* Returns the function-cv-quals for TYPE, which must be a FUNCTION_TYPE or
> METHOD_TYPE. */
>
> -int
> +cv_qualifier
> type_memfn_quals (const_tree type)
> {
> if (TREE_CODE (type) == FUNCTION_TYPE)
> - return TYPE_QUALS (type);
> + {
> + cv_qualifier quals;
> + addr_space_t as;
> + std::tie (quals, as) = TYPE_QUALS (type).split ();
> + /* No address space support yet. */
> + gcc_assert (ADDR_SPACE_GENERIC_P (as));
> + return quals;
> + }
> else if (TREE_CODE (type) == METHOD_TYPE)
> return cp_type_quals (class_of_this_parm (type));
> else
> @@ -12224,7 +12234,7 @@ cp_has_mutable_p (const_tree type)
> initializer is non-constant. */
>
> void
> -cp_apply_type_quals_to_decl (int type_quals, tree decl)
> +cp_apply_type_quals_to_decl (cv_qualifier type_quals, tree decl)
> {
> tree type = TREE_TYPE (decl);
>
> @@ -12257,8 +12267,8 @@ cp_apply_type_quals_to_decl (int type_quals, tree decl)
> static void
> casts_away_constness_r (tree *t1, tree *t2, tsubst_flags_t complain)
> {
> - int quals1;
> - int quals2;
> + cv_qualifier quals1;
> + cv_qualifier quals2;
>
> /* [expr.const.cast]
>
> diff --git a/gcc/d/d-codegen.cc b/gcc/d/d-codegen.cc
> index 0f51fd70eb64..7422354bb499 100644
> --- a/gcc/d/d-codegen.cc
> +++ b/gcc/d/d-codegen.cc
> @@ -2487,7 +2487,7 @@ build_vthis_function (tree basetype, tree type)
> tree fntype = build_function_type (TREE_TYPE (type), argtypes);
>
> /* Copy volatile qualifiers from the original function type. */
> - if (TYPE_QUALS (type) & TYPE_QUAL_VOLATILE)
> + if (TYPE_QUALS (type).has (TYPE_QUAL_VOLATILE))
> fntype = build_qualified_type (fntype, TYPE_QUAL_VOLATILE);
>
> if (RECORD_OR_UNION_TYPE_P (basetype))
> diff --git a/gcc/d/types.cc b/gcc/d/types.cc
> index 4ad28993b89f..a55baebcee1b 100644
> --- a/gcc/d/types.cc
> +++ b/gcc/d/types.cc
> @@ -226,7 +226,7 @@ make_struct_type (const char *name, int nfields, ...)
> tree
> insert_type_modifiers (tree type, unsigned mod)
> {
> - int quals = 0;
> + cv_qualifier quals = TYPE_UNQUALIFIED;
>
> switch (mod)
> {
> diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
> index 0b974d63c805..e7d93fa17c63 100644
> --- a/gcc/dwarf2out.cc
> +++ b/gcc/dwarf2out.cc
> @@ -3866,8 +3866,8 @@ static void output_line_info (bool);
> static void output_file_names (void);
> static bool is_base_type (tree);
> static dw_die_ref subrange_type_die (tree, tree, tree, tree, dw_die_ref);
> -static int decl_quals (const_tree);
> -static dw_die_ref modified_type_die (tree, int, tree, bool, dw_die_ref);
> +static cv_qualifier decl_quals (const_tree);
> +static dw_die_ref modified_type_die (tree, cv_qualifier, tree, bool, dw_die_ref);
> static dw_die_ref generic_parameter_die (tree, tree, bool, dw_die_ref);
> static dw_die_ref template_parameter_pack_die (tree, tree, dw_die_ref);
> static unsigned int debugger_reg_number (const_rtx);
> @@ -3935,7 +3935,7 @@ static dw_die_ref scope_die_for (tree, dw_die_ref);
> static inline bool local_scope_p (dw_die_ref);
> static inline bool class_scope_p (dw_die_ref);
> static inline bool class_or_namespace_scope_p (dw_die_ref);
> -static void add_type_attribute (dw_die_ref, tree, int, bool, dw_die_ref);
> +static void add_type_attribute (dw_die_ref, tree, cv_qualifier, bool, dw_die_ref);
> static void add_calling_convention_attribute (dw_die_ref, tree);
> static const char *type_tag (const_tree);
> static tree member_declared_type (const_tree);
> @@ -13580,7 +13580,7 @@ subrange_type_die (tree type, tree low, tree high, tree bias,
> the decl node. This will normally be augmented with the
> cv_qualifiers of the underlying type in add_type_attribute. */
>
> -static int
> +static cv_qualifier
> decl_quals (const_tree decl)
> {
> return ((TREE_READONLY (decl)
> @@ -13597,11 +13597,13 @@ decl_quals (const_tree decl)
> of the given TYPE_QUALS, and return its qualifiers. Ignore all
> qualifiers outside QUAL_MASK. */
>
> -static int
> -get_nearest_type_subqualifiers (tree type, int type_quals, int qual_mask)
> +static cv_qualifier
> +get_nearest_type_subqualifiers (tree type, cv_qualifier type_quals,
> + cv_qualifier qual_mask)
> {
> tree t;
> - int best_rank = 0, best_qual = 0, max_rank;
> + int best_rank = 0, max_rank;
> + cv_qualifier best_qual = TYPE_UNQUALIFIED;
>
> type_quals &= qual_mask;
> max_rank = popcount_hwi (type_quals) - 1;
> @@ -13609,7 +13611,7 @@ get_nearest_type_subqualifiers (tree type, int type_quals, int qual_mask)
> for (t = TYPE_MAIN_VARIANT (type); t && best_rank < max_rank;
> t = TYPE_NEXT_VARIANT (t))
> {
> - int q = TYPE_QUALS (t) & qual_mask;
> + auto q = TYPE_QUALS (t).intersect (qual_mask);
>
> if ((q & type_quals) == q && q != type_quals
> && check_base_type (t, type))
> @@ -13627,7 +13629,7 @@ get_nearest_type_subqualifiers (tree type, int type_quals, int qual_mask)
> return best_qual;
> }
>
> -struct dwarf_qual_info_t { int q; enum dwarf_tag t; };
> +struct dwarf_qual_info_t { cv_qualifier q; enum dwarf_tag t; };
> static const dwarf_qual_info_t dwarf_qual_info[] =
> {
> { TYPE_QUAL_CONST, DW_TAG_const_type },
> @@ -13642,7 +13644,7 @@ static const unsigned int dwarf_qual_info_size = ARRAY_SIZE (dwarf_qual_info);
> qualifiers added compared to the returned DIE. */
>
> static dw_die_ref
> -qualified_die_p (dw_die_ref die, int *mask, unsigned int depth)
> +qualified_die_p (dw_die_ref die, cv_qualifier *mask, unsigned int depth)
> {
> unsigned int i;
> for (i = 0; i < dwarf_qual_info_size; i++)
> @@ -13898,7 +13900,7 @@ maybe_gen_btf_decl_tag_dies (tree t, dw_die_ref target)
> in the reverse storage order wrt the target order. */
>
> static dw_die_ref
> -modified_type_die (tree type, int cv_quals, tree type_attrs, bool reverse,
> +modified_type_die (tree type, cv_qualifier cv_quals, tree type_attrs, bool reverse,
> dw_die_ref context_die)
> {
> enum tree_code code = TREE_CODE (type);
> @@ -13911,8 +13913,8 @@ modified_type_die (tree type, int cv_quals, tree type_attrs, bool reverse,
> dw_die_ref mod_scope;
> struct array_descr_info info;
> /* Only these cv-qualifiers are currently handled. */
> - const int cv_qual_mask = (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE
> - | TYPE_QUAL_RESTRICT | TYPE_QUAL_ATOMIC);
> + const auto cv_qual_mask = (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE
> + | TYPE_QUAL_RESTRICT | TYPE_QUAL_ATOMIC);
> /* DW_AT_endianity is specified only for base types in the standard. */
> const bool reverse_type
> = need_endianity_attribute_p (reverse)
> @@ -14023,7 +14025,7 @@ modified_type_die (tree type, int cv_quals, tree type_attrs, bool reverse,
> }
> else
> {
> - int dquals = TYPE_QUALS_NO_ADDR_SPACE (dtype);
> + auto dquals = TYPE_QUALS_NO_ADDR_SPACE (dtype);
> dquals &= cv_qual_mask;
> if ((dquals & ~cv_quals) != TYPE_UNQUALIFIED
> || (cv_quals == dquals && DECL_ORIGINAL_TYPE (name) != type))
> @@ -14065,7 +14067,7 @@ modified_type_die (tree type, int cv_quals, tree type_attrs, bool reverse,
>
> if (cv_quals)
> {
> - int sub_quals = 0, first_quals = 0;
> + cv_qualifier sub_quals = TYPE_UNQUALIFIED, first_quals = TYPE_UNQUALIFIED;
> unsigned i;
> dw_die_ref first = NULL, last = NULL;
>
> @@ -14086,7 +14088,7 @@ modified_type_die (tree type, int cv_quals, tree type_attrs, bool reverse,
> needed = true;
> else if (needed && (dwarf_qual_info[i].q & cv_quals))
> {
> - sub_quals = 0;
> + sub_quals = TYPE_UNQUALIFIED;
> break;
> }
> }
> @@ -14111,7 +14113,7 @@ modified_type_die (tree type, int cv_quals, tree type_attrs, bool reverse,
> count < (1U << dwarf_qual_info_size);
> count++, last = last->die_sib)
> {
> - int quals = 0;
> + cv_qualifier quals = TYPE_UNQUALIFIED;
> if (last == mod_scope->die_child)
> break;
> if (qualified_die_p (last->die_sib, &quals, dwarf_qual_info_size)
> @@ -14128,7 +14130,7 @@ modified_type_die (tree type, int cv_quals, tree type_attrs, bool reverse,
> {
> for (d = first->die_sib; ; d = d->die_sib)
> {
> - int quals = 0;
> + cv_qualifier quals = TYPE_UNQUALIFIED;
> qualified_die_p (d, &quals, dwarf_qual_info_size);
> if (quals == (first_quals | dwarf_qual_info[i].q))
> break;
> @@ -22746,7 +22748,7 @@ class_or_namespace_scope_p (dw_die_ref context_die)
> adds a DW_AT_type attribute below the given die. */
>
> static void
> -add_type_attribute (dw_die_ref object_die, tree type, int cv_quals,
> +add_type_attribute (dw_die_ref object_die, tree type, cv_qualifier cv_quals,
> bool reverse, dw_die_ref context_die)
> {
> enum tree_code code = TREE_CODE (type);
> @@ -22771,7 +22773,7 @@ add_type_attribute (dw_die_ref object_die, tree type, int cv_quals,
> return;
>
> type_die = modified_type_die (type,
> - cv_quals | TYPE_QUALS (type),
> + cv_quals | TYPE_QUALS_NO_ADDR_SPACE (type),
> TYPE_ATTRIBUTES (type),
> reverse,
> context_die);
> @@ -24869,7 +24871,7 @@ override_type_for_decl_p (tree decl, dw_die_ref old_die,
> dw_die_ref context_die)
> {
> tree type = TREE_TYPE (decl);
> - int cv_quals;
> + cv_qualifier cv_quals;
>
> if (decl_by_reference_p (decl))
> {
> @@ -24881,7 +24883,7 @@ override_type_for_decl_p (tree decl, dw_die_ref old_die,
>
> dw_die_ref type_die
> = modified_type_die (type,
> - cv_quals | TYPE_QUALS (type),
> + cv_quals | TYPE_QUALS_NO_ADDR_SPACE (type),
> TYPE_ATTRIBUTES (type),
> false,
> context_die);
> diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
> index 11d1129f1253..708654f0a980 100644
> --- a/gcc/fold-const.cc
> +++ b/gcc/fold-const.cc
> @@ -9209,7 +9209,7 @@ fold_unary_loc (location_t loc, enum tree_code code, tree type, tree op0)
> && known_eq (bitpos, 0)
> && (TYPE_MAIN_VARIANT (TREE_TYPE (type))
> == TYPE_MAIN_VARIANT (TREE_TYPE (base)))
> - && TYPE_QUALS (type) == TYPE_UNQUALIFIED)
> + && TYPE_QUALS (type) == qualifier_set {})
> return fold_convert_loc (loc, type,
> build_fold_addr_expr_loc (loc, base));
> }
> diff --git a/gcc/fortran/trans-openmp.cc b/gcc/fortran/trans-openmp.cc
> index eb0012714970..bba1a4955afc 100644
> --- a/gcc/fortran/trans-openmp.cc
> +++ b/gcc/fortran/trans-openmp.cc
> @@ -1731,7 +1731,7 @@ gfc_omp_finish_clause (tree c, gimple_seq *pre_p, bool openacc)
> bool always_modifier = false;
>
> if (!openacc
> - && !(TYPE_QUALS (TREE_TYPE (ptr)) & TYPE_QUAL_RESTRICT))
> + && !TYPE_QUALS (TREE_TYPE (ptr)).has (TYPE_QUAL_RESTRICT))
> always_modifier = true;
>
> if (present)
> diff --git a/gcc/fortran/trans-types.cc b/gcc/fortran/trans-types.cc
> index bb33cddbfa07..fbf0a06e4e89 100644
> --- a/gcc/fortran/trans-types.cc
> +++ b/gcc/fortran/trans-types.cc
> @@ -2385,7 +2385,8 @@ gfc_nonrestricted_type (tree t)
> else
> ret = build_reference_type (totype);
> ret = build_qualified_type (ret,
> - TYPE_QUALS (t) & ~TYPE_QUAL_RESTRICT);
> + TYPE_QUALS (t)
> + .without (TYPE_QUAL_RESTRICT));
> }
> break;
>
> diff --git a/gcc/gimple-lower-bitint.cc b/gcc/gimple-lower-bitint.cc
> index 19e39f4d7efb..7eb0a0e5acc6 100644
> --- a/gcc/gimple-lower-bitint.cc
> +++ b/gcc/gimple-lower-bitint.cc
> @@ -640,8 +640,8 @@ bitint_large_huge::limb_access (tree type, tree var, tree idx, bool write_p,
> if (DECL_P (var) && tree_fits_uhwi_p (idx))
> {
> if (as != TYPE_ADDR_SPACE (ltype))
> - ltype = build_qualified_type (ltype, TYPE_QUALS (ltype)
> - | ENCODE_QUAL_ADDR_SPACE (as));
> + ltype = build_qualified_type (ltype,
> + TYPE_QUALS (ltype).with_as (as));
> tree ptype = build_pointer_type (strip_array_types (TREE_TYPE (var)));
> unsigned HOST_WIDE_INT off = tree_to_uhwi (idx) * m_limb_size;
> if (bitint_big_endian)
> @@ -655,8 +655,8 @@ bitint_large_huge::limb_access (tree type, tree var, tree idx, bool write_p,
> else if (TREE_CODE (var) == MEM_REF && tree_fits_uhwi_p (idx))
> {
> if (as != TYPE_ADDR_SPACE (ltype))
> - ltype = build_qualified_type (ltype, TYPE_QUALS (ltype)
> - | ENCODE_QUAL_ADDR_SPACE (as));
> + ltype = build_qualified_type (ltype,
> + TYPE_QUALS (ltype).with_as (as));
> unsigned HOST_WIDE_INT off = tree_to_uhwi (idx) * m_limb_size;
> if (bitint_big_endian)
> off += m_limb_size - tree_to_uhwi (TYPE_SIZE_UNIT (ltype));
> @@ -673,8 +673,8 @@ bitint_large_huge::limb_access (tree type, tree var, tree idx, bool write_p,
> {
> ltype = m_limb_type;
> if (as != TYPE_ADDR_SPACE (ltype))
> - ltype = build_qualified_type (ltype, TYPE_QUALS (ltype)
> - | ENCODE_QUAL_ADDR_SPACE (as));
> + ltype = build_qualified_type (ltype,
> + TYPE_QUALS (ltype).with_as (as));
> var = unshare_expr (var);
> if (TREE_CODE (TREE_TYPE (var)) != ARRAY_TYPE
> || !useless_type_conversion_p (m_limb_type,
> @@ -713,8 +713,8 @@ bitint_large_huge::build_bit_field_ref (tree ftype, tree obj,
> tree ltype = m_limb_type;
> addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (obj));
> if (as != TYPE_ADDR_SPACE (ltype))
> - ltype = build_qualified_type (ltype, TYPE_QUALS (ltype)
> - | ENCODE_QUAL_ADDR_SPACE (as));
> + ltype = build_qualified_type (ltype,
> + TYPE_QUALS (ltype).with_as (as));
> tree atype = build_array_type_nelts (ltype, nelts);
> obj = build1 (VIEW_CONVERT_EXPR, atype, obj);
> }
> @@ -6504,8 +6504,7 @@ bitint_large_huge::lower_stmt (gimple *stmt)
> if (as != TYPE_ADDR_SPACE (ltype))
> ltype
> = build_qualified_type (ltype,
> - TYPE_QUALS (ltype)
> - | ENCODE_QUAL_ADDR_SPACE (as));
> + TYPE_QUALS (ltype).with_as (as));
> rhs1 = build1 (VIEW_CONVERT_EXPR, ltype, unshare_expr (mem));
> gimple_assign_set_rhs1 (stmt, rhs1);
> }
> @@ -6610,7 +6609,7 @@ bitint_large_huge::lower_stmt (gimple *stmt)
> ltype
> = build_qualified_type (ltype,
> TYPE_QUALS (TREE_TYPE (lhs))
> - | ENCODE_QUAL_ADDR_SPACE (as));
> + .with_as (as));
> lhs = build1 (VIEW_CONVERT_EXPR, ltype, lhs);
> gimple_assign_set_lhs (stmt, lhs);
> gimple_assign_set_rhs1 (stmt, rhs1);
> diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
> index 6c5d182ffab2..9c64b91cb154 100644
> --- a/gcc/gimplify.cc
> +++ b/gcc/gimplify.cc
> @@ -3153,12 +3153,19 @@ canonicalize_component_ref (tree *expr_p)
> #ifdef ENABLE_TYPES_CHECKING
> tree old_type = TREE_TYPE (expr);
> #endif
> - int type_quals;
> + auto type_quals = TYPE_QUALS (type);
> + gcc_checking_assert (/* Fields should lack address space
> + qualification. */
> + ADDR_SPACE_GENERIC_P (type_quals.addr_space ()));
>
> /* We need to preserve qualifiers and propagate them from
> operand 0. */
> - type_quals = TYPE_QUALS (type)
> - | TYPE_QUALS (TREE_TYPE (TREE_OPERAND (expr, 0)));
> + addr_space_t op_as;
> + cv_qualifier op_cv;
> + std::tie (op_cv, op_as)
> + = TYPE_QUALS (TREE_TYPE (TREE_OPERAND (expr, 0))).split();
> + type_quals |= op_cv;
> + type_quals.set_as (op_as);
> if (TYPE_QUALS (type) != type_quals)
> type = build_qualified_type (TYPE_MAIN_VARIANT (type), type_quals);
>
> diff --git a/gcc/ipa-free-lang-data.cc b/gcc/ipa-free-lang-data.cc
> index 3ff3f9638ff3..04df50d62074 100644
> --- a/gcc/ipa-free-lang-data.cc
> +++ b/gcc/ipa-free-lang-data.cc
> @@ -444,9 +444,8 @@ free_lang_data_in_type (tree type, class free_lang_data_d *fld)
> tree arg_type = TREE_VALUE (p);
> if (TYPE_READONLY (arg_type) || TYPE_VOLATILE (arg_type))
> {
> - int quals = TYPE_QUALS (arg_type)
> - & ~TYPE_QUAL_CONST
> - & ~TYPE_QUAL_VOLATILE;
> + auto quals = (TYPE_QUALS (arg_type)
> + .without (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE));
> TREE_VALUE (p) = build_qualified_type (arg_type, quals);
> if (!fld->pset.add (TREE_VALUE (p)))
> free_lang_data_in_type (TREE_VALUE (p), fld);
> diff --git a/gcc/jit/dummy-frontend.cc b/gcc/jit/dummy-frontend.cc
> index 4df9eada65bd..8a8dc4cbaa06 100644
> --- a/gcc/jit/dummy-frontend.cc
> +++ b/gcc/jit/dummy-frontend.cc
> @@ -1344,7 +1344,7 @@ recording::type* tree_type_to_jit_type (tree type)
> tree tp = TYPE_MAIN_VARIANT (type);
> for ( ; tp != NULL ; tp = TYPE_NEXT_VARIANT (tp))
> {
> - if (TYPE_QUALS (tp) == 0 && type != tp)
> + if (TYPE_QUALS (tp) == qualifier_set {} && type != tp)
> {
> recording::type* result = tree_type_to_jit_type (tp);
> if (result != NULL)
> diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h
> index 33a99266187c..5031463a054b 100644
> --- a/gcc/langhooks-def.h
> +++ b/gcc/langhooks-def.h
> @@ -176,7 +176,7 @@ extern const char *lhd_get_sarif_source_language (const char *);
>
> /* Tree dump hooks. */
> extern bool lhd_tree_dump_dump_tree (void *, tree);
> -extern int lhd_tree_dump_type_quals (const_tree);
> +extern qualifier_set lhd_tree_dump_type_quals (const_tree);
> extern tree lhd_make_node (enum tree_code);
>
> #define LANG_HOOKS_TREE_DUMP_DUMP_TREE_FN lhd_tree_dump_dump_tree
> diff --git a/gcc/langhooks.cc b/gcc/langhooks.cc
> index d55e2ca78560..8711386bb206 100644
> --- a/gcc/langhooks.cc
> +++ b/gcc/langhooks.cc
> @@ -262,7 +262,7 @@ lhd_tree_dump_dump_tree (void *di ATTRIBUTE_UNUSED, tree t ATTRIBUTE_UNUSED)
> /* lang_hooks.tree_dump.type_qual: Determine type qualifiers in a
> language-specific way. */
>
> -int
> +qualifier_set
> lhd_tree_dump_type_quals (const_tree t)
> {
> return TYPE_QUALS (t);
> diff --git a/gcc/langhooks.h b/gcc/langhooks.h
> index 546d7ddcdfb9..39eec160c2de 100644
> --- a/gcc/langhooks.h
> +++ b/gcc/langhooks.h
> @@ -28,6 +28,9 @@ struct gimplify_omp_ctx;
>
> struct array_descr_info;
>
> +/* Forward-declaration for qualifier_set in tree.h. */
> +struct qualifier_set;
> +
> /* A print hook for print_tree (). */
> typedef void (*lang_print_tree_hook) (FILE *, tree, int indent);
>
> @@ -53,7 +56,7 @@ struct lang_hooks_for_tree_dump
> bool (*dump_tree) (void *, tree);
>
> /* Determine type qualifiers in a language-specific way. */
> - int (*type_quals) (const_tree);
> + qualifier_set (*type_quals) (const_tree);
> };
>
> /* Hooks related to types. */
> diff --git a/gcc/objc/objc-act.cc b/gcc/objc/objc-act.cc
> index f41a8b42b6dc..f49ff9f245f2 100644
> --- a/gcc/objc/objc-act.cc
> +++ b/gcc/objc/objc-act.cc
> @@ -8461,11 +8461,7 @@ objc_push_parm (tree parm)
> = lang_hooks.types.type_promotes_to (TREE_TYPE (parm));
>
> /* Record constancy and volatility. */
> - c_apply_type_quals_to_decl
> - ((TYPE_READONLY (TREE_TYPE (parm)) ? TYPE_QUAL_CONST : 0)
> - | (TYPE_RESTRICT (TREE_TYPE (parm)) ? TYPE_QUAL_RESTRICT : 0)
> - | (TYPE_ATOMIC (TREE_TYPE (parm)) ? TYPE_QUAL_ATOMIC : 0)
> - | (TYPE_VOLATILE (TREE_TYPE (parm)) ? TYPE_QUAL_VOLATILE : 0), parm);
> + c_apply_type_quals_to_decl (TYPE_QUALS (TREE_TYPE (parm)), parm);
>
> objc_parmlist = chainon (objc_parmlist, parm);
> }
> diff --git a/gcc/omp-low.cc b/gcc/omp-low.cc
> index ad5b2225d279..96b36c0b03cc 100644
> --- a/gcc/omp-low.cc
> +++ b/gcc/omp-low.cc
> @@ -835,7 +835,9 @@ install_var_field (tree var, bool by_ref, int mask, omp_context *ctx,
> the pointed-to type will be ignored by points-to analysis. */
> if (POINTER_TYPE_P (type)
> && TYPE_RESTRICT (type))
> - type = build_qualified_type (type, TYPE_QUALS (type) & ~TYPE_QUAL_RESTRICT);
> + type = build_qualified_type (type,
> + TYPE_QUALS (type)
> + .without (TYPE_QUAL_RESTRICT));
>
> if (mask & 4)
> {
> diff --git a/gcc/omp-oacc-neuter-broadcast.cc b/gcc/omp-oacc-neuter-broadcast.cc
> index ef3f02bf0b5a..8ce248d0ea23 100644
> --- a/gcc/omp-oacc-neuter-broadcast.cc
> +++ b/gcc/omp-oacc-neuter-broadcast.cc
> @@ -573,7 +573,9 @@ install_var_field (tree var, tree record_type, field_map_t *fields)
>
> if (POINTER_TYPE_P (type)
> && TYPE_RESTRICT (type))
> - type = build_qualified_type (type, TYPE_QUALS (type) & ~TYPE_QUAL_RESTRICT);
> + type = build_qualified_type (type,
> + TYPE_QUALS (type)
> + .without (TYPE_QUAL_RESTRICT));
>
> tree field = build_decl (BUILTINS_LOCATION, FIELD_DECL, name, type);
>
> diff --git a/gcc/omp-offload.cc b/gcc/omp-offload.cc
> index 7cd2a572b7c0..19dde16b3c25 100644
> --- a/gcc/omp-offload.cc
> +++ b/gcc/omp-offload.cc
> @@ -1891,26 +1891,33 @@ oacc_rewrite_var_decl (tree *tp, int *walk_subtrees, void *data)
> if (!new_decl)
> return NULL;
>
> - int base_quals = TYPE_QUALS (TREE_TYPE (*new_decl));
> + auto base_quals = TYPE_QUALS (TREE_TYPE (*new_decl));
> + cv_qualifier base_cv;
> + addr_space_t base_as;
> + std::tie (base_cv, base_as) = base_quals.split ();
> tree field = TREE_OPERAND (*tp, 1);
>
> /* Adjust the type of the field. */
> - int field_quals = TYPE_QUALS (TREE_TYPE (field));
> - if (TREE_CODE (field) == FIELD_DECL && field_quals != base_quals)
> + auto field_quals = TYPE_QUALS (TREE_TYPE (field));
> + if (TREE_CODE (field) == FIELD_DECL
> + && field_quals != base_quals)
> {
> tree *field_type = &TREE_TYPE (field);
> while (TREE_CODE (*field_type) == ARRAY_TYPE)
> field_type = &TREE_TYPE (*field_type);
> - field_quals |= base_quals;
> + field_quals |= base_cv;
> + field_quals.set_as (base_as);
> *field_type = build_qualified_type (*field_type, field_quals);
> }
>
> /* Adjust the type of the component ref itself. */
> tree comp_type = TREE_TYPE (*tp);
> - int comp_quals = TYPE_QUALS (comp_type);
> - if (TREE_CODE (*tp) == COMPONENT_REF && comp_quals != base_quals)
> + auto comp_quals = TYPE_QUALS (comp_type);
> + if (TREE_CODE (*tp) == COMPONENT_REF
> + && comp_quals != base_quals)
> {
> - comp_quals |= base_quals;
> + comp_quals |= base_cv;
> + comp_quals.set_as (base_as);
> TREE_TYPE (*tp)
> = build_qualified_type (comp_type, comp_quals);
> }
> diff --git a/gcc/rust/backend/rust-tree.cc b/gcc/rust/backend/rust-tree.cc
> index b7a50376ecd8..bc09acf8ed88 100644
> --- a/gcc/rust/backend/rust-tree.cc
> +++ b/gcc/rust/backend/rust-tree.cc
> @@ -951,10 +951,9 @@ decl_maybe_constant_var_p (tree decl)
> /* Returns the type qualifiers for this type, including the qualifiers on the
> elements for an array type. */
>
> -int
> +cv_qualifier
> rs_type_quals (const_tree type)
> {
> - int quals;
> /* This CONST_CAST is okay because strip_array_types returns its
> argument unmodified and we assign it to a const_tree. */
> type = strip_array_types (const_cast<tree> (type));
> @@ -962,12 +961,17 @@ rs_type_quals (const_tree type)
> /* Quals on a FUNCTION_TYPE are memfn quals. */
> || TREE_CODE (type) == FUNCTION_TYPE)
> return TYPE_UNQUALIFIED;
> - quals = TYPE_QUALS (type);
> +
> + addr_space_t addr_space;
> + cv_qualifier quals;
> + std::tie (quals, addr_space) = TYPE_QUALS (type).split ();
> /* METHOD and REFERENCE_TYPEs should never have quals. */
> // gcc_assert (
> // (TREE_CODE (type) != METHOD_TYPE && !TYPE_REF_P (type))
> // || ((quals & (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE)) ==
> // TYPE_UNQUALIFIED));
> + /* At the moment, the Rust front-end does not do address spaces. */
> + gcc_assert (ADDR_SPACE_GENERIC_P (addr_space));
> return quals;
> }
>
> @@ -1292,15 +1296,24 @@ lookup_add (tree fns, tree lookup)
> /* Returns the function-cv-quals for TYPE, which must be a FUNCTION_TYPE or
> METHOD_TYPE. */
>
> -int
> +cv_qualifier
> type_memfn_quals (const_tree type)
> {
> + qualifier_set quals;
> if (TREE_CODE (type) == FUNCTION_TYPE)
> - return TYPE_QUALS (type);
> + quals = TYPE_QUALS (type);
> else if (TREE_CODE (type) == METHOD_TYPE)
> - return rs_type_quals (class_of_this_parm (type));
> + quals = rs_type_quals (class_of_this_parm (type));
> else
> rust_unreachable ();
> +
> + addr_space_t as;
> + cv_qualifier cv;
> + std::tie (cv, as) = quals.split ();
> + /* These should never include an address space, at least for the time
> + being. */
> + gcc_checking_assert (ADDR_SPACE_GENERIC_P (as));
> + return cv;
> }
>
> // forked from gcc/cp/pt.cc find_parameter_pack_data
> @@ -2502,11 +2515,11 @@ build_cplus_array_type (tree elt_type, tree index_type, int dependent)
> in a similar manner for restricting non-pointer types. */
>
> tree
> -rs_build_qualified_type_real (tree type, int type_quals,
> +rs_build_qualified_type_real (tree type, cv_qualifier type_quals,
> tsubst_flags_t complain)
> {
> tree result;
> - int bad_quals = TYPE_UNQUALIFIED;
> + auto bad_quals = TYPE_UNQUALIFIED;
>
> if (type == error_mark_node)
> return type;
> @@ -3323,12 +3336,10 @@ check_for_uninitialized_const_var (tree decl, bool constexpr_context_p,
> tree
> cv_unqualified (tree type)
> {
> - int quals;
> -
> if (type == error_mark_node)
> return type;
>
> - quals = rs_type_quals (type);
> + auto quals = rs_type_quals (type);
> quals &= ~(TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE);
> return rs_build_qualified_type (type, quals);
> }
> @@ -3843,7 +3854,7 @@ strip_top_quals (tree t)
> {
> if (TREE_CODE (t) == ARRAY_TYPE)
> return t;
> - return rs_build_qualified_type (t, 0);
> + return rs_build_qualified_type (t, TYPE_UNQUALIFIED);
> }
>
> // forked from gcc/cp/typeck2.cc cxx_incomplete_type_inform
> diff --git a/gcc/rust/backend/rust-tree.h b/gcc/rust/backend/rust-tree.h
> index b995d0afd8a8..74fc876cd539 100644
> --- a/gcc/rust/backend/rust-tree.h
> +++ b/gcc/rust/backend/rust-tree.h
> @@ -2976,7 +2976,7 @@ extern bool maybe_constexpr_fn (tree);
>
> extern bool var_in_maybe_constexpr_fn (tree);
>
> -extern int rs_type_quals (const_tree type);
> +extern cv_qualifier rs_type_quals (const_tree type);
>
> inline bool type_unknown_p (const_tree);
>
> @@ -3004,7 +3004,7 @@ extern bool builtin_pack_fn_p (tree);
>
> extern tree make_conv_op_name (tree);
>
> -extern int type_memfn_quals (const_tree);
> +extern cv_qualifier type_memfn_quals (const_tree);
>
> struct c_fileinfo *get_fileinfo (const char *);
>
> @@ -3024,7 +3024,7 @@ extern bool rs_tree_equal (tree, tree);
>
> extern bool compparms (const_tree, const_tree);
>
> -extern tree rs_build_qualified_type_real (tree, int, tsubst_flags_t);
> +extern tree rs_build_qualified_type_real (tree, cv_qualifier, tsubst_flags_t);
> #define rs_build_qualified_type(TYPE, QUALS) \
> rs_build_qualified_type_real ((TYPE), (QUALS), tf_warning_or_error)
> extern bool cv_qualified_p (const_tree);
> @@ -3215,10 +3215,6 @@ enum
> ce_exact
> };
>
> -extern tree rs_build_qualified_type_real (tree, int, tsubst_flags_t);
> -#define rs_build_qualified_type(TYPE, QUALS) \
> - rs_build_qualified_type_real ((TYPE), (QUALS), tf_warning_or_error)
> -
> extern tree rs_walk_subtrees (tree *, int *, walk_tree_fn, void *,
> hash_set<tree> *);
> #define rs_walk_tree(tp, func, data, pset) \
> diff --git a/gcc/tree-core.h b/gcc/tree-core.h
> index 918e077af1b3..f222e21e272a 100644
> --- a/gcc/tree-core.h
> +++ b/gcc/tree-core.h
> @@ -1,4 +1,4 @@
> -/* Core data structures for the 'tree' type.
> +/* Core data structures for the 'tree' type. -*- C++ -*-
> Copyright (C) 1989-2026 Free Software Foundation, Inc.
>
> This file is part of GCC.
> @@ -689,17 +689,73 @@ enum omp_memory_order {
> };
> #define OMP_FAIL_MEMORY_ORDER_SHIFT 3
>
> -/* There is a TYPE_QUAL value for each type qualifier. They can be
> - combined by bitwise-or to form the complete set of qualifiers for a
> - type. */
> -enum cv_qualifier {
> +/* There is a TYPE_QUAL value for each type qualifier, except for address
> + spaces. Note that the 'qualifier_set' type, used to represent the totality
> + of qualifiers of a type, including these, will need adjustment if a new
> + qualifier is added here. */
> +enum cv_qualifier : unsigned char {
> TYPE_UNQUALIFIED = 0x0,
> TYPE_QUAL_CONST = 0x1,
> TYPE_QUAL_VOLATILE = 0x2,
> TYPE_QUAL_RESTRICT = 0x4,
> - TYPE_QUAL_ATOMIC = 0x8
> + TYPE_QUAL_ATOMIC = 0x8,
> +
> + /* Useful as a mask. */
> + TYPE_QUAL_ALL = (TYPE_QUAL_CONST
> + | TYPE_QUAL_VOLATILE
> + | TYPE_QUAL_RESTRICT
> + | TYPE_QUAL_ATOMIC)
> };
>
> +/* Convenience operator, making it so that the bit-ops of two CV-qualifiers is
> + also of type cv_qualifier, rather than 'int'. This is sound for
> + CV-qualifiers as they act like sets (unlike general qualifier sets, which
> + are slightly more complex). */
> +
> +constexpr cv_qualifier
> +operator| (cv_qualifier l, cv_qualifier r)
> +{
> + return (cv_qualifier) ((static_cast<unsigned char> (l)
> + | static_cast<unsigned char> (r))
> + & TYPE_QUAL_ALL);
> +}
> +constexpr cv_qualifier &
> +operator|= (cv_qualifier &l, cv_qualifier r)
> +{
> + return l = l | r;
> +}
> +
> +constexpr cv_qualifier
> +operator& (cv_qualifier l, cv_qualifier r)
> +{
> + return (cv_qualifier) (static_cast<unsigned char> (l)
> + & static_cast<unsigned char> (r)
> + & TYPE_QUAL_ALL);
> +}
> +constexpr cv_qualifier &
> +operator&= (cv_qualifier &l, cv_qualifier r)
> +{
> + return l = l & r;
> +}
> +
> +constexpr cv_qualifier
> +operator^ (cv_qualifier l, cv_qualifier r)
> +{
> + return (cv_qualifier) ((static_cast<unsigned char> (l)
> + ^ static_cast<unsigned char> (r))
> + & TYPE_QUAL_ALL);
> +}
> +constexpr cv_qualifier &
> +operator^= (cv_qualifier &l, cv_qualifier r)
> +{
> + return l = l ^ r;
> +}
> +
> +
> +constexpr cv_qualifier
> +operator~ (cv_qualifier x)
> +{ return (cv_qualifier) (~static_cast<unsigned char> (x) & TYPE_QUAL_ALL); }
> +
> /* Standard named or nameless data types of the C compiler. */
> enum tree_index : unsigned {
> TI_ERROR_MARK,
> diff --git a/gcc/tree-dump.cc b/gcc/tree-dump.cc
> index 6f96cceff56c..85487c992b9e 100644
> --- a/gcc/tree-dump.cc
> +++ b/gcc/tree-dump.cc
> @@ -367,7 +367,9 @@ dequeue_and_dump (dump_info_p di)
> else if (code_class == tcc_type)
> {
> /* All types have qualifiers. */
> - int quals = lang_hooks.tree_dump.type_quals (t);
> + cv_qualifier quals;
> + addr_space_t as;
> + std::tie (quals, as) = lang_hooks.tree_dump.type_quals (t).split ();
>
> if (quals != TYPE_UNQUALIFIED)
> {
> @@ -378,6 +380,9 @@ dequeue_and_dump (dump_info_p di)
> di->column += 14;
> }
>
> + if (!ADDR_SPACE_GENERIC_P (as))
> + dump_int (di, "addr-space", as);
> +
> /* All types have associated declarations. */
> dump_child ("name", TYPE_NAME (t));
>
> diff --git a/gcc/tree-inline.cc b/gcc/tree-inline.cc
> index 8162b1f5051a..5061782b1962 100644
> --- a/gcc/tree-inline.cc
> +++ b/gcc/tree-inline.cc
> @@ -426,7 +426,7 @@ remap_type_1 (tree type, copy_body_data *id)
> new_tree = build_pointer_type_for_mode (remap_type (TREE_TYPE (type), id),
> TYPE_MODE (type),
> TYPE_REF_CAN_ALIAS_ALL (type));
> - if (TYPE_ATTRIBUTES (type) || TYPE_QUALS (type))
> + if (TYPE_ATTRIBUTES (type) || TYPE_QUALS (type) != qualifier_set {})
> new_tree = build_type_attribute_qual_variant (new_tree,
> TYPE_ATTRIBUTES (type),
> TYPE_QUALS (type));
> @@ -438,7 +438,7 @@ remap_type_1 (tree type, copy_body_data *id)
> new_tree = build_reference_type_for_mode (remap_type (TREE_TYPE (type), id),
> TYPE_MODE (type),
> TYPE_REF_CAN_ALIAS_ALL (type));
> - if (TYPE_ATTRIBUTES (type) || TYPE_QUALS (type))
> + if (TYPE_ATTRIBUTES (type) || TYPE_QUALS (type) != qualifier_set {})
> new_tree = build_type_attribute_qual_variant (new_tree,
> TYPE_ATTRIBUTES (type),
> TYPE_QUALS (type));
> diff --git a/gcc/tree-pretty-print.cc b/gcc/tree-pretty-print.cc
> index b470a9dee7b5..0d9ca91a28ab 100644
> --- a/gcc/tree-pretty-print.cc
> +++ b/gcc/tree-pretty-print.cc
> @@ -2271,7 +2271,7 @@ dump_generic_node (pretty_printer *pp, tree node, int spc, dump_flags_t flags,
> case BITINT_TYPE:
> case OPAQUE_TYPE:
> {
> - unsigned int quals = TYPE_QUALS (node);
> + auto quals = TYPE_QUALS_NO_ADDR_SPACE (node);
> enum tree_code_class tclass;
>
> if (quals & TYPE_QUAL_ATOMIC)
> @@ -2445,17 +2445,17 @@ dump_generic_node (pretty_printer *pp, tree node, int spc, dump_flags_t flags,
> }
> else
> {
> - unsigned int quals = TYPE_QUALS (node);
> + auto quals = TYPE_QUALS (node);
>
> dump_generic_node (pp, TREE_TYPE (node), spc, flags, false);
> pp_space (pp);
> pp_string (pp, str);
>
> - if (quals & TYPE_QUAL_CONST)
> + if (quals.has (TYPE_QUAL_CONST))
> pp_string (pp, " const");
> - if (quals & TYPE_QUAL_VOLATILE)
> + if (quals.has (TYPE_QUAL_VOLATILE))
> pp_string (pp, " volatile");
> - if (quals & TYPE_QUAL_RESTRICT)
> + if (quals.has (TYPE_QUAL_RESTRICT))
> pp_string (pp, " restrict");
>
> if (!ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (node)))
> @@ -2481,7 +2481,7 @@ dump_generic_node (pretty_printer *pp, tree node, int spc, dump_flags_t flags,
>
> case ARRAY_TYPE:
> {
> - unsigned int quals = TYPE_QUALS (node);
> + auto quals = TYPE_QUALS_NO_ADDR_SPACE (node);
> tree tmp;
>
> if (quals & TYPE_QUAL_ATOMIC)
> @@ -2511,7 +2511,7 @@ dump_generic_node (pretty_printer *pp, tree node, int spc, dump_flags_t flags,
> case UNION_TYPE:
> case QUAL_UNION_TYPE:
> {
> - unsigned int quals = TYPE_QUALS (node);
> + auto quals = TYPE_QUALS_NO_ADDR_SPACE (node);
>
> if (quals & TYPE_QUAL_ATOMIC)
> pp_string (pp, "atomic ");
> diff --git a/gcc/tree-profile.cc b/gcc/tree-profile.cc
> index a03f1f3704fa..762317c4ab46 100644
> --- a/gcc/tree-profile.cc
> +++ b/gcc/tree-profile.cc
> @@ -2049,7 +2049,7 @@ tree_profiling (void)
> tree fntype = gimple_call_fntype (call);
> if (fntype && TYPE_READONLY (fntype))
> {
> - int quals = TYPE_QUALS (fntype) & ~TYPE_QUAL_CONST;
> + auto quals = TYPE_QUALS (fntype).without (TYPE_QUAL_CONST);
> fntype = build_qualified_type (fntype, quals);
> gimple_call_set_fntype (call, fntype);
> }
> diff --git a/gcc/tree-sra.cc b/gcc/tree-sra.cc
> index f951a38442a5..173536f69df6 100644
> --- a/gcc/tree-sra.cc
> +++ b/gcc/tree-sra.cc
> @@ -1904,8 +1904,7 @@ build_ref_for_offset (location_t loc, tree base, poly_int64 offset,
> addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (base));
> if (as != TYPE_ADDR_SPACE (exp_type))
> exp_type = build_qualified_type (exp_type,
> - TYPE_QUALS (exp_type)
> - | ENCODE_QUAL_ADDR_SPACE (as));
> + TYPE_QUALS (exp_type).with_as (as));
>
> poly_int64 byte_offset = exact_div (offset, BITS_PER_UNIT);
> get_object_alignment_1 (base, &align, &misalign);
> diff --git a/gcc/tree-ssa-address.cc b/gcc/tree-ssa-address.cc
> index 30baf81d7892..d10de56b3acb 100644
> --- a/gcc/tree-ssa-address.cc
> +++ b/gcc/tree-ssa-address.cc
> @@ -450,7 +450,6 @@ move_hint_to_base (tree type, struct mem_address *parts, tree base_hint,
> {
> unsigned i;
> tree val = NULL_TREE;
> - int qual;
>
> for (i = 0; i < addr->n; i++)
> {
> @@ -469,7 +468,7 @@ move_hint_to_base (tree type, struct mem_address *parts, tree base_hint,
> to TYPE directly, as the back-end will assume registers of pointer
> type are aligned, and just the base itself may not actually be.
> We use void pointer to the type's address space instead. */
> - qual = ENCODE_QUAL_ADDR_SPACE (TYPE_ADDR_SPACE (type));
> + qualifier_set qual {TYPE_UNQUALIFIED, TYPE_ADDR_SPACE (type)};
> type = build_qualified_type (void_type_node, qual);
> parts->base = fold_convert (build_pointer_type (type), val);
> aff_combination_remove_elt (addr, i);
> diff --git a/gcc/tree-switch-conversion.cc b/gcc/tree-switch-conversion.cc
> index 6fcb88f3ae05..e04423c9e980 100644
> --- a/gcc/tree-switch-conversion.cc
> +++ b/gcc/tree-switch-conversion.cc
> @@ -1012,8 +1012,7 @@ switch_conversion::build_one_array (int num, tree arr_index_type,
> ARTIFICIAL_RODATA_CSWITCH);
> if (!ADDR_SPACE_GENERIC_P (as))
> {
> - int quals = (TYPE_QUALS_NO_ADDR_SPACE (value_type)
> - | ENCODE_QUAL_ADDR_SPACE (as));
> + qualifier_set quals {TYPE_QUALS_NO_ADDR_SPACE (value_type), as};
> value_type = build_qualified_type (value_type, quals);
> array_type = build_array_type (value_type, arr_index_type);
> }
> diff --git a/gcc/tree-vect-stmts.cc b/gcc/tree-vect-stmts.cc
> index 76e24b2c1699..4a8074dabacb 100644
> --- a/gcc/tree-vect-stmts.cc
> +++ b/gcc/tree-vect-stmts.cc
> @@ -13667,7 +13667,8 @@ get_related_vectype_for_scalar_type (machine_mode prevailing_mode,
> type. */
> if (TYPE_ADDR_SPACE (orig_scalar_type) != TYPE_ADDR_SPACE (vectype))
> return build_qualified_type
> - (vectype, KEEP_QUAL_ADDR_SPACE (TYPE_QUALS (orig_scalar_type)));
> + (vectype, qualifier_set {TYPE_UNQUALIFIED,
> + TYPE_ADDR_SPACE (orig_scalar_type)});
>
> return vectype;
> }
> diff --git a/gcc/tree.cc b/gcc/tree.cc
> index 411752d14636..39e37ebf0ca6 100644
> --- a/gcc/tree.cc
> +++ b/gcc/tree.cc
> @@ -28,6 +28,7 @@ along with GCC; see the file COPYING3. If not see
> calls language-dependent routines. */
>
> #include "config.h"
> +#define INCLUDE_FUNCTIONAL // for expected.h
> #include "system.h"
> #include "coretypes.h"
> #include "backend.h"
> @@ -77,6 +78,9 @@ along with GCC; see the file COPYING3. If not see
> #include "ubsan.h"
> #include "attr-callback.h"
>
> +/* For try_quals_merge. */
> +#include "util/expected.h"
> +
> /* Names of tree components.
> Used for printing out the tree and error messages. */
> #define DEFTREECODE(SYM, NAME, TYPE, LEN) NAME,
> @@ -283,7 +287,7 @@ static GTY ((cache ("gt_value_expr_mark")))
> static GTY ((cache))
> hash_table<tree_vec_map_cache_hasher> *debug_args_for_decl;
>
> -static void set_type_quals (tree, int);
> +static void set_type_quals (tree, qualifier_set);
> static void print_type_hash_statistics (void);
> static void print_debug_expr_statistics (void);
> static void print_value_expr_statistics (void);
> @@ -5672,17 +5676,126 @@ protected_set_expr_location_if_unset (tree t, location_t loc)
> protected_set_expr_location (t, loc);
> }
>
> -/* Set the type qualifiers for TYPE to TYPE_QUALS, which is a bitmask
> - of the various TYPE_QUAL values. */
> +
> +/* Documented next to declaration in tree.h. */
> +tl::expected<qualifier_set, qualifier_set::merge_error>
> +qualifier_set::merge (qualifier_set other,
> + bool strict_addr_space /* = false */) const
> +{
> + using ME = qualifier_set::merge_error;
> +
> + if (has (TYPE_QUAL_ATOMIC) != other.has (TYPE_QUAL_ATOMIC))
> + return tl::make_unexpected (ME::atomic_mismatch);
> +
> + auto cv_merged = cv_quals () | other.cv_quals ();
> + auto as1 = addr_space ();
> + auto as2 = other.addr_space ();
> +
> + addr_space_t as_super;
> + if (as1 == as2)
> + as_super = as1;
> + else if (!strict_addr_space
> + && targetm.addr_space.subset_p (as1, as2))
> + as_super = as2;
> + else if (!strict_addr_space
> + && targetm.addr_space.subset_p (as2, as1))
> + as_super = as1;
> + else
> + return tl::make_unexpected (ME::disjoint_address_spaces);
> +
> + return qualifier_set {cv_merged, as_super};
> +}
> +
> +tl::expected<qualifier_set, qualifier_set::join_error>
> +qualifier_set::join (qualifier_set other) const
> +{
> + using JE = qualifier_set::join_error;
> + auto cv_merged = cv_quals () | other.cv_quals ();
> + auto as1 = addr_space ();
> + auto as2 = other.addr_space ();
> +
> + addr_space_t as = ADDR_SPACE_GENERIC;
> + if (as1 == as2)
> + as = as1;
> + else if (!ADDR_SPACE_GENERIC_P (as1) && !ADDR_SPACE_GENERIC_P (as2))
> + return tl::make_unexpected (JE::double_addr_space);
> + else if (ADDR_SPACE_GENERIC_P (as1))
> + as = as2;
> + else
> + as = as1;
> +
> + return qualifier_set {cv_merged, as};
> +}
> +
> +bool
> +qualifier_set::can_qualify (qualifier_set subset,
> + bool nop_only /* = false */) const
> +{
> + /* Documented next to declaration in tree.h. */
> +
> + /* SUBSET is included in SUPERSET if its address space is a subset of that of
> + SUPERSET, and all the CV-quals of SUBSET are present on SUPERSET. */
> + auto cv_sup = cv_quals ();
> + auto cv_sub = subset.cv_quals ();
> + auto as_sup = addr_space ();
> + auto as_sub = subset.addr_space ();
> + return ((cv_sup & cv_sub) == cv_sub
> + /* Differences in the _Atomic qualifier cannot be crossed. */
> + && (cv_sup & TYPE_QUAL_ATOMIC) == (cv_sub & TYPE_QUAL_ATOMIC)
> + && (as_sup == as_sub
> + || (!nop_only
> + && targetm.addr_space.subset_p (as_sub, as_sup))));
> +}
> +
> +DEBUG_FUNCTION void
> +qualifier_set::debug () const
> +{
> + putc ('{', stderr);
> + cv_qualifier cv;
> + addr_space_t as;
> + std::tie (cv, as) = split ();
> + bool has_previous = false;
> + auto handle_bit = [&] (cv_qualifier bit, const char *lbl)
> + {
> + if (!(cv & bit))
> + return;
> +
> + if (has_previous)
> + fputs (", ", stderr);
> +
> + has_previous = true;
> + fputs (lbl, stderr);
> + };
> +
> + handle_bit (TYPE_QUAL_CONST, "const");
> + handle_bit (TYPE_QUAL_VOLATILE, "volatile");
> + handle_bit (TYPE_QUAL_RESTRICT, "restrict");
> + handle_bit (TYPE_QUAL_ATOMIC, "_Atomic");
> +
> + /* If new bits appear, let the developer know. */
> + static_assert ((TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE | TYPE_QUAL_RESTRICT
> + | TYPE_QUAL_ATOMIC)
> + == TYPE_QUAL_ALL,
> + "new qualifiers added, update handle_bit calls above");
> +
> + if (has_previous)
> + fputs (", ", stderr);
> +
> + fprintf (stderr, "AS%d", as);
> +
> + fputs ("}\n", stderr);
> +}
> +
> +/* Set the type qualifiers for TYPE to TYPE_QUALS. */
>
> static void
> -set_type_quals (tree type, int type_quals)
> +set_type_quals (tree type, qualifier_set type_quals)
> {
> - TYPE_READONLY (type) = (type_quals & TYPE_QUAL_CONST) != 0;
> - TYPE_VOLATILE (type) = (type_quals & TYPE_QUAL_VOLATILE) != 0;
> - TYPE_RESTRICT (type) = (type_quals & TYPE_QUAL_RESTRICT) != 0;
> - TYPE_ATOMIC (type) = (type_quals & TYPE_QUAL_ATOMIC) != 0;
> - TYPE_ADDR_SPACE (type) = DECODE_QUAL_ADDR_SPACE (type_quals);
> + TYPE_READONLY (type) = type_quals.has (TYPE_QUAL_CONST);
> + TYPE_VOLATILE (type) = type_quals.has (TYPE_QUAL_VOLATILE);
> + TYPE_RESTRICT (type) = type_quals.has (TYPE_QUAL_RESTRICT);
> + TYPE_ATOMIC (type) = type_quals.has (TYPE_QUAL_ATOMIC);
> + TYPE_ADDR_SPACE (type) = type_quals.addr_space ();
> }
>
> /* Returns true iff CAND and BASE have equivalent language-specific
> @@ -5758,7 +5871,7 @@ check_base_type (const_tree cand, const_tree base)
> return true;
> /* Atomic types increase minimal alignment. We must to do so as well
> or we get duplicated canonical types. See PR88686. */
> - if ((TYPE_QUALS (cand) & TYPE_QUAL_ATOMIC))
> + if (TYPE_QUALS (cand).has (TYPE_QUAL_ATOMIC))
> {
> /* See if this object can map to a basic atomic type. */
> tree atomic_type = find_atomic_core_type (cand);
> @@ -5771,7 +5884,7 @@ check_base_type (const_tree cand, const_tree base)
> /* Returns true iff CAND is equivalent to BASE with TYPE_QUALS. */
>
> bool
> -check_qualified_type (const_tree cand, const_tree base, int type_quals)
> +check_qualified_type (const_tree cand, const_tree base, qualifier_set type_quals)
> {
> return (TYPE_QUALS (cand) == type_quals
> && check_base_type (cand, base)
> @@ -5802,7 +5915,7 @@ check_aligned_type (const_tree cand, const_tree base, unsigned int align)
> return NULL_TREE. */
>
> tree
> -get_qualified_type (tree type, int type_quals)
> +get_qualified_type (tree type, qualifier_set type_quals)
> {
> if (TYPE_QUALS (type) == type_quals)
> return type;
> @@ -5834,7 +5947,7 @@ get_qualified_type (tree type, int type_quals)
> exist. This function never returns NULL_TREE. */
>
> tree
> -build_qualified_type (tree type, int type_quals MEM_STAT_DECL)
> +build_qualified_type (tree type, qualifier_set type_quals MEM_STAT_DECL)
> {
> tree t;
>
> @@ -5847,7 +5960,7 @@ build_qualified_type (tree type, int type_quals MEM_STAT_DECL)
> t = build_variant_type_copy (type PASS_MEM_STAT);
> set_type_quals (t, type_quals);
>
> - if (((type_quals & TYPE_QUAL_ATOMIC) == TYPE_QUAL_ATOMIC))
> + if (type_quals.has (TYPE_QUAL_ATOMIC))
> {
> /* See if this object can map to a basic atomic type. */
> tree atomic_type = find_atomic_core_type (type);
> @@ -9477,7 +9590,8 @@ make_vector_type (tree innertype, poly_int64 nunits, machine_mode mode)
>
> /* We have built a main variant, based on the main variant of the
> inner type. Use it to build the variant we return. */
> - if ((TYPE_ATTRIBUTES (innertype) || TYPE_QUALS (innertype))
> + if ((TYPE_ATTRIBUTES (innertype)
> + || TYPE_QUALS (innertype) != qualifier_set{})
> && TREE_TYPE (t) != innertype)
> return build_type_attribute_qual_variant (t,
> TYPE_ATTRIBUTES (innertype),
> @@ -9603,11 +9717,12 @@ build_atomic_base (tree type, unsigned int align)
> tree t;
>
> /* Make sure its not already registered. */
> - if ((t = get_qualified_type (type, TYPE_QUAL_ATOMIC)))
> + qualifier_set atomic_quals {TYPE_QUAL_ATOMIC};
> + if ((t = get_qualified_type (type, atomic_quals)))
> return t;
>
> t = build_variant_type_copy (type);
> - set_type_quals (t, TYPE_QUAL_ATOMIC);
> + set_type_quals (t, atomic_quals);
>
> if (align)
> SET_TYPE_ALIGN (t, align);
> diff --git a/gcc/tree.h b/gcc/tree.h
> index 73a26dbe75c7..a1c8f55cc413 100644
> --- a/gcc/tree.h
> +++ b/gcc/tree.h
> @@ -21,8 +21,10 @@ along with GCC; see the file COPYING3. If not see
> #define GCC_TREE_H
>
> #include "tree-core.h"
> +#include "coretypes.h"
> #include "options.h"
> #include "vec.h"
> +#include "util/expected_fwd.h"
>
> /* Convert a target-independent built-in function code to a combined_fn. */
>
> @@ -2558,39 +2560,289 @@ extern tree vector_element_bits_tree (const_tree);
> /* The address space the type is in. */
> #define TYPE_ADDR_SPACE(NODE) (TYPE_CHECK (NODE)->base.u.bits.address_space)
>
> -/* 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 DECODE_QUAL_ADDR_SPACE(X) (((X) >> 8) & 0xFF)
> +/* A qualifier set is the aggregate of all qualifiers on a given type. It
> + consists of the 'const', 'volatile', 'restrict', and 'atomic' qualification,
> + which are all either present or absent, and an address space qualifier,
> + which is always present (but possibly ADDR_SPACE_GENERIC). */
>
> -/* Return all qualifiers except for the address space qualifiers. */
> -#define CLEAR_QUAL_ADDR_SPACE(X) ((X) & ~0xFF00)
> +struct qualifier_set
> +{
> + /* Construct an empty qualifier set with the generic address space. Such a
> + qualifier set corresponds to unqualified types. */
> + qualifier_set () = default;
> + static_assert (cv_qualifier {} == TYPE_UNQUALIFIED
> + && addr_space_t {} == ADDR_SPACE_GENERIC,
> + "We want the trivial default constructor to use those vals");
>
> -/* Only keep the address space out of the qualifiers and discard the other
> - qualifiers. */
> -#define KEEP_QUAL_ADDR_SPACE(X) ((X) & 0xFF00)
> + /* Construct a qualifier set containing the qualifiers CV_QUALS and the
> + generic address space. */
> + constexpr
> + qualifier_set (cv_qualifier cv_quals)
> + : m_cv_quals {cv_quals},
> + m_addr_space {ADDR_SPACE_GENERIC}
> + {}
>
> -/* The set of type qualifiers for this type. */
> -#define TYPE_QUALS(NODE) \
> - ((int) ((TYPE_READONLY (NODE) * TYPE_QUAL_CONST) \
> - | (TYPE_VOLATILE (NODE) * TYPE_QUAL_VOLATILE) \
> - | (TYPE_ATOMIC (NODE) * TYPE_QUAL_ATOMIC) \
> - | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT) \
> - | (ENCODE_QUAL_ADDR_SPACE (TYPE_ADDR_SPACE (NODE)))))
> + /* Construct a qualifier set containing the qualifiers CV_QUALS and the
> + address space AS. */
> + constexpr
> + qualifier_set (cv_qualifier cv_quals, addr_space_t as)
> + : m_cv_quals {cv_quals},
> + m_addr_space {as}
> + {}
> +
> + explicit qualifier_set (tree type); /* At end of file, due to macro mess. */
> +
> + /* Add QUAL to this qualifier set. */
> + constexpr void
> + add (cv_qualifier qual)
> + {
> + m_cv_quals |= qual;
> + }
> +
> + /* Remove QUAL from this qualifier set. */
> + constexpr void
> + remove (cv_qualifier qual)
> + {
> + m_cv_quals &= ~qual;
> + }
> +
> + /* Set the address space of this qualifier set to AS. */
> + constexpr void
> + set_as (addr_space_t as)
> + {
> + this->m_addr_space = as;
> + }
> +
> + /* Returns a new qualifier set, with QUAL added (as with 'add'). */
> + constexpr qualifier_set
> + with (cv_qualifier qual) const
> + {
> + auto ret = *this;
> + ret.add (qual);
> + return ret;
> + }
> +
> + /* Returns a new qualifier set, with QUAL removed (as with 'remove'). */
> + WARN_UNUSED_RESULT constexpr qualifier_set
> + without (cv_qualifier qual) const
> + {
> + auto ret = *this;
> + ret.remove (qual);
> + return ret;
> + }
> +
> + /* Return this qualifier set, with address space set to AS. */
> + WARN_UNUSED_RESULT constexpr qualifier_set
> + with_as (addr_space_t as) const
> + {
> + auto ret = *this;
> + ret.set_as (as);
> + return ret;
> + }
> +
> + /* Get CV qualifiers of this qualifier set.
> +
> + Using this getter alone is usually a mistake; most of the time, where
> + there is a qualifier_set, there should be handling for all its components,
> + rather than just one of them. Prefer using 'split' at least once in a
> + given hunk. */
> + constexpr cv_qualifier
> + cv_quals () const
> + { return m_cv_quals; }
> +
> + /* Get address space qualifier of this qualifier set.
> +
> + Using this getter alone is usually a mistake; most of the time, where
> + there is a qualifier_set, there should be handling for all its components,
> + rather than just one of them. Prefer using 'split' at least once in a
> + given hunk. */
> + constexpr addr_space_t
> + addr_space () const
> + { return m_addr_space; }
> +
> + /* Return all qualifiers both in this qualifier set and in OTHER_CV. */
> + constexpr cv_qualifier
> + intersect (cv_qualifier other_cv) const
> + { return cv_quals () & other_cv; }
> +
> + /* Return true iff any of OTHER_CV are contained in THIS. */
> + constexpr bool
> + has (cv_qualifier other_cv) const
> + { return intersect (other_cv); }
> +
> + /* Get the symmetric difference of this qualifier set with CV-qualifiers
> + QUAL. Of course, as QUAL lacks an address space, the address space of
> + this qualifier set is preserved. */
> + WARN_UNUSED_RESULT constexpr qualifier_set
> + symmetric_difference (cv_qualifier qual) const
> + {
> + return {cv_quals () ^ qual, addr_space ()};
> + }
> +
> + /* Returns true if qualifiers in SUBSET can be replaced with qualifiers in
> + THIS safely.
> +
> + In general, this means that an object qualified per SUBSET can be used as
> + if it was qualified per this qualifier set (e.g. 'T' as 'const T', or
> + 'const T' as 'const volatile AS1 T', presuming that AS1 is a superset of
> + the generic address space).
> +
> + If NOP_ONLY, return 'true' iff a pointer with a pointee qualified via
> + SUBSET can be converted into a pointer with a pointee qualified via THIS
> + (i.e. if a NOP_EXPR conversion would be valid). In particular, this means
> + address space mismatches are forbidden. */
> + bool can_qualify (qualifier_set subset, bool nop_only = false) const;
> +
> + enum class merge_error
> + {
> + /* The address spaces of the to-be-merged qualifier sets were disjoint,
> + i.e. neither contained the other. */
> + disjoint_address_spaces,
> +
> + /* The to-be-merged qualifier sets differed in TYPE_QUAL_ATOMIC. */
> + atomic_mismatch,
> + };
> +
> + /* Attempt to produce a qualifier_set that's a merge of qualifiers in THIS
> + and OTHER. Such a qualifier set can be used instead of either THIS or
> + OTHERT safely. (i.e. if a type was qualified by either THIS or OTHER, it
> + can be qualified by their merge instead safely, possibly through a
> + conversion)
> +
> + This operation may fail. In that case, the error value returned provides
> + reasoning for the failure.
> +
> + If STRICT_ADDR_SPACE, then no address space mismatch is permitted. This
> + is useful if merging below the top-level of pointers (i.e. in a case such
> + as 'AS1 T**' vs 'AS2 T**').
> +
> + You'll need to include expected.h to use this. */
> +
> + tl::expected<qualifier_set, merge_error>
> + merge (qualifier_set other, bool strict_addr_space = false) const;
> +
> + enum class join_error
> + {
> + /* The joined qualifier set would've contained two address space
> + qualifiers. */
> + double_addr_space,
> + };
> +
> + /* Return a qualifier set that has all the qualifiers of THIS and OTHER.
> + Unlike 'merge', this operation operates purely syntactically; if THIS is
> + {q1_1, q1_2, ..., q1_i} and OTHER {q2_1, q2_2, ..., q2_j}, then returns
> + the qualifier set obtained by concatenating the sequences q1 and q2
> + without duplicates, if such a qualifier set is valid.
> +
> + Specifically, this implies that if THIS or OTHER both (syntactically)
> + contain an address space qualifier, and they're different, the operation
> + fails (even if one is subset of the other).
> +
> + This operation may fail. In that case, the error value returned provides
> + reasoning for the failure.
> +
> + You'll need to include expected.h to use this. */
> +
> + tl::expected<qualifier_set, join_error> join (qualifier_set other) const;
> +
> + constexpr bool
> + operator== (const qualifier_set &other) const
> + {
> + return ((cv_quals () == other.cv_quals ())
> + && (addr_space () == other.addr_space ()));
> + }
> +
> + constexpr bool
> + operator!= (const qualifier_set &other) const
> + {
> + return !operator== (other);
> + }
> +
> + /* Split this qualifier set into its constituent parts. Useful where you
> + need to make sure you've handled all the components of a qualifier
> + set. */
> +
> + constexpr std::pair<cv_qualifier, addr_space_t>
> + split () const
> + { return std::make_pair (cv_quals (), addr_space ()); }
> +
> + /* True iff this qualifier set is different to {ADDR_SPACE_GENERIC} (i.e. if
> + it is syntactically non-empty). */
> + constexpr bool
> + nonempty_p () const
> + { return *this != qualifier_set {}; }
> +
> + /* Dump the contents of this qualifier set to stderr. */
> + void debug() const;
> +
> +private:
> + cv_qualifier m_cv_quals;
> + addr_space_t m_addr_space;
> +};
> +static_assert (std::is_trivially_copyable<qualifier_set>::value, "");
> +static_assert (std::is_trivially_default_constructible<qualifier_set>::value,
> + "");
> +static_assert (std::is_trivially_destructible<qualifier_set>::value, "");
> +
> +/* Operators & and &= are intentionally omitted, as they permit losing
> + information too easily, and silently changed the meaning of existing code.
> + Use 'can_qualify', 'intersect' and 'has' instead. */
> +
> +/* For OR (union) and XOR (mutual difference) operations, we could keep an
> + address space, ergo we must return qualifier_sets. */
> +
> +constexpr qualifier_set
> +operator| (qualifier_set qs, cv_qualifier cvs)
> +{
> + return qs.with (cvs);
> +}
> +
> +constexpr qualifier_set
> +operator| (cv_qualifier cvs, qualifier_set qs)
> +{
> + return qs.with (cvs);
> +}
> +
> +constexpr qualifier_set
> +operator^ (qualifier_set qs, cv_qualifier cvs)
> +{
> + return qs.symmetric_difference (cvs);
> +}
> +
> +constexpr qualifier_set
> +operator^ (cv_qualifier cvs, qualifier_set qs)
> +{
> + return qs.symmetric_difference (cvs);
> +}
> +
> +constexpr qualifier_set &
> +operator|= (qualifier_set &qs, cv_qualifier quals)
> +{
> + return qs = qs | quals;
> +}
> +constexpr qualifier_set &
> +operator^= (qualifier_set &qs, cv_qualifier quals)
> +{
> + return qs = qs ^ quals;
> +}
>
> /* The same as TYPE_QUALS without the address space qualifications. */
> -#define TYPE_QUALS_NO_ADDR_SPACE(NODE) \
> - ((int) ((TYPE_READONLY (NODE) * TYPE_QUAL_CONST) \
> - | (TYPE_VOLATILE (NODE) * TYPE_QUAL_VOLATILE) \
> - | (TYPE_ATOMIC (NODE) * TYPE_QUAL_ATOMIC) \
> - | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)))
> +#define TYPE_QUALS_NO_ADDR_SPACE(NODE) \
> + (cv_qualifier ((TYPE_READONLY (NODE) * TYPE_QUAL_CONST) \
> + | (TYPE_VOLATILE (NODE) * TYPE_QUAL_VOLATILE) \
> + | (TYPE_ATOMIC (NODE) * TYPE_QUAL_ATOMIC) \
> + | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)))
> +
> +/* The set of type qualifiers for this type. */
> +#define TYPE_QUALS(NODE) \
> + qualifier_set {TYPE_QUALS_NO_ADDR_SPACE (NODE), TYPE_ADDR_SPACE (NODE)}
>
> /* The same as TYPE_QUALS without the address space and atomic
> qualifications. */
> -#define TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC(NODE) \
> - ((int) ((TYPE_READONLY (NODE) * TYPE_QUAL_CONST) \
> - | (TYPE_VOLATILE (NODE) * TYPE_QUAL_VOLATILE) \
> - | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)))
> +#define TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC(NODE) \
> + (cv_qualifier ((TYPE_READONLY (NODE) * TYPE_QUAL_CONST) \
> + | (TYPE_VOLATILE (NODE) * TYPE_QUAL_VOLATILE) \
> + | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)))
>
> /* These flags are available for each language front end to use internally. */
> #define TYPE_LANG_FLAG_0(NODE) (TYPE_CHECK (NODE)->type_common.lang_flag_0)
> @@ -5260,18 +5512,18 @@ extern bool check_base_type (const_tree cand, const_tree base);
> /* Check whether CAND is suitable to be returned from get_qualified_type
> (BASE, TYPE_QUALS). */
>
> -extern bool check_qualified_type (const_tree, const_tree, int);
> +extern bool check_qualified_type (const_tree, const_tree, qualifier_set);
>
> /* Return a version of the TYPE, qualified as indicated by the
> TYPE_QUALS, if one exists. If no qualified version exists yet,
> return NULL_TREE. */
>
> -extern tree get_qualified_type (tree, int);
> +extern tree get_qualified_type (tree, qualifier_set);
>
> /* Like get_qualified_type, but creates the type if it does not
> exist. This function never returns NULL_TREE. */
>
> -extern tree build_qualified_type (tree, int CXX_MEM_STAT_INFO);
> +extern tree build_qualified_type (tree, qualifier_set CXX_MEM_STAT_INFO);
>
> /* Create a variant of type T with alignment ALIGN. */
>
> @@ -5283,9 +5535,10 @@ extern tree build_aligned_type (tree, unsigned int);
> build_qualified_type instead. */
>
> #define build_type_variant(TYPE, CONST_P, VOLATILE_P) \
> - build_qualified_type ((TYPE), \
> - ((CONST_P) ? TYPE_QUAL_CONST : 0) \
> - | ((VOLATILE_P) ? TYPE_QUAL_VOLATILE : 0))
> + (build_qualified_type \
> + ((TYPE), \
> + (((CONST_P) ? TYPE_QUAL_CONST : TYPE_UNQUALIFIED) \
> + | ((VOLATILE_P) ? TYPE_QUAL_VOLATILE : TYPE_UNQUALIFIED))))
>
> /* Make a copy of a type node. */
>
> @@ -7272,4 +7525,11 @@ make_expanded_omp_iterator (void)
> return make_tree_vec (10);
> }
>
> +/* Construct a qualifier set containing all the qualifiers of TYPE. */
> +inline
> +qualifier_set::qualifier_set (tree type)
> + : m_cv_quals {TYPE_QUALS_NO_ADDR_SPACE (type)}
> + , m_addr_space {TYPE_ADDR_SPACE (type)}
> +{}
> +
> #endif /* GCC_TREE_H */
> diff --git a/gcc/ubsan.cc b/gcc/ubsan.cc
> index 79a863b46ce6..74b7cb45e26f 100644
> --- a/gcc/ubsan.cc
> +++ b/gcc/ubsan.cc
> @@ -1775,8 +1775,8 @@ instrument_bool_enum_load (gimple_stmt_iterator *gsi)
>
> addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (rhs));
> if (as != TYPE_ADDR_SPACE (utype))
> - utype = build_qualified_type (utype, TYPE_QUALS (utype)
> - | ENCODE_QUAL_ADDR_SPACE (as));
> + utype = build_qualified_type (utype,
> + TYPE_QUALS (utype) .with_as(as));
> bool ends_bb = stmt_ends_bb_p (stmt);
> location_t loc = gimple_location (stmt);
> tree lhs = gimple_assign_lhs (stmt);
> diff --git a/gcc/vtable-verify.cc b/gcc/vtable-verify.cc
> index 05e14b24788d..5a8b4a7b437f 100644
> --- a/gcc/vtable-verify.cc
> +++ b/gcc/vtable-verify.cc
> @@ -378,7 +378,6 @@ vtbl_map_get_node (tree class_type)
>
> tree class_type_decl;
> tree class_name;
> - unsigned int type_quals;
>
> if (!vtbl_map_hash)
> return NULL;
> @@ -390,8 +389,8 @@ vtbl_map_get_node (tree class_type)
> class_type_decl = TYPE_NAME (class_type);
>
> /* Verify that there aren't any qualifiers on the type. */
> - type_quals = TYPE_QUALS (TREE_TYPE (class_type_decl));
> - gcc_assert (type_quals == TYPE_UNQUALIFIED);
> + auto type_quals = TYPE_QUALS (TREE_TYPE (class_type_decl));
> + gcc_assert (type_quals == qualifier_set {});
>
> /* Get the mangled name for the unqualified type. */
> gcc_assert (HAS_DECL_ASSEMBLER_NAME_P (class_type_decl));
> @@ -417,7 +416,6 @@ find_or_create_vtbl_map_node (tree base_class_type)
> struct vtbl_map_node *node;
> struct vtbl_map_node **slot;
> tree class_type_decl;
> - unsigned int type_quals;
>
> if (!vtbl_map_hash)
> vtbl_map_hash = new vtbl_map_table_type (10);
> @@ -426,8 +424,8 @@ find_or_create_vtbl_map_node (tree base_class_type)
> class_type_decl = TYPE_NAME (base_class_type);
>
> /* Verify that there aren't any type qualifiers on type. */
> - type_quals = TYPE_QUALS (TREE_TYPE (class_type_decl));
> - gcc_assert (type_quals == TYPE_UNQUALIFIED);
> + auto type_quals = TYPE_QUALS (TREE_TYPE (class_type_decl));
> + gcc_assert (type_quals == qualifier_set {});
>
> gcc_assert (HAS_DECL_ASSEMBLER_NAME_P (class_type_decl));
> key.class_name = DECL_ASSEMBLER_NAME (class_type_decl);
> diff --git a/libcc1/libcc1plugin.cc b/libcc1/libcc1plugin.cc
> index 8b875eb0605b..cf5585e3d828 100644
> --- a/libcc1/libcc1plugin.cc
> +++ b/libcc1/libcc1plugin.cc
> @@ -699,7 +699,7 @@ plugin_build_qualified_type (cc1_plugin::connection *,
> enum gcc_qualifiers qualifiers)
> {
> tree unqualified_type = convert_in (unqualified_type_in);
> - int quals = 0;
> + qualifier_set quals {};
>
> if ((qualifiers & GCC_QUALIFIER_CONST) != 0)
> quals |= TYPE_QUAL_CONST;
> diff --git a/libcc1/libcp1plugin.cc b/libcc1/libcp1plugin.cc
> index e62c6ef9b9bd..b34a546df576 100644
> --- a/libcc1/libcp1plugin.cc
> +++ b/libcc1/libcp1plugin.cc
> @@ -1999,7 +1999,7 @@ plugin_build_method_type (cc1_plugin::connection *self,
> {
> tree class_type = convert_in (class_type_in);
> tree func_type = convert_in (func_type_in);
> - cp_cv_quals quals = 0;
> + cp_cv_quals quals {};
> cp_ref_qualifier rquals;
>
> if ((quals_in & GCC_CP_QUALIFIER_CONST) != 0)
> @@ -3401,7 +3401,7 @@ plugin_build_qualified_type (cc1_plugin::connection *,
> enum gcc_cp_qualifiers qualifiers)
> {
> tree unqualified_type = convert_in (unqualified_type_in);
> - cp_cv_quals quals = 0;
> + cp_cv_quals quals = {};
>
> if ((qualifiers & GCC_CP_QUALIFIER_CONST) != 0)
> quals |= TYPE_QUAL_CONST;