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;