[RFC TREE-WIDE] gcc: stop using 'int' to represent sets of qualifiers
Arsen Arsenović <[email protected]> Fri, 26 Jun 2026 21:16:17 +0200
| Newsgroups | gmane.comp.gcc.patches,gmane.comp.gcc.devel |
|---|---|
| Message-ID | <[email protected]> |
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.
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). It is implemented as an
enum class with the 'unsigned' underlying type.
I'd have preferred qualifier_set to be a struct but, unfortunately, some
ABIs do not pass small, trivial stricts (even of size equal to
'unsigned') through registers; though, this seems not to be an issue on
most 64-bit platforms, and thus it may be worth doing anyway.
Doing so would've made working with qualifier sets less verbose, by
providing member functions and conversion operators/constructors, it'd
have eliminated many cases that appear in this patch where an
convenience overload is added so that a qualifier_set function can
accept cv_qualifiers also, and it'd have forbidden constructing
qualifier_sets with invalid contents.
I'd like opinions on this, I think it'd be beneficial enough to justify
the presumably tiny runtime hit on such platforms, personally.
I elected to refactor the C and C++ frontends for the purpose of this
RFC. I wouldn't mind also doing the rest (and I wouldn't mind someone
else doing them either ;-) ), but these two are sufficient for the
demonstration.
The C FE, of course, already handles address spaces, so changes in it
were mostly mechanical.
The C++ FE, however, does not. In this patch, I've strung in address
space support where the compiler told me to, where it was also obvious
how to do so. In the places where the compiler told me to, but it was
not clear what to do with address spaces, I've left an assert.
This demonstrates why utilizing the type system in this way is quite
useful; it forces the user to at least be aware (and, sometimes, even to
take into account) the restrictions that exist on the qualifier types.
It also presented a way to find where named address space support work
actually needs to be done (indeed, it found quite a few cases that the
patch series that implements C++ NAS support missed).
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.
I also provided a split_quals. The idea with this one was to induce
compile errors if, at some point, split_quals stops being two
components (cv_qualifier and address_space_t), and becomes more. It's
somewhat cumbersome to use because we can't use structured binding,
though. Hence, it may not be worth it.
Another interesting point is the cp-tree.h+cp/tree.cc function
cp_try_quals_merge. It shows how I imagined frontends should deal with
the now-partial qualifier union operator for the most common case,
though it delegates to the language-independent and silent
try_quals_merge.
This patch also copies the rust/util/{expected,optional}.h into the
toplevel, so that they can be included in the rest of the
compiler. (these can eventually be removed when we start using
C++17/23)
What do you think about this approach? I'd like to get this refactor
done ASAP to allow for most possible testing time, and so that C++ named
address space discussed in [2] can be added. (In fact, the message that
sparked this refactor was
<https://inbox.sourceware.org/gcc-patches/[email protected]/>).
PS: Please do not waste time reviewing all the logic in c/, c-family/
and cp/. I only ported them so that you can get a feel for how real
code dealing with qualifiers would look like after the change. I didn't
even test it. Best save the effort for when we agree on how to proceed.
:-)
[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]/
TODO:
- Port all the other frontends and backends. I've only made sure that
the --enable-languages=c,lto --{build,host,target}=x86_64-linux-gnu
configuration builds (but not necessarily bootstraps).
- Regstrap and fix the bugs doing so finds.
- Use split_quals more, or remove it. It'd be nice to provide a way to
raise a compile-time error should more qualifiers that don't fit in
the "CVRA"-style component appear, but that's unlikely, and somewhat
cumbersome without structured binding.
- Bikeshed names and such.
- Add debug_quals (cv_qualifier) and debug_quals (qualifier_set). Might
as well.
- Make cv_qualifier scoped also maybe. This is more churn (~600 uses of
the constants with this patch applied), so I left it out at least for
now.
- Figure out whether it is correct to make cp_cv_quals just
cv_qualifier. It seems to me that this is not universally correct,
and that it varies case-to-case. I think this typedef can probably be
removed anyway, since 'cv_qualifier' is a type that exists now. If it
is removed, all its usages would be replaced by either cv_qualifier or
qualifier_set.
- Split out some of the unrelated changes.
- Fix inconsistencies.
---
gcc/attribs.cc | 9 +-
gcc/attribs.h | 2 +-
gcc/c-family/c-ada-spec.cc | 2 +-
gcc/c-family/c-common.cc | 14 +-
gcc/c-family/c-common.h | 16 +-
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 | 122 +-
gcc/c/c-objc-common.cc | 17 +-
gcc/c/c-parser.cc | 12 +-
gcc/c/c-tree.h | 6 +-
gcc/c/c-typeck.cc | 86 +-
gcc/config/i386/i386-builtins.cc | 2 +-
gcc/config/i386/i386.cc | 3 +-
gcc/cp/call.cc | 42 +-
gcc/cp/class.cc | 10 +-
gcc/cp/cp-tree.h | 38 +-
gcc/cp/decl.cc | 48 +-
gcc/cp/decl2.cc | 2 +-
gcc/cp/mangle.cc | 16 +-
gcc/cp/method.cc | 50 +-
gcc/cp/module.cc | 14 +-
gcc/cp/parser.cc | 8 +-
gcc/cp/pt.cc | 170 ++-
gcc/cp/reflect.cc | 16 +-
gcc/cp/rtti.cc | 7 +-
gcc/cp/search.cc | 12 +-
gcc/cp/semantics.cc | 29 +-
gcc/cp/tree.cc | 98 +-
gcc/cp/typeck.cc | 117 +-
gcc/cp/typeck2.cc | 11 +-
gcc/dwarf2out.cc | 46 +-
gcc/fold-const.cc | 2 +-
gcc/gimple-lower-bitint.cc | 29 +-
gcc/gimplify.cc | 11 +-
gcc/ipa-free-lang-data.cc | 6 +-
gcc/langhooks-def.h | 2 +-
gcc/langhooks.cc | 2 +-
gcc/langhooks.h | 2 +-
gcc/omp-offload.cc | 12 +-
gcc/tree-core.h | 68 +-
gcc/tree-dump.cc | 2 +-
gcc/tree-inline.cc | 4 +-
gcc/tree-pretty-print.cc | 8 +-
gcc/tree-profile.cc | 3 +-
gcc/tree-sra.cc | 4 +-
gcc/tree-ssa-address.cc | 3 +-
gcc/tree-switch-conversion.cc | 4 +-
gcc/tree-vect-stmts.cc | 3 +-
gcc/tree.cc | 76 +-
gcc/tree.h | 253 +++-
gcc/ubsan.cc | 4 +-
gcc/util/expected.h | 2439 ++++++++++++++++++++++++++++++
gcc/util/expected_fwd.h | 24 +
gcc/util/optional.h | 2131 ++++++++++++++++++++++++++
gcc/util/optional_fwd.h | 24 +
gcc/vtable-verify.cc | 10 +-
59 files changed, 5693 insertions(+), 473 deletions(-)
create mode 100644 gcc/util/expected.h
create mode 100644 gcc/util/expected_fwd.h
create mode 100644 gcc/util/optional.h
create mode 100644 gcc/util/optional_fwd.h
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..e109ec688a55 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))
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index a16288f4441c..e4b3ab0ff68c 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,
+ remove_quals_from (quals, 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,7 +8006,7 @@ 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)
{
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 5711e1740498..ee90d2753de3 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -897,7 +897,12 @@ 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);
+/* This function can never do anything with the address space, so permit the
+ downcast from qualifier_set to cv_qualifier on it. */
+inline void
+c_apply_type_quals_to_decl (qualifier_set quals, tree decl)
+{ c_apply_type_quals_to_decl (get_quals_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 +981,14 @@ 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);
+inline tree
+c_build_qualified_type (tree type, cv_qualifier type_quals,
+ tree orig_qual_type = NULL_TREE,
+ size_t orig_qual_indirect = 0)
+{ return c_build_qualified_type(type, build_qualifier_set (type_quals),
+ orig_qual_type, orig_qual_indirect); }
/* 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 1b2f2b4edece..bc47581193b2 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -2320,13 +2320,13 @@ 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 = get_quals_addr_space (new_quals);
+ addr_space_t old_addr = get_quals_addr_space (old_quals);
if (new_addr != old_addr)
{
if (ADDR_SPACE_GENERIC_P (new_addr))
@@ -2345,8 +2345,8 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl,
newdecl);
}
- if (CLEAR_QUAL_ADDR_SPACE (new_quals)
- != CLEAR_QUAL_ADDR_SPACE (old_quals))
+ if (get_quals_cv (new_quals)
+ != get_quals_cv (old_quals))
error ("conflicting type qualifiers for %q+D", newdecl);
}
else
@@ -5439,14 +5439,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
@@ -5464,7 +5463,7 @@ quals_from_declspecs (const struct c_declspecs *specs)
&& !specs->inline_p
&& !specs->noreturn_p
&& !specs->thread_p);
- return quals;
+ return build_qualifier_set (cv_quals, specs->address_space);
}
/* Construct an array declarator. LOC is the location of the
@@ -5496,7 +5495,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;
@@ -6879,14 +6878,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;
@@ -7093,16 +7092,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 =
+ build_qualifier_set ((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;
@@ -7251,7 +7251,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)
{
@@ -7259,7 +7259,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;
}
@@ -7580,10 +7580,11 @@ 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 = get_quals_addr_space (type_quals);
if (!ADDR_SPACE_GENERIC_P (as) && as != TYPE_ADDR_SPACE (type))
type = c_build_qualified_type (type,
- ENCODE_QUAL_ADDR_SPACE (as));
+ build_qualifier_set
+ (TYPE_UNQUALIFIED, as));
if (array_parm_vla_unspec_p)
type = c_build_array_type_unspecified (type);
else
@@ -7625,13 +7626,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;
}
@@ -7690,7 +7691,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[] =
{
@@ -7715,12 +7716,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 = build_qualifier_set (quals_used & 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");
@@ -7739,7 +7742,7 @@ grokdeclarator (const struct c_declarator *declarator,
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);
@@ -7766,13 +7769,13 @@ grokdeclarator (const struct c_declarator *declarator,
{
error_at (loc,
"%<_Atomic%>-qualified function type");
- type_quals &= ~TYPE_QUAL_ATOMIC;
+ type_quals = remove_quals_from (type_quals, 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;
@@ -7816,7 +7819,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 = get_quals_addr_space (type_quals);
if (!ADDR_SPACE_GENERIC_P (address_space))
{
if (decl_context == NORMAL)
@@ -7880,7 +7883,7 @@ grokdeclarator (const struct c_declarator *declarator,
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 = remove_quals_from (type_quals, TYPE_QUAL_ATOMIC);
}
}
@@ -7934,13 +7937,13 @@ grokdeclarator (const struct c_declarator *declarator,
{
error_at (loc,
"%<_Atomic%>-qualified function type");
- type_quals &= ~TYPE_QUAL_ATOMIC;
+ type_quals = remove_quals_from (type_quals, 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,
@@ -7988,13 +7991,13 @@ grokdeclarator (const struct c_declarator *declarator,
{
error_at (loc,
"%<_Atomic%>-qualified function type");
- type_quals &= ~TYPE_QUAL_ATOMIC;
+ type_quals = remove_quals_from (type_quals, 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;
@@ -8054,7 +8057,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);
@@ -8069,7 +8072,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. */
@@ -8086,17 +8089,17 @@ grokdeclarator (const struct c_declarator *declarator,
{
error_at (loc,
"%<_Atomic%>-qualified function type");
- type_quals &= ~TYPE_QUAL_ATOMIC;
+ type_quals = remove_quals_from (type_quals, 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,
@@ -8207,9 +8210,11 @@ grokdeclarator (const struct c_declarator *declarator,
{
error_at (loc,
"%<_Atomic%>-qualified function type");
- type_quals &= ~TYPE_QUAL_ATOMIC;
+ type_quals = remove_quals_from (type_quals, 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");
@@ -8644,7 +8649,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");
@@ -9452,12 +9457,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
+ && TYPE_QUALS (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
{
@@ -12050,7 +12056,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..8206119ce912 100644
--- a/gcc/c/c-objc-common.cc
+++ b/gcc/c/c-objc-common.cc
@@ -385,7 +385,22 @@ 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);
+ /* We can't ensure that the caller uses the right type here, so we hope
+ that they'll pass either a full qualifier set or just CV-qualifiers.
+
+ This is because previously, they'd have used
+ TYPE_QUALS{,_NO_ADDR_SPACE{,_NO_ATOMIC}}, which used to produce 'int',
+ but now produce either one of those types. Both are
+ fixed-underlying-type enums of unsigned char/int.
+
+ Therefore: one of those types is promoted to an int from unsigned
+ char, meaning all its values are representable as unsigned, and the
+ other is promoted to unsigned int. */
+ static_assert (sizeof (cv_qualifier) < sizeof (unsigned));
+ static_assert (sizeof (qualifier_set) <= sizeof (unsigned));
+ pp_c_cv_qualifiers (cpp,
+ cv_qualifier (va_arg (*text->m_args_ptr, unsigned)),
+ hash);
return true;
default:
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index b532dcb8a1a1..cf7ea19e4dac 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,8 +4833,8 @@ 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 & ~TYPE_QUAL_ATOMIC : get_quals_cv (quals))
!= TYPE_UNQUALIFIED)
{
ret.spec = TYPE_MAIN_VARIANT (ret.spec);
@@ -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)));
}
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 27784a2ffbad..903d33856dd9 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_BITFIELD 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 c8127cc1f053..9a2c1b1aa3be 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,10 @@ 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));
+ build_qualifier_set
+ (TYPE_QUALS_NO_ADDR_SPACE (type)
+ | TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC (like),
+ as_common));
}
@@ -489,7 +490,7 @@ 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. */
@@ -518,8 +519,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);
@@ -886,7 +887,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 +1109,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 +1144,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 = build_qualifier_set (quals1 & quals2);
else
- target_quals = (quals1 | quals2);
+ target_quals = build_qualifier_set (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 +1156,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_quals_addr_space (target_quals, as_common);
t1 = c_build_pointer_type (c_build_qualified_type (target, target_quals));
return c_build_type_attribute_variant (t1, attributes);
@@ -1181,10 +1182,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)
@@ -2638,7 +2639,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 +3378,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 +3394,12 @@ 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));
+ {
+ /* We expect the address-space qualifier to match. */
+ gcc_assert (TYPE_ADDR_SPACE (TREE_TYPE (datum))
+ == get_quals_addr_space (quals));
+ quals |= TYPE_QUALS_NO_ADDR_SPACE (TREE_TYPE (datum));
+ }
subtype = c_build_qualified_type (TREE_TYPE (subdatum), quals);
ref = build3 (COMPONENT_REF, subtype, datum, subdatum,
@@ -4388,7 +4394,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 +4413,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 +4450,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 +6212,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 +6226,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 +6272,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 +6895,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)))
+ && !quals_includes_p (TYPE_QUALS (t1), TYPE_QUALS (t2_stripped)))
{
if (!flag_isoc23)
warning_at (colon_loc, OPT_Wdiscarded_array_qualifiers,
@@ -6915,7 +6920,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);
+ auto qual = build_qualifier_set (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 +7315,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 (!quals_includes_p (TYPE_QUALS (in_otype), TYPE_QUALS (in_type))
&& !is_const)
{
warning_at (loc, OPT_Wcast_qual,
@@ -8758,7 +8763,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 +8778,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 +9042,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 +9086,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 +9155,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 +14951,7 @@ build_binary_op (location_t location, enum tree_code code,
if (result_type == NULL_TREE)
{
- int qual = ENCODE_QUAL_ADDR_SPACE (as_common);
+ auto qual = build_qualifier_set (TYPE_UNQUALIFIED, as_common);
result_type = c_build_pointer_type
(c_build_qualified_type (void_type_node, qual));
}
@@ -15075,7 +15085,7 @@ build_binary_op (location_t location, enum tree_code code,
}
else
{
- int qual = ENCODE_QUAL_ADDR_SPACE (as_common);
+ auto qual = build_qualifier_set (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 +18666,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;
@@ -18723,7 +18733,7 @@ c_build_qualified_type (tree type, int type_quals, tree orig_qual_type,
|| !C_TYPE_OBJECT_OR_INCOMPLETE_P (TREE_TYPE (type))))
{
error ("invalid use of %<restrict%>");
- type_quals &= ~TYPE_QUAL_RESTRICT;
+ type_quals = remove_quals_from (type_quals, TYPE_QUAL_RESTRICT);
}
tree var_type = (orig_qual_type && orig_qual_indirect == 0
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..7b0ad58e95f9 100644
--- a/gcc/config/i386/i386.cc
+++ b/gcc/config/i386/i386.cc
@@ -25503,7 +25503,8 @@ 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);
+ auto qual = build_qualifier_set (TYPE_UNQUALIFIED,
+ ix86_stack_protector_guard_reg);
tree type = build_qualified_type (type_node, qual);
tree t;
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index dc77a8876784..c9952240bdab 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see
/* High-level class interface. */
#include "config.h"
+#define INCLUDE_FUNCTIONAL // for optional.h
#include "system.h"
#include "coretypes.h"
#include "target.h"
@@ -47,6 +48,8 @@ along with GCC; see the file COPYING3. If not see
#include "tree-pretty-print-markup.h"
#include "contracts.h" // maybe_contract_wrap_call
+#include "util/optional.h"
+
/* The various kinds of conversion. */
enum conversion_kind {
@@ -1277,7 +1280,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, qualifier_set {});
}
/* Returns the standard conversion path (see [conv]) from type FROM to type
@@ -1428,10 +1431,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) != qualifier_set {})
to_pointee = build_qualified_type (to_pointee, TYPE_UNQUALIFIED);
if (TREE_CODE (from_pointee) == FUNCTION_TYPE
- && TYPE_QUALS (from_pointee))
+ && TYPE_QUALS (from_pointee) != qualifier_set {})
from_pointee = build_qualified_type (from_pointee, TYPE_UNQUALIFIED);
}
else
@@ -1450,7 +1453,8 @@ 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 = remove_quals_from (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);
@@ -1548,7 +1552,16 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
{
from = build_memfn_type (fstat,
tbase,
- cp_type_quals (tbase),
+ /* XXX: Is it OK to drop the address-space
+ qualifier here? Not sure how METHOD_TYPE
+ should relate to those. This is close
+ enough for this RFC, anyway.
+
+ It demonstrates that the goal of the
+ patch was achieved: it forced me to
+ check many places for address-space
+ handling. */
+ get_quals_cv (cp_type_quals (tbase)),
type_memfn_rqual (tofn));
from = build_ptrmemfunc_type (build_pointer_type (from));
conv = build_conv (ck_pmem, from, conv);
@@ -6266,10 +6279,15 @@ build_conditional_expr (const op_location_t &loc,
if (converted
&& CLASS_TYPE_P (arg2_type)
&& cp_type_quals (arg2_type) != cp_type_quals (arg3_type))
- arg2_type = arg3_type =
- cp_build_qualified_type (arg2_type,
- cp_type_quals (arg2_type)
- | cp_type_quals (arg3_type));
+ {
+ auto common_quals = cp_try_quals_merge (loc, arg2_type, arg3_type,
+ complain);
+ if (!common_quals)
+ return error_mark_node;
+
+ arg2_type = arg3_type =
+ cp_build_qualified_type (arg2_type, *common_quals);
+ }
}
/* [expr.cond]
@@ -13098,13 +13116,13 @@ compare_ics (conversion *ics1, conversion *ics2)
else if (!c1 && c2)
return 1;
- int q1 = cp_type_quals (TREE_TYPE (ref_conv1->type));
- int q2 = cp_type_quals (TREE_TYPE (ref_conv2->type));
+ auto q1 = cp_type_quals (TREE_TYPE (ref_conv1->type));
+ auto q2 = cp_type_quals (TREE_TYPE (ref_conv2->type));
if (ref_conv1->bad_p)
{
/* Prefer the one that drops fewer cv-quals. */
tree ftype = next_conversion (ref_conv1)->type;
- int fquals = cp_type_quals (ftype);
+ auto fquals = get_quals_cv (cp_type_quals (ftype));
q1 ^= fquals;
q2 ^= fquals;
}
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 2fcaa6cd81bd..631e967631cb 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),
@@ -1120,9 +1120,9 @@ iobj_parm_corresponds_to (tree iobj_fn, tree xobj_param, tree context)
cp_ref_qualifier const iobj_ref_qual = type_memfn_rqual (iobj_fn_type);
/* We only care about cv qualifiers when determining correspondence. */
- static constexpr cp_cv_quals cv_bits = TYPE_QUAL_VOLATILE
- | TYPE_QUAL_CONST;
- cp_cv_quals const iobj_cv_quals = type_memfn_quals (iobj_fn_type) & cv_bits;
+ static constexpr cv_qualifier cv_bits = (TYPE_QUAL_VOLATILE
+ | TYPE_QUAL_CONST);
+ const auto iobj_cv_quals = type_memfn_quals (iobj_fn_type) & cv_bits;
/* We need to ignore the ref qualifier of the xobj parameter if the iobj
member function lacks a ref qualifier.
@@ -1190,7 +1190,7 @@ iobj_parm_corresponds_to (tree iobj_fn, tree xobj_param, tree context)
/* Even if we are ignoring the reference qualifier, the xobj parameter
was still a reference so we still take the cv qualifiers into
account. */
- cp_cv_quals const xobj_cv_quals
+ auto xobj_cv_quals
= cp_type_quals (TREE_TYPE (xobj_param)) & cv_bits;
/* Finally, if the qualifications don't match exactly, the object
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 76ed1e59dec3..436eaaba31e8 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -26,6 +26,8 @@ along with GCC; see the file COPYING3. If not see
#include "function.h"
#include "tristate.h"
+#include "util/optional_fwd.h"
+
/* In order for the format checking to accept the C++ front end
diagnostic framework extensions, you must include this file before
diagnostic-core.h, not after. We override the definition of GCC_DIAG_STYLE
@@ -6890,7 +6892,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 +8008,10 @@ 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, qualifier_set, bool);
+inline tree
+build_stub_type (tree type, cv_qualifier quals, bool rvalue)
+{ return build_stub_type (type, build_qualifier_set(quals), rvalue); }
extern tree build_stub_object (tree);
extern bool is_stub_object (tree);
extern tree build_invoke (tree, const_tree,
@@ -8157,7 +8162,10 @@ extern void cp_finish_omp_range_for (tree, tree);
extern bool cp_maybe_parse_omp_decl (tree, tree);
extern bool parsing_nsdmi (void);
extern bool parsing_function_declarator ();
-extern void inject_this_parameter (tree, cp_cv_quals);
+extern void inject_this_parameter (tree, qualifier_set);
+inline void
+inject_this_parameter (tree ctype, cv_qualifier quals)
+{ inject_this_parameter (ctype, build_qualifier_set (quals)); }
extern location_t defparse_location (tree);
extern void maybe_show_extern_c_location (void);
extern bool literal_integer_zerop (const_tree);
@@ -8866,8 +8874,19 @@ 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, qualifier_set,
tsubst_flags_t = tf_warning_or_error);
+inline tree
+cp_build_qualified_type (tree tree,
+ cv_qualifier cv_quals,
+ tsubst_flags_t complain = tf_warning_or_error)
+{
+ return cp_build_qualified_type (tree,
+ build_qualifier_set (cv_quals),
+ complain);
+}
+extern tl::optional<qualifier_set> cp_try_quals_merge (location_t, tree, tree,
+ tsubst_flags_t = tf_warning_or_error);
extern tree cp_build_function_type (tree, tree);
extern bool cv_qualified_p (const_tree);
extern tree cv_unqualified (tree);
@@ -8932,7 +8951,7 @@ extern bool next_common_initial_sequence (tree &, tree &);
extern bool layout_compatible_type_p (tree, tree, bool = false);
extern bool compparms (const_tree, const_tree);
extern int comp_cv_qualification (const_tree, const_tree);
-extern int comp_cv_qualification (int, int);
+extern int comp_cv_qualification (qualifier_set, qualifier_set);
extern int comp_cv_qual_signature (tree, tree);
extern tree cxx_sizeof_or_alignof_expr (location_t, tree,
enum tree_code, bool, bool);
@@ -9029,14 +9048,17 @@ 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 qualifier_set 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);
+inline void
+cp_apply_type_quals_to_decl (qualifier_set quals, tree decl)
+{ cp_apply_type_quals_to_decl (get_quals_cv (quals), decl); }
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 dbe4fa44ce52..282d3e782b10 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;
@@ -6277,6 +6277,7 @@ get_type_quals (const cp_decl_specifier_seq *declspecs)
type_quals |= TYPE_QUAL_VOLATILE;
if (decl_spec_seq_has_spec_p (declspecs, ds_restrict))
type_quals |= TYPE_QUAL_RESTRICT;
+ /* TODO(arsen): this will need to handle ds_addr_space soon. */
return type_quals;
}
@@ -12814,7 +12815,7 @@ grokvardecl (tree type,
tree orig_declarator,
const cp_decl_specifier_seq *declspecs,
int initialized,
- int type_quals,
+ qualifier_set type_quals,
int inlinep,
bool conceptp,
int template_count,
@@ -12992,7 +12993,8 @@ build_ptrmemfunc_type (tree type)
/* Make sure that we always have the unqualified pointer-to-member
type first. */
- if (cp_cv_quals quals = cp_type_quals (type))
+ auto quals = cp_type_quals (type);
+ if (quals != qualifier_set {})
{
tree unqual = build_ptrmemfunc_type (TYPE_MAIN_VARIANT (type));
return cp_build_qualified_type (unqual, quals);
@@ -13579,7 +13581,7 @@ smallest_type_quals_location (int type_quals, const location_t* locations)
/* Returns the smallest among the latter and locations[ds_type_spec]. */
static location_t
-smallest_type_location (int type_quals, const location_t* locations)
+smallest_type_location (cv_qualifier type_quals, const location_t* locations)
{
location_t loc = smallest_type_quals_location (type_quals, locations);
return min_location (loc, locations[ds_type_spec]);
@@ -13588,7 +13590,7 @@ smallest_type_location (int type_quals, const location_t* locations)
static location_t
smallest_type_location (const cp_decl_specifier_seq *declspecs)
{
- int type_quals = get_type_quals (declspecs);
+ auto type_quals = get_type_quals (declspecs);
return smallest_type_location (type_quals, declspecs->locations);
}
@@ -13620,7 +13622,7 @@ static tree
check_special_function_return_type (special_function_kind sfk,
tree type,
tree optype,
- int type_quals,
+ cv_qualifier type_quals,
const cp_declarator** declarator,
const location_t* locations)
{
@@ -13898,7 +13900,7 @@ check_decltype_auto (location_t loc, tree type)
"%<decltype(auto)%>", type);
return true;
}
- else if (TYPE_QUALS (type) != TYPE_UNQUALIFIED)
+ else if (TYPE_QUALS (type) != qualifier_set {})
{
error_at (loc, "%<decltype(auto)%> cannot be cv-qualified");
return true;
@@ -14016,7 +14018,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_cv_quals = get_type_quals (declspecs);
tree raises = NULL_TREE;
int template_count = 0;
tree returned_attrs = NULL_TREE;
@@ -14080,7 +14082,7 @@ grokdeclarator (const cp_declarator *declarator,
funcdef_flag = true;
location_t typespec_loc = loc_or_input_loc (smallest_type_location
- (type_quals,
+ (type_cv_quals,
declspecs->locations));
location_t id_loc;
location_t init_loc;
@@ -14452,10 +14454,10 @@ grokdeclarator (const cp_declarator *declarator,
{
type = check_special_function_return_type (sfk, type,
ctor_return_type,
- type_quals,
+ type_cv_quals,
&declarator,
declspecs->locations);
- type_quals = TYPE_UNQUALIFIED;
+ type_cv_quals = TYPE_UNQUALIFIED;
}
else if (type == NULL_TREE)
{
@@ -14683,10 +14685,12 @@ grokdeclarator (const cp_declarator *declarator,
if (CLASS_TYPE_P (type)
&& DECL_SELF_REFERENCE_P (TYPE_NAME (type))
&& type == TREE_TYPE (TYPE_NAME (type))
- && (declarator || type_quals))
+ && (declarator || type_cv_quals))
type = DECL_ORIGINAL_TYPE (TYPE_NAME (type));
- type_quals |= cp_type_quals (type);
+ auto old_quals = cp_type_quals (type);
+ auto type_quals = add_quals_to(cp_type_quals (type), type_cv_quals);
+
type = cp_build_qualified_type
(type, type_quals, ((((typedef_decl && !DECL_ARTIFICIAL (typedef_decl))
|| declspecs->decltype_p)
@@ -15359,7 +15363,7 @@ grokdeclarator (const cp_declarator *declarator,
type_quals = cp_type_quals (type);
}
- if (type_quals != TYPE_UNQUALIFIED)
+ if (type_quals != qualifier_set {})
{
/* It's wrong, for instance, to issue a -Wignored-qualifiers
warning for
@@ -15377,7 +15381,7 @@ grokdeclarator (const cp_declarator *declarator,
/* We now know that the TYPE_QUALS don't apply to the
decl, but to its return type. */
- type_quals = TYPE_UNQUALIFIED;
+ type_quals = qualifier_set {};
}
/* Error about some types functions can't return. */
@@ -15656,7 +15660,7 @@ grokdeclarator (const cp_declarator *declarator,
/* We now know that the TYPE_QUALS don't apply to the decl,
but to the target of the pointer. */
- type_quals = TYPE_UNQUALIFIED;
+ type_quals = qualifier_set {};
/* This code used to handle METHOD_TYPE, but I don't think it's
possible to get it here anymore. */
@@ -16128,7 +16132,7 @@ grokdeclarator (const cp_declarator *declarator,
|| enum_with_enumerator_for_linkage_p (type))
&& declspecs->type_definition_p
&& attributes_naming_typedef_ok (*attrlist)
- && cp_type_quals (type) == TYPE_UNQUALIFIED)
+ && cp_type_quals (type) == qualifier_set {})
name_unnamed_type (type, decl);
if (signed_p
@@ -16189,7 +16193,7 @@ grokdeclarator (const cp_declarator *declarator,
the non-static member function. */
memfn_quals |= type_memfn_quals (type);
rqual = type_memfn_rqual (type);
- type_quals = TYPE_UNQUALIFIED;
+ type_quals = qualifier_set {};
raises = TYPE_RAISES_EXCEPTIONS (type);
}
}
@@ -16350,7 +16354,7 @@ grokdeclarator (const cp_declarator *declarator,
{
/* Transfer const-ness of array into that of type pointed to. */
type = build_pointer_type (TREE_TYPE (type));
- type_quals = TYPE_UNQUALIFIED;
+ type_quals = qualifier_set {};
array_parameter_p = true;
}
else if (TREE_CODE (type) == FUNCTION_TYPE)
@@ -17439,7 +17443,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, qualifier_set {});
if (TREE_CODE (type) == METHOD_TYPE)
{
error ("parameter %qD invalidly declared method type", decl);
@@ -17866,7 +17870,7 @@ grok_op_properties (tree decl, bool complain)
if (TYPE_REF_P (t)
&& !TYPE_REF_IS_RVALUE (t)
&& (t = TREE_TYPE (t))
- && TYPE_QUALS (t) == TYPE_QUAL_CONST
+ && TYPE_QUALS (t) == build_qualifier_set (TYPE_QUAL_CONST)
&& is_std_class (t, "nothrow_t"))
DECL_IS_REPLACEABLE_OPERATOR (decl) = 1;
}
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 6e84b58e0b7f..2bce0eac283f 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1636,7 +1636,7 @@ grokbitfield (const cp_declarator *declarator,
/* [class.bit]/2 "An unnamed bit-field shall not be declared with
a cv-qualified type." */
- if (!DECL_NAME (value) && TYPE_QUALS (type) != TYPE_UNQUALIFIED)
+ if (!DECL_NAME (value) && TYPE_QUALS (type) != qualifier_set {})
pedwarn (DECL_SOURCE_LOCATION (value), 0,
"unnamed bit-field cannot be cv-qualified");
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index 8c67b67b1974..7fce357fd5db 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -665,7 +665,7 @@ find_substitution (tree node)
std::basic_string <char,
std::char_traits<char>,
std::allocator<char> > . */
- if (cp_type_quals (type) == TYPE_UNQUALIFIED
+ if (cp_type_quals (type) == qualifier_set {}
&& CLASSTYPE_USE_TEMPLATE (type))
{
tree args = CLASSTYPE_TI_ARGS (type);
@@ -685,7 +685,7 @@ find_substitution (tree node)
/* Check for basic_{i,o,io}stream. */
else if (TYPE_P (node)
- && cp_type_quals (type) == TYPE_UNQUALIFIED
+ && cp_type_quals (type) == qualifier_set {}
&& CLASS_TYPE_P (type)
&& CLASSTYPE_USE_TEMPLATE (type)
&& CLASSTYPE_TEMPLATE_INFO (type) != NULL)
@@ -2621,7 +2621,7 @@ write_type (tree type)
if (TREE_CODE (target) == FUNCTION_TYPE)
{
if (abi_warn_or_compat_version_crosses (5)
- && TYPE_QUALS (target) != TYPE_UNQUALIFIED)
+ && TYPE_QUALS (target) != qualifier_set {})
G.need_abi_warning = 1;
if (abi_version_at_least (5))
target = build_qualified_type (target, TYPE_UNQUALIFIED);
@@ -2892,8 +2892,16 @@ 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);
+ auto quals = TYPE_QUALS (type);
+ if (addr_space_t as = get_quals_addr_space (quals))
+ {
+ const char *as_name = c_addr_space_name (as);
+ write_char ('U');
+ write_unsigned_number (strlen (as_name));
+ write_string (as_name);
+ ++num_qualifiers;
+ }
if (quals & TYPE_QUAL_RESTRICT)
{
write_char ('r');
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index 4c432efb56ae..1a2d8fee74b0 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,11 +767,14 @@ 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;
- quals |= cp_type_quals (expr_type);
+ quals = remove_quals_from (quals, TYPE_QUAL_CONST);
+ auto expr_quals = cp_type_quals (expr_type);
+ /* TODO(arsen): handle address spaces. */
+ gcc_assert (has_only_cv_quals_p (expr_quals));
+ quals |= get_quals_cv (expr_quals);
expr_type = cp_build_qualified_type (expr_type, quals);
}
@@ -820,7 +823,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 +859,7 @@ do_build_copy_assign (tree fndecl)
tree init = parm;
tree field = fields;
tree expr_type;
- int quals;
+ qualifier_set quals;
if (TREE_CODE (field) != FIELD_DECL || DECL_ARTIFICIAL (field))
continue;
@@ -894,7 +897,7 @@ do_build_copy_assign (tree fndecl)
/* Compute the type of init->field */
quals = cvquals;
if (DECL_MUTABLE_P (field))
- quals &= ~TYPE_QUAL_CONST;
+ quals = remove_quals_from (quals, TYPE_QUAL_CONST);
expr_type = cp_build_qualified_type (expr_type, quals);
init = build3 (COMPONENT_REF, expr_type, init, field, NULL_TREE);
@@ -1222,7 +1225,8 @@ early_check_defaulted_comparison (tree fn)
saw_byval = true;
else if (TREE_CODE (parmtype) == REFERENCE_TYPE
&& !TYPE_REF_IS_RVALUE (parmtype)
- && TYPE_QUALS (TREE_TYPE (parmtype)) == TYPE_QUAL_CONST)
+ && (TYPE_QUALS (TREE_TYPE (parmtype))
+ == build_qualifier_set (TYPE_QUAL_CONST)))
{
saw_byref = true;
parmtype = TREE_TYPE (parmtype);
@@ -1896,11 +1900,11 @@ maybe_synthesize_method (tree fndecl)
return synthesize_method (fndecl);
}
-/* Build a reference to type TYPE with cv-quals QUALS, which is an
- rvalue if RVALUE is true. */
+/* Build a reference to type TYPE with qualifiers QUALS, which is an rvalue if
+ RVALUE is true. */
tree
-build_stub_type (tree type, int quals, bool rvalue)
+build_stub_type (tree type, qualifier_set quals, bool rvalue)
{
tree argtype
= cp_build_qualified_type (type, quals,
@@ -2196,8 +2200,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 +2215,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 +2683,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,9 +2849,9 @@ 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;
+ mem_quals = remove_quals_from (mem_quals, TYPE_QUAL_CONST);
argtype = build_stub_type (mem_type, mem_quals, SFK_MOVE_P (sfk));
}
else
@@ -2926,9 +2930,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 +3122,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 +3523,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..d3de1bb6caf0 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -9664,11 +9664,15 @@ trees_out::type_node (tree type)
if (streaming_p ())
{
/* Qualifiers. */
- int rquals = cp_type_quals (root);
- int quals = cp_type_quals (type);
+ auto rquals = cp_type_quals (root);
+ auto quals = cp_type_quals (type);
+ /* TODO(arsen) */
+ gcc_assert (has_only_cv_quals_p (rquals));
+ gcc_assert (has_only_cv_quals_p (quals));
if (quals == rquals)
- quals = -1;
- i (quals);
+ i (-1);
+ else
+ i (int {get_quals_cv (quals)});
}
if (ref_node (type) != WK_none)
@@ -10793,7 +10797,7 @@ 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, (cv_qualifier) quals);
int tag = i ();
if (!tag)
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 8194106c6e93..f025f38641c2 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -27636,7 +27636,7 @@ cp_parser_virt_specifier_seq_opt (cp_parser* parser)
is in scope even though it isn't real. */
void
-inject_this_parameter (tree ctype, cp_cv_quals quals)
+inject_this_parameter (tree ctype, qualifier_set quals)
{
tree this_parm;
@@ -27651,7 +27651,9 @@ inject_this_parameter (tree ctype, cp_cv_quals quals)
return;
}
- this_parm = build_this_parm (NULL_TREE, ctype, quals);
+ /* TODO(arsen) */
+ gcc_assert (has_only_cv_quals_p (quals));
+ this_parm = build_this_parm (NULL_TREE, ctype, get_quals_cv (quals));
/* Clear this first to avoid shortcut in cp_build_indirect_ref. */
current_class_ptr = NULL_TREE;
current_class_ref
@@ -30113,7 +30115,7 @@ cp_parser_class_specifier (cp_parser* parser)
{
tree ctx = type_context_for_name_lookup (decl);
switch_to_class (ctx);
- inject_this_parameter (class_type, TYPE_UNQUALIFIED);
+ inject_this_parameter (class_type, qualifier_set {});
cp_parser_late_parsing_nsdmi (parser, decl);
}
vec_safe_truncate (unparsed_nsdmis, 0);
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index f7aa10226801..daadbcd4713f 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -28,6 +28,7 @@ along with GCC; see the file COPYING3. If not see
#include "config.h"
#define INCLUDE_ALGORITHM // for std::equal
+#define INCLUDE_FUNCTIONAL // for optional.h
#include "system.h"
#include "coretypes.h"
#include "cp-tree.h"
@@ -51,6 +52,8 @@ along with GCC; see the file COPYING3. If not see
#include "pretty-print-markup.h"
#include "contracts.h"
+#include "util/optional.h"
+
/* The type of functions taking a tree, and some additional data, and
returning an int. */
typedef int (*tree_fn_t) (tree, void*);
@@ -14618,8 +14621,13 @@ tsubst_pack_index (tree t, tree args, tsubst_flags_t complain, tree in_decl)
else
r = make_pack_index (pack, index);
if (TREE_CODE (t) == PACK_INDEX_TYPE)
- r = cp_build_qualified_type (r, cp_type_quals (t) | cp_type_quals (r),
- complain | tf_ignore_bad_quals);
+ {
+ auto merged_quals = cp_try_quals_merge (input_location, t, r, complain);
+ if (!merged_quals)
+ return error_mark_node;
+ r = cp_build_qualified_type (r, *merged_quals,
+ complain | tf_ignore_bad_quals);
+ }
return r;
}
@@ -17018,8 +17026,14 @@ tsubst_splice_scope (tree t, tree args, tsubst_flags_t complain, tree in_decl)
}
if (type_p)
- r = cp_build_qualified_type (r, cp_type_quals (t) | cp_type_quals (r),
- complain | tf_ignore_bad_quals);
+ {
+ auto merged_quals = cp_try_quals_merge (input_location, t, r, complain);
+ if (!merged_quals)
+ return error_mark_node;
+
+ r = cp_build_qualified_type (r, *merged_quals,
+ complain | tf_ignore_bad_quals);
+ }
return r;
}
@@ -17228,15 +17242,18 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
if (r)
{
r = TREE_TYPE (r);
- r = cp_build_qualified_type
- (r, cp_type_quals (t) | cp_type_quals (r),
- complain | tf_ignore_bad_quals);
+ auto merged_quals = cp_try_quals_merge (input_location, t, r,
+ complain);
+ if (!merged_quals)
+ return error_mark_node;
+ r = cp_build_qualified_type (r, *merged_quals,
+ complain | tf_ignore_bad_quals);
return r;
}
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,14 +17435,15 @@ 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 merged_quals = cp_try_quals_merge (input_location, arg, t,
+ complain);
+ if (!merged_quals)
+ return error_mark_node;
- return cp_build_qualified_type
- (arg, quals, complain | tf_ignore_bad_quals);
+ return cp_build_qualified_type (arg, *merged_quals,
+ complain | tf_ignore_bad_quals);
}
else if (code == BOUND_TEMPLATE_TEMPLATE_PARM)
{
@@ -17488,8 +17506,12 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
argvec, in_decl,
DECL_CONTEXT (arg),
complain);
- return cp_build_qualified_type
- (r, cp_type_quals (t) | cp_type_quals (r), complain);
+ auto merged_quals = cp_try_quals_merge (input_location, t, r,
+ complain);
+ if (!merged_quals)
+ return error_mark_node;
+
+ return cp_build_qualified_type (r, *merged_quals, complain);
}
else if (code == TEMPLATE_TEMPLATE_PARM)
return arg;
@@ -17515,13 +17537,13 @@ 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;
+ qualifier_set quals;
switch (code)
{
case TEMPLATE_TYPE_PARM:
case TEMPLATE_TEMPLATE_PARM:
quals = cp_type_quals (t);
- if (quals)
+ if (quals != qualifier_set {})
{
gcc_checking_assert (code == TEMPLATE_TYPE_PARM);
t = TYPE_MAIN_VARIANT (t);
@@ -17558,7 +17580,7 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
TYPE_CANONICAL (r) = canonical_type_parameter (r);
}
- if (quals)
+ if (quals != qualifier_set {})
r = cp_build_qualified_type (r, quals,
complain | tf_ignore_bad_quals);
break;
@@ -17895,8 +17917,11 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
f = TREE_TYPE (decl);
}
}
- return cp_build_qualified_type
- (f, cp_type_quals (f) | cp_type_quals (t), complain);
+ auto merged_quals = cp_try_quals_merge (input_location, f, t,
+ complain);
+ if (!merged_quals)
+ return error_mark_node;
+ return cp_build_qualified_type (f, *merged_quals, complain);
}
if (!MAYBE_CLASS_TYPE_P (ctx))
@@ -17960,8 +17985,11 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
}
}
- return cp_build_qualified_type
- (f, cp_type_quals (f) | cp_type_quals (t), complain);
+ auto merged_quals = cp_try_quals_merge(input_location, f, t, complain);
+ if (!merged_quals)
+ return error_mark_node;
+
+ return cp_build_qualified_type (f, *merged_quals, complain);
}
case UNBOUND_CLASS_TEMPLATE:
@@ -18000,10 +18028,11 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
--c_inhibit_evaluation_warnings;
type = finish_typeof (type);
- return cp_build_qualified_type (type,
- cp_type_quals (t)
- | cp_type_quals (type),
- complain);
+ auto merged_quals = cp_try_quals_merge (input_location, t, type,
+ complain);
+ if (!merged_quals)
+ return error_mark_node;
+ return cp_build_qualified_type (type, *merged_quals, complain);
}
case DECLTYPE_TYPE:
@@ -18037,9 +18066,12 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
id = false;
type = finish_decltype_type (type, id, complain);
}
- return cp_build_qualified_type (type,
- cp_type_quals (t)
- | cp_type_quals (type),
+ auto maybe_quals = cp_try_quals_merge (input_location, t, type,
+ complain);
+ if (!maybe_quals)
+ return error_mark_node;
+
+ return cp_build_qualified_type (type, *maybe_quals,
complain | tf_ignore_bad_quals);
}
@@ -18052,8 +18084,11 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
type1 = tsubst_expr (type1, args, complain, in_decl);
tree type2 = tsubst (TRAIT_TYPE_TYPE2 (t), args, complain, in_decl);
type = finish_trait_type (TRAIT_TYPE_KIND (t), type1, type2, complain);
- return cp_build_qualified_type (type,
- cp_type_quals (t) | cp_type_quals (type),
+ auto maybe_quals = cp_try_quals_merge (input_location, t, type,
+ complain);
+ if (!maybe_quals)
+ return error_mark_node;
+ return cp_build_qualified_type (type, *maybe_quals,
complain | tf_ignore_bad_quals);
}
@@ -24498,7 +24533,7 @@ forwarding_reference_p (tree parm, tree tmpl)
if (TYPE_REF_P (parm)
&& TYPE_REF_IS_RVALUE (parm)
&& TREE_CODE (TREE_TYPE (parm)) == TEMPLATE_TYPE_PARM
- && cp_type_quals (TREE_TYPE (parm)) == TYPE_UNQUALIFIED)
+ && cp_type_quals (TREE_TYPE (parm)) == qualifier_set {})
{
parm = TREE_TYPE (parm);
/* [temp.deduct.call], "... that does not represent a template parameter
@@ -25860,8 +25895,28 @@ template_decl_level (tree decl)
static int
check_cv_quals_for_unify (int strict, tree arg, tree parm)
{
- int arg_quals = cp_type_quals (arg);
- int parm_quals = cp_type_quals (parm);
+ auto arg_quals = cp_type_quals (arg);
+ auto parm_quals = cp_type_quals (parm);
+
+ auto arg_as = get_quals_addr_space (arg_quals);
+ auto parm_as = get_quals_addr_space (parm_quals);
+ if (arg_as == parm_as)
+ /* Always OK. */;
+ else if (!ADDR_SPACE_GENERIC_P (arg_as) && !ADDR_SPACE_GENERIC_P (parm_as))
+ /* While it is possible to unify different non-generic address spaces in a
+ case where there's a qualification conversion, we implement that case in
+ 'unify' in POINTER_TYPE handling. At this point, any difference that a
+ qualification conversion can cross has been removed. */
+ return false;
+ else if (!(strict & (UNIFY_ALLOW_MORE_CV_QUAL | UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
+ && ADDR_SPACE_GENERIC_P (arg_as))
+ /* ARG lacks an address space, but PARM has it. This means PARM is more
+ qualified, but we don't allow that. */
+ return false;
+ else if (!(strict & (UNIFY_ALLOW_LESS_CV_QUAL | UNIFY_ALLOW_OUTER_LESS_CV_QUAL))
+ && ADDR_SPACE_GENERIC_P (parm_as))
+ /* Conversely, ARG is more qualified. */
+ return false;
if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM
&& !(strict & UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
@@ -25883,11 +25938,11 @@ check_cv_quals_for_unify (int strict, tree arg, tree parm)
}
if (!(strict & (UNIFY_ALLOW_MORE_CV_QUAL | UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
- && (arg_quals & parm_quals) != parm_quals)
+ && !quals_includes_p (arg_quals, parm_quals))
return 0;
if (!(strict & (UNIFY_ALLOW_LESS_CV_QUAL | UNIFY_ALLOW_OUTER_LESS_CV_QUAL))
- && (parm_quals & arg_quals) != arg_quals)
+ && !quals_includes_p (parm_quals, arg_quals))
return 0;
return 1;
@@ -26507,9 +26562,21 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
return unify_cv_qual_mismatch (explain_p, parm, arg);
/* Consider the case where ARG is `const volatile int' and
- PARM is `const T'. Then, T should be `volatile int'. */
- arg = cp_build_qualified_type
- (arg, cp_type_quals (arg) & ~cp_type_quals (parm), tf_none);
+ PARM is `const T'. Then, T should be `volatile int'.
+
+ Similarly, for address spaces, due to check_cv_quals_for_unify,
+ either only one of PARM or ARG has an address space, or they are
+ equal. Ergo, if PARM has it, then T should lack it. */
+ auto arg_quals_p = split_quals (cp_type_quals (arg));
+ auto parm_quals_p = split_quals (cp_type_quals (arg));
+
+ cv_qualifier t_cvs = arg_quals_p.first & ~parm_quals_p.first;
+ auto t_as = (parm_quals_p.second
+ ? ADDR_SPACE_GENERIC
+ : parm_quals_p.second);
+
+ arg = cp_build_qualified_type (arg, build_qualifier_set (t_cvs, t_as),
+ tf_none);
if (arg == error_mark_node)
return unify_invalid (explain_p);
@@ -27350,8 +27417,8 @@ more_specialized_fn (tree pat1, tree pat2, int len)
tree arg1 = TREE_VALUE (args1);
tree arg2 = TREE_VALUE (args2);
int deduce1, deduce2;
- int quals1 = -1;
- int quals2 = -1;
+ tl::optional<qualifier_set> quals1 = tl::nullopt;
+ tl::optional<qualifier_set> quals2 = tl::nullopt;
int ref1 = 0;
int ref2 = 0;
@@ -27474,11 +27541,11 @@ more_specialized_fn (tree pat1, tree pat2, int len)
else
lose2 = true;
}
- else if (quals1 != quals2 && quals1 >= 0 && quals2 >= 0)
+ else if (quals1 != quals2 && quals1 && quals2)
{
- if ((quals1 & quals2) == quals2)
+ if (quals_includes_p (*quals1, *quals2))
lose2 = true;
- if ((quals1 & quals2) == quals1)
+ if (quals_includes_p (*quals2, *quals1))
lose1 = true;
}
}
@@ -31076,7 +31143,6 @@ resolve_typename_type (tree type, bool only_current_p)
tree scope;
tree name;
tree decl;
- int quals;
tree pushed_scope;
tree result;
@@ -31198,9 +31264,17 @@ resolve_typename_type (tree type, bool only_current_p)
}
/* Qualify the resulting type. */
- quals = cp_type_quals (type);
- if (quals)
- result = cp_build_qualified_type (result, cp_type_quals (result) | quals);
+ if (cp_type_quals (type) != qualifier_set {})
+ {
+ auto merged_quals = cp_try_quals_merge (cp_expr_loc_or_input_loc (type),
+ result, type, tf_none);
+ /* XXX: It's not immediately clear to me what we should do here should
+ merging qualifiers not be possible. This is probably wrong. */
+ if (!merged_quals)
+ return type;
+
+ result = cp_build_qualified_type (result, *merged_quals);
+ }
return result;
}
diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
index 3d6d24eb23ac..9600badd0473 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,8 +5234,8 @@ static tree
eval_remove_volatile (location_t loc, tree type)
{
type = strip_typedefs (type);
- int quals = cp_type_quals (type);
- quals &= ~TYPE_QUAL_VOLATILE;
+ auto quals = cp_type_quals (type);
+ quals = remove_quals_from (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,8 +5391,8 @@ 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);
- quals &= (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE);
+ auto quals = cp_type_quals (type);
+ quals = remove_quals_from (quals, TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE);
ret = cp_build_qualified_type (ret, quals);
}
else
diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc
index 7e6fa51936ab..3f8fcf54cfb0 100644
--- a/gcc/cp/rtti.cc
+++ b/gcc/cp/rtti.cc
@@ -859,7 +859,8 @@ static int
qualifier_flags (tree type)
{
int flags = 0;
- int quals = cp_type_quals (type);
+ auto quals = cp_type_quals (type);
+ gcc_assert (has_only_cv_quals_p (quals));
if (quals & TYPE_QUAL_CONST)
flags |= 1;
@@ -1127,8 +1128,8 @@ typeinfo_in_lib_p (tree type)
/* The typeinfo objects for `T*' and `const T*' are in the runtime
library for simple types T. */
if (TYPE_PTR_P (type)
- && (cp_type_quals (TREE_TYPE (type)) == TYPE_QUAL_CONST
- || cp_type_quals (TREE_TYPE (type)) == TYPE_UNQUALIFIED))
+ && (cp_type_quals (TREE_TYPE (type)) == build_qualifier_set (TYPE_QUAL_CONST)
+ || cp_type_quals (TREE_TYPE (type)) == qualifier_set {}))
type = TREE_TYPE (type);
switch (TREE_CODE (type))
diff --git a/gcc/cp/search.cc b/gcc/cp/search.cc
index a51fd1cd6a75..8d50217ef719 100644
--- a/gcc/cp/search.cc
+++ b/gcc/cp/search.cc
@@ -2043,9 +2043,6 @@ check_final_overrider (tree overrider, tree basefn)
|| (TREE_CODE (base_return) == TREE_CODE (over_return)
&& INDIRECT_TYPE_P (base_return)))
{
- /* Potentially covariant. */
- unsigned base_quals, over_quals;
-
fail = !INDIRECT_TYPE_P (base_return);
if (!fail)
{
@@ -2060,10 +2057,13 @@ check_final_overrider (tree overrider, tree basefn)
base_return = TREE_TYPE (base_return);
over_return = TREE_TYPE (over_return);
}
- base_quals = cp_type_quals (base_return);
- over_quals = cp_type_quals (over_return);
- if ((base_quals & over_quals) != over_quals)
+ /* XXX(arsen): It may make sense to assert that this only has
+ CV-quals. */
+ auto base_quals = cp_type_quals (base_return);
+ auto over_quals = cp_type_quals (over_return);
+
+ if (!quals_includes_p (base_quals, over_quals))
fail = 1;
if (CLASS_TYPE_P (base_return) && CLASS_TYPE_P (over_return))
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 3a7e19c190a4..766ad4719f82 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -2808,12 +2808,16 @@ 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;
+ quals = remove_quals_from (quals, TYPE_QUAL_CONST);
- quals |= cp_type_quals (TREE_TYPE (decl));
+ auto memb_quals = cp_type_quals (TREE_TYPE (decl));
+ /* It should not be possible to declare an address space on a
+ member. */
+ gcc_checking_assert (has_only_cv_quals_p (memb_quals));
+ quals |= get_quals_cv (memb_quals);
type = cp_build_qualified_type (type, quals);
}
@@ -4486,7 +4490,7 @@ finish_base_specifier (tree base, tree access, bool virtual_p,
}
else
{
- if (cp_type_quals (base) != 0)
+ if (cp_type_quals (base) != qualifier_set {})
{
/* DR 484: Can a base-specifier name a cv-qualified
class type? */
@@ -12840,7 +12844,7 @@ cexpr_str::type_check (location_t location, bool allow_char8_t /*=false*/)
&& (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (message_data)))
== char8_type_node)
&& (TYPE_QUALS (TREE_TYPE (TREE_TYPE (message_data)))
- == TYPE_QUAL_CONST))
+ == build_qualifier_set (TYPE_QUAL_CONST)))
return true;
message_data = build_converted_constant_expr (const_string_type_node,
@@ -13402,7 +13406,7 @@ finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
{
/* decltype of an NTTP object is the type of the template
parameter, which is the object type modulo cv-quals. */
- int quals = cp_type_quals (type);
+ auto quals = cp_type_quals (type);
gcc_checking_assert (quals & TYPE_QUAL_CONST);
type = cv_unqualified (type);
}
@@ -13472,7 +13476,7 @@ finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
if (type && !TYPE_REF_P (type))
{
- int quals;
+ qualifier_set quals;
if (current_function_decl
&& LAMBDA_FUNCTION_P (current_function_decl)
&& DECL_XOBJ_MEMBER_FUNCTION_P (current_function_decl))
@@ -13486,14 +13490,19 @@ finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
return TREE_TYPE (t);
return t;
};
+ auto obtype_quals = cp_type_quals (direct_type (obtype));
+ /* Currently, there shouldn't be any way to get a lambda such
+ that it is qualified with an address space. */
+ gcc_checking_assert (has_only_cv_quals_p (obtype_quals));
quals = (cp_type_quals (type)
- | cp_type_quals (direct_type (obtype)));
+ | get_quals_cv (obtype_quals));
}
else
/* We are in the parameter clause, trailing return type, or
the requires clause and have no relevant c_f_decl yet. */
- quals = (LAMBDA_EXPR_CONST_QUAL_P (lam)
- ? TYPE_QUAL_CONST : TYPE_UNQUALIFIED);
+ quals = build_qualifier_set (LAMBDA_EXPR_CONST_QUAL_P (lam)
+ ? TYPE_QUAL_CONST
+ : TYPE_UNQUALIFIED);
type = cp_build_qualified_type (type, quals);
type = build_reference_type (type);
}
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index dc885e47c01e..b1e48c8bdf36 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -19,6 +19,7 @@ along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
+#define INCLUDE_FUNCTIONAL // for optional.h
#include "system.h"
#include "coretypes.h"
#include "tree.h"
@@ -37,6 +38,10 @@ along with GCC; see the file COPYING3. If not see
#include "flags.h"
#include "selftest.h"
+/* For {,cp_}try_quals_merge. */
+#include "util/expected.h"
+#include "util/optional.h"
+
static tree bot_manip (tree *, int *, void *);
static tree bot_replace (tree *, int *, void *);
static hashval_t list_hash_pieces (tree, tree, tree);
@@ -1469,7 +1474,8 @@ 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);
@@ -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, qualifier_set 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;
@@ -1586,12 +1592,17 @@ cp_build_qualified_type (tree type, int type_quals,
if (TYPE_REF_P (type)
&& (!typedef_variant_p (type) || FUNC_OR_METHOD_TYPE_P (type)))
bad_quals |= type_quals & (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE);
- type_quals &= ~(TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE);
+ type_quals = remove_quals_from (type_quals,
+ TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE);
}
/* But preserve any function-cv-quals on a FUNCTION_TYPE. */
if (TREE_CODE (type) == FUNCTION_TYPE)
- type_quals |= type_memfn_quals (type);
+ {
+ /* Those cannot have address spaces. */
+ gcc_checking_assert (ADDR_SPACE_GENERIC_P (get_quals_addr_space (type_quals)));
+ type_quals |= type_memfn_quals (type);
+ }
/* A restrict-qualified type must be a pointer (or reference)
to object or incomplete type. */
@@ -1601,7 +1612,7 @@ cp_build_qualified_type (tree type, int type_quals,
&& !INDIRECT_TYPE_P (type))
{
bad_quals |= TYPE_QUAL_RESTRICT;
- type_quals &= ~TYPE_QUAL_RESTRICT;
+ type_quals = remove_quals_from (type_quals, TYPE_QUAL_RESTRICT);
}
if (bad_quals == TYPE_UNQUALIFIED
@@ -1622,6 +1633,65 @@ cp_build_qualified_type (tree type, int type_quals,
return result;
}
+/* Attempt to merge qualifier sets of types T1 and T2, reporting an error on
+ failure, if allowed.
+
+ Returns the merged set, or an empty optional if not possible. */
+
+tl::optional<qualifier_set>
+cp_try_quals_merge (location_t loc, tree t1, tree t2,
+ tsubst_flags_t complain)
+{
+ gcc_assert (TYPE_P (t1) && TYPE_P (t2));
+ auto q1 = TYPE_QUALS (t1), q2 = TYPE_QUALS (t2);
+ auto merged = try_quals_merge (q1, q2);
+
+ if (!merged)
+ {
+ if (!(complain & tf_error))
+ /* We aren't reporting errors, don't bother with error recovery. */
+ return tl::nullopt;
+
+ auto_diagnostic_group _;
+ error_at (loc, "conflicting qualifiers for %q% and %qT",
+ t1, t2);
+
+ switch (merged.error ())
+ {
+ case try_quals_merge_error::disjoint_address_spaces:
+ {
+ auto as1 = get_quals_addr_space (q1);
+ auto as2 = get_quals_addr_space (q2);
+ gcc_assert (as1 != as2);
+
+ if (ADDR_SPACE_GENERIC_P (as2))
+ /* Ensure generic address space is AS1, if any address space
+ is generic. */
+ std::swap (as1, as2);
+
+ if (ADDR_SPACE_GENERIC_P (as1))
+ inform (loc,
+ "address space %qs does not overlap with generic "
+ "address space",
+ c_addr_space_name (as2));
+ else
+ inform (loc, "address spaces %qs and %qs are disjoint",
+ c_addr_space_name (as1), c_addr_space_name (as2));
+ }
+ break;
+ default:
+ gcc_unreachable();
+ }
+
+
+ /* As error recovery, continue with AS of Q1. */
+ return q1 | get_quals_cv (q2);
+ }
+
+ return *merged;
+}
+
+
/* Return a FUNCTION_TYPE for a function returning VALUE_TYPE
with ARG_TYPES arguments. Wrapper around build_function_type
which ensures TYPE_NO_NAMED_ARGS_STDARG_P is set if ARG_TYPES
@@ -1640,13 +1710,14 @@ 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);
- quals &= ~(TYPE_QUAL_CONST|TYPE_QUAL_VOLATILE);
+ /* XXX(arsen): This seems a bit dubious, if we want unqualified, why isn't
+ that just TYPE_MAIN_VARIANT? */
+ auto quals = cp_type_quals (type);
+ quals = remove_quals_from (quals, TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE);
return cp_build_qualified_type (type, quals);
}
@@ -2448,8 +2519,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,
+ qualifier_set type_quals, cp_ref_qualifier rqual,
+ tree raises, bool late)
{
return (TYPE_QUALS (cand) == type_quals
&& check_base_type (cand, base)
@@ -2937,7 +3009,7 @@ tree
build_cp_fntype_variant (tree type, cp_ref_qualifier rqual,
tree raises, bool late)
{
- cp_cv_quals type_quals = TYPE_QUALS (type);
+ auto type_quals = TYPE_QUALS (type);
if (cp_check_qualified_type (type, type, type_quals, rqual, raises, late))
return type;
@@ -4659,7 +4731,7 @@ maybe_dummy_object (tree type, tree* binfop)
non-lambda) 'this' if available. */
if (ctype)
{
- int quals = TYPE_UNQUALIFIED;
+ qualifier_set quals {};
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..a25726dd727f 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see
checks, and some optimization. */
#include "config.h"
+#define INCLUDE_FUNCTIONAL // for optional.h
#include "system.h"
#include "coretypes.h"
#include "target.h"
@@ -41,6 +42,8 @@ along with GCC; see the file COPYING3. If not see
#include "asan.h"
#include "gimplify.h"
+#include "util/optional.h"
+
static tree cp_build_addr_expr_strict (tree, tsubst_flags_t);
static tree cp_build_function_call (tree, tree, tsubst_flags_t);
static tree pfn_from_ptrmemfunc (tree);
@@ -241,7 +244,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,17 +722,19 @@ 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 = cp_try_quals_merge (location, t1, t2, complain);
+ if (!quals)
+ return error_mark_node;
result_type = cp_build_qualified_type (result_type,
- (quals | (*add_const
- ? TYPE_QUAL_CONST
- : TYPE_UNQUALIFIED)));
+ (*quals | (*add_const
+ ? TYPE_QUAL_CONST
+ : TYPE_UNQUALIFIED)));
/* The cv-combined type can add "const" as per [conv.qual]/3.3 (except for
the TLQ). The reason is that both T1 and T2 can then be converted to the
cv-combined type of T1 and T2. */
- if (quals != q1 || quals != q2)
+ if (*quals != q1 || *quals != q2)
*add_const = true;
/* If the original types were pointers to members, so is the
result. */
@@ -828,11 +833,15 @@ composite_pointer_type (const op_location_t &location,
}
else
return error_mark_node;
- }
+ }
+
+ auto maybe_quals = cp_try_quals_merge (location, TREE_TYPE (t1),
+ TREE_TYPE (t2), complain);
+ if (!maybe_quals)
+ return error_mark_node;
+
result_type
- = cp_build_qualified_type (void_type_node,
- (cp_type_quals (TREE_TYPE (t1))
- | cp_type_quals (TREE_TYPE (t2))));
+ = cp_build_qualified_type (void_type_node, *maybe_quals);
result_type = build_pointer_type (result_type);
/* Merge the attributes. */
attributes = (*targetm.merge_type_attributes) (t1, t2);
@@ -987,7 +996,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 +1014,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),
@@ -1053,7 +1061,7 @@ merge_types (tree t1, tree t2)
else
parms = commonparms (p1, p2);
- cp_cv_quals quals = type_memfn_quals (t1);
+ cv_qualifier quals = type_memfn_quals (t1);
cp_ref_qualifier rqual = type_memfn_rqual (t1);
gcc_assert (quals == type_memfn_quals (t2));
gcc_assert (rqual == type_memfn_rqual (t2));
@@ -1184,14 +1192,16 @@ comp_except_types (tree a, tree b, bool exact)
return true;
else if (!exact)
{
- if (cp_type_quals (a) || cp_type_quals (b))
+ if (cp_type_quals (a) != qualifier_set {}
+ || cp_type_quals (b) != qualifier_set {})
return false;
if (TYPE_PTR_P (a) && TYPE_PTR_P (b))
{
a = TREE_TYPE (a);
b = TREE_TYPE (b);
- if (cp_type_quals (a) || cp_type_quals (b))
+ if ((cp_type_quals (a) != qualifier_set {})
+ || (cp_type_quals (b) != qualifier_set {}))
return false;
}
@@ -2084,25 +2094,30 @@ layout_compatible_type_p (tree type1, tree type2, bool explain/*=false*/)
bool
at_least_as_qualified_p (const_tree type1, const_tree type2)
{
- int q1 = cp_type_quals (type1);
- int q2 = cp_type_quals (type2);
+ auto q1 = cp_type_quals (type1);
+ auto q2 = cp_type_quals (type2);
/* All qualifiers for TYPE2 must also appear in TYPE1. */
- return (q1 & q2) == q2;
+ return quals_includes_p (q1, q2);
}
/* Returns 1 if TYPE1 is more cv-qualified than TYPE2, -1 if TYPE2 is
more cv-qualified that TYPE1, and 0 otherwise. */
int
-comp_cv_qualification (int q1, int q2)
+comp_cv_qualification (qualifier_set q1, qualifier_set q2)
{
- if (q1 == q2)
+ auto as1 = get_quals_addr_space (q1);
+ auto as2 = get_quals_addr_space (q2);
+ auto as_eq = (targetm.addr_space.subset_p (as2, as1)
+ && targetm.addr_space.subset_p (as1, as2));
+
+ if (get_quals_cv (q1) == get_quals_cv (q2) && as_eq)
return 0;
- if ((q1 & q2) == q2)
+ if (quals_includes_p (q1, q2))
return 1;
- else if ((q1 & q2) == q1)
+ else if (quals_includes_p (q2, q1))
return -1;
return 0;
@@ -2111,8 +2126,8 @@ comp_cv_qualification (int q1, int q2)
int
comp_cv_qualification (const_tree type1, const_tree type2)
{
- int q1 = cp_type_quals (type1);
- int q2 = cp_type_quals (type2);
+ auto q1 = cp_type_quals (type1);
+ auto q2 = cp_type_quals (type2);
return comp_cv_qualification (q1, q2);
}
@@ -3047,7 +3062,6 @@ build_class_member_access_expr (cp_expr object, tree member,
{
/* A non-static data member. */
bool null_object_p;
- int type_quals;
tree member_type;
if (INDIRECT_REF_P (object))
@@ -3124,19 +3138,27 @@ build_class_member_access_expr (cp_expr object, tree member,
}
/* Compute the type of the field, as described in [expr.ref]. */
- type_quals = TYPE_UNQUALIFIED;
+ auto type_quals = TYPE_UNQUALIFIED;
member_type = TREE_TYPE (member);
if (!TYPE_REF_P (member_type))
{
- type_quals = (cp_type_quals (member_type)
- | cp_type_quals (object_type));
+ auto object_quals = cp_type_quals (object_type);
+ type_quals = (get_quals_cv (cp_type_quals (member_type))
+ | get_quals_cv (object_quals));
/* A field is const (volatile) if the enclosing object, or the
field itself, is const (volatile). But, a mutable field is
not const, even within a const object. */
if (DECL_MUTABLE_P (member))
type_quals &= ~TYPE_QUAL_CONST;
- member_type = cp_build_qualified_type (member_type, type_quals);
+
+ /* The address space of a member is the address space of the object
+ itself. */
+ auto object_as = get_quals_addr_space (object_quals);
+ member_type = cp_build_qualified_type (member_type,
+ build_qualifier_set
+ (type_quals,
+ object_as));
}
result = build3_loc (input_location, COMPONENT_REF, member_type,
@@ -12123,17 +12145,17 @@ 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
+qualifier_set
cp_type_quals (const_tree type)
{
- int quals;
+ qualifier_set 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));
if (type == error_mark_node
/* Quals on a FUNCTION_TYPE are memfn quals. */
|| TREE_CODE (type) == FUNCTION_TYPE)
- return TYPE_UNQUALIFIED;
+ return qualifier_set {};
quals = TYPE_QUALS (type);
/* METHOD and REFERENCE_TYPEs should never have quals. */
gcc_assert ((TREE_CODE (type) != METHOD_TYPE
@@ -12161,15 +12183,20 @@ type_memfn_rqual (const_tree type)
/* Returns the function-cv-quals for TYPE, which must be a FUNCTION_TYPE or
METHOD_TYPE. */
-int
+cp_cv_quals
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 cp_type_quals (class_of_this_parm (type));
+ quals = cp_type_quals (class_of_this_parm (type));
else
gcc_unreachable ();
+
+ /* These should never include an address space. */
+ gcc_assert (has_only_cv_quals_p (quals));
+ return get_quals_cv (quals);
}
/* Returns the FUNCTION_TYPE TYPE with its function-cv-quals changed to
@@ -12180,7 +12207,9 @@ apply_memfn_quals (tree type, cp_cv_quals memfn_quals, cp_ref_qualifier rqual)
{
/* Could handle METHOD_TYPE here if necessary. */
gcc_assert (TREE_CODE (type) == FUNCTION_TYPE);
- if (TYPE_QUALS (type) == memfn_quals
+ /* But neither of those can accept an address-space qualifier. */
+ gcc_assert (ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type)));
+ if (TYPE_QUALS_NO_ADDR_SPACE (type) == memfn_quals
&& type_memfn_rqual (type) == rqual)
return type;
@@ -12195,7 +12224,7 @@ apply_memfn_quals (tree type, cp_cv_quals memfn_quals, cp_ref_qualifier rqual)
bool
cv_qualified_p (const_tree type)
{
- int quals = cp_type_quals (type);
+ auto quals = cp_type_quals (type);
return (quals & (TYPE_QUAL_CONST|TYPE_QUAL_VOLATILE)) != 0;
}
@@ -12224,7 +12253,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 +12286,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;
+ qualifier_set quals1;
+ qualifier_set quals2;
/* [expr.const.cast]
@@ -12464,7 +12493,7 @@ check_literal_operator_args (const_tree decl,
{
bool maybe_raw_p = false;
t = TREE_TYPE (t);
- if (cp_type_quals (t) != TYPE_QUAL_CONST)
+ if (cp_type_quals (t) != build_qualifier_set (TYPE_QUAL_CONST))
return false;
t = TYPE_MAIN_VARIANT (t);
if ((maybe_raw_p = same_type_p (t, char_type_node))
diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
index 12c34ba6b3a7..917f28ea8551 100644
--- a/gcc/cp/typeck2.cc
+++ b/gcc/cp/typeck2.cc
@@ -26,6 +26,7 @@ along with GCC; see the file COPYING3. If not see
checks, and some optimization. */
#include "config.h"
+#define INCLUDE_FUNCTIONAL // for optional.h
#include "system.h"
#include "coretypes.h"
#include "cp-tree.h"
@@ -35,6 +36,8 @@ along with GCC; see the file COPYING3. If not see
#include "gcc-rich-location.h"
#include "target.h"
+#include "util/optional.h"
+
static tree
process_init_constructor (tree type, tree init, int nested, int flags,
tsubst_flags_t complain);
@@ -2485,9 +2488,11 @@ build_m_component_ref (tree datum, tree component, tsubst_flags_t complain)
There's no such thing as a mutable pointer-to-member, so
things are not as complex as they are for references to
non-static data members. */
- type = cp_build_qualified_type (type,
- (cp_type_quals (type)
- | cp_type_quals (TREE_TYPE (datum))));
+ auto merged_quals = cp_try_quals_merge (input_location, type,
+ TREE_TYPE (datum), complain);
+ if (!merged_quals)
+ return error_mark_node;
+ type = cp_build_qualified_type (type, *merged_quals);
datum = cp_build_addr_expr (datum, complain);
diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
index 7ba18f6f57c2..c2f2f3de2aaa 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) & 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..6447704c002f 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)
+ && get_quals_cv (TYPE_QUALS (type)) == TYPE_UNQUALIFIED)
return fold_convert_loc (loc, type,
build_fold_addr_expr_loc (loc, base));
}
diff --git a/gcc/gimple-lower-bitint.cc b/gcc/gimple-lower-bitint.cc
index 19e39f4d7efb..7fd12c74e2c5 100644
--- a/gcc/gimple-lower-bitint.cc
+++ b/gcc/gimple-lower-bitint.cc
@@ -640,8 +640,9 @@ 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,
+ set_quals_addr_space (TYPE_QUALS (ltype),
+ 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 +656,9 @@ 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,
+ set_quals_addr_space (TYPE_QUALS (ltype),
+ 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 +675,9 @@ 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,
+ set_quals_addr_space (TYPE_QUALS (ltype),
+ as));
var = unshare_expr (var);
if (TREE_CODE (TREE_TYPE (var)) != ARRAY_TYPE
|| !useless_type_conversion_p (m_limb_type,
@@ -713,8 +716,9 @@ 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,
+ set_quals_addr_space (TYPE_QUALS (ltype),
+ as));
tree atype = build_array_type_nelts (ltype, nelts);
obj = build1 (VIEW_CONVERT_EXPR, atype, obj);
}
@@ -6504,8 +6508,8 @@ 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));
+ set_quals_addr_space
+ (TYPE_QUALS (ltype), as));
rhs1 = build1 (VIEW_CONVERT_EXPR, ltype, unshare_expr (mem));
gimple_assign_set_rhs1 (stmt, rhs1);
}
@@ -6609,8 +6613,9 @@ bitint_large_huge::lower_stmt (gimple *stmt)
addr_space_t as = TYPE_ADDR_SPACE (TREE_TYPE (lhs));
ltype
= build_qualified_type (ltype,
- TYPE_QUALS (TREE_TYPE (lhs))
- | ENCODE_QUAL_ADDR_SPACE (as));
+ set_quals_addr_space
+ (TYPE_QUALS (TREE_TYPE (lhs)),
+ 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 8f64979571b7..1c3c92458bd3 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -3122,12 +3122,17 @@ 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);
+ auto op_quals = TYPE_QUALS (TREE_TYPE (TREE_OPERAND (expr, 0)));
+ gcc_checking_assert (/* It should not be possible for part of the object
+ to reside in a different address space to the
+ rest of it. */
+ get_quals_addr_space (type_quals)
+ == get_quals_addr_space (op_quals));
/* We need to preserve qualifiers and propagate them from
operand 0. */
- type_quals = TYPE_QUALS (type)
- | TYPE_QUALS (TREE_TYPE (TREE_OPERAND (expr, 0)));
+ type_quals |= get_quals_cv (op_quals);
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..1aae65ca270a 100644
--- a/gcc/ipa-free-lang-data.cc
+++ b/gcc/ipa-free-lang-data.cc
@@ -444,9 +444,9 @@ 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 = remove_quals_from (TYPE_QUALS (arg_type),
+ 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/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..e09cd7135035 100644
--- a/gcc/langhooks.h
+++ b/gcc/langhooks.h
@@ -53,7 +53,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/omp-offload.cc b/gcc/omp-offload.cc
index 7cd2a572b7c0..e3c0934e4256 100644
--- a/gcc/omp-offload.cc
+++ b/gcc/omp-offload.cc
@@ -1891,12 +1891,13 @@ 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 = get_quals_cv (TYPE_QUALS (TREE_TYPE (*new_decl)));
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
+ && get_quals_cv (field_quals) != base_quals)
{
tree *field_type = &TREE_TYPE (field);
while (TREE_CODE (*field_type) == ARRAY_TYPE)
@@ -1907,8 +1908,9 @@ oacc_rewrite_var_decl (tree *tp, int *walk_subtrees, void *data)
/* 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
+ && get_quals_cv (comp_quals) != base_quals)
{
comp_quals |= base_quals;
TREE_TYPE (*tp)
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index 6992a5acf81e..d699920e2178 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.
@@ -688,17 +688,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..b39b98e69967 100644
--- a/gcc/tree-dump.cc
+++ b/gcc/tree-dump.cc
@@ -367,7 +367,7 @@ 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);
+ auto quals = get_quals_cv (lang_hooks.tree_dump.type_quals (t));
if (quals != TYPE_UNQUALIFIED)
{
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 9d3cb4d81bd3..8d584d7f986f 100644
--- a/gcc/tree-pretty-print.cc
+++ b/gcc/tree-pretty-print.cc
@@ -2268,7 +2268,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)
@@ -2442,7 +2442,7 @@ 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);
@@ -2478,7 +2478,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)
@@ -2508,7 +2508,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..91f93338d58b 100644
--- a/gcc/tree-profile.cc
+++ b/gcc/tree-profile.cc
@@ -2049,7 +2049,8 @@ tree_profiling (void)
tree fntype = gimple_call_fntype (call);
if (fntype && TYPE_READONLY (fntype))
{
- int quals = TYPE_QUALS (fntype) & ~TYPE_QUAL_CONST;
+ auto quals = remove_quals_from (TYPE_QUALS (fntype),
+ 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..51251f7cc7b5 100644
--- a/gcc/tree-sra.cc
+++ b/gcc/tree-sra.cc
@@ -1904,8 +1904,8 @@ 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));
+ set_quals_addr_space (TYPE_QUALS (exp_type),
+ 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 818ae35a873f..e9b2f92e6a49 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));
+ auto qual = build_qualifier_set (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..0f3b939b4f71 100644
--- a/gcc/tree-switch-conversion.cc
+++ b/gcc/tree-switch-conversion.cc
@@ -1012,8 +1012,8 @@ 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));
+ auto quals = (build_qualifier_set
+ (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 b69f9de5de82..61b57dac4a1f 100644
--- a/gcc/tree-vect-stmts.cc
+++ b/gcc/tree-vect-stmts.cc
@@ -13678,7 +13678,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, build_qualifier_set (TYPE_UNQUALIFIED,
+ TYPE_ADDR_SPACE (orig_scalar_type)));
return vectype;
}
diff --git a/gcc/tree.cc b/gcc/tree.cc
index 90c8f2a35ea4..ceed2cc3e838 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);
@@ -5675,17 +5679,53 @@ 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. */
+tl::expected<qualifier_set, try_quals_merge_error>
+try_quals_merge (qualifier_set qs1, qualifier_set qs2)
+{
+ /* Documented next to declaration in tree.h. */
+ auto cv_merged = get_quals_cv (qs1) | get_quals_cv (qs2);
+ auto as1 = get_quals_addr_space (qs1);
+ auto as2 = get_quals_addr_space (qs2);
+
+ addr_space_t as_super;
+ if (targetm.addr_space.subset_p (as1, as2))
+ as_super = as2;
+ else if (targetm.addr_space.subset_p (as2, as1))
+ as_super = as1;
+ else
+ {
+ gcc_checking_assert (as1 != as2);
+ return tl::unexpected {try_quals_merge_error::disjoint_address_spaces};
+ }
+
+ return build_qualifier_set (cv_merged, as_super);
+}
+
+bool
+quals_includes_p (qualifier_set superset, qualifier_set subset)
+{
+ /* 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 = get_quals_cv (superset);
+ auto cv_sub = get_quals_cv (subset);
+ auto as_sup = get_quals_addr_space (superset);
+ auto as_sub = get_quals_addr_space (subset);
+ return ((cv_sup & cv_sub) == cv_sub
+ && targetm.addr_space.subset_p (as_sub, as_sup));
+}
+
+/* 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 & TYPE_QUAL_CONST);
+ TYPE_VOLATILE (type) = !!(type_quals & TYPE_QUAL_VOLATILE);
+ TYPE_RESTRICT (type) = !!(type_quals & TYPE_QUAL_RESTRICT);
+ TYPE_ATOMIC (type) = !!(type_quals & TYPE_QUAL_ATOMIC);
+ TYPE_ADDR_SPACE (type) = get_quals_addr_space (type_quals);
}
/* Returns true iff CAND and BASE have equivalent language-specific
@@ -5761,7 +5801,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 (quals_intersect (TYPE_QUALS (cand), TYPE_QUAL_ATOMIC))
{
/* See if this object can map to a basic atomic type. */
tree atomic_type = find_atomic_core_type (cand);
@@ -5774,7 +5814,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)
@@ -5805,7 +5845,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;
@@ -5837,7 +5877,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;
@@ -5850,7 +5890,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 & TYPE_QUAL_ATOMIC))
{
/* See if this object can map to a basic atomic type. */
tree atomic_type = find_atomic_core_type (type);
@@ -9480,7 +9520,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),
@@ -9606,11 +9647,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)))
+ auto atomic_quals = build_qualifier_set (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..33f2162229cd 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,213 @@ 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)
+/* Number of bits to reserve for qualifiers. */
+constexpr unsigned qualifier_reserved_bits = sizeof (cv_qualifier) * __CHAR_BIT__;
-/* Return all qualifiers except for the address space qualifiers. */
-#define CLEAR_QUAL_ADDR_SPACE(X) ((X) & ~0xFF00)
+/* 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).
-/* Only keep the address space out of the qualifiers and discard the other
- qualifiers. */
-#define KEEP_QUAL_ADDR_SPACE(X) ((X) & 0xFF00)
+ This is represented by a distinct 'enum class' type such that unsound
+ operations (such as bitwise-OR) are forbidden, without inducing extra
+ call/return overhead that a struct of equal size might bring in on some less
+ efficient ABIs.
-/* 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)))))
+ Unfortunately, that means that we cannot prevent bad direct construction of
+ qualifier sets, and that we must dump all functionality into free functions.
+ :-( */
+enum class qualifier_set : unsigned {};
+
+/* Ensure that qualifier_set has enough bits for the qualifiers and address
+ space. */
+static_assert (sizeof (qualifier_set) * __CHAR_BIT__
+ >= (sizeof (addr_space_t) * __CHAR_BIT__
+ + qualifier_reserved_bits));
+/* We want qualifier_set{} to be an empty set with a generic address space.
+ For the qualifiers, they are absent when their bit is zero, ergo,
+ qualifier_set{} will lack all qualifiers always. */
+static_assert(ADDR_SPACE_GENERIC == 0);
+
+/* Return QS with address space set to AS. */
+[[nodiscard]] constexpr qualifier_set
+set_quals_addr_space (qualifier_set qs, addr_space_t as)
+{
+ auto qs_raw = static_cast<unsigned> (qs);
+ qs_raw &= ~(addr_space_t (-1U) << qualifier_reserved_bits);
+ qs_raw |= as << qualifier_reserved_bits;
+ return qualifier_set {qs_raw};
+}
+
+/* Remove the address space from QS (i.e. replace it with the generic AS). */
+[[nodiscard]] constexpr qualifier_set
+clear_quals_addr_space (qualifier_set qs)
+{ return set_quals_addr_space (qs, ADDR_SPACE_GENERIC); }
+
+/* Return a qualifier set with CV-qualifiers CV and address space AS. */
+constexpr qualifier_set
+build_qualifier_set (cv_qualifier cv, addr_space_t as = ADDR_SPACE_GENERIC)
+{ return set_quals_addr_space(qualifier_set{cv}, as); }
+
+/* Return just the address space of QS. */
+constexpr addr_space_t
+get_quals_addr_space (qualifier_set qs)
+{
+ auto qs_raw = static_cast<unsigned> (qs);
+ /* Masked off by return conversion. */
+ return qs_raw >> qualifier_reserved_bits;
+}
+
+/* Return just the CV-qualifiers of QS. */
+constexpr cv_qualifier
+get_quals_cv (qualifier_set qs)
+{
+ auto qs_raw = static_cast<unsigned> (qs);
+ return cv_qualifier (qs_raw & TYPE_QUAL_ALL);
+}
+
+/* True if it is safe to treat QS as if it were *only* cv_qualifier. Useful
+ for asserts. */
+constexpr bool
+has_only_cv_quals_p (qualifier_set qs)
+{ return ADDR_SPACE_GENERIC_P (get_quals_addr_space (qs)); }
+
+/* Returns QS broken down into its components. Preferably, this'd be
+ implemented as structural binding support directly on qualifier_set, but
+ that's C++17-only. The reason this is useful is because it permits the
+ components contained in qualifier_set to change and cause compile errors
+ where the new components need to be handled. */
+constexpr std::pair<cv_qualifier, addr_space_t>
+split_quals (qualifier_set qs)
+{ return std::make_pair (get_quals_cv (qs), get_quals_addr_space (qs)); }
+
+/* Returns qualifiers present in both QS and QUALS. */
+constexpr cv_qualifier
+quals_intersect (qualifier_set qs, cv_qualifier quals)
+{ return get_quals_cv (qs) & quals; }
+
+/* Returns true if qualifiers in SUBSET are completely included in SUPERSET.
+
+ In general, this means that an object qualified per SUBSET can be used as if
+ it was qualified per SUPERSET safely (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). */
+bool quals_includes_p (qualifier_set superset, qualifier_set subset);
+
+/* Return QS with CV-qualifiers QUALS added. */
+[[nodiscard]] constexpr qualifier_set
+add_quals_to (qualifier_set qs, cv_qualifier quals)
+{
+ auto qs_raw = static_cast<unsigned> (qs);
+ qs_raw |= quals;
+ return qualifier_set {qs_raw};
+}
+
+/* Return QS with CV-qualifiers QUALS removed. */
+[[nodiscard]] constexpr qualifier_set
+remove_quals_from (qualifier_set qs, cv_qualifier quals)
+{
+ auto qs_raw = static_cast<unsigned> (qs);
+ qs_raw &= ~quals;
+ return qualifier_set {qs_raw};
+}
+
+enum class try_quals_merge_error
+{
+ /* The address spaces of the to-be-merged qualifier sets were disjoint,
+ i.e. neither contained the other. */
+ disjoint_address_spaces,
+};
+
+/* Attempt to produce a qualifier_set that's a merge of qualifiers in QS1 and
+ QS2. Such a qualifier set can be used instead of either QS1 or QS2 safely.
+ (i.e. if a type was qualified by either QS1 or QS2, it can be qualified by
+ their merge instead safely)
+
+ 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. */
+
+extern tl::expected<qualifier_set, try_quals_merge_error>
+try_quals_merge (qualifier_set qs1, qualifier_set qs2);
+
+/* QUAL_SET bitop CV_QUAL is a safe and frequent pattern. Provide helpers for
+ those. */
+
+/* For &, there can never be an address space as a result of intersecting with
+ a few CV-qualifiers, so return CV-qualifiers. */
+constexpr cv_qualifier
+operator& (qualifier_set qs, cv_qualifier quals)
+{
+ return quals_intersect (qs, quals);
+}
+
+constexpr cv_qualifier
+operator& (cv_qualifier quals, qualifier_set qs)
+{
+ return qs & quals;
+}
+
+/* Intentionally omitted operator&=. It'd enable the incorrect pattern of
+ qualifier_set &= ~cv_qualifier. This would also delete address space. */
+
+/* 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 quals)
+{
+ return add_quals_to (qs, quals);
+}
+
+constexpr qualifier_set
+operator| (cv_qualifier quals, qualifier_set qs)
+{
+ return qs | quals;
+}
+
+constexpr qualifier_set
+operator^ (qualifier_set qs, cv_qualifier quals)
+{
+ return add_quals_to (qs, quals);
+}
+
+constexpr qualifier_set
+operator^ (cv_qualifier quals, qualifier_set qs)
+{
+ return qs ^ quals;
+}
+
+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) \
+ build_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 +5436,32 @@ 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 the above, for the fairly frequent case of passing a CV-qualifier. */
+
+inline tree get_qualified_type (tree type, cv_qualifier type_quals)
+{ return get_qualified_type (type, build_qualifier_set (type_quals)); }
/* 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);
+
+/* Like the above, for the fairly frequent case of passing a CV-qualifier. */
+
+inline tree
+build_qualified_type (tree type, cv_qualifier type_quals CXX_MEM_STAT_INFO)
+{
+ return build_qualified_type (type, build_qualifier_set (type_quals)
+ PASS_MEM_STAT);
+}
/* Create a variant of type T with alignment ALIGN. */
@@ -5283,9 +5473,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. */
diff --git a/gcc/ubsan.cc b/gcc/ubsan.cc
index 79a863b46ce6..05322d606e72 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,
+ set_quals_addr_space (TYPE_QUALS (utype), 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/util/expected.h b/gcc/util/expected.h
new file mode 100644
index 000000000000..9a596dbe905e
--- /dev/null
+++ b/gcc/util/expected.h
@@ -0,0 +1,2439 @@
+// clang-format off
+///
+// expected - An implementation of std::expected with extensions
+// Written in 2017 by Sy Brand ([email protected], @TartanLlama)
+//
+// Documentation available at http://tl.tartanllama.xyz/
+//
+// To the extent possible under law, the author(s) have dedicated all
+// copyright and related and neighboring rights to this software to the
+// public domain worldwide. This software is distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication
+// along with this software. If not, see
+// <http://creativecommons.org/publicdomain/zero/1.0/>.
+///
+
+#ifndef TL_EXPECTED_HPP
+#define TL_EXPECTED_HPP
+
+#define TL_EXPECTED_VERSION_MAJOR 1
+#define TL_EXPECTED_VERSION_MINOR 1
+#define TL_EXPECTED_VERSION_PATCH 0
+
+/* Includes removed, make sure to include system.h first. */
+#include "expected_fwd.h"
+
+#if defined(__EXCEPTIONS) || defined(_CPPUNWIND)
+#define TL_EXPECTED_EXCEPTIONS_ENABLED
+#endif
+
+#if (defined(_MSC_VER) && _MSC_VER == 1900)
+#define TL_EXPECTED_MSVC2015
+#define TL_EXPECTED_MSVC2015_CONSTEXPR
+#else
+#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
+ !defined(__clang__))
+#define TL_EXPECTED_GCC49
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \
+ !defined(__clang__))
+#define TL_EXPECTED_GCC54
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \
+ !defined(__clang__))
+#define TL_EXPECTED_GCC55
+#endif
+
+#if !defined(TL_ASSERT)
+//can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug
+#if (__cplusplus > 201103L) && !defined(TL_EXPECTED_GCC49)
+#include <cassert>
+#define TL_ASSERT(x) assert(x)
+#else
+#define TL_ASSERT(x)
+#endif
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
+ !defined(__clang__))
+// GCC < 5 doesn't support overloading on const&& for member functions
+
+#define TL_EXPECTED_NO_CONSTRR
+// GCC < 5 doesn't support some standard C++11 type traits
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
+ std::has_trivial_copy_constructor<T>
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
+ std::has_trivial_copy_assign<T>
+
+// This one will be different for GCC 5.7 if it's ever supported
+#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
+ std::is_trivially_destructible<T>
+
+// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks
+// std::vector for non-copyable types
+#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__))
+#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
+#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
+namespace tl {
+namespace detail {
+template <class T>
+struct is_trivially_copy_constructible
+ : std::is_trivially_copy_constructible<T> {};
+#ifdef _GLIBCXX_VECTOR
+template <class T, class A>
+struct is_trivially_copy_constructible<std::vector<T, A>> : std::false_type {};
+#endif
+} // namespace detail
+} // namespace tl
+#endif
+
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
+ tl::detail::is_trivially_copy_constructible<T>
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
+ std::is_trivially_copy_assignable<T>
+#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
+ std::is_trivially_destructible<T>
+#else
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
+ std::is_trivially_copy_constructible<T>
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
+ std::is_trivially_copy_assignable<T>
+#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
+ std::is_trivially_destructible<T>
+#endif
+
+#if __cplusplus > 201103L
+#define TL_EXPECTED_CXX14
+#endif
+
+#ifdef TL_EXPECTED_GCC49
+#define TL_EXPECTED_GCC49_CONSTEXPR
+#else
+#define TL_EXPECTED_GCC49_CONSTEXPR constexpr
+#endif
+
+#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \
+ defined(TL_EXPECTED_GCC49))
+#define TL_EXPECTED_11_CONSTEXPR
+#else
+#define TL_EXPECTED_11_CONSTEXPR constexpr
+#endif
+
+namespace tl {
+template <class T, class E> class expected;
+
+#ifndef TL_MONOSTATE_INPLACE_MUTEX
+#define TL_MONOSTATE_INPLACE_MUTEX
+class monostate {};
+
+struct in_place_t {
+ explicit in_place_t() = default;
+};
+static constexpr in_place_t in_place{};
+#endif
+
+template <class E> class unexpected {
+public:
+ static_assert(!std::is_same<E, void>::value, "E must not be void");
+
+ unexpected() = delete;
+ constexpr explicit unexpected(const E &e) : m_val(e) {}
+
+ constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {}
+
+ template <class... Args, typename std::enable_if<std::is_constructible<
+ E, Args &&...>::value>::type * = nullptr>
+ constexpr explicit unexpected(Args &&...args)
+ : m_val(std::forward<Args>(args)...) {}
+ template <
+ class U, class... Args,
+ typename std::enable_if<std::is_constructible<
+ E, std::initializer_list<U> &, Args &&...>::value>::type * = nullptr>
+ constexpr explicit unexpected(std::initializer_list<U> l, Args &&...args)
+ : m_val(l, std::forward<Args>(args)...) {}
+
+ constexpr const E &value() const & { return m_val; }
+ TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; }
+ TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); }
+ constexpr const E &&value() const && { return std::move(m_val); }
+
+private:
+ E m_val;
+};
+
+#ifdef __cpp_deduction_guides
+template <class E> unexpected(E) -> unexpected<E>;
+#endif
+
+template <class E>
+constexpr bool operator==(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+ return lhs.value() == rhs.value();
+}
+template <class E>
+constexpr bool operator!=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+ return lhs.value() != rhs.value();
+}
+template <class E>
+constexpr bool operator<(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+ return lhs.value() < rhs.value();
+}
+template <class E>
+constexpr bool operator<=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+ return lhs.value() <= rhs.value();
+}
+template <class E>
+constexpr bool operator>(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+ return lhs.value() > rhs.value();
+}
+template <class E>
+constexpr bool operator>=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+ return lhs.value() >= rhs.value();
+}
+
+template <class E>
+unexpected<typename std::decay<E>::type> make_unexpected(E &&e) {
+ return unexpected<typename std::decay<E>::type>(std::forward<E>(e));
+}
+
+struct unexpect_t {
+ unexpect_t() = default;
+};
+static constexpr unexpect_t unexpect{};
+
+namespace detail {
+template <typename E>
+[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) {
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+ throw std::forward<E>(e);
+#else
+ (void)e;
+ gcc_unreachable();
+#endif
+}
+
+#ifndef TL_TRAITS_MUTEX
+#define TL_TRAITS_MUTEX
+// C++14-style aliases for brevity
+template <class T> using remove_const_t = typename std::remove_const<T>::type;
+template <class T>
+using remove_reference_t = typename std::remove_reference<T>::type;
+template <class T> using decay_t = typename std::decay<T>::type;
+template <bool E, class T = void>
+using enable_if_t = typename std::enable_if<E, T>::type;
+template <bool B, class T, class F>
+using conditional_t = typename std::conditional<B, T, F>::type;
+
+// std::conjunction from C++17
+template <class...> struct conjunction : std::true_type {};
+template <class B> struct conjunction<B> : B {};
+template <class B, class... Bs>
+struct conjunction<B, Bs...>
+ : std::conditional<bool(B::value), conjunction<Bs...>, B>::type {};
+
+#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L
+#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
+#endif
+
+// In C++11 mode, there's an issue in libc++'s std::mem_fn
+// which results in a hard-error when using it in a noexcept expression
+// in some cases. This is a check to workaround the common failing case.
+#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
+template <class T>
+struct is_pointer_to_non_const_member_func : std::false_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...)>
+ : std::true_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &>
+ : std::true_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &&>
+ : std::true_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile>
+ : std::true_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile &>
+ : std::true_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile &&>
+ : std::true_type {};
+
+template <class T> struct is_const_or_const_ref : std::false_type {};
+template <class T> struct is_const_or_const_ref<T const &> : std::true_type {};
+template <class T> struct is_const_or_const_ref<T const> : std::true_type {};
+#endif
+
+// std::invoke from C++17
+// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
+template <
+ typename Fn, typename... Args,
+#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
+ typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value &&
+ is_const_or_const_ref<Args...>::value)>,
+#endif
+ typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>, int = 0>
+constexpr auto invoke(Fn &&f, Args &&...args) noexcept(
+ noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
+ -> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) {
+ return std::mem_fn(f)(std::forward<Args>(args)...);
+}
+
+template <typename Fn, typename... Args,
+ typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>::value>>
+constexpr auto invoke(Fn &&f, Args &&...args) noexcept(
+ noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
+ -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) {
+ return std::forward<Fn>(f)(std::forward<Args>(args)...);
+}
+
+// std::invoke_result from C++17
+template <class F, class, class... Us> struct invoke_result_impl;
+
+template <class F, class... Us>
+struct invoke_result_impl<
+ F,
+ decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()),
+ Us...> {
+ using type =
+ decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
+};
+
+template <class F, class... Us>
+using invoke_result = invoke_result_impl<F, void, Us...>;
+
+template <class F, class... Us>
+using invoke_result_t = typename invoke_result<F, Us...>::type;
+
+#if defined(_MSC_VER) && _MSC_VER <= 1900
+// TODO make a version which works with MSVC 2015
+template <class T, class U = T> struct is_swappable : std::true_type {};
+
+template <class T, class U = T> struct is_nothrow_swappable : std::true_type {};
+#else
+// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept
+namespace swap_adl_tests {
+// if swap ADL finds this then it would call std::swap otherwise (same
+// signature)
+struct tag {};
+
+template <class T> tag swap(T &, T &);
+template <class T, std::size_t N> tag swap(T (&a)[N], T (&b)[N]);
+
+// helper functions to test if an unqualified swap is possible, and if it
+// becomes std::swap
+template <class, class> std::false_type can_swap(...) noexcept(false);
+template <class T, class U,
+ class = decltype(swap(std::declval<T &>(), std::declval<U &>()))>
+std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T &>(),
+ std::declval<U &>())));
+
+template <class, class> std::false_type uses_std(...);
+template <class T, class U>
+std::is_same<decltype(swap(std::declval<T &>(), std::declval<U &>())), tag>
+uses_std(int);
+
+template <class T>
+struct is_std_swap_noexcept
+ : std::integral_constant<bool,
+ std::is_nothrow_move_constructible<T>::value &&
+ std::is_nothrow_move_assignable<T>::value> {};
+
+template <class T, std::size_t N>
+struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> {};
+
+template <class T, class U>
+struct is_adl_swap_noexcept
+ : std::integral_constant<bool, noexcept(can_swap<T, U>(0))> {};
+} // namespace swap_adl_tests
+
+template <class T, class U = T>
+struct is_swappable
+ : std::integral_constant<
+ bool,
+ decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value &&
+ (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value ||
+ (std::is_move_assignable<T>::value &&
+ std::is_move_constructible<T>::value))> {};
+
+template <class T, std::size_t N>
+struct is_swappable<T[N], T[N]>
+ : std::integral_constant<
+ bool,
+ decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value &&
+ (!decltype(detail::swap_adl_tests::uses_std<T[N], T[N]>(
+ 0))::value ||
+ is_swappable<T, T>::value)> {};
+
+template <class T, class U = T>
+struct is_nothrow_swappable
+ : std::integral_constant<
+ bool,
+ is_swappable<T, U>::value &&
+ ((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
+ detail::swap_adl_tests::is_std_swap_noexcept<T>::value) ||
+ (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
+ detail::swap_adl_tests::is_adl_swap_noexcept<T, U>::value))> {};
+#endif
+#endif
+
+// Trait for checking if a type is a tl::expected
+template <class T> struct is_expected_impl : std::false_type {};
+template <class T, class E>
+struct is_expected_impl<expected<T, E>> : std::true_type {};
+template <class T> using is_expected = is_expected_impl<decay_t<T>>;
+
+template <class T, class E, class U>
+using expected_enable_forward_value = detail::enable_if_t<
+ std::is_constructible<T, U &&>::value &&
+ !std::is_same<detail::decay_t<U>, in_place_t>::value &&
+ !std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
+ !std::is_same<unexpected<E>, detail::decay_t<U>>::value>;
+
+template <class T, class E, class U, class G, class UR, class GR>
+using expected_enable_from_other = detail::enable_if_t<
+ std::is_constructible<T, UR>::value &&
+ std::is_constructible<E, GR>::value &&
+ !std::is_constructible<T, expected<U, G> &>::value &&
+ !std::is_constructible<T, expected<U, G> &&>::value &&
+ !std::is_constructible<T, const expected<U, G> &>::value &&
+ !std::is_constructible<T, const expected<U, G> &&>::value &&
+ !std::is_convertible<expected<U, G> &, T>::value &&
+ !std::is_convertible<expected<U, G> &&, T>::value &&
+ !std::is_convertible<const expected<U, G> &, T>::value &&
+ !std::is_convertible<const expected<U, G> &&, T>::value>;
+
+template <class T, class U>
+using is_void_or = conditional_t<std::is_void<T>::value, std::true_type, U>;
+
+template <class T>
+using is_copy_constructible_or_void =
+ is_void_or<T, std::is_copy_constructible<T>>;
+
+template <class T>
+using is_move_constructible_or_void =
+ is_void_or<T, std::is_move_constructible<T>>;
+
+template <class T>
+using is_copy_assignable_or_void = is_void_or<T, std::is_copy_assignable<T>>;
+
+template <class T>
+using is_move_assignable_or_void = is_void_or<T, std::is_move_assignable<T>>;
+
+} // namespace detail
+
+namespace detail {
+struct no_init_t {};
+static constexpr no_init_t no_init{};
+
+// Implements the storage of the values, and ensures that the destructor is
+// trivial if it can be.
+//
+// This specialization is for where neither `T` or `E` is trivially
+// destructible, so the destructors must be called on destruction of the
+// `expected`
+template <class T, class E, bool = std::is_trivially_destructible<T>::value,
+ bool = std::is_trivially_destructible<E>::value>
+struct expected_storage_base {
+ constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+ constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+ nullptr>
+ constexpr expected_storage_base(in_place_t, Args &&...args)
+ : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
+ Args &&...args)
+ : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+ : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list<U> il,
+ Args &&...args)
+ : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() {
+ if (m_has_val) {
+ m_val.~T();
+ } else {
+ m_unexpect.~unexpected<E>();
+ }
+ }
+ union {
+ T m_val;
+ unexpected<E> m_unexpect;
+ char m_no_init;
+ };
+ bool m_has_val;
+};
+
+// This specialization is for when both `T` and `E` are trivially-destructible,
+// so the destructor of the `expected` can be trivial.
+template <class T, class E> struct expected_storage_base<T, E, true, true> {
+ constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+ constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+ nullptr>
+ constexpr expected_storage_base(in_place_t, Args &&...args)
+ : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
+ Args &&...args)
+ : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+ : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list<U> il,
+ Args &&...args)
+ : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() = default;
+ union {
+ T m_val;
+ unexpected<E> m_unexpect;
+ char m_no_init;
+ };
+ bool m_has_val;
+};
+
+// T is trivial, E is not.
+template <class T, class E> struct expected_storage_base<T, E, true, false> {
+ constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+ TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t)
+ : m_no_init(), m_has_val(false) {}
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+ nullptr>
+ constexpr expected_storage_base(in_place_t, Args &&...args)
+ : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
+ Args &&...args)
+ : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+ : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list<U> il,
+ Args &&...args)
+ : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() {
+ if (!m_has_val) {
+ m_unexpect.~unexpected<E>();
+ }
+ }
+
+ union {
+ T m_val;
+ unexpected<E> m_unexpect;
+ char m_no_init;
+ };
+ bool m_has_val;
+};
+
+// E is trivial, T is not.
+template <class T, class E> struct expected_storage_base<T, E, false, true> {
+ constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+ constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+ nullptr>
+ constexpr expected_storage_base(in_place_t, Args &&...args)
+ : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
+ Args &&...args)
+ : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+ : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list<U> il,
+ Args &&...args)
+ : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() {
+ if (m_has_val) {
+ m_val.~T();
+ }
+ }
+ union {
+ T m_val;
+ unexpected<E> m_unexpect;
+ char m_no_init;
+ };
+ bool m_has_val;
+};
+
+// `T` is `void`, `E` is trivially-destructible
+template <class E> struct expected_storage_base<void, E, false, true> {
+ #if __GNUC__ <= 5
+ //no constexpr for GCC 4/5 bug
+ #else
+ TL_EXPECTED_MSVC2015_CONSTEXPR
+ #endif
+ expected_storage_base() : m_has_val(true) {}
+
+ constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {}
+
+ constexpr expected_storage_base(in_place_t) : m_has_val(true) {}
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+ : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list<U> il,
+ Args &&...args)
+ : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() = default;
+ struct dummy {};
+ union {
+ unexpected<E> m_unexpect;
+ dummy m_val;
+ };
+ bool m_has_val;
+};
+
+// `T` is `void`, `E` is not trivially-destructible
+template <class E> struct expected_storage_base<void, E, false, false> {
+ constexpr expected_storage_base() : m_dummy(), m_has_val(true) {}
+ constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {}
+
+ constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {}
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+ : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list<U> il,
+ Args &&...args)
+ : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() {
+ if (!m_has_val) {
+ m_unexpect.~unexpected<E>();
+ }
+ }
+
+ union {
+ unexpected<E> m_unexpect;
+ char m_dummy;
+ };
+ bool m_has_val;
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class T, class E>
+struct expected_operations_base : expected_storage_base<T, E> {
+ using expected_storage_base<T, E>::expected_storage_base;
+
+ template <class... Args> void construct(Args &&...args) noexcept {
+ new (std::addressof(this->m_val)) T(std::forward<Args>(args)...);
+ this->m_has_val = true;
+ }
+
+ template <class Rhs> void construct_with(Rhs &&rhs) noexcept {
+ new (std::addressof(this->m_val)) T(std::forward<Rhs>(rhs).get());
+ this->m_has_val = true;
+ }
+
+ template <class... Args> void construct_error(Args &&...args) noexcept {
+ new (std::addressof(this->m_unexpect))
+ unexpected<E>(std::forward<Args>(args)...);
+ this->m_has_val = false;
+ }
+
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+
+ // These assign overloads ensure that the most efficient assignment
+ // implementation is used while maintaining the strong exception guarantee.
+ // The problematic case is where rhs has a value, but *this does not.
+ //
+ // This overload handles the case where we can just copy-construct `T`
+ // directly into place without throwing.
+ template <class U = T,
+ detail::enable_if_t<std::is_nothrow_copy_constructible<U>::value>
+ * = nullptr>
+ void assign(const expected_operations_base &rhs) noexcept {
+ if (!this->m_has_val && rhs.m_has_val) {
+ geterr().~unexpected<E>();
+ construct(rhs.get());
+ } else {
+ assign_common(rhs);
+ }
+ }
+
+ // This overload handles the case where we can attempt to create a copy of
+ // `T`, then no-throw move it into place if the copy was successful.
+ template <class U = T,
+ detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
+ std::is_nothrow_move_constructible<U>::value>
+ * = nullptr>
+ void assign(const expected_operations_base &rhs) noexcept {
+ if (!this->m_has_val && rhs.m_has_val) {
+ T tmp = rhs.get();
+ geterr().~unexpected<E>();
+ construct(std::move(tmp));
+ } else {
+ assign_common(rhs);
+ }
+ }
+
+ // This overload is the worst-case, where we have to move-construct the
+ // unexpected value into temporary storage, then try to copy the T into place.
+ // If the construction succeeds, then everything is fine, but if it throws,
+ // then we move the old unexpected value back into place before rethrowing the
+ // exception.
+ template <class U = T,
+ detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
+ !std::is_nothrow_move_constructible<U>::value>
+ * = nullptr>
+ void assign(const expected_operations_base &rhs) {
+ if (!this->m_has_val && rhs.m_has_val) {
+ auto tmp = std::move(geterr());
+ geterr().~unexpected<E>();
+
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+ try {
+ construct(rhs.get());
+ } catch (...) {
+ geterr() = std::move(tmp);
+ throw;
+ }
+#else
+ construct(rhs.get());
+#endif
+ } else {
+ assign_common(rhs);
+ }
+ }
+
+ // These overloads do the same as above, but for rvalues
+ template <class U = T,
+ detail::enable_if_t<std::is_nothrow_move_constructible<U>::value>
+ * = nullptr>
+ void assign(expected_operations_base &&rhs) noexcept {
+ if (!this->m_has_val && rhs.m_has_val) {
+ geterr().~unexpected<E>();
+ construct(std::move(rhs).get());
+ } else {
+ assign_common(std::move(rhs));
+ }
+ }
+
+ template <class U = T,
+ detail::enable_if_t<!std::is_nothrow_move_constructible<U>::value>
+ * = nullptr>
+ void assign(expected_operations_base &&rhs) {
+ if (!this->m_has_val && rhs.m_has_val) {
+ auto tmp = std::move(geterr());
+ geterr().~unexpected<E>();
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+ try {
+ construct(std::move(rhs).get());
+ } catch (...) {
+ geterr() = std::move(tmp);
+ throw;
+ }
+#else
+ construct(std::move(rhs).get());
+#endif
+ } else {
+ assign_common(std::move(rhs));
+ }
+ }
+
+#else
+
+ // If exceptions are disabled then we can just copy-construct
+ void assign(const expected_operations_base &rhs) noexcept {
+ if (!this->m_has_val && rhs.m_has_val) {
+ geterr().~unexpected<E>();
+ construct(rhs.get());
+ } else {
+ assign_common(rhs);
+ }
+ }
+
+ void assign(expected_operations_base &&rhs) noexcept {
+ if (!this->m_has_val && rhs.m_has_val) {
+ geterr().~unexpected<E>();
+ construct(std::move(rhs).get());
+ } else {
+ assign_common(std::move(rhs));
+ }
+ }
+
+#endif
+
+ // The common part of move/copy assigning
+ template <class Rhs> void assign_common(Rhs &&rhs) {
+ if (this->m_has_val) {
+ if (rhs.m_has_val) {
+ get() = std::forward<Rhs>(rhs).get();
+ } else {
+ destroy_val();
+ construct_error(std::forward<Rhs>(rhs).geterr());
+ }
+ } else {
+ if (!rhs.m_has_val) {
+ geterr() = std::forward<Rhs>(rhs).geterr();
+ }
+ }
+ }
+
+ bool has_value() const { return this->m_has_val; }
+
+ TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; }
+ constexpr const T &get() const & { return this->m_val; }
+ TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); }
+#ifndef TL_EXPECTED_NO_CONSTRR
+ constexpr const T &&get() const && { return std::move(this->m_val); }
+#endif
+
+ TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & {
+ return this->m_unexpect;
+ }
+ constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; }
+ TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && {
+ return std::move(this->m_unexpect);
+ }
+#ifndef TL_EXPECTED_NO_CONSTRR
+ constexpr const unexpected<E> &&geterr() const && {
+ return std::move(this->m_unexpect);
+ }
+#endif
+
+ TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); }
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class E>
+struct expected_operations_base<void, E> : expected_storage_base<void, E> {
+ using expected_storage_base<void, E>::expected_storage_base;
+
+ template <class... Args> void construct() noexcept { this->m_has_val = true; }
+
+ // This function doesn't use its argument, but needs it so that code in
+ // levels above this can work independently of whether T is void
+ template <class Rhs> void construct_with(Rhs &&) noexcept {
+ this->m_has_val = true;
+ }
+
+ template <class... Args> void construct_error(Args &&...args) noexcept {
+ new (std::addressof(this->m_unexpect))
+ unexpected<E>(std::forward<Args>(args)...);
+ this->m_has_val = false;
+ }
+
+ template <class Rhs> void assign(Rhs &&rhs) noexcept {
+ if (!this->m_has_val) {
+ if (rhs.m_has_val) {
+ geterr().~unexpected<E>();
+ construct();
+ } else {
+ geterr() = std::forward<Rhs>(rhs).geterr();
+ }
+ } else {
+ if (!rhs.m_has_val) {
+ construct_error(std::forward<Rhs>(rhs).geterr());
+ }
+ }
+ }
+
+ bool has_value() const { return this->m_has_val; }
+
+ TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & {
+ return this->m_unexpect;
+ }
+ constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; }
+ TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && {
+ return std::move(this->m_unexpect);
+ }
+#ifndef TL_EXPECTED_NO_CONSTRR
+ constexpr const unexpected<E> &&geterr() const && {
+ return std::move(this->m_unexpect);
+ }
+#endif
+
+ TL_EXPECTED_11_CONSTEXPR void destroy_val() {
+ // no-op
+ }
+};
+
+// This class manages conditionally having a trivial copy constructor
+// This specialization is for when T and E are trivially copy constructible
+template <class T, class E,
+ bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>::
+ value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value>
+struct expected_copy_base : expected_operations_base<T, E> {
+ using expected_operations_base<T, E>::expected_operations_base;
+};
+
+// This specialization is for when T or E are not trivially copy constructible
+template <class T, class E>
+struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
+ using expected_operations_base<T, E>::expected_operations_base;
+
+ expected_copy_base() = default;
+ expected_copy_base(const expected_copy_base &rhs)
+ : expected_operations_base<T, E>(no_init) {
+ if (rhs.has_value()) {
+ this->construct_with(rhs);
+ } else {
+ this->construct_error(rhs.geterr());
+ }
+ }
+
+ expected_copy_base(expected_copy_base &&rhs) = default;
+ expected_copy_base &operator=(const expected_copy_base &rhs) = default;
+ expected_copy_base &operator=(expected_copy_base &&rhs) = default;
+};
+
+// This class manages conditionally having a trivial move constructor
+// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
+// doesn't implement an analogue to std::is_trivially_move_constructible. We
+// have to make do with a non-trivial move constructor even if T is trivially
+// move constructible
+#ifndef TL_EXPECTED_GCC49
+template <class T, class E,
+ bool = is_void_or<T, std::is_trivially_move_constructible<T>>::value
+ &&std::is_trivially_move_constructible<E>::value>
+struct expected_move_base : expected_copy_base<T, E> {
+ using expected_copy_base<T, E>::expected_copy_base;
+};
+#else
+template <class T, class E, bool = false> struct expected_move_base;
+#endif
+template <class T, class E>
+struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
+ using expected_copy_base<T, E>::expected_copy_base;
+
+ expected_move_base() = default;
+ expected_move_base(const expected_move_base &rhs) = default;
+
+ expected_move_base(expected_move_base &&rhs) noexcept(
+ std::is_nothrow_move_constructible<T>::value)
+ : expected_copy_base<T, E>(no_init) {
+ if (rhs.has_value()) {
+ this->construct_with(std::move(rhs));
+ } else {
+ this->construct_error(std::move(rhs.geterr()));
+ }
+ }
+ expected_move_base &operator=(const expected_move_base &rhs) = default;
+ expected_move_base &operator=(expected_move_base &&rhs) = default;
+};
+
+// This class manages conditionally having a trivial copy assignment operator
+template <class T, class E,
+ bool = is_void_or<
+ T, conjunction<TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T),
+ TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
+ TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value
+ &&TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value
+ &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value
+ &&TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value>
+struct expected_copy_assign_base : expected_move_base<T, E> {
+ using expected_move_base<T, E>::expected_move_base;
+};
+
+template <class T, class E>
+struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
+ using expected_move_base<T, E>::expected_move_base;
+
+ expected_copy_assign_base() = default;
+ expected_copy_assign_base(const expected_copy_assign_base &rhs) = default;
+
+ expected_copy_assign_base(expected_copy_assign_base &&rhs) = default;
+ expected_copy_assign_base &operator=(const expected_copy_assign_base &rhs) {
+ this->assign(rhs);
+ return *this;
+ }
+ expected_copy_assign_base &
+ operator=(expected_copy_assign_base &&rhs) = default;
+};
+
+// This class manages conditionally having a trivial move assignment operator
+// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
+// doesn't implement an analogue to std::is_trivially_move_assignable. We have
+// to make do with a non-trivial move assignment operator even if T is trivially
+// move assignable
+#ifndef TL_EXPECTED_GCC49
+template <class T, class E,
+ bool =
+ is_void_or<T, conjunction<std::is_trivially_destructible<T>,
+ std::is_trivially_move_constructible<T>,
+ std::is_trivially_move_assignable<T>>>::
+ value &&std::is_trivially_destructible<E>::value
+ &&std::is_trivially_move_constructible<E>::value
+ &&std::is_trivially_move_assignable<E>::value>
+struct expected_move_assign_base : expected_copy_assign_base<T, E> {
+ using expected_copy_assign_base<T, E>::expected_copy_assign_base;
+};
+#else
+template <class T, class E, bool = false> struct expected_move_assign_base;
+#endif
+
+template <class T, class E>
+struct expected_move_assign_base<T, E, false>
+ : expected_copy_assign_base<T, E> {
+ using expected_copy_assign_base<T, E>::expected_copy_assign_base;
+
+ expected_move_assign_base() = default;
+ expected_move_assign_base(const expected_move_assign_base &rhs) = default;
+
+ expected_move_assign_base(expected_move_assign_base &&rhs) = default;
+
+ expected_move_assign_base &
+ operator=(const expected_move_assign_base &rhs) = default;
+
+ expected_move_assign_base &
+ operator=(expected_move_assign_base &&rhs) noexcept(
+ std::is_nothrow_move_constructible<T>::value
+ &&std::is_nothrow_move_assignable<T>::value) {
+ this->assign(std::move(rhs));
+ return *this;
+ }
+};
+
+// expected_delete_ctor_base will conditionally delete copy and move
+// constructors depending on whether T is copy/move constructible
+template <class T, class E,
+ bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
+ std::is_copy_constructible<E>::value),
+ bool EnableMove = (is_move_constructible_or_void<T>::value &&
+ std::is_move_constructible<E>::value)>
+struct expected_delete_ctor_base {
+ expected_delete_ctor_base() = default;
+ expected_delete_ctor_base(const expected_delete_ctor_base &) = default;
+ expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default;
+ expected_delete_ctor_base &
+ operator=(const expected_delete_ctor_base &) = default;
+ expected_delete_ctor_base &
+ operator=(expected_delete_ctor_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_ctor_base<T, E, true, false> {
+ expected_delete_ctor_base() = default;
+ expected_delete_ctor_base(const expected_delete_ctor_base &) = default;
+ expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete;
+ expected_delete_ctor_base &
+ operator=(const expected_delete_ctor_base &) = default;
+ expected_delete_ctor_base &
+ operator=(expected_delete_ctor_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_ctor_base<T, E, false, true> {
+ expected_delete_ctor_base() = default;
+ expected_delete_ctor_base(const expected_delete_ctor_base &) = delete;
+ expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default;
+ expected_delete_ctor_base &
+ operator=(const expected_delete_ctor_base &) = default;
+ expected_delete_ctor_base &
+ operator=(expected_delete_ctor_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_ctor_base<T, E, false, false> {
+ expected_delete_ctor_base() = default;
+ expected_delete_ctor_base(const expected_delete_ctor_base &) = delete;
+ expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete;
+ expected_delete_ctor_base &
+ operator=(const expected_delete_ctor_base &) = default;
+ expected_delete_ctor_base &
+ operator=(expected_delete_ctor_base &&) noexcept = default;
+};
+
+// expected_delete_assign_base will conditionally delete copy and move
+// constructors depending on whether T and E are copy/move constructible +
+// assignable
+template <class T, class E,
+ bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
+ std::is_copy_constructible<E>::value &&
+ is_copy_assignable_or_void<T>::value &&
+ std::is_copy_assignable<E>::value),
+ bool EnableMove = (is_move_constructible_or_void<T>::value &&
+ std::is_move_constructible<E>::value &&
+ is_move_assignable_or_void<T>::value &&
+ std::is_move_assignable<E>::value)>
+struct expected_delete_assign_base {
+ expected_delete_assign_base() = default;
+ expected_delete_assign_base(const expected_delete_assign_base &) = default;
+ expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
+ default;
+ expected_delete_assign_base &
+ operator=(const expected_delete_assign_base &) = default;
+ expected_delete_assign_base &
+ operator=(expected_delete_assign_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_assign_base<T, E, true, false> {
+ expected_delete_assign_base() = default;
+ expected_delete_assign_base(const expected_delete_assign_base &) = default;
+ expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
+ default;
+ expected_delete_assign_base &
+ operator=(const expected_delete_assign_base &) = default;
+ expected_delete_assign_base &
+ operator=(expected_delete_assign_base &&) noexcept = delete;
+};
+
+template <class T, class E>
+struct expected_delete_assign_base<T, E, false, true> {
+ expected_delete_assign_base() = default;
+ expected_delete_assign_base(const expected_delete_assign_base &) = default;
+ expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
+ default;
+ expected_delete_assign_base &
+ operator=(const expected_delete_assign_base &) = delete;
+ expected_delete_assign_base &
+ operator=(expected_delete_assign_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_assign_base<T, E, false, false> {
+ expected_delete_assign_base() = default;
+ expected_delete_assign_base(const expected_delete_assign_base &) = default;
+ expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
+ default;
+ expected_delete_assign_base &
+ operator=(const expected_delete_assign_base &) = delete;
+ expected_delete_assign_base &
+ operator=(expected_delete_assign_base &&) noexcept = delete;
+};
+
+// This is needed to be able to construct the expected_default_ctor_base which
+// follows, while still conditionally deleting the default constructor.
+struct default_constructor_tag {
+ explicit constexpr default_constructor_tag() = default;
+};
+
+// expected_default_ctor_base will ensure that expected has a deleted default
+// consturctor if T is not default constructible.
+// This specialization is for when T is default constructible
+template <class T, class E,
+ bool Enable =
+ std::is_default_constructible<T>::value || std::is_void<T>::value>
+struct expected_default_ctor_base {
+ constexpr expected_default_ctor_base() noexcept = default;
+ constexpr expected_default_ctor_base(
+ expected_default_ctor_base const &) noexcept = default;
+ constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept =
+ default;
+ expected_default_ctor_base &
+ operator=(expected_default_ctor_base const &) noexcept = default;
+ expected_default_ctor_base &
+ operator=(expected_default_ctor_base &&) noexcept = default;
+
+ constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
+};
+
+// This specialization is for when T is not default constructible
+template <class T, class E> struct expected_default_ctor_base<T, E, false> {
+ constexpr expected_default_ctor_base() noexcept = delete;
+ constexpr expected_default_ctor_base(
+ expected_default_ctor_base const &) noexcept = default;
+ constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept =
+ default;
+ expected_default_ctor_base &
+ operator=(expected_default_ctor_base const &) noexcept = default;
+ expected_default_ctor_base &
+ operator=(expected_default_ctor_base &&) noexcept = default;
+
+ constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
+};
+} // namespace detail
+
+template <class E> class bad_expected_access : public std::exception {
+public:
+ explicit bad_expected_access(E e) : m_val(std::move(e)) {}
+
+ virtual const char *what() const noexcept override {
+ return "Bad expected access";
+ }
+
+ const E &error() const & { return m_val; }
+ E &error() & { return m_val; }
+ const E &&error() const && { return std::move(m_val); }
+ E &&error() && { return std::move(m_val); }
+
+private:
+ E m_val;
+};
+
+/// An `expected<T, E>` object is an object that contains the storage for
+/// another object and manages the lifetime of this contained object `T`.
+/// Alternatively it could contain the storage for another unexpected object
+/// `E`. The contained object may not be initialized after the expected object
+/// has been initialized, and may not be destroyed before the expected object
+/// has been destroyed. The initialization state of the contained object is
+/// tracked by the expected object.
+template <class T, class E>
+class expected : private detail::expected_move_assign_base<T, E>,
+ private detail::expected_delete_ctor_base<T, E>,
+ private detail::expected_delete_assign_base<T, E>,
+ private detail::expected_default_ctor_base<T, E> {
+ static_assert(!std::is_reference<T>::value, "T must not be a reference");
+ static_assert(!std::is_same<T, std::remove_cv<in_place_t>::type>::value,
+ "T must not be in_place_t");
+ static_assert(!std::is_same<T, std::remove_cv<unexpect_t>::type>::value,
+ "T must not be unexpect_t");
+ static_assert(
+ !std::is_same<T, typename std::remove_cv<unexpected<E>>::type>::value,
+ "T must not be unexpected<E>");
+ static_assert(!std::is_reference<E>::value, "E must not be a reference");
+
+ T *valptr() { return std::addressof(this->m_val); }
+ const T *valptr() const { return std::addressof(this->m_val); }
+ unexpected<E> *errptr() { return std::addressof(this->m_unexpect); }
+ const unexpected<E> *errptr() const {
+ return std::addressof(this->m_unexpect);
+ }
+
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR U &val() {
+ return this->m_val;
+ }
+ TL_EXPECTED_11_CONSTEXPR unexpected<E> &err() { return this->m_unexpect; }
+
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ constexpr const U &val() const {
+ return this->m_val;
+ }
+ constexpr const unexpected<E> &err() const { return this->m_unexpect; }
+
+ using impl_base = detail::expected_move_assign_base<T, E>;
+ using ctor_base = detail::expected_default_ctor_base<T, E>;
+
+public:
+ typedef T value_type;
+ typedef E error_type;
+ typedef unexpected<E> unexpected_type;
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
+ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & {
+ return and_then_impl(*this, std::forward<F>(f));
+ }
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && {
+ return and_then_impl(std::move(*this), std::forward<F>(f));
+ }
+ template <class F> constexpr auto and_then(F &&f) const & {
+ return and_then_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+ template <class F> constexpr auto and_then(F &&f) const && {
+ return and_then_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+
+#else
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR auto
+ and_then(F &&f) & -> decltype(and_then_impl(std::declval<expected &>(),
+ std::forward<F>(f))) {
+ return and_then_impl(*this, std::forward<F>(f));
+ }
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR auto
+ and_then(F &&f) && -> decltype(and_then_impl(std::declval<expected &&>(),
+ std::forward<F>(f))) {
+ return and_then_impl(std::move(*this), std::forward<F>(f));
+ }
+ template <class F>
+ constexpr auto and_then(F &&f) const & -> decltype(and_then_impl(
+ std::declval<expected const &>(), std::forward<F>(f))) {
+ return and_then_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+ template <class F>
+ constexpr auto and_then(F &&f) const && -> decltype(and_then_impl(
+ std::declval<expected const &&>(), std::forward<F>(f))) {
+ return and_then_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+#endif
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
+ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & {
+ return expected_map_impl(*this, std::forward<F>(f));
+ }
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && {
+ return expected_map_impl(std::move(*this), std::forward<F>(f));
+ }
+ template <class F> constexpr auto map(F &&f) const & {
+ return expected_map_impl(*this, std::forward<F>(f));
+ }
+ template <class F> constexpr auto map(F &&f) const && {
+ return expected_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#else
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
+ std::declval<expected &>(), std::declval<F &&>()))
+ map(F &&f) & {
+ return expected_map_impl(*this, std::forward<F>(f));
+ }
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
+ std::declval<F &&>()))
+ map(F &&f) && {
+ return expected_map_impl(std::move(*this), std::forward<F>(f));
+ }
+ template <class F>
+ constexpr decltype(expected_map_impl(std::declval<const expected &>(),
+ std::declval<F &&>()))
+ map(F &&f) const & {
+ return expected_map_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+ template <class F>
+ constexpr decltype(expected_map_impl(std::declval<const expected &&>(),
+ std::declval<F &&>()))
+ map(F &&f) const && {
+ return expected_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+#endif
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
+ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & {
+ return expected_map_impl(*this, std::forward<F>(f));
+ }
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && {
+ return expected_map_impl(std::move(*this), std::forward<F>(f));
+ }
+ template <class F> constexpr auto transform(F &&f) const & {
+ return expected_map_impl(*this, std::forward<F>(f));
+ }
+ template <class F> constexpr auto transform(F &&f) const && {
+ return expected_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#else
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
+ std::declval<expected &>(), std::declval<F &&>()))
+ transform(F &&f) & {
+ return expected_map_impl(*this, std::forward<F>(f));
+ }
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
+ std::declval<F &&>()))
+ transform(F &&f) && {
+ return expected_map_impl(std::move(*this), std::forward<F>(f));
+ }
+ template <class F>
+ constexpr decltype(expected_map_impl(std::declval<const expected &>(),
+ std::declval<F &&>()))
+ transform(F &&f) const & {
+ return expected_map_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+ template <class F>
+ constexpr decltype(expected_map_impl(std::declval<const expected &&>(),
+ std::declval<F &&>()))
+ transform(F &&f) const && {
+ return expected_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+#endif
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
+ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & {
+ return map_error_impl(*this, std::forward<F>(f));
+ }
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && {
+ return map_error_impl(std::move(*this), std::forward<F>(f));
+ }
+ template <class F> constexpr auto map_error(F &&f) const & {
+ return map_error_impl(*this, std::forward<F>(f));
+ }
+ template <class F> constexpr auto map_error(F &&f) const && {
+ return map_error_impl(std::move(*this), std::forward<F>(f));
+ }
+#else
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(),
+ std::declval<F &&>()))
+ map_error(F &&f) & {
+ return map_error_impl(*this, std::forward<F>(f));
+ }
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(),
+ std::declval<F &&>()))
+ map_error(F &&f) && {
+ return map_error_impl(std::move(*this), std::forward<F>(f));
+ }
+ template <class F>
+ constexpr decltype(map_error_impl(std::declval<const expected &>(),
+ std::declval<F &&>()))
+ map_error(F &&f) const & {
+ return map_error_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+ template <class F>
+ constexpr decltype(map_error_impl(std::declval<const expected &&>(),
+ std::declval<F &&>()))
+ map_error(F &&f) const && {
+ return map_error_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+#endif
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
+ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & {
+ return map_error_impl(*this, std::forward<F>(f));
+ }
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && {
+ return map_error_impl(std::move(*this), std::forward<F>(f));
+ }
+ template <class F> constexpr auto transform_error(F &&f) const & {
+ return map_error_impl(*this, std::forward<F>(f));
+ }
+ template <class F> constexpr auto transform_error(F &&f) const && {
+ return map_error_impl(std::move(*this), std::forward<F>(f));
+ }
+#else
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(),
+ std::declval<F &&>()))
+ transform_error(F &&f) & {
+ return map_error_impl(*this, std::forward<F>(f));
+ }
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(),
+ std::declval<F &&>()))
+ transform_error(F &&f) && {
+ return map_error_impl(std::move(*this), std::forward<F>(f));
+ }
+ template <class F>
+ constexpr decltype(map_error_impl(std::declval<const expected &>(),
+ std::declval<F &&>()))
+ transform_error(F &&f) const & {
+ return map_error_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+ template <class F>
+ constexpr decltype(map_error_impl(std::declval<const expected &&>(),
+ std::declval<F &&>()))
+ transform_error(F &&f) const && {
+ return map_error_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+#endif
+ template <class F> expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & {
+ return or_else_impl(*this, std::forward<F>(f));
+ }
+
+ template <class F> expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && {
+ return or_else_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ template <class F> expected constexpr or_else(F &&f) const & {
+ return or_else_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+ template <class F> expected constexpr or_else(F &&f) const && {
+ return or_else_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+ constexpr expected() = default;
+ constexpr expected(const expected &rhs) = default;
+ constexpr expected(expected &&rhs) = default;
+ expected &operator=(const expected &rhs) = default;
+ expected &operator=(expected &&rhs) = default;
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+ nullptr>
+ constexpr expected(in_place_t, Args &&...args)
+ : impl_base(in_place, std::forward<Args>(args)...),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr expected(in_place_t, std::initializer_list<U> il, Args &&...args)
+ : impl_base(in_place, il, std::forward<Args>(args)...),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ template <class G = E,
+ detail::enable_if_t<std::is_constructible<E, const G &>::value> * =
+ nullptr,
+ detail::enable_if_t<!std::is_convertible<const G &, E>::value> * =
+ nullptr>
+ explicit constexpr expected(const unexpected<G> &e)
+ : impl_base(unexpect, e.value()),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ template <
+ class G = E,
+ detail::enable_if_t<std::is_constructible<E, const G &>::value> * =
+ nullptr,
+ detail::enable_if_t<std::is_convertible<const G &, E>::value> * = nullptr>
+ constexpr expected(unexpected<G> const &e)
+ : impl_base(unexpect, e.value()),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ template <
+ class G = E,
+ detail::enable_if_t<std::is_constructible<E, G &&>::value> * = nullptr,
+ detail::enable_if_t<!std::is_convertible<G &&, E>::value> * = nullptr>
+ explicit constexpr expected(unexpected<G> &&e) noexcept(
+ std::is_nothrow_constructible<E, G &&>::value)
+ : impl_base(unexpect, std::move(e.value())),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ template <
+ class G = E,
+ detail::enable_if_t<std::is_constructible<E, G &&>::value> * = nullptr,
+ detail::enable_if_t<std::is_convertible<G &&, E>::value> * = nullptr>
+ constexpr expected(unexpected<G> &&e) noexcept(
+ std::is_nothrow_constructible<E, G &&>::value)
+ : impl_base(unexpect, std::move(e.value())),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+ nullptr>
+ constexpr explicit expected(unexpect_t, Args &&...args)
+ : impl_base(unexpect, std::forward<Args>(args)...),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected(unexpect_t, std::initializer_list<U> il,
+ Args &&...args)
+ : impl_base(unexpect, il, std::forward<Args>(args)...),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ template <class U, class G,
+ detail::enable_if_t<!(std::is_convertible<U const &, T>::value &&
+ std::is_convertible<G const &, E>::value)> * =
+ nullptr,
+ detail::expected_enable_from_other<T, E, U, G, const U &, const G &>
+ * = nullptr>
+ explicit TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs)
+ : ctor_base(detail::default_constructor_tag{}) {
+ if (rhs.has_value()) {
+ this->construct(*rhs);
+ } else {
+ this->construct_error(rhs.error());
+ }
+ }
+
+ template <class U, class G,
+ detail::enable_if_t<(std::is_convertible<U const &, T>::value &&
+ std::is_convertible<G const &, E>::value)> * =
+ nullptr,
+ detail::expected_enable_from_other<T, E, U, G, const U &, const G &>
+ * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs)
+ : ctor_base(detail::default_constructor_tag{}) {
+ if (rhs.has_value()) {
+ this->construct(*rhs);
+ } else {
+ this->construct_error(rhs.error());
+ }
+ }
+
+ template <
+ class U, class G,
+ detail::enable_if_t<!(std::is_convertible<U &&, T>::value &&
+ std::is_convertible<G &&, E>::value)> * = nullptr,
+ detail::expected_enable_from_other<T, E, U, G, U &&, G &&> * = nullptr>
+ explicit TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs)
+ : ctor_base(detail::default_constructor_tag{}) {
+ if (rhs.has_value()) {
+ this->construct(std::move(*rhs));
+ } else {
+ this->construct_error(std::move(rhs.error()));
+ }
+ }
+
+ template <
+ class U, class G,
+ detail::enable_if_t<(std::is_convertible<U &&, T>::value &&
+ std::is_convertible<G &&, E>::value)> * = nullptr,
+ detail::expected_enable_from_other<T, E, U, G, U &&, G &&> * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs)
+ : ctor_base(detail::default_constructor_tag{}) {
+ if (rhs.has_value()) {
+ this->construct(std::move(*rhs));
+ } else {
+ this->construct_error(std::move(rhs.error()));
+ }
+ }
+
+ template <
+ class U = T,
+ detail::enable_if_t<!std::is_convertible<U &&, T>::value> * = nullptr,
+ detail::expected_enable_forward_value<T, E, U> * = nullptr>
+ explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v)
+ : expected(in_place, std::forward<U>(v)) {}
+
+ template <
+ class U = T,
+ detail::enable_if_t<std::is_convertible<U &&, T>::value> * = nullptr,
+ detail::expected_enable_forward_value<T, E, U> * = nullptr>
+ TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v)
+ : expected(in_place, std::forward<U>(v)) {}
+
+ template <
+ class U = T, class G = T,
+ detail::enable_if_t<std::is_nothrow_constructible<T, U &&>::value> * =
+ nullptr,
+ detail::enable_if_t<!std::is_void<G>::value> * = nullptr,
+ detail::enable_if_t<
+ (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
+ !detail::conjunction<std::is_scalar<T>,
+ std::is_same<T, detail::decay_t<U>>>::value &&
+ std::is_constructible<T, U>::value &&
+ std::is_assignable<G &, U>::value &&
+ std::is_nothrow_move_constructible<E>::value)> * = nullptr>
+ expected &operator=(U &&v) {
+ if (has_value()) {
+ val() = std::forward<U>(v);
+ } else {
+ err().~unexpected<E>();
+ ::new (valptr()) T(std::forward<U>(v));
+ this->m_has_val = true;
+ }
+
+ return *this;
+ }
+
+ template <
+ class U = T, class G = T,
+ detail::enable_if_t<!std::is_nothrow_constructible<T, U &&>::value> * =
+ nullptr,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr,
+ detail::enable_if_t<
+ (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
+ !detail::conjunction<std::is_scalar<T>,
+ std::is_same<T, detail::decay_t<U>>>::value &&
+ std::is_constructible<T, U>::value &&
+ std::is_assignable<G &, U>::value &&
+ std::is_nothrow_move_constructible<E>::value)> * = nullptr>
+ expected &operator=(U &&v) {
+ if (has_value()) {
+ val() = std::forward<U>(v);
+ } else {
+ auto tmp = std::move(err());
+ err().~unexpected<E>();
+
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+ try {
+ ::new (valptr()) T(std::forward<U>(v));
+ this->m_has_val = true;
+ } catch (...) {
+ err() = std::move(tmp);
+ throw;
+ }
+#else
+ ::new (valptr()) T(std::forward<U>(v));
+ this->m_has_val = true;
+#endif
+ }
+
+ return *this;
+ }
+
+ template <class G = E,
+ detail::enable_if_t<std::is_nothrow_copy_constructible<G>::value &&
+ std::is_assignable<G &, G>::value> * = nullptr>
+ expected &operator=(const unexpected<G> &rhs) {
+ if (!has_value()) {
+ err() = rhs;
+ } else {
+ this->destroy_val();
+ ::new (errptr()) unexpected<E>(rhs);
+ this->m_has_val = false;
+ }
+
+ return *this;
+ }
+
+ template <class G = E,
+ detail::enable_if_t<std::is_nothrow_move_constructible<G>::value &&
+ std::is_move_assignable<G>::value> * = nullptr>
+ expected &operator=(unexpected<G> &&rhs) noexcept {
+ if (!has_value()) {
+ err() = std::move(rhs);
+ } else {
+ this->destroy_val();
+ ::new (errptr()) unexpected<E>(std::move(rhs));
+ this->m_has_val = false;
+ }
+
+ return *this;
+ }
+
+ template <class... Args, detail::enable_if_t<std::is_nothrow_constructible<
+ T, Args &&...>::value> * = nullptr>
+ void emplace(Args &&...args) {
+ if (has_value()) {
+ val().~T();
+ } else {
+ err().~unexpected<E>();
+ this->m_has_val = true;
+ }
+ ::new (valptr()) T(std::forward<Args>(args)...);
+ }
+
+ template <class... Args, detail::enable_if_t<!std::is_nothrow_constructible<
+ T, Args &&...>::value> * = nullptr>
+ void emplace(Args &&...args) {
+ if (has_value()) {
+ val().~T();
+ ::new (valptr()) T(std::forward<Args>(args)...);
+ } else {
+ auto tmp = std::move(err());
+ err().~unexpected<E>();
+
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+ try {
+ ::new (valptr()) T(std::forward<Args>(args)...);
+ this->m_has_val = true;
+ } catch (...) {
+ err() = std::move(tmp);
+ throw;
+ }
+#else
+ ::new (valptr()) T(std::forward<Args>(args)...);
+ this->m_has_val = true;
+#endif
+ }
+ }
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_nothrow_constructible<
+ T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ void emplace(std::initializer_list<U> il, Args &&...args) {
+ if (has_value()) {
+ T t(il, std::forward<Args>(args)...);
+ val() = std::move(t);
+ } else {
+ err().~unexpected<E>();
+ ::new (valptr()) T(il, std::forward<Args>(args)...);
+ this->m_has_val = true;
+ }
+ }
+
+ template <class U, class... Args,
+ detail::enable_if_t<!std::is_nothrow_constructible<
+ T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ void emplace(std::initializer_list<U> il, Args &&...args) {
+ if (has_value()) {
+ T t(il, std::forward<Args>(args)...);
+ val() = std::move(t);
+ } else {
+ auto tmp = std::move(err());
+ err().~unexpected<E>();
+
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+ try {
+ ::new (valptr()) T(il, std::forward<Args>(args)...);
+ this->m_has_val = true;
+ } catch (...) {
+ err() = std::move(tmp);
+ throw;
+ }
+#else
+ ::new (valptr()) T(il, std::forward<Args>(args)...);
+ this->m_has_val = true;
+#endif
+ }
+ }
+
+private:
+ using t_is_void = std::true_type;
+ using t_is_not_void = std::false_type;
+ using t_is_nothrow_move_constructible = std::true_type;
+ using move_constructing_t_can_throw = std::false_type;
+ using e_is_nothrow_move_constructible = std::true_type;
+ using move_constructing_e_can_throw = std::false_type;
+
+ void swap_where_both_have_value(expected & /*rhs*/, t_is_void) noexcept {
+ // swapping void is a no-op
+ }
+
+ void swap_where_both_have_value(expected &rhs, t_is_not_void) {
+ using std::swap;
+ swap(val(), rhs.val());
+ }
+
+ void swap_where_only_one_has_value(expected &rhs, t_is_void) noexcept(
+ std::is_nothrow_move_constructible<E>::value) {
+ ::new (errptr()) unexpected_type(std::move(rhs.err()));
+ rhs.err().~unexpected_type();
+ std::swap(this->m_has_val, rhs.m_has_val);
+ }
+
+ void swap_where_only_one_has_value(expected &rhs, t_is_not_void) {
+ swap_where_only_one_has_value_and_t_is_not_void(
+ rhs, typename std::is_nothrow_move_constructible<T>::type{},
+ typename std::is_nothrow_move_constructible<E>::type{});
+ }
+
+ void swap_where_only_one_has_value_and_t_is_not_void(
+ expected &rhs, t_is_nothrow_move_constructible,
+ e_is_nothrow_move_constructible) noexcept {
+ auto temp = std::move(val());
+ val().~T();
+ ::new (errptr()) unexpected_type(std::move(rhs.err()));
+ rhs.err().~unexpected_type();
+ ::new (rhs.valptr()) T(std::move(temp));
+ std::swap(this->m_has_val, rhs.m_has_val);
+ }
+
+ void swap_where_only_one_has_value_and_t_is_not_void(
+ expected &rhs, t_is_nothrow_move_constructible,
+ move_constructing_e_can_throw) {
+ auto temp = std::move(val());
+ val().~T();
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+ try {
+ ::new (errptr()) unexpected_type(std::move(rhs.err()));
+ rhs.err().~unexpected_type();
+ ::new (rhs.valptr()) T(std::move(temp));
+ std::swap(this->m_has_val, rhs.m_has_val);
+ } catch (...) {
+ val() = std::move(temp);
+ throw;
+ }
+#else
+ ::new (errptr()) unexpected_type(std::move(rhs.err()));
+ rhs.err().~unexpected_type();
+ ::new (rhs.valptr()) T(std::move(temp));
+ std::swap(this->m_has_val, rhs.m_has_val);
+#endif
+ }
+
+ void swap_where_only_one_has_value_and_t_is_not_void(
+ expected &rhs, move_constructing_t_can_throw,
+ e_is_nothrow_move_constructible) {
+ auto temp = std::move(rhs.err());
+ rhs.err().~unexpected_type();
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+ try {
+ ::new (rhs.valptr()) T(std::move(val()));
+ val().~T();
+ ::new (errptr()) unexpected_type(std::move(temp));
+ std::swap(this->m_has_val, rhs.m_has_val);
+ } catch (...) {
+ rhs.err() = std::move(temp);
+ throw;
+ }
+#else
+ ::new (rhs.valptr()) T(std::move(val()));
+ val().~T();
+ ::new (errptr()) unexpected_type(std::move(temp));
+ std::swap(this->m_has_val, rhs.m_has_val);
+#endif
+ }
+
+public:
+ template <class OT = T, class OE = E>
+ detail::enable_if_t<detail::is_swappable<OT>::value &&
+ detail::is_swappable<OE>::value &&
+ (std::is_nothrow_move_constructible<OT>::value ||
+ std::is_nothrow_move_constructible<OE>::value)>
+ swap(expected &rhs) noexcept(
+ std::is_nothrow_move_constructible<T>::value
+ &&detail::is_nothrow_swappable<T>::value
+ &&std::is_nothrow_move_constructible<E>::value
+ &&detail::is_nothrow_swappable<E>::value) {
+ if (has_value() && rhs.has_value()) {
+ swap_where_both_have_value(rhs, typename std::is_void<T>::type{});
+ } else if (!has_value() && rhs.has_value()) {
+ rhs.swap(*this);
+ } else if (has_value()) {
+ swap_where_only_one_has_value(rhs, typename std::is_void<T>::type{});
+ } else {
+ using std::swap;
+ swap(err(), rhs.err());
+ }
+ }
+
+ constexpr const T *operator->() const {
+ TL_ASSERT(has_value());
+ return valptr();
+ }
+ TL_EXPECTED_11_CONSTEXPR T *operator->() {
+ TL_ASSERT(has_value());
+ return valptr();
+ }
+
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ constexpr const U &operator*() const & {
+ TL_ASSERT(has_value());
+ return val();
+ }
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR U &operator*() & {
+ TL_ASSERT(has_value());
+ return val();
+ }
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ constexpr const U &&operator*() const && {
+ TL_ASSERT(has_value());
+ return std::move(val());
+ }
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR U &&operator*() && {
+ TL_ASSERT(has_value());
+ return std::move(val());
+ }
+
+ constexpr bool has_value() const noexcept { return this->m_has_val; }
+ constexpr explicit operator bool() const noexcept { return this->m_has_val; }
+
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR const U &value() const & {
+ if (!has_value())
+ detail::throw_exception(bad_expected_access<E>(err().value()));
+ return val();
+ }
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR U &value() & {
+ if (!has_value())
+ detail::throw_exception(bad_expected_access<E>(err().value()));
+ return val();
+ }
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR const U &&value() const && {
+ if (!has_value())
+ detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
+ return std::move(val());
+ }
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR U &&value() && {
+ if (!has_value())
+ detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
+ return std::move(val());
+ }
+
+ constexpr const E &error() const & {
+ TL_ASSERT(!has_value());
+ return err().value();
+ }
+ TL_EXPECTED_11_CONSTEXPR E &error() & {
+ TL_ASSERT(!has_value());
+ return err().value();
+ }
+ constexpr const E &&error() const && {
+ TL_ASSERT(!has_value());
+ return std::move(err().value());
+ }
+ TL_EXPECTED_11_CONSTEXPR E &&error() && {
+ TL_ASSERT(!has_value());
+ return std::move(err().value());
+ }
+
+ template <class U> constexpr T value_or(U &&v) const & {
+ static_assert(std::is_copy_constructible<T>::value &&
+ std::is_convertible<U &&, T>::value,
+ "T must be copy-constructible and convertible to from U&&");
+ return bool(*this) ? **this : static_cast<T>(std::forward<U>(v));
+ }
+ template <class U> TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && {
+ static_assert(std::is_move_constructible<T>::value &&
+ std::is_convertible<U &&, T>::value,
+ "T must be move-constructible and convertible to from U&&");
+ return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v));
+ }
+};
+
+namespace detail {
+template <class Exp> using exp_t = typename detail::decay_t<Exp>::value_type;
+template <class Exp> using err_t = typename detail::decay_t<Exp>::error_type;
+template <class Exp, class Ret> using ret_t = expected<Ret, err_t<Exp>>;
+
+#ifdef TL_EXPECTED_CXX14
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ *std::declval<Exp>()))>
+constexpr auto and_then_impl(Exp &&exp, F &&f) {
+ static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+
+ return exp.has_value()
+ ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
+ : Ret(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>()))>
+constexpr auto and_then_impl(Exp &&exp, F &&f) {
+ static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+
+ return exp.has_value() ? detail::invoke(std::forward<F>(f))
+ : Ret(unexpect, std::forward<Exp>(exp).error());
+}
+#else
+template <class> struct TC;
+template <class Exp, class F,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ *std::declval<Exp>())),
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr>
+auto and_then_impl(Exp &&exp, F &&f) -> Ret {
+ static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+
+ return exp.has_value()
+ ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
+ : Ret(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ class Ret = decltype(detail::invoke(std::declval<F>())),
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr>
+constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret {
+ static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+
+ return exp.has_value() ? detail::invoke(std::forward<F>(f))
+ : Ret(unexpect, std::forward<Exp>(exp).error());
+}
+#endif
+
+#ifdef TL_EXPECTED_CXX14
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ *std::declval<Exp>())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto expected_map_impl(Exp &&exp, F &&f) {
+ using result = ret_t<Exp, detail::decay_t<Ret>>;
+ return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
+ *std::forward<Exp>(exp)))
+ : result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ *std::declval<Exp>())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto expected_map_impl(Exp &&exp, F &&f) {
+ using result = expected<void, err_t<Exp>>;
+ if (exp.has_value()) {
+ detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
+ return result();
+ }
+
+ return result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto expected_map_impl(Exp &&exp, F &&f) {
+ using result = ret_t<Exp, detail::decay_t<Ret>>;
+ return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
+ : result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto expected_map_impl(Exp &&exp, F &&f) {
+ using result = expected<void, err_t<Exp>>;
+ if (exp.has_value()) {
+ detail::invoke(std::forward<F>(f));
+ return result();
+ }
+
+ return result(unexpect, std::forward<Exp>(exp).error());
+}
+#else
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ *std::declval<Exp>())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+
+constexpr auto expected_map_impl(Exp &&exp, F &&f)
+ -> ret_t<Exp, detail::decay_t<Ret>> {
+ using result = ret_t<Exp, detail::decay_t<Ret>>;
+
+ return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
+ *std::forward<Exp>(exp)))
+ : result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ *std::declval<Exp>())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+
+auto expected_map_impl(Exp &&exp, F &&f) -> expected<void, err_t<Exp>> {
+ if (exp.has_value()) {
+ detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
+ return {};
+ }
+
+ return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+
+constexpr auto expected_map_impl(Exp &&exp, F &&f)
+ -> ret_t<Exp, detail::decay_t<Ret>> {
+ using result = ret_t<Exp, detail::decay_t<Ret>>;
+
+ return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
+ : result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+
+auto expected_map_impl(Exp &&exp, F &&f) -> expected<void, err_t<Exp>> {
+ if (exp.has_value()) {
+ detail::invoke(std::forward<F>(f));
+ return {};
+ }
+
+ return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
+}
+#endif
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
+ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto map_error_impl(Exp &&exp, F &&f) {
+ using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
+ return exp.has_value()
+ ? result(*std::forward<Exp>(exp))
+ : result(unexpect, detail::invoke(std::forward<F>(f),
+ std::forward<Exp>(exp).error()));
+}
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto map_error_impl(Exp &&exp, F &&f) {
+ using result = expected<exp_t<Exp>, monostate>;
+ if (exp.has_value()) {
+ return result(*std::forward<Exp>(exp));
+ }
+
+ detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
+ return result(unexpect, monostate{});
+}
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto map_error_impl(Exp &&exp, F &&f) {
+ using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
+ return exp.has_value()
+ ? result()
+ : result(unexpect, detail::invoke(std::forward<F>(f),
+ std::forward<Exp>(exp).error()));
+}
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto map_error_impl(Exp &&exp, F &&f) {
+ using result = expected<exp_t<Exp>, monostate>;
+ if (exp.has_value()) {
+ return result();
+ }
+
+ detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
+ return result(unexpect, monostate{});
+}
+#else
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto map_error_impl(Exp &&exp, F &&f)
+ -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
+ using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
+
+ return exp.has_value()
+ ? result(*std::forward<Exp>(exp))
+ : result(unexpect, detail::invoke(std::forward<F>(f),
+ std::forward<Exp>(exp).error()));
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> {
+ using result = expected<exp_t<Exp>, monostate>;
+ if (exp.has_value()) {
+ return result(*std::forward<Exp>(exp));
+ }
+
+ detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
+ return result(unexpect, monostate{});
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto map_error_impl(Exp &&exp, F &&f)
+ -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
+ using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
+
+ return exp.has_value()
+ ? result()
+ : result(unexpect, detail::invoke(std::forward<F>(f),
+ std::forward<Exp>(exp).error()));
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> {
+ using result = expected<exp_t<Exp>, monostate>;
+ if (exp.has_value()) {
+ return result();
+ }
+
+ detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
+ return result(unexpect, monostate{});
+}
+#endif
+
+#ifdef TL_EXPECTED_CXX14
+template <class Exp, class F,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto or_else_impl(Exp &&exp, F &&f) {
+ static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+ return exp.has_value() ? std::forward<Exp>(exp)
+ : detail::invoke(std::forward<F>(f),
+ std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
+ return exp.has_value() ? std::forward<Exp>(exp)
+ : (detail::invoke(std::forward<F>(f),
+ std::forward<Exp>(exp).error()),
+ std::forward<Exp>(exp));
+}
+#else
+template <class Exp, class F,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+auto or_else_impl(Exp &&exp, F &&f) -> Ret {
+ static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+ return exp.has_value() ? std::forward<Exp>(exp)
+ : detail::invoke(std::forward<F>(f),
+ std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
+ return exp.has_value() ? std::forward<Exp>(exp)
+ : (detail::invoke(std::forward<F>(f),
+ std::forward<Exp>(exp).error()),
+ std::forward<Exp>(exp));
+}
+#endif
+} // namespace detail
+
+template <class T, class E, class U, class F>
+constexpr bool operator==(const expected<T, E> &lhs,
+ const expected<U, F> &rhs) {
+ return (lhs.has_value() != rhs.has_value())
+ ? false
+ : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs);
+}
+template <class T, class E, class U, class F>
+constexpr bool operator!=(const expected<T, E> &lhs,
+ const expected<U, F> &rhs) {
+ return (lhs.has_value() != rhs.has_value())
+ ? true
+ : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs);
+}
+template <class E, class F>
+constexpr bool operator==(const expected<void, E> &lhs,
+ const expected<void, F> &rhs) {
+ return (lhs.has_value() != rhs.has_value())
+ ? false
+ : (!lhs.has_value() ? lhs.error() == rhs.error() : true);
+}
+template <class E, class F>
+constexpr bool operator!=(const expected<void, E> &lhs,
+ const expected<void, F> &rhs) {
+ return (lhs.has_value() != rhs.has_value())
+ ? true
+ : (!lhs.has_value() ? lhs.error() == rhs.error() : false);
+}
+
+template <class T, class E, class U>
+constexpr bool operator==(const expected<T, E> &x, const U &v) {
+ return x.has_value() ? *x == v : false;
+}
+template <class T, class E, class U>
+constexpr bool operator==(const U &v, const expected<T, E> &x) {
+ return x.has_value() ? *x == v : false;
+}
+template <class T, class E, class U>
+constexpr bool operator!=(const expected<T, E> &x, const U &v) {
+ return x.has_value() ? *x != v : true;
+}
+template <class T, class E, class U>
+constexpr bool operator!=(const U &v, const expected<T, E> &x) {
+ return x.has_value() ? *x != v : true;
+}
+
+template <class T, class E>
+constexpr bool operator==(const expected<T, E> &x, const unexpected<E> &e) {
+ return x.has_value() ? false : x.error() == e.value();
+}
+template <class T, class E>
+constexpr bool operator==(const unexpected<E> &e, const expected<T, E> &x) {
+ return x.has_value() ? false : x.error() == e.value();
+}
+template <class T, class E>
+constexpr bool operator!=(const expected<T, E> &x, const unexpected<E> &e) {
+ return x.has_value() ? true : x.error() != e.value();
+}
+template <class T, class E>
+constexpr bool operator!=(const unexpected<E> &e, const expected<T, E> &x) {
+ return x.has_value() ? true : x.error() != e.value();
+}
+
+template <class T, class E,
+ detail::enable_if_t<(std::is_void<T>::value ||
+ std::is_move_constructible<T>::value) &&
+ detail::is_swappable<T>::value &&
+ std::is_move_constructible<E>::value &&
+ detail::is_swappable<E>::value> * = nullptr>
+void swap(expected<T, E> &lhs,
+ expected<T, E> &rhs) noexcept(noexcept(lhs.swap(rhs))) {
+ lhs.swap(rhs);
+}
+} // namespace tl
+
+#endif
diff --git a/gcc/util/expected_fwd.h b/gcc/util/expected_fwd.h
new file mode 100644
index 000000000000..cdf1b73c3325
--- /dev/null
+++ b/gcc/util/expected_fwd.h
@@ -0,0 +1,24 @@
+/* Forward declarations of tl::expected. -*- C++ -*-
+
+ Technically written by Arsen Arsenović <[email protected]>, but
+ probably not copyrightable.
+
+ To the extent possible under law, the author(s) have dedicated all
+ copyright and related and neighboring rights to this software to the
+ public domain worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication
+ along with this software. If not, see
+ <http://creativecommons.org/publicdomain/zero/1.0/>. */
+
+#ifndef GCC_EXPECTED_FWD_HPP
+#define GCC_EXPECTED_FWD_HPP
+
+namespace tl {
+
+template<class T, class E>
+class expected;
+
+} /* namespace tl */
+
+#endif /* GCC_EXPECTED_FWD_HPP */
diff --git a/gcc/util/optional.h b/gcc/util/optional.h
new file mode 100644
index 000000000000..9808b199f35e
--- /dev/null
+++ b/gcc/util/optional.h
@@ -0,0 +1,2131 @@
+// clang-format off
+
+///
+// optional - An implementation of std::optional with extensions
+// Written in 2017 by Sy Brand ([email protected], @TartanLlama)
+//
+// Documentation available at https://tl.tartanllama.xyz/
+//
+// To the extent possible under law, the author(s) have dedicated all
+// copyright and related and neighboring rights to this software to the
+// public domain worldwide. This software is distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication
+// along with this software. If not, see
+// <http://creativecommons.org/publicdomain/zero/1.0/>.
+///
+
+#ifndef TL_OPTIONAL_HPP
+#define TL_OPTIONAL_HPP
+
+#include "optional_fwd.h"
+
+#define TL_OPTIONAL_VERSION_MAJOR 1
+#define TL_OPTIONAL_VERSION_MINOR 1
+#define TL_OPTIONAL_VERSION_PATCH 0
+
+#if (defined(_MSC_VER) && _MSC_VER == 1900)
+#define TL_OPTIONAL_MSVC2015
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
+ !defined(__clang__))
+#define TL_OPTIONAL_GCC49
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \
+ !defined(__clang__))
+#define TL_OPTIONAL_GCC54
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \
+ !defined(__clang__))
+#define TL_OPTIONAL_GCC55
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
+ !defined(__clang__))
+// GCC < 5 doesn't support overloading on const&& for member functions
+#define TL_OPTIONAL_NO_CONSTRR
+
+// GCC < 5 doesn't support some standard C++11 type traits
+#define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
+ std::has_trivial_copy_constructor<T>::value
+#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) std::has_trivial_copy_assign<T>::value
+
+// This one will be different for GCC 5.7 if it's ever supported
+#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible<T>::value
+
+// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks std::vector
+// for non-copyable types
+#elif (defined(__GNUC__) && __GNUC__ < 8 && \
+ !defined(__clang__))
+#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
+#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
+namespace tl {
+ namespace detail {
+ template<class T>
+ struct is_trivially_copy_constructible : std::is_trivially_copy_constructible<T>{};
+#ifdef _GLIBCXX_VECTOR
+ template<class T, class A>
+ struct is_trivially_copy_constructible<std::vector<T,A>>
+ : std::is_trivially_copy_constructible<T>{};
+#endif
+ }
+}
+#endif
+
+#define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
+ tl::detail::is_trivially_copy_constructible<T>::value
+#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
+ std::is_trivially_copy_assignable<T>::value
+#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible<T>::value
+#else
+#define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
+ std::is_trivially_copy_constructible<T>::value
+#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
+ std::is_trivially_copy_assignable<T>::value
+#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible<T>::value
+#endif
+
+#if __cplusplus > 201103L
+#define TL_OPTIONAL_CXX14
+#endif
+
+// constexpr implies const in C++11, not C++14
+#if (__cplusplus == 201103L || defined(TL_OPTIONAL_MSVC2015) || \
+ defined(TL_OPTIONAL_GCC49))
+#define TL_OPTIONAL_11_CONSTEXPR
+#else
+#define TL_OPTIONAL_11_CONSTEXPR constexpr
+#endif
+
+namespace tl {
+#ifndef TL_MONOSTATE_INPLACE_MUTEX
+#define TL_MONOSTATE_INPLACE_MUTEX
+/// Used to represent an optional with no data; essentially a bool
+class monostate {};
+
+/// A tag type to tell optional to construct its value in-place
+struct in_place_t {
+ explicit in_place_t() = default;
+};
+/// A tag to tell optional to construct its value in-place
+static constexpr in_place_t in_place{};
+#endif
+
+template <class T> class optional;
+
+namespace detail {
+#ifndef TL_TRAITS_MUTEX
+#define TL_TRAITS_MUTEX
+// C++14-style aliases for brevity
+template <class T> using remove_const_t = typename std::remove_const<T>::type;
+template <class T>
+using remove_reference_t = typename std::remove_reference<T>::type;
+template <class T> using decay_t = typename std::decay<T>::type;
+template <bool E, class T = void>
+using enable_if_t = typename std::enable_if<E, T>::type;
+template <bool B, class T, class F>
+using conditional_t = typename std::conditional<B, T, F>::type;
+
+// std::conjunction from C++17
+template <class...> struct conjunction : std::true_type {};
+template <class B> struct conjunction<B> : B {};
+template <class B, class... Bs>
+struct conjunction<B, Bs...>
+ : std::conditional<bool(B::value), conjunction<Bs...>, B>::type {};
+
+#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L
+#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
+#endif
+
+// In C++11 mode, there's an issue in libc++'s std::mem_fn
+// which results in a hard-error when using it in a noexcept expression
+// in some cases. This is a check to workaround the common failing case.
+#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
+template <class T> struct is_pointer_to_non_const_member_func : std::false_type{};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*) (Args...)> : std::true_type{};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*) (Args...)&> : std::true_type{};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*) (Args...)&&> : std::true_type{};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*) (Args...) volatile> : std::true_type{};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*) (Args...) volatile&> : std::true_type{};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*) (Args...) volatile&&> : std::true_type{};
+
+template <class T> struct is_const_or_const_ref : std::false_type{};
+template <class T> struct is_const_or_const_ref<T const&> : std::true_type{};
+template <class T> struct is_const_or_const_ref<T const> : std::true_type{};
+#endif
+
+// std::invoke from C++17
+// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
+template <typename Fn, typename... Args,
+#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
+ typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value
+ && is_const_or_const_ref<Args...>::value)>,
+#endif
+ typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>,
+ int = 0>
+constexpr auto invoke(Fn &&f, Args &&... args) noexcept(
+ noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
+ -> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) {
+ return std::mem_fn(f)(std::forward<Args>(args)...);
+}
+
+template <typename Fn, typename... Args,
+ typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>::value>>
+constexpr auto invoke(Fn &&f, Args &&... args) noexcept(
+ noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
+ -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) {
+ return std::forward<Fn>(f)(std::forward<Args>(args)...);
+}
+
+// std::invoke_result from C++17
+template <class F, class, class... Us> struct invoke_result_impl;
+
+template <class F, class... Us>
+struct invoke_result_impl<
+ F, decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()),
+ Us...> {
+ using type = decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
+};
+
+template <class F, class... Us>
+using invoke_result = invoke_result_impl<F, void, Us...>;
+
+template <class F, class... Us>
+using invoke_result_t = typename invoke_result<F, Us...>::type;
+
+#if defined(_MSC_VER) && _MSC_VER <= 1900
+// TODO make a version which works with MSVC 2015
+template <class T, class U = T> struct is_swappable : std::true_type {};
+
+template <class T, class U = T> struct is_nothrow_swappable : std::true_type {};
+#else
+// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept
+namespace swap_adl_tests {
+// if swap ADL finds this then it would call std::swap otherwise (same
+// signature)
+struct tag {};
+
+template <class T> tag swap(T &, T &);
+template <class T, std::size_t N> tag swap(T (&a)[N], T (&b)[N]);
+
+// helper functions to test if an unqualified swap is possible, and if it
+// becomes std::swap
+template <class, class> std::false_type can_swap(...) noexcept(false);
+template <class T, class U,
+ class = decltype(swap(std::declval<T &>(), std::declval<U &>()))>
+std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T &>(),
+ std::declval<U &>())));
+
+template <class, class> std::false_type uses_std(...);
+template <class T, class U>
+std::is_same<decltype(swap(std::declval<T &>(), std::declval<U &>())), tag>
+uses_std(int);
+
+template <class T>
+struct is_std_swap_noexcept
+ : std::integral_constant<bool,
+ std::is_nothrow_move_constructible<T>::value &&
+ std::is_nothrow_move_assignable<T>::value> {};
+
+template <class T, std::size_t N>
+struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> {};
+
+template <class T, class U>
+struct is_adl_swap_noexcept
+ : std::integral_constant<bool, noexcept(can_swap<T, U>(0))> {};
+} // namespace swap_adl_tests
+
+template <class T, class U = T>
+struct is_swappable
+ : std::integral_constant<
+ bool,
+ decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value &&
+ (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value ||
+ (std::is_move_assignable<T>::value &&
+ std::is_move_constructible<T>::value))> {};
+
+template <class T, std::size_t N>
+struct is_swappable<T[N], T[N]>
+ : std::integral_constant<
+ bool,
+ decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value &&
+ (!decltype(
+ detail::swap_adl_tests::uses_std<T[N], T[N]>(0))::value ||
+ is_swappable<T, T>::value)> {};
+
+template <class T, class U = T>
+struct is_nothrow_swappable
+ : std::integral_constant<
+ bool,
+ is_swappable<T, U>::value &&
+ ((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value
+ &&detail::swap_adl_tests::is_std_swap_noexcept<T>::value) ||
+ (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
+ detail::swap_adl_tests::is_adl_swap_noexcept<T,
+ U>::value))> {
+};
+#endif
+#endif
+
+// std::void_t from C++17
+template <class...> struct voider { using type = void; };
+template <class... Ts> using void_t = typename voider<Ts...>::type;
+
+// Trait for checking if a type is a tl::optional
+template <class T> struct is_optional_impl : std::false_type {};
+template <class T> struct is_optional_impl<optional<T>> : std::true_type {};
+template <class T> using is_optional = is_optional_impl<decay_t<T>>;
+
+// Change void to tl::monostate
+template <class U>
+using fixup_void = conditional_t<std::is_void<U>::value, monostate, U>;
+
+template <class F, class U, class = invoke_result_t<F, U>>
+using get_map_return = optional<fixup_void<invoke_result_t<F, U>>>;
+
+// Check if invoking F for some Us returns void
+template <class F, class = void, class... U> struct returns_void_impl;
+template <class F, class... U>
+struct returns_void_impl<F, void_t<invoke_result_t<F, U...>>, U...>
+ : std::is_void<invoke_result_t<F, U...>> {};
+template <class F, class... U>
+using returns_void = returns_void_impl<F, void, U...>;
+
+template <class T, class... U>
+using enable_if_ret_void = enable_if_t<returns_void<T &&, U...>::value>;
+
+template <class T, class... U>
+using disable_if_ret_void = enable_if_t<!returns_void<T &&, U...>::value>;
+
+template <class T, class U>
+using enable_forward_value =
+ detail::enable_if_t<std::is_constructible<T, U &&>::value &&
+ !std::is_same<detail::decay_t<U>, in_place_t>::value &&
+ !std::is_same<optional<T>, detail::decay_t<U>>::value>;
+
+template <class T, class U, class Other>
+using enable_from_other = detail::enable_if_t<
+ std::is_constructible<T, Other>::value &&
+ !std::is_constructible<T, optional<U> &>::value &&
+ !std::is_constructible<T, optional<U> &&>::value &&
+ !std::is_constructible<T, const optional<U> &>::value &&
+ !std::is_constructible<T, const optional<U> &&>::value &&
+ !std::is_convertible<optional<U> &, T>::value &&
+ !std::is_convertible<optional<U> &&, T>::value &&
+ !std::is_convertible<const optional<U> &, T>::value &&
+ !std::is_convertible<const optional<U> &&, T>::value>;
+
+template <class T, class U>
+using enable_assign_forward = detail::enable_if_t<
+ !std::is_same<optional<T>, detail::decay_t<U>>::value &&
+ !detail::conjunction<std::is_scalar<T>,
+ std::is_same<T, detail::decay_t<U>>>::value &&
+ std::is_constructible<T, U>::value && std::is_assignable<T &, U>::value>;
+
+template <class T, class U, class Other>
+using enable_assign_from_other = detail::enable_if_t<
+ std::is_constructible<T, Other>::value &&
+ std::is_assignable<T &, Other>::value &&
+ !std::is_constructible<T, optional<U> &>::value &&
+ !std::is_constructible<T, optional<U> &&>::value &&
+ !std::is_constructible<T, const optional<U> &>::value &&
+ !std::is_constructible<T, const optional<U> &&>::value &&
+ !std::is_convertible<optional<U> &, T>::value &&
+ !std::is_convertible<optional<U> &&, T>::value &&
+ !std::is_convertible<const optional<U> &, T>::value &&
+ !std::is_convertible<const optional<U> &&, T>::value &&
+ !std::is_assignable<T &, optional<U> &>::value &&
+ !std::is_assignable<T &, optional<U> &&>::value &&
+ !std::is_assignable<T &, const optional<U> &>::value &&
+ !std::is_assignable<T &, const optional<U> &&>::value>;
+
+// The storage base manages the actual storage, and correctly propagates
+// trivial destruction from T. This case is for when T is not trivially
+// destructible.
+template <class T, bool = ::std::is_trivially_destructible<T>::value>
+struct optional_storage_base {
+ TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept
+ : m_dummy(), m_has_value(false) {}
+
+ template <class... U>
+ TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U &&... u)
+ : m_value(std::forward<U>(u)...), m_has_value(true) {}
+
+ ~optional_storage_base() {
+ if (m_has_value) {
+ m_value.~T();
+ m_has_value = false;
+ }
+ }
+
+ struct dummy {};
+ union {
+ dummy m_dummy;
+ T m_value;
+ };
+
+ bool m_has_value;
+};
+
+// This case is for when T is trivially destructible.
+template <class T> struct optional_storage_base<T, true> {
+ TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept
+ : m_dummy(), m_has_value(false) {}
+
+ template <class... U>
+ TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U &&... u)
+ : m_value(std::forward<U>(u)...), m_has_value(true) {}
+
+ // No destructor, so this class is trivially destructible
+
+ struct dummy {};
+ union {
+ dummy m_dummy;
+ T m_value;
+ };
+
+ bool m_has_value = false;
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class T> struct optional_operations_base : optional_storage_base<T> {
+ using optional_storage_base<T>::optional_storage_base;
+
+ void hard_reset() noexcept {
+ get().~T();
+ this->m_has_value = false;
+ }
+
+ template <class... Args> void construct(Args &&... args) {
+ new (std::addressof(this->m_value)) T(std::forward<Args>(args)...);
+ this->m_has_value = true;
+ }
+
+ template <class Opt> void assign(Opt &&rhs) {
+ if (this->has_value()) {
+ if (rhs.has_value()) {
+ this->m_value = std::forward<Opt>(rhs).get();
+ } else {
+ this->m_value.~T();
+ this->m_has_value = false;
+ }
+ }
+
+ else if (rhs.has_value()) {
+ construct(std::forward<Opt>(rhs).get());
+ }
+ }
+
+ bool has_value() const { return this->m_has_value; }
+
+ TL_OPTIONAL_11_CONSTEXPR T &get() & { return this->m_value; }
+ TL_OPTIONAL_11_CONSTEXPR const T &get() const & { return this->m_value; }
+ TL_OPTIONAL_11_CONSTEXPR T &&get() && { return std::move(this->m_value); }
+#ifndef TL_OPTIONAL_NO_CONSTRR
+ constexpr const T &&get() const && { return std::move(this->m_value); }
+#endif
+};
+
+// This class manages conditionally having a trivial copy constructor
+// This specialization is for when T is trivially copy constructible
+template <class T, bool = TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>
+struct optional_copy_base : optional_operations_base<T> {
+ using optional_operations_base<T>::optional_operations_base;
+};
+
+// This specialization is for when T is not trivially copy constructible
+template <class T>
+struct optional_copy_base<T, false> : optional_operations_base<T> {
+ using optional_operations_base<T>::optional_operations_base;
+
+ optional_copy_base() = default;
+ optional_copy_base(const optional_copy_base &rhs)
+ : optional_operations_base<T>() {
+ if (rhs.has_value()) {
+ this->construct(rhs.get());
+ } else {
+ this->m_has_value = false;
+ }
+ }
+
+ optional_copy_base(optional_copy_base &&rhs) = default;
+ optional_copy_base &operator=(const optional_copy_base &rhs) = default;
+ optional_copy_base &operator=(optional_copy_base &&rhs) = default;
+};
+
+// This class manages conditionally having a trivial move constructor
+// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
+// doesn't implement an analogue to std::is_trivially_move_constructible. We
+// have to make do with a non-trivial move constructor even if T is trivially
+// move constructible
+#ifndef TL_OPTIONAL_GCC49
+template <class T, bool = std::is_trivially_move_constructible<T>::value>
+struct optional_move_base : optional_copy_base<T> {
+ using optional_copy_base<T>::optional_copy_base;
+};
+#else
+template <class T, bool = false> struct optional_move_base;
+#endif
+template <class T> struct optional_move_base<T, false> : optional_copy_base<T> {
+ using optional_copy_base<T>::optional_copy_base;
+
+ optional_move_base() = default;
+ optional_move_base(const optional_move_base &rhs) = default;
+
+ optional_move_base(optional_move_base &&rhs) noexcept(
+ std::is_nothrow_move_constructible<T>::value) {
+ if (rhs.has_value()) {
+ this->construct(std::move(rhs.get()));
+ } else {
+ this->m_has_value = false;
+ }
+ }
+ optional_move_base &operator=(const optional_move_base &rhs) = default;
+ optional_move_base &operator=(optional_move_base &&rhs) = default;
+};
+
+// This class manages conditionally having a trivial copy assignment operator
+template <class T, bool = TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) &&
+ TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) &&
+ TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T)>
+struct optional_copy_assign_base : optional_move_base<T> {
+ using optional_move_base<T>::optional_move_base;
+};
+
+template <class T>
+struct optional_copy_assign_base<T, false> : optional_move_base<T> {
+ using optional_move_base<T>::optional_move_base;
+
+ optional_copy_assign_base() = default;
+ optional_copy_assign_base(const optional_copy_assign_base &rhs) = default;
+
+ optional_copy_assign_base(optional_copy_assign_base &&rhs) = default;
+ optional_copy_assign_base &operator=(const optional_copy_assign_base &rhs) {
+ this->assign(rhs);
+ return *this;
+ }
+ optional_copy_assign_base &
+ operator=(optional_copy_assign_base &&rhs) = default;
+};
+
+// This class manages conditionally having a trivial move assignment operator
+// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
+// doesn't implement an analogue to std::is_trivially_move_assignable. We have
+// to make do with a non-trivial move assignment operator even if T is trivially
+// move assignable
+#ifndef TL_OPTIONAL_GCC49
+template <class T, bool = std::is_trivially_destructible<T>::value
+ &&std::is_trivially_move_constructible<T>::value
+ &&std::is_trivially_move_assignable<T>::value>
+struct optional_move_assign_base : optional_copy_assign_base<T> {
+ using optional_copy_assign_base<T>::optional_copy_assign_base;
+};
+#else
+template <class T, bool = false> struct optional_move_assign_base;
+#endif
+
+template <class T>
+struct optional_move_assign_base<T, false> : optional_copy_assign_base<T> {
+ using optional_copy_assign_base<T>::optional_copy_assign_base;
+
+ optional_move_assign_base() = default;
+ optional_move_assign_base(const optional_move_assign_base &rhs) = default;
+
+ optional_move_assign_base(optional_move_assign_base &&rhs) = default;
+
+ optional_move_assign_base &
+ operator=(const optional_move_assign_base &rhs) = default;
+
+ optional_move_assign_base &
+ operator=(optional_move_assign_base &&rhs) noexcept(
+ std::is_nothrow_move_constructible<T>::value
+ &&std::is_nothrow_move_assignable<T>::value) {
+ this->assign(std::move(rhs));
+ return *this;
+ }
+};
+
+// optional_delete_ctor_base will conditionally delete copy and move
+// constructors depending on whether T is copy/move constructible
+template <class T, bool EnableCopy = std::is_copy_constructible<T>::value,
+ bool EnableMove = std::is_move_constructible<T>::value>
+struct optional_delete_ctor_base {
+ optional_delete_ctor_base() = default;
+ optional_delete_ctor_base(const optional_delete_ctor_base &) = default;
+ optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = default;
+ optional_delete_ctor_base &
+ operator=(const optional_delete_ctor_base &) = default;
+ optional_delete_ctor_base &
+ operator=(optional_delete_ctor_base &&) noexcept = default;
+};
+
+template <class T> struct optional_delete_ctor_base<T, true, false> {
+ optional_delete_ctor_base() = default;
+ optional_delete_ctor_base(const optional_delete_ctor_base &) = default;
+ optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = delete;
+ optional_delete_ctor_base &
+ operator=(const optional_delete_ctor_base &) = default;
+ optional_delete_ctor_base &
+ operator=(optional_delete_ctor_base &&) noexcept = default;
+};
+
+template <class T> struct optional_delete_ctor_base<T, false, true> {
+ optional_delete_ctor_base() = default;
+ optional_delete_ctor_base(const optional_delete_ctor_base &) = delete;
+ optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = default;
+ optional_delete_ctor_base &
+ operator=(const optional_delete_ctor_base &) = default;
+ optional_delete_ctor_base &
+ operator=(optional_delete_ctor_base &&) noexcept = default;
+};
+
+template <class T> struct optional_delete_ctor_base<T, false, false> {
+ optional_delete_ctor_base() = default;
+ optional_delete_ctor_base(const optional_delete_ctor_base &) = delete;
+ optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = delete;
+ optional_delete_ctor_base &
+ operator=(const optional_delete_ctor_base &) = default;
+ optional_delete_ctor_base &
+ operator=(optional_delete_ctor_base &&) noexcept = default;
+};
+
+// optional_delete_assign_base will conditionally delete copy and move
+// constructors depending on whether T is copy/move constructible + assignable
+template <class T,
+ bool EnableCopy = (std::is_copy_constructible<T>::value &&
+ std::is_copy_assignable<T>::value),
+ bool EnableMove = (std::is_move_constructible<T>::value &&
+ std::is_move_assignable<T>::value)>
+struct optional_delete_assign_base {
+ optional_delete_assign_base() = default;
+ optional_delete_assign_base(const optional_delete_assign_base &) = default;
+ optional_delete_assign_base(optional_delete_assign_base &&) noexcept =
+ default;
+ optional_delete_assign_base &
+ operator=(const optional_delete_assign_base &) = default;
+ optional_delete_assign_base &
+ operator=(optional_delete_assign_base &&) noexcept = default;
+};
+
+template <class T> struct optional_delete_assign_base<T, true, false> {
+ optional_delete_assign_base() = default;
+ optional_delete_assign_base(const optional_delete_assign_base &) = default;
+ optional_delete_assign_base(optional_delete_assign_base &&) noexcept =
+ default;
+ optional_delete_assign_base &
+ operator=(const optional_delete_assign_base &) = default;
+ optional_delete_assign_base &
+ operator=(optional_delete_assign_base &&) noexcept = delete;
+};
+
+template <class T> struct optional_delete_assign_base<T, false, true> {
+ optional_delete_assign_base() = default;
+ optional_delete_assign_base(const optional_delete_assign_base &) = default;
+ optional_delete_assign_base(optional_delete_assign_base &&) noexcept =
+ default;
+ optional_delete_assign_base &
+ operator=(const optional_delete_assign_base &) = delete;
+ optional_delete_assign_base &
+ operator=(optional_delete_assign_base &&) noexcept = default;
+};
+
+template <class T> struct optional_delete_assign_base<T, false, false> {
+ optional_delete_assign_base() = default;
+ optional_delete_assign_base(const optional_delete_assign_base &) = default;
+ optional_delete_assign_base(optional_delete_assign_base &&) noexcept =
+ default;
+ optional_delete_assign_base &
+ operator=(const optional_delete_assign_base &) = delete;
+ optional_delete_assign_base &
+ operator=(optional_delete_assign_base &&) noexcept = delete;
+};
+
+} // namespace detail
+
+/// A tag type to represent an empty optional
+struct nullopt_t {
+ struct do_not_use {};
+ constexpr explicit nullopt_t(do_not_use, do_not_use) noexcept {}
+};
+/// Represents an empty optional
+static constexpr nullopt_t nullopt{nullopt_t::do_not_use{},
+ nullopt_t::do_not_use{}};
+
+/// An optional object is an object that contains the storage for another
+/// object and manages the lifetime of this contained object, if any. The
+/// contained object may be initialized after the optional object has been
+/// initialized, and may be destroyed before the optional object has been
+/// destroyed. The initialization state of the contained object is tracked by
+/// the optional object.
+template <class T>
+class optional : private detail::optional_move_assign_base<T>,
+ private detail::optional_delete_ctor_base<T>,
+ private detail::optional_delete_assign_base<T> {
+ using base = detail::optional_move_assign_base<T>;
+
+ static_assert(!std::is_same<T, in_place_t>::value,
+ "instantiation of optional with in_place_t is ill-formed");
+ static_assert(!std::is_same<detail::decay_t<T>, nullopt_t>::value,
+ "instantiation of optional with nullopt_t is ill-formed");
+
+public:
+// The different versions for C++14 and 11 are needed because deduced return
+// types are not SFINAE-safe. This provides better support for things like
+// generic lambdas. C.f.
+// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0826r0.html
+#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \
+ !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55)
+ /// Carries out some operation which returns an optional on the stored
+ /// object if there is one.
+ template <class F> TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) & {
+ using result = detail::invoke_result_t<F, T &>;
+ static_assert(detail::is_optional<result>::value,
+ "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this)
+ : result(nullopt);
+ }
+
+ template <class F> TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) && {
+ using result = detail::invoke_result_t<F, T &&>;
+ static_assert(detail::is_optional<result>::value,
+ "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
+ : result(nullopt);
+ }
+
+ template <class F> constexpr auto and_then(F &&f) const & {
+ using result = detail::invoke_result_t<F, const T &>;
+ static_assert(detail::is_optional<result>::value,
+ "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this)
+ : result(nullopt);
+ }
+
+#ifndef TL_OPTIONAL_NO_CONSTRR
+ template <class F> constexpr auto and_then(F &&f) const && {
+ using result = detail::invoke_result_t<F, const T &&>;
+ static_assert(detail::is_optional<result>::value,
+ "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
+ : result(nullopt);
+ }
+#endif
+#else
+ /// Carries out some operation which returns an optional on the stored
+ /// object if there is one.
+ template <class F>
+ TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T &> and_then(F &&f) & {
+ using result = detail::invoke_result_t<F, T &>;
+ static_assert(detail::is_optional<result>::value,
+ "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+
+ template <class F>
+ TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T &&> and_then(F &&f) && {
+ using result = detail::invoke_result_t<F, T &&>;
+ static_assert(detail::is_optional<result>::value,
+ "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
+ : result(nullopt);
+ }
+
+ template <class F>
+ constexpr detail::invoke_result_t<F, const T &> and_then(F &&f) const & {
+ using result = detail::invoke_result_t<F, const T &>;
+ static_assert(detail::is_optional<result>::value,
+ "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this)
+ : result(nullopt);
+ }
+
+#ifndef TL_OPTIONAL_NO_CONSTRR
+ template <class F>
+ constexpr detail::invoke_result_t<F, const T &&> and_then(F &&f) const && {
+ using result = detail::invoke_result_t<F, const T &&>;
+ static_assert(detail::is_optional<result>::value,
+ "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
+ : result(nullopt);
+ }
+#endif
+#endif
+
+#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \
+ !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55)
+ /// Carries out some operation on the stored object if there is one.
+ template <class F> TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) & {
+ return optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ template <class F> TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) && {
+ return optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ template <class F> constexpr auto map(F &&f) const & {
+ return optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ template <class F> constexpr auto map(F &&f) const && {
+ return optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#else
+ /// Carries out some operation on the stored object if there is one.
+ template <class F>
+ TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval<optional &>(),
+ std::declval<F &&>()))
+ map(F &&f) & {
+ return optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ template <class F>
+ TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval<optional &&>(),
+ std::declval<F &&>()))
+ map(F &&f) && {
+ return optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ template <class F>
+ constexpr decltype(optional_map_impl(std::declval<const optional &>(),
+ std::declval<F &&>()))
+ map(F &&f) const & {
+ return optional_map_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef TL_OPTIONAL_NO_CONSTRR
+ template <class F>
+ constexpr decltype(optional_map_impl(std::declval<const optional &&>(),
+ std::declval<F &&>()))
+ map(F &&f) const && {
+ return optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+#endif
+
+#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \
+ !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55)
+ /// Carries out some operation on the stored object if there is one.
+ template <class F> TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) & {
+ return optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ template <class F> TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) && {
+ return optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ template <class F> constexpr auto transform(F&& f) const & {
+ return optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ template <class F> constexpr auto transform(F&& f) const && {
+ return optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#else
+ /// Carries out some operation on the stored object if there is one.
+ template <class F>
+ TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval<optional&>(),
+ std::declval<F&&>()))
+ transform(F&& f) & {
+ return optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ template <class F>
+ TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval<optional&&>(),
+ std::declval<F&&>()))
+ transform(F&& f) && {
+ return optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ template <class F>
+ constexpr decltype(optional_map_impl(std::declval<const optional&>(),
+ std::declval<F&&>()))
+ transform(F&& f) const & {
+ return optional_map_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef TL_OPTIONAL_NO_CONSTRR
+ template <class F>
+ constexpr decltype(optional_map_impl(std::declval<const optional&&>(),
+ std::declval<F&&>()))
+ transform(F&& f) const && {
+ return optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+#endif
+
+ /// Calls `f` if the optional is empty
+ template <class F, detail::enable_if_ret_void<F> * = nullptr>
+ optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & {
+ if (has_value())
+ return *this;
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ template <class F, detail::disable_if_ret_void<F> * = nullptr>
+ optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & {
+ return has_value() ? *this : std::forward<F>(f)();
+ }
+
+ template <class F, detail::enable_if_ret_void<F> * = nullptr>
+ optional<T> or_else(F &&f) && {
+ if (has_value())
+ return std::move(*this);
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ template <class F, detail::disable_if_ret_void<F> * = nullptr>
+ optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) && {
+ return has_value() ? std::move(*this) : std::forward<F>(f)();
+ }
+
+ template <class F, detail::enable_if_ret_void<F> * = nullptr>
+ optional<T> or_else(F &&f) const & {
+ if (has_value())
+ return *this;
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ template <class F, detail::disable_if_ret_void<F> * = nullptr>
+ optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) const & {
+ return has_value() ? *this : std::forward<F>(f)();
+ }
+
+#ifndef TL_OPTIONAL_NO_CONSTRR
+ template <class F, detail::enable_if_ret_void<F> * = nullptr>
+ optional<T> or_else(F &&f) const && {
+ if (has_value())
+ return std::move(*this);
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ template <class F, detail::disable_if_ret_void<F> * = nullptr>
+ optional<T> or_else(F &&f) const && {
+ return has_value() ? std::move(*this) : std::forward<F>(f)();
+ }
+#endif
+
+ /// Maps the stored value with `f` if there is one, otherwise returns `u`.
+ template <class F, class U> U map_or(F &&f, U &&u) & {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this)
+ : std::forward<U>(u);
+ }
+
+ template <class F, class U> U map_or(F &&f, U &&u) && {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
+ : std::forward<U>(u);
+ }
+
+ template <class F, class U> U map_or(F &&f, U &&u) const & {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this)
+ : std::forward<U>(u);
+ }
+
+#ifndef TL_OPTIONAL_NO_CONSTRR
+ template <class F, class U> U map_or(F &&f, U &&u) const && {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
+ : std::forward<U>(u);
+ }
+#endif
+
+ /// Maps the stored value with `f` if there is one, otherwise calls
+ /// `u` and returns the result.
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F &&f, U &&u) & {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this)
+ : std::forward<U>(u)();
+ }
+
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F &&f, U &&u) && {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
+ : std::forward<U>(u)();
+ }
+
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F &&f, U &&u) const & {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this)
+ : std::forward<U>(u)();
+ }
+
+#ifndef TL_OPTIONAL_NO_CONSTRR
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F &&f, U &&u) const && {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
+ : std::forward<U>(u)();
+ }
+#endif
+
+ /// Returns `u` if `*this` has a value, otherwise an empty optional.
+ template <class U>
+ constexpr optional<typename std::decay<U>::type> conjunction(U &&u) const {
+ using result = optional<detail::decay_t<U>>;
+ return has_value() ? result{u} : result{nullopt};
+ }
+
+ /// Returns `rhs` if `*this` is empty, otherwise the current value.
+ TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) & {
+ return has_value() ? *this : rhs;
+ }
+
+ constexpr optional disjunction(const optional &rhs) const & {
+ return has_value() ? *this : rhs;
+ }
+
+ TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) && {
+ return has_value() ? std::move(*this) : rhs;
+ }
+
+#ifndef TL_OPTIONAL_NO_CONSTRR
+ constexpr optional disjunction(const optional &rhs) const && {
+ return has_value() ? std::move(*this) : rhs;
+ }
+#endif
+
+ TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) & {
+ return has_value() ? *this : std::move(rhs);
+ }
+
+ constexpr optional disjunction(optional &&rhs) const & {
+ return has_value() ? *this : std::move(rhs);
+ }
+
+ TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) && {
+ return has_value() ? std::move(*this) : std::move(rhs);
+ }
+
+#ifndef TL_OPTIONAL_NO_CONSTRR
+ constexpr optional disjunction(optional &&rhs) const && {
+ return has_value() ? std::move(*this) : std::move(rhs);
+ }
+#endif
+
+ /// Takes the value out of the optional, leaving it empty
+ optional take() {
+ optional ret = std::move(*this);
+ reset();
+ return ret;
+ }
+
+ using value_type = T;
+
+ /// Constructs an optional that does not contain a value.
+ constexpr optional() noexcept = default;
+
+ constexpr optional(nullopt_t) noexcept {}
+
+ /// Copy constructor
+ ///
+ /// If `rhs` contains a value, the stored value is direct-initialized with
+ /// it. Otherwise, the constructed optional is empty.
+ TL_OPTIONAL_11_CONSTEXPR optional(const optional &rhs) = default;
+
+ /// Move constructor
+ ///
+ /// If `rhs` contains a value, the stored value is direct-initialized with
+ /// it. Otherwise, the constructed optional is empty.
+ TL_OPTIONAL_11_CONSTEXPR optional(optional &&rhs) = default;
+
+ /// Constructs the stored value in-place using the given arguments.
+ template <class... Args>
+ constexpr explicit optional(
+ detail::enable_if_t<std::is_constructible<T, Args...>::value, in_place_t>,
+ Args &&... args)
+ : base(in_place, std::forward<Args>(args)...) {}
+
+ template <class U, class... Args>
+ TL_OPTIONAL_11_CONSTEXPR explicit optional(
+ detail::enable_if_t<std::is_constructible<T, std::initializer_list<U> &,
+ Args &&...>::value,
+ in_place_t>,
+ std::initializer_list<U> il, Args &&... args) {
+ this->construct(il, std::forward<Args>(args)...);
+ }
+
+ /// Constructs the stored value with `u`.
+ template <
+ class U = T,
+ detail::enable_if_t<std::is_convertible<U &&, T>::value> * = nullptr,
+ detail::enable_forward_value<T, U> * = nullptr>
+ constexpr optional(U &&u) : base(in_place, std::forward<U>(u)) {}
+
+ template <
+ class U = T,
+ detail::enable_if_t<!std::is_convertible<U &&, T>::value> * = nullptr,
+ detail::enable_forward_value<T, U> * = nullptr>
+ constexpr explicit optional(U &&u) : base(in_place, std::forward<U>(u)) {}
+
+ /// Converting copy constructor.
+ template <
+ class U, detail::enable_from_other<T, U, const U &> * = nullptr,
+ detail::enable_if_t<std::is_convertible<const U &, T>::value> * = nullptr>
+ optional(const optional<U> &rhs) {
+ if (rhs.has_value()) {
+ this->construct(*rhs);
+ }
+ }
+
+ template <class U, detail::enable_from_other<T, U, const U &> * = nullptr,
+ detail::enable_if_t<!std::is_convertible<const U &, T>::value> * =
+ nullptr>
+ explicit optional(const optional<U> &rhs) {
+ if (rhs.has_value()) {
+ this->construct(*rhs);
+ }
+ }
+
+ /// Converting move constructor.
+ template <
+ class U, detail::enable_from_other<T, U, U &&> * = nullptr,
+ detail::enable_if_t<std::is_convertible<U &&, T>::value> * = nullptr>
+ optional(optional<U> &&rhs) {
+ if (rhs.has_value()) {
+ this->construct(std::move(*rhs));
+ }
+ }
+
+ template <
+ class U, detail::enable_from_other<T, U, U &&> * = nullptr,
+ detail::enable_if_t<!std::is_convertible<U &&, T>::value> * = nullptr>
+ explicit optional(optional<U> &&rhs) {
+ if (rhs.has_value()) {
+ this->construct(std::move(*rhs));
+ }
+ }
+
+ /// Destroys the stored value if there is one.
+ ~optional() = default;
+
+ /// Assignment to empty.
+ ///
+ /// Destroys the current value if there is one.
+ optional &operator=(nullopt_t) noexcept {
+ if (has_value()) {
+ this->m_value.~T();
+ this->m_has_value = false;
+ }
+
+ return *this;
+ }
+
+ /// Copy assignment.
+ ///
+ /// Copies the value from `rhs` if there is one. Otherwise resets the stored
+ /// value in `*this`.
+ optional &operator=(const optional &rhs) = default;
+
+ /// Move assignment.
+ ///
+ /// Moves the value from `rhs` if there is one. Otherwise resets the stored
+ /// value in `*this`.
+ optional &operator=(optional &&rhs) = default;
+
+ /// Assigns the stored value from `u`, destroying the old value if there was
+ /// one.
+ template <class U = T, detail::enable_assign_forward<T, U> * = nullptr>
+ optional &operator=(U &&u) {
+ if (has_value()) {
+ this->m_value = std::forward<U>(u);
+ } else {
+ this->construct(std::forward<U>(u));
+ }
+
+ return *this;
+ }
+
+ /// Converting copy assignment operator.
+ ///
+ /// Copies the value from `rhs` if there is one. Otherwise resets the stored
+ /// value in `*this`.
+ template <class U,
+ detail::enable_assign_from_other<T, U, const U &> * = nullptr>
+ optional &operator=(const optional<U> &rhs) {
+ if (has_value()) {
+ if (rhs.has_value()) {
+ this->m_value = *rhs;
+ } else {
+ this->hard_reset();
+ }
+ }
+
+ else if (rhs.has_value()) {
+ this->construct(*rhs);
+ }
+
+ return *this;
+ }
+
+ // TODO check exception guarantee
+ /// Converting move assignment operator.
+ ///
+ /// Moves the value from `rhs` if there is one. Otherwise resets the stored
+ /// value in `*this`.
+ template <class U, detail::enable_assign_from_other<T, U, U> * = nullptr>
+ optional &operator=(optional<U> &&rhs) {
+ if (has_value()) {
+ if (rhs.has_value()) {
+ this->m_value = std::move(*rhs);
+ } else {
+ this->hard_reset();
+ }
+ }
+
+ else if (rhs.has_value()) {
+ this->construct(std::move(*rhs));
+ }
+
+ return *this;
+ }
+
+ /// Constructs the value in-place, destroying the current one if there is
+ /// one.
+ template <class... Args> T &emplace(Args &&... args) {
+ static_assert(std::is_constructible<T, Args &&...>::value,
+ "T must be constructible with Args");
+
+ *this = nullopt;
+ this->construct(std::forward<Args>(args)...);
+ return value();
+ }
+
+ template <class U, class... Args>
+ detail::enable_if_t<
+ std::is_constructible<T, std::initializer_list<U> &, Args &&...>::value,
+ T &>
+ emplace(std::initializer_list<U> il, Args &&... args) {
+ *this = nullopt;
+ this->construct(il, std::forward<Args>(args)...);
+ return value();
+ }
+
+ /// Swaps this optional with the other.
+ ///
+ /// If neither optionals have a value, nothing happens.
+ /// If both have a value, the values are swapped.
+ /// If one has a value, it is moved to the other and the movee is left
+ /// valueless.
+ void
+ swap(optional &rhs) noexcept(std::is_nothrow_move_constructible<T>::value
+ &&detail::is_nothrow_swappable<T>::value) {
+ using std::swap;
+ if (has_value()) {
+ if (rhs.has_value()) {
+ swap(**this, *rhs);
+ } else {
+ new (std::addressof(rhs.m_value)) T(std::move(this->m_value));
+ this->m_value.T::~T();
+ }
+ } else if (rhs.has_value()) {
+ new (std::addressof(this->m_value)) T(std::move(rhs.m_value));
+ rhs.m_value.T::~T();
+ }
+ swap(this->m_has_value, rhs.m_has_value);
+ }
+
+ /// Returns a pointer to the stored value
+ constexpr const T *operator->() const {
+ // constexpr function must only contain a return statement in C++11
+#ifdef TL_OPTIONAL_CXX14
+ // undefined behavior if we don't have a value
+ gcc_assert(has_value ());
+#endif
+
+ return std::addressof(this->m_value);
+ }
+
+ TL_OPTIONAL_11_CONSTEXPR T *operator->() {
+ // constexpr function must only contain a return statement in C++11
+#ifdef TL_OPTIONAL_CXX14
+ // undefined behavior if we don't have a value
+ gcc_assert(has_value ());
+#endif
+
+ return std::addressof(this->m_value);
+ }
+
+ /// Returns the stored value
+ TL_OPTIONAL_11_CONSTEXPR T &operator*() &
+ {
+ // constexpr function must only contain a return statement in C++11
+#ifdef TL_OPTIONAL_CXX14
+ // undefined behavior if we don't have a value
+ gcc_assert(has_value ());
+#endif
+
+ return this->m_value;
+ }
+
+ constexpr const T &operator*() const &
+ {
+ // constexpr function must only contain a return statement in C++11
+#ifdef TL_OPTIONAL_CXX14
+ // undefined behavior if we don't have a value
+ gcc_assert(has_value ());
+#endif
+
+ return this->m_value;
+ }
+
+ TL_OPTIONAL_11_CONSTEXPR T &&operator*() &&
+ {
+ // constexpr function must only contain a return statement in C++11
+#ifdef TL_OPTIONAL_CXX14
+ // undefined behavior if we don't have a value
+ gcc_assert(has_value ());
+#endif
+
+ return std::move(this->m_value);
+ }
+
+#ifndef TL_OPTIONAL_NO_CONSTRR
+ constexpr const T &&operator*() const && { return std::move(this->m_value); }
+#endif
+
+ /// Returns whether or not the optional has a value
+ constexpr bool has_value() const noexcept { return this->m_has_value; }
+
+ constexpr explicit operator bool() const noexcept {
+ return this->m_has_value;
+ }
+
+ /// Returns the contained value if there is one, otherwise throws bad_optional_access
+ TL_OPTIONAL_11_CONSTEXPR T &value() & {
+ if (has_value())
+ return this->m_value;
+
+ gcc_unreachable();
+ }
+ TL_OPTIONAL_11_CONSTEXPR const T &value() const & {
+ if (has_value())
+ return this->m_value;
+
+ gcc_unreachable();
+ }
+ TL_OPTIONAL_11_CONSTEXPR T &&value() && {
+ if (has_value())
+ return std::move(this->m_value);
+
+ gcc_unreachable();
+ }
+
+#ifndef TL_OPTIONAL_NO_CONSTRR
+ TL_OPTIONAL_11_CONSTEXPR const T &&value() const && {
+ if (has_value())
+ return std::move(this->m_value);
+
+ gcc_unreachable();
+ }
+#endif
+
+ /// Returns the stored value if there is one, otherwise returns `u`
+ template <class U> constexpr T value_or(U &&u) const & {
+ static_assert(std::is_copy_constructible<T>::value &&
+ std::is_convertible<U &&, T>::value,
+ "T must be copy constructible and convertible from U");
+ return has_value() ? **this : static_cast<T>(std::forward<U>(u));
+ }
+
+ template <class U> TL_OPTIONAL_11_CONSTEXPR T value_or(U &&u) && {
+ static_assert(std::is_move_constructible<T>::value &&
+ std::is_convertible<U &&, T>::value,
+ "T must be move constructible and convertible from U");
+ return has_value() ? std::move(**this) : static_cast<T>(std::forward<U>(u));
+ }
+
+ /// Destroys the stored value if one exists, making the optional empty
+ void reset() noexcept {
+ if (has_value()) {
+ this->m_value.~T();
+ this->m_has_value = false;
+ }
+ }
+};
+
+/// Compares two optional objects
+template <class T, class U>
+inline constexpr bool operator==(const optional<T> &lhs,
+ const optional<U> &rhs) {
+ return lhs.has_value() == rhs.has_value() &&
+ (!lhs.has_value() || *lhs == *rhs);
+}
+template <class T, class U>
+inline constexpr bool operator!=(const optional<T> &lhs,
+ const optional<U> &rhs) {
+ return lhs.has_value() != rhs.has_value() ||
+ (lhs.has_value() && *lhs != *rhs);
+}
+template <class T, class U>
+inline constexpr bool operator<(const optional<T> &lhs,
+ const optional<U> &rhs) {
+ return rhs.has_value() && (!lhs.has_value() || *lhs < *rhs);
+}
+template <class T, class U>
+inline constexpr bool operator>(const optional<T> &lhs,
+ const optional<U> &rhs) {
+ return lhs.has_value() && (!rhs.has_value() || *lhs > *rhs);
+}
+template <class T, class U>
+inline constexpr bool operator<=(const optional<T> &lhs,
+ const optional<U> &rhs) {
+ return !lhs.has_value() || (rhs.has_value() && *lhs <= *rhs);
+}
+template <class T, class U>
+inline constexpr bool operator>=(const optional<T> &lhs,
+ const optional<U> &rhs) {
+ return !rhs.has_value() || (lhs.has_value() && *lhs >= *rhs);
+}
+
+/// Compares an optional to a `nullopt`
+template <class T>
+inline constexpr bool operator==(const optional<T> &lhs, nullopt_t) noexcept {
+ return !lhs.has_value();
+}
+template <class T>
+inline constexpr bool operator==(nullopt_t, const optional<T> &rhs) noexcept {
+ return !rhs.has_value();
+}
+template <class T>
+inline constexpr bool operator!=(const optional<T> &lhs, nullopt_t) noexcept {
+ return lhs.has_value();
+}
+template <class T>
+inline constexpr bool operator!=(nullopt_t, const optional<T> &rhs) noexcept {
+ return rhs.has_value();
+}
+template <class T>
+inline constexpr bool operator<(const optional<T> &, nullopt_t) noexcept {
+ return false;
+}
+template <class T>
+inline constexpr bool operator<(nullopt_t, const optional<T> &rhs) noexcept {
+ return rhs.has_value();
+}
+template <class T>
+inline constexpr bool operator<=(const optional<T> &lhs, nullopt_t) noexcept {
+ return !lhs.has_value();
+}
+template <class T>
+inline constexpr bool operator<=(nullopt_t, const optional<T> &) noexcept {
+ return true;
+}
+template <class T>
+inline constexpr bool operator>(const optional<T> &lhs, nullopt_t) noexcept {
+ return lhs.has_value();
+}
+template <class T>
+inline constexpr bool operator>(nullopt_t, const optional<T> &) noexcept {
+ return false;
+}
+template <class T>
+inline constexpr bool operator>=(const optional<T> &, nullopt_t) noexcept {
+ return true;
+}
+template <class T>
+inline constexpr bool operator>=(nullopt_t, const optional<T> &rhs) noexcept {
+ return !rhs.has_value();
+}
+
+/// Compares the optional with a value.
+template <class T, class U>
+inline constexpr bool operator==(const optional<T> &lhs, const U &rhs) {
+ return lhs.has_value() ? *lhs == rhs : false;
+}
+template <class T, class U>
+inline constexpr bool operator==(const U &lhs, const optional<T> &rhs) {
+ return rhs.has_value() ? lhs == *rhs : false;
+}
+template <class T, class U>
+inline constexpr bool operator!=(const optional<T> &lhs, const U &rhs) {
+ return lhs.has_value() ? *lhs != rhs : true;
+}
+template <class T, class U>
+inline constexpr bool operator!=(const U &lhs, const optional<T> &rhs) {
+ return rhs.has_value() ? lhs != *rhs : true;
+}
+template <class T, class U>
+inline constexpr bool operator<(const optional<T> &lhs, const U &rhs) {
+ return lhs.has_value() ? *lhs < rhs : true;
+}
+template <class T, class U>
+inline constexpr bool operator<(const U &lhs, const optional<T> &rhs) {
+ return rhs.has_value() ? lhs < *rhs : false;
+}
+template <class T, class U>
+inline constexpr bool operator<=(const optional<T> &lhs, const U &rhs) {
+ return lhs.has_value() ? *lhs <= rhs : true;
+}
+template <class T, class U>
+inline constexpr bool operator<=(const U &lhs, const optional<T> &rhs) {
+ return rhs.has_value() ? lhs <= *rhs : false;
+}
+template <class T, class U>
+inline constexpr bool operator>(const optional<T> &lhs, const U &rhs) {
+ return lhs.has_value() ? *lhs > rhs : false;
+}
+template <class T, class U>
+inline constexpr bool operator>(const U &lhs, const optional<T> &rhs) {
+ return rhs.has_value() ? lhs > *rhs : true;
+}
+template <class T, class U>
+inline constexpr bool operator>=(const optional<T> &lhs, const U &rhs) {
+ return lhs.has_value() ? *lhs >= rhs : false;
+}
+template <class T, class U>
+inline constexpr bool operator>=(const U &lhs, const optional<T> &rhs) {
+ return rhs.has_value() ? lhs >= *rhs : true;
+}
+
+template <class T,
+ detail::enable_if_t<std::is_move_constructible<T>::value> * = nullptr,
+ detail::enable_if_t<detail::is_swappable<T>::value> * = nullptr>
+void swap(optional<T> &lhs,
+ optional<T> &rhs) noexcept(noexcept(lhs.swap(rhs))) {
+ return lhs.swap(rhs);
+}
+
+namespace detail {
+struct i_am_secret {};
+} // namespace detail
+
+template <class T = detail::i_am_secret, class U,
+ class Ret =
+ detail::conditional_t<std::is_same<T, detail::i_am_secret>::value,
+ detail::decay_t<U>, T>>
+inline constexpr optional<Ret> make_optional(U &&v) {
+ return optional<Ret>(std::forward<U>(v));
+}
+
+template <class T, class... Args>
+inline constexpr optional<T> make_optional(Args &&... args) {
+ return optional<T>(in_place, std::forward<Args>(args)...);
+}
+template <class T, class U, class... Args>
+inline constexpr optional<T> make_optional(std::initializer_list<U> il,
+ Args &&... args) {
+ return optional<T>(in_place, il, std::forward<Args>(args)...);
+}
+
+#if __cplusplus >= 201703L
+template <class T> optional(T)->optional<T>;
+#endif
+
+/// \exclude
+namespace detail {
+#ifdef TL_OPTIONAL_CXX14
+template <class Opt, class F,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ *std::declval<Opt>())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto optional_map_impl(Opt &&opt, F &&f) {
+ return opt.has_value()
+ ? detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt))
+ : optional<Ret>(nullopt);
+}
+
+template <class Opt, class F,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ *std::declval<Opt>())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto optional_map_impl(Opt &&opt, F &&f) {
+ if (opt.has_value()) {
+ detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt));
+ return make_optional(monostate{});
+ }
+
+ return optional<monostate>(nullopt);
+}
+#else
+template <class Opt, class F,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ *std::declval<Opt>())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+
+constexpr auto optional_map_impl(Opt &&opt, F &&f) -> optional<Ret> {
+ return opt.has_value()
+ ? detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt))
+ : optional<Ret>(nullopt);
+}
+
+template <class Opt, class F,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ *std::declval<Opt>())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+
+auto optional_map_impl(Opt &&opt, F &&f) -> optional<monostate> {
+ if (opt.has_value()) {
+ detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt));
+ return monostate{};
+ }
+
+ return nullopt;
+}
+#endif
+} // namespace detail
+
+/// Specialization for when `T` is a reference. `optional<T&>` acts similarly
+/// to a `T*`, but provides more operations and shows intent more clearly.
+template <class T> class optional<T &> {
+public:
+// The different versions for C++14 and 11 are needed because deduced return
+// types are not SFINAE-safe. This provides better support for things like
+// generic lambdas. C.f.
+// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0826r0.html
+#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \
+ !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55)
+
+ /// Carries out some operation which returns an optional on the stored
+ /// object if there is one.
+ template <class F> TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) & {
+ using result = detail::invoke_result_t<F, T &>;
+ static_assert(detail::is_optional<result>::value,
+ "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this)
+ : result(nullopt);
+ }
+
+ template <class F> TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) && {
+ using result = detail::invoke_result_t<F, T &>;
+ static_assert(detail::is_optional<result>::value,
+ "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this)
+ : result(nullopt);
+ }
+
+ template <class F> constexpr auto and_then(F &&f) const & {
+ using result = detail::invoke_result_t<F, const T &>;
+ static_assert(detail::is_optional<result>::value,
+ "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this)
+ : result(nullopt);
+ }
+
+#ifndef TL_OPTIONAL_NO_CONSTRR
+ template <class F> constexpr auto and_then(F &&f) const && {
+ using result = detail::invoke_result_t<F, const T &>;
+ static_assert(detail::is_optional<result>::value,
+ "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this)
+ : result(nullopt);
+ }
+#endif
+#else
+ /// Carries out some operation which returns an optional on the stored
+ /// object if there is one.
+ template <class F>
+ TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T &> and_then(F &&f) & {
+ using result = detail::invoke_result_t<F, T &>;
+ static_assert(detail::is_optional<result>::value,
+ "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this)
+ : result(nullopt);
+ }
+
+ template <class F>
+ TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T &> and_then(F &&f) && {
+ using result = detail::invoke_result_t<F, T &>;
+ static_assert(detail::is_optional<result>::value,
+ "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this)
+ : result(nullopt);
+ }
+
+ template <class F>
+ constexpr detail::invoke_result_t<F, const T &> and_then(F &&f) const & {
+ using result = detail::invoke_result_t<F, const T &>;
+ static_assert(detail::is_optional<result>::value,
+ "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this)
+ : result(nullopt);
+ }
+
+#ifndef TL_OPTIONAL_NO_CONSTRR
+ template <class F>
+ constexpr detail::invoke_result_t<F, const T &> and_then(F &&f) const && {
+ using result = detail::invoke_result_t<F, const T &>;
+ static_assert(detail::is_optional<result>::value,
+ "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
+ : result(nullopt);
+ }
+#endif
+#endif
+
+#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \
+ !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55)
+ /// Carries out some operation on the stored object if there is one.
+ template <class F> TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) & {
+ return detail::optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ template <class F> TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) && {
+ return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ template <class F> constexpr auto map(F &&f) const & {
+ return detail::optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ template <class F> constexpr auto map(F &&f) const && {
+ return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#else
+ /// Carries out some operation on the stored object if there is one.
+ template <class F>
+ TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval<optional &>(),
+ std::declval<F &&>()))
+ map(F &&f) & {
+ return detail::optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ template <class F>
+ TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval<optional &&>(),
+ std::declval<F &&>()))
+ map(F &&f) && {
+ return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ template <class F>
+ constexpr decltype(detail::optional_map_impl(std::declval<const optional &>(),
+ std::declval<F &&>()))
+ map(F &&f) const & {
+ return detail::optional_map_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef TL_OPTIONAL_NO_CONSTRR
+ template <class F>
+ constexpr decltype(detail::optional_map_impl(std::declval<const optional &&>(),
+ std::declval<F &&>()))
+ map(F &&f) const && {
+ return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+#endif
+
+#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \
+ !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55)
+ /// Carries out some operation on the stored object if there is one.
+ template <class F> TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) & {
+ return detail::optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ template <class F> TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) && {
+ return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ template <class F> constexpr auto transform(F&& f) const & {
+ return detail::optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ template <class F> constexpr auto transform(F&& f) const && {
+ return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#else
+ /// Carries out some operation on the stored object if there is one.
+ template <class F>
+ TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval<optional&>(),
+ std::declval<F&&>()))
+ transform(F&& f) & {
+ return detail::optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> auto transform(F &&f) &&;
+ template <class F>
+ TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval<optional&&>(),
+ std::declval<F&&>()))
+ transform(F&& f) && {
+ return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ template <class F>
+ constexpr decltype(detail::optional_map_impl(std::declval<const optional&>(),
+ std::declval<F&&>()))
+ transform(F&& f) const & {
+ return detail::optional_map_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef TL_OPTIONAL_NO_CONSTRR
+ template <class F>
+ constexpr decltype(detail::optional_map_impl(std::declval<const optional&&>(),
+ std::declval<F&&>()))
+ transform(F&& f) const && {
+ return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+#endif
+
+ /// Calls `f` if the optional is empty
+ template <class F, detail::enable_if_ret_void<F> * = nullptr>
+ optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & {
+ if (has_value())
+ return *this;
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ template <class F, detail::disable_if_ret_void<F> * = nullptr>
+ optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & {
+ return has_value() ? *this : std::forward<F>(f)();
+ }
+
+ template <class F, detail::enable_if_ret_void<F> * = nullptr>
+ optional<T> or_else(F &&f) && {
+ if (has_value())
+ return std::move(*this);
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ template <class F, detail::disable_if_ret_void<F> * = nullptr>
+ optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) && {
+ return has_value() ? std::move(*this) : std::forward<F>(f)();
+ }
+
+ template <class F, detail::enable_if_ret_void<F> * = nullptr>
+ optional<T> or_else(F &&f) const & {
+ if (has_value())
+ return *this;
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ template <class F, detail::disable_if_ret_void<F> * = nullptr>
+ optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) const & {
+ return has_value() ? *this : std::forward<F>(f)();
+ }
+
+#ifndef TL_OPTIONAL_NO_CONSTRR
+ template <class F, detail::enable_if_ret_void<F> * = nullptr>
+ optional<T> or_else(F &&f) const && {
+ if (has_value())
+ return std::move(*this);
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ template <class F, detail::disable_if_ret_void<F> * = nullptr>
+ optional<T> or_else(F &&f) const && {
+ return has_value() ? std::move(*this) : std::forward<F>(f)();
+ }
+#endif
+
+ /// Maps the stored value with `f` if there is one, otherwise returns `u`
+ template <class F, class U> U map_or(F &&f, U &&u) & {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this)
+ : std::forward<U>(u);
+ }
+
+ template <class F, class U> U map_or(F &&f, U &&u) && {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
+ : std::forward<U>(u);
+ }
+
+ template <class F, class U> U map_or(F &&f, U &&u) const & {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this)
+ : std::forward<U>(u);
+ }
+
+#ifndef TL_OPTIONAL_NO_CONSTRR
+ template <class F, class U> U map_or(F &&f, U &&u) const && {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
+ : std::forward<U>(u);
+ }
+#endif
+
+ /// Maps the stored value with `f` if there is one, otherwise calls
+ /// `u` and returns the result.
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F &&f, U &&u) & {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this)
+ : std::forward<U>(u)();
+ }
+
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F &&f, U &&u) && {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
+ : std::forward<U>(u)();
+ }
+
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F &&f, U &&u) const & {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this)
+ : std::forward<U>(u)();
+ }
+
+#ifndef TL_OPTIONAL_NO_CONSTRR
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F &&f, U &&u) const && {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this))
+ : std::forward<U>(u)();
+ }
+#endif
+
+ /// Returns `u` if `*this` has a value, otherwise an empty optional.
+ template <class U>
+ constexpr optional<typename std::decay<U>::type> conjunction(U &&u) const {
+ using result = optional<detail::decay_t<U>>;
+ return has_value() ? result{u} : result{nullopt};
+ }
+
+ /// Returns `rhs` if `*this` is empty, otherwise the current value.
+ TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) & {
+ return has_value() ? *this : rhs;
+ }
+
+ constexpr optional disjunction(const optional &rhs) const & {
+ return has_value() ? *this : rhs;
+ }
+
+ TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) && {
+ return has_value() ? std::move(*this) : rhs;
+ }
+
+#ifndef TL_OPTIONAL_NO_CONSTRR
+ constexpr optional disjunction(const optional &rhs) const && {
+ return has_value() ? std::move(*this) : rhs;
+ }
+#endif
+
+ TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) & {
+ return has_value() ? *this : std::move(rhs);
+ }
+
+ constexpr optional disjunction(optional &&rhs) const & {
+ return has_value() ? *this : std::move(rhs);
+ }
+
+ TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) && {
+ return has_value() ? std::move(*this) : std::move(rhs);
+ }
+
+#ifndef TL_OPTIONAL_NO_CONSTRR
+ constexpr optional disjunction(optional &&rhs) const && {
+ return has_value() ? std::move(*this) : std::move(rhs);
+ }
+#endif
+
+ /// Takes the value out of the optional, leaving it empty
+ optional take() {
+ optional ret = std::move(*this);
+ reset();
+ return ret;
+ }
+
+ using value_type = T &;
+
+ /// Constructs an optional that does not contain a value.
+ constexpr optional() noexcept : m_value(nullptr) {}
+
+ constexpr optional(nullopt_t) noexcept : m_value(nullptr) {}
+
+ /// Copy constructor
+ ///
+ /// If `rhs` contains a value, the stored value is direct-initialized with
+ /// it. Otherwise, the constructed optional is empty.
+ TL_OPTIONAL_11_CONSTEXPR optional(const optional &rhs) noexcept = default;
+
+ /// Move constructor
+ ///
+ /// If `rhs` contains a value, the stored value is direct-initialized with
+ /// it. Otherwise, the constructed optional is empty.
+ TL_OPTIONAL_11_CONSTEXPR optional(optional &&rhs) = default;
+
+ /// Constructs the stored value with `u`.
+ template <class U = T,
+ detail::enable_if_t<!detail::is_optional<detail::decay_t<U>>::value>
+ * = nullptr>
+ constexpr optional(U &&u) noexcept : m_value(std::addressof(u)) {
+ static_assert(std::is_lvalue_reference<U>::value, "U must be an lvalue");
+ }
+
+ template <class U>
+ constexpr explicit optional(const optional<U> &rhs) noexcept : optional(*rhs) {}
+
+ /// No-op
+ ~optional() = default;
+
+ /// Assignment to empty.
+ ///
+ /// Destroys the current value if there is one.
+ optional &operator=(nullopt_t) noexcept {
+ m_value = nullptr;
+ return *this;
+ }
+
+ /// Copy assignment.
+ ///
+ /// Rebinds this optional to the referee of `rhs` if there is one. Otherwise
+ /// resets the stored value in `*this`.
+ optional &operator=(const optional &rhs) = default;
+
+ /// Rebinds this optional to `u`.
+ template <class U = T,
+ detail::enable_if_t<!detail::is_optional<detail::decay_t<U>>::value>
+ * = nullptr>
+ optional &operator=(U &&u) {
+ static_assert(std::is_lvalue_reference<U>::value, "U must be an lvalue");
+ m_value = std::addressof(u);
+ return *this;
+ }
+
+ /// Converting copy assignment operator.
+ ///
+ /// Rebinds this optional to the referee of `rhs` if there is one. Otherwise
+ /// resets the stored value in `*this`.
+ template <class U> optional &operator=(const optional<U> &rhs) noexcept {
+ m_value = std::addressof(rhs.value());
+ return *this;
+ }
+
+ /// Rebinds this optional to `u`.
+ template <class U = T,
+ detail::enable_if_t<!detail::is_optional<detail::decay_t<U>>::value>
+ * = nullptr>
+ optional &emplace(U &&u) noexcept {
+ return *this = std::forward<U>(u);
+ }
+
+ void swap(optional &rhs) noexcept { std::swap(m_value, rhs.m_value); }
+
+ /// Returns a pointer to the stored value
+ constexpr const T *operator->() const noexcept
+ {
+ // constexpr function must only contain a return statement in C++11
+#ifdef TL_OPTIONAL_CXX14
+ // undefined behavior if we don't have a value
+ gcc_assert(has_value ());
+#endif
+
+ return m_value;
+ }
+
+ TL_OPTIONAL_11_CONSTEXPR T *operator->() noexcept
+ {
+ // constexpr function must only contain a return statement in C++11
+#ifdef TL_OPTIONAL_CXX14
+ // undefined behavior if we don't have a value
+ gcc_assert(has_value ());
+#endif
+
+ return m_value;
+ }
+
+ /// Returns the stored value
+ TL_OPTIONAL_11_CONSTEXPR T &operator*() noexcept {
+ // constexpr function must only contain a return statement in C++11
+#ifdef TL_OPTIONAL_CXX14
+ // undefined behavior if we don't have a value
+ gcc_assert(has_value ());
+#endif
+
+ return *m_value;
+ }
+
+ constexpr const T &operator*() const noexcept
+ {
+ // constexpr function must only contain a return statement in C++11
+#ifdef TL_OPTIONAL_CXX14
+ // undefined behavior if we don't have a value
+ gcc_assert(has_value ());
+#endif
+
+ return *m_value;
+ }
+
+ constexpr bool has_value() const noexcept { return m_value != nullptr; }
+
+ constexpr explicit operator bool() const noexcept {
+ return m_value != nullptr;
+ }
+
+ /// Returns the contained value if there is one, otherwise throws bad_optional_access
+ TL_OPTIONAL_11_CONSTEXPR T &value() {
+ if (has_value())
+ return *m_value;
+
+ gcc_unreachable();
+ }
+ TL_OPTIONAL_11_CONSTEXPR const T &value() const {
+ if (has_value())
+ return *m_value;
+
+ gcc_unreachable();
+ }
+
+ /// Returns the stored value if there is one, otherwise returns `u`
+ template <class U> constexpr T value_or(U &&u) const & noexcept {
+ static_assert(std::is_copy_constructible<T>::value &&
+ std::is_convertible<U &&, T>::value,
+ "T must be copy constructible and convertible from U");
+ return has_value() ? **this : static_cast<T>(std::forward<U>(u));
+ }
+
+ /// \group value_or
+ template <class U> TL_OPTIONAL_11_CONSTEXPR T value_or(U &&u) && noexcept {
+ static_assert(std::is_move_constructible<T>::value &&
+ std::is_convertible<U &&, T>::value,
+ "T must be move constructible and convertible from U");
+ return has_value() ? **this : static_cast<T>(std::forward<U>(u));
+ }
+
+ /// Destroys the stored value if one exists, making the optional empty
+ void reset() noexcept { m_value = nullptr; }
+
+private:
+ T *m_value;
+};
+
+
+
+} // namespace tl
+
+namespace std {
+// TODO SFINAE
+template <class T> struct hash<tl::optional<T>> {
+ ::std::size_t operator()(const tl::optional<T> &o) const {
+ if (!o.has_value())
+ return 0;
+
+ return std::hash<tl::detail::remove_const_t<T>>()(*o);
+ }
+};
+} // namespace std
+
+#endif
diff --git a/gcc/util/optional_fwd.h b/gcc/util/optional_fwd.h
new file mode 100644
index 000000000000..1aba8e252843
--- /dev/null
+++ b/gcc/util/optional_fwd.h
@@ -0,0 +1,24 @@
+/* Forward declarations of tl::optional. -*- C++ -*-
+
+ Technically written by Arsen Arsenović <[email protected]>, but
+ probably not copyrightable.
+
+ To the extent possible under law, the author(s) have dedicated all
+ copyright and related and neighboring rights to this software to the
+ public domain worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication
+ along with this software. If not, see
+ <http://creativecommons.org/publicdomain/zero/1.0/>. */
+
+#ifndef GCC_OPTIONAL_FWD_HPP
+#define GCC_OPTIONAL_FWD_HPP
+
+namespace tl {
+
+template<class T>
+class optional;
+
+} /* namespace tl */
+
+#endif /* GCC_OPTIONAL_FWD_HPP */
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);
--
2.54.0