On Thu, Jul 7, 2022 at 10:00 PM Alexandre Oliva via Gcc-patches <gcc-patches@gcc.gnu.org> wrote: > > > This patch introduces hardened booleans in C. The hardbool attribute, > when attached to an integral type, turns it into an enumerate type > with boolean semantics, using the named or implied constants as > representations for false and true. > > Expressions of such types decay to _Bool, trapping if the value is > neither true nor false, and _Bool can convert implicitly back to them. > Other conversions go through _Bool first. > > Regstrapped on x86_64-linux-gnu. Ok to install?
Does this follow some other compilers / language? Is such feature used in existing code? Why is it useful to allow arbitrary values for true/false? Why is the default 0 and ~0 rather than 0 and 1 as for _Bool? When writing > +@smallexample > +typedef char __attribute__ ((__hardbool__ (0x5a))) hbool; > +hbool first = 0; /* False, stored as (char)0x5a. */ > +hbool second = !first; /* True, stored as ~(char)0x5a. */ hbool thrid; what's the initial value of 'third'? Does "zero-initialization" mean initialization to the corresponding False value (0x5a) or does it initialize to a zero representation and thus have indeterminate state? The documentation should probably be explicit about this case. If the underlying representation is an Enum, why not have hardened_enum instead? A hardened _Bool might want to have a special NaT value as well I guess? Richard. > for gcc/c-family/ChangeLog > > * c-attribs.cc (c_common_attribute_table): Add hardbool. > (handle_hardbool_attribute): New. > (type_valid_for_vector_size): Reject hardbool. > * c-common.cc (convert_and_check): Skip warnings for convert > and check for hardbool. > (c_hardbool_type_attr_1): New. > * c-common.h (c_hardbool_type_attr): New. > > for gcc/c/ChangeLog > > * c-typeck.cc (convert_lvalue_to_rvalue): Decay hardbools. > * c-convert.cc (convert): Convert to hardbool through > truthvalue. > * c-decl.cc (check_bitfield_type_and_width): Skip enumeral > truncation warnings for hardbool. > (finish_struct): Propagate hardbool attribute to bitfield > types. > (digest_init): Convert to hardbool. > > for gcc/ChangeLog > > * doc/extend.texi (hardbool): New type attribute. > > for gcc/testsuite/ChangeLog > > * gcc.dg/hardbool-err.c: New. > * gcc.dg/hardbool-trap.c: New. > * gcc.dg/hardbool.c: New. > * gcc.dg/hardbool-s.c: New. > * gcc.dg/hardbool-us.c: New. > * gcc.dg/hardbool-i.c: New. > * gcc.dg/hardbool-ul.c: New. > * gcc.dg/hardbool-ll.c: New. > * gcc.dg/hardbool-5a.c: New. > * gcc.dg/hardbool-s-5a.c: New. > * gcc.dg/hardbool-us-5a.c: New. > * gcc.dg/hardbool-i-5a.c: New. > * gcc.dg/hardbool-ul-5a.c: New. > * gcc.dg/hardbool-ll-5a.c: New. > --- > gcc/c-family/c-attribs.cc | 97 ++++++++++++++++++++- > gcc/c-family/c-common.cc | 21 ++++ > gcc/c-family/c-common.h | 18 ++++ > gcc/c/c-convert.cc | 14 +++ > gcc/c/c-decl.cc | 10 ++ > gcc/c/c-typeck.cc | 31 ++++++- > gcc/doc/extend.texi | 37 ++++++++ > gcc/testsuite/gcc.dg/hardbool-err.c | 28 ++++++ > gcc/testsuite/gcc.dg/hardbool-trap.c | 13 +++ > gcc/testsuite/gcc.dg/torture/hardbool-5a.c | 6 + > gcc/testsuite/gcc.dg/torture/hardbool-i-5a.c | 6 + > gcc/testsuite/gcc.dg/torture/hardbool-i.c | 5 + > gcc/testsuite/gcc.dg/torture/hardbool-ll-5a.c | 6 + > gcc/testsuite/gcc.dg/torture/hardbool-ll.c | 5 + > gcc/testsuite/gcc.dg/torture/hardbool-s-5a.c | 6 + > gcc/testsuite/gcc.dg/torture/hardbool-s.c | 5 + > gcc/testsuite/gcc.dg/torture/hardbool-ul-5a.c | 6 + > gcc/testsuite/gcc.dg/torture/hardbool-ul.c | 5 + > gcc/testsuite/gcc.dg/torture/hardbool-us-5a.c | 6 + > gcc/testsuite/gcc.dg/torture/hardbool-us.c | 5 + > gcc/testsuite/gcc.dg/torture/hardbool.c | 118 > +++++++++++++++++++++++++ > 21 files changed, 444 insertions(+), 4 deletions(-) > create mode 100644 gcc/testsuite/gcc.dg/hardbool-err.c > create mode 100644 gcc/testsuite/gcc.dg/hardbool-trap.c > create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool-5a.c > create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool-i-5a.c > create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool-i.c > create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool-ll-5a.c > create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool-ll.c > create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool-s-5a.c > create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool-s.c > create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool-ul-5a.c > create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool-ul.c > create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool-us-5a.c > create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool-us.c > create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool.c > > diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc > index c8d96723f4c30..e385d780c49ce 100644 > --- a/gcc/c-family/c-attribs.cc > +++ b/gcc/c-family/c-attribs.cc > @@ -172,6 +172,7 @@ static tree handle_objc_root_class_attribute (tree *, > tree, tree, int, bool *); > static tree handle_objc_nullability_attribute (tree *, tree, tree, int, bool > *); > static tree handle_signed_bool_precision_attribute (tree *, tree, tree, int, > bool *); > +static tree handle_hardbool_attribute (tree *, tree, tree, int, bool *); > static tree handle_retain_attribute (tree *, tree, tree, int, bool *); > > /* Helper to define attribute exclusions. */ > @@ -288,6 +289,8 @@ const struct attribute_spec c_common_attribute_table[] = > affects_type_identity, handler, exclude } */ > { "signed_bool_precision", 1, 1, false, true, false, true, > handle_signed_bool_precision_attribute, NULL }, > + { "hardbool", 0, 2, false, true, false, true, > + handle_hardbool_attribute, NULL }, > { "packed", 0, 0, false, false, false, false, > handle_packed_attribute, > attr_aligned_exclusions }, > @@ -975,6 +978,95 @@ handle_signed_bool_precision_attribute (tree *node, tree > name, tree args, > return NULL_TREE; > } > > +/* Handle a "hardbool" attribute; arguments as in struct > + attribute_spec.handler. */ > + > +static tree > +handle_hardbool_attribute (tree *node, tree name, tree args, > + int /* flags */, bool *no_add_attrs) > +{ > + if (c_language != clk_c) > + { > + error ("%qE attribute only supported in C", name); > + *no_add_attrs = TRUE; > + return NULL_TREE; > + } > + > + if (!TYPE_P (*node) || TREE_CODE (*node) != INTEGER_TYPE) > + { > + error ("%qE attribute only supported on " > + "integral types", name); > + *no_add_attrs = TRUE; > + return NULL_TREE; > + } > + > + tree orig = *node; > + *node = build_duplicate_type (orig); > + > + TREE_SET_CODE (*node, ENUMERAL_TYPE); > + > + tree false_value; > + if (args) > + false_value = fold_convert (*node, TREE_VALUE (args)); > + else > + false_value = fold_convert (*node, integer_zero_node); > + > + if (TREE_OVERFLOW_P (false_value)) > + { > + warning (OPT_Wattributes, > + "overflows in conversion from %qT to %qT " > + "changes value from %qE to %qE", > + TREE_TYPE (TREE_VALUE (args)), *node, > + TREE_VALUE (args), false_value); > + TREE_OVERFLOW (false_value) = false; > + } > + > + tree true_value; > + if (args && TREE_CHAIN (args)) > + true_value = fold_convert (*node, TREE_VALUE (TREE_CHAIN (args))); > + else > + true_value = fold_build1 (BIT_NOT_EXPR, *node, false_value); > + > + if (TREE_OVERFLOW_P (true_value)) > + { > + warning (OPT_Wattributes, > + "overflows in conversion from %qT to %qT " > + "changes value from %qE to %qE", > + TREE_TYPE (TREE_VALUE (TREE_CHAIN (args))), *node, > + TREE_VALUE (TREE_CHAIN (args)), true_value); > + TREE_OVERFLOW (true_value) = false; > + } > + > + if (tree_int_cst_compare (false_value, true_value) == 0) > + { > + error ("%qE attribute requires different values for" > + " %<false%> and %<true%> for type %qT", > + name, *node); > + *no_add_attrs = TRUE; > + return NULL_TREE; > + } > + > + tree values = build_tree_list (get_identifier ("false"), > + false_value); > + TREE_CHAIN (values) = build_tree_list (get_identifier ("true"), > + true_value); > + > + /* Do *not* set TYPE_MIN_VALUE, TYPE_MAX_VALUE, nor TYPE_PRECISION > according > + to the false and true values. That might cause the constants to be the > + only acceptable values, which would drop the very hardening checks this > + attribute is supposed to add. */ > + > + TYPE_ATTRIBUTES (*node) = tree_cons (name, args, > + TYPE_ATTRIBUTES (*node)); > + *no_add_attrs = TRUE; > + > + gcc_checking_assert (!TYPE_CACHED_VALUES_P (*node)); > + TYPE_VALUES (*node) = values; > + TYPE_NAME (*node) = orig; > + > + return NULL_TREE; > +} > + > /* Handle a "packed" attribute; arguments as in > struct attribute_spec.handler. */ > > @@ -4289,7 +4381,8 @@ static tree > type_valid_for_vector_size (tree type, tree atname, tree args, > unsigned HOST_WIDE_INT *ptrnunits) > { > - bool error_p = ptrnunits != NULL; > + bool hardbool_p = c_hardbool_type_attr (type); > + bool error_p = ptrnunits != NULL || hardbool_p; > > /* Get the mode of the type being modified. */ > machine_mode orig_mode = TYPE_MODE (type); > @@ -4301,7 +4394,7 @@ type_valid_for_vector_size (tree type, tree atname, > tree args, > && GET_MODE_CLASS (orig_mode) != MODE_INT > && !ALL_SCALAR_FIXED_POINT_MODE_P (orig_mode)) > || !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type)) > - || TREE_CODE (type) == BOOLEAN_TYPE) > + || TREE_CODE (type) == BOOLEAN_TYPE || hardbool_p) > { > if (error_p) > error ("invalid vector type for attribute %qE", atname); > diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc > index 1b8e73f7bc5d8..9a8584a5fe095 100644 > --- a/gcc/c-family/c-common.cc > +++ b/gcc/c-family/c-common.cc > @@ -1780,7 +1780,8 @@ convert_and_check (location_t loc, tree type, tree > expr, bool init_const) > > if (c_inhibit_evaluation_warnings == 0 > && !TREE_OVERFLOW_P (expr) > - && result != error_mark_node) > + && result != error_mark_node > + && !c_hardbool_type_attr (type)) > warnings_for_convert_and_check (loc, type, expr_for_warning, result); > > return result; > @@ -9488,4 +9489,22 @@ c_common_finalize_early_debug (void) > (*debug_hooks->early_global_decl) (cnode->decl); > } > > +/* This is the slow path of c-common.h's c_hardbool_type_attr. */ > + > +tree > +c_hardbool_type_attr_1 (tree type, tree *false_value, tree *true_value) > +{ > + tree attr = lookup_attribute ("hardbool", TYPE_ATTRIBUTES (type)); > + if (!attr) > + return attr; > + > + if (false_value) > + *false_value = TREE_VALUE (TYPE_VALUES (type)); > + > + if (true_value) > + *true_value = TREE_VALUE (TREE_CHAIN (TYPE_VALUES (type))); > + > + return attr; > +} > + > #include "gt-c-family-c-common.h" > diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h > index c0900848965a3..a8d6691d0813d 100644 > --- a/gcc/c-family/c-common.h > +++ b/gcc/c-family/c-common.h > @@ -912,6 +912,7 @@ extern tree c_common_get_narrower (tree, int *); > extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *); > extern void c_common_finalize_early_debug (void); > extern bool c_option_is_from_cpp_diagnostics (int); > +extern tree c_hardbool_type_attr_1 (tree, tree *, tree *); > > /* Used by convert_and_check; in front ends. */ > extern tree convert_init (tree, tree); > @@ -1291,6 +1292,23 @@ c_tree_chain_next (tree t) > return NULL; > } > > +/* Return the hardbool attribute associated with TYPE, if there is one, > provided > + that TYPE looks like an enumeral type that might have been set up by > + handle_hardbool_attribute. Return NULL otherwise. > + > + If FALSE_VALUE or TRUE_VALUE are non-NULL and TYPE is a hardened boolean > + type, store the corresponding representation values. */ > +static inline tree > +c_hardbool_type_attr (tree type, > + tree *false_value = NULL, tree *true_value = NULL) > +{ > + if (TREE_CODE (type) != ENUMERAL_TYPE > + || TYPE_LANG_SPECIFIC (type)) > + return NULL_TREE; > + > + return c_hardbool_type_attr_1 (type, false_value, true_value); > +} > + > /* Mask used by tm_stmt_attr. */ > #define TM_STMT_ATTR_OUTER 2 > #define TM_STMT_ATTR_ATOMIC 4 > diff --git a/gcc/c/c-convert.cc b/gcc/c/c-convert.cc > index 18083d5961826..1399cbf9d8d37 100644 > --- a/gcc/c/c-convert.cc > +++ b/gcc/c/c-convert.cc > @@ -105,6 +105,20 @@ c_convert (tree type, tree expr, bool init_const) > return error_mark_node; > } > > + { > + tree false_value, true_value; > + if (c_hardbool_type_attr (type, &false_value, &true_value)) > + { > + bool save = in_late_binary_op; > + in_late_binary_op = true; > + expr = c_objc_common_truthvalue_conversion (input_location, expr); > + in_late_binary_op = save; > + > + return fold_build3_loc (loc, COND_EXPR, type, > + expr, true_value, false_value); > + } > + } > + > switch (code) > { > case VOID_TYPE: > diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc > index ae8990c138fd2..e103582d677cd 100644 > --- a/gcc/c/c-decl.cc > +++ b/gcc/c/c-decl.cc > @@ -6163,6 +6163,12 @@ check_bitfield_type_and_width (location_t loc, tree > *type, tree *width, > else > w = tree_to_uhwi (*width); > > + /* Truncation of hardbool false and true representation values is always > safe: > + either the values remain different, or we'll report a problem when > creating > + the narrower type. */ > + if (c_hardbool_type_attr (*type)) > + return; > + > if (TREE_CODE (*type) == ENUMERAL_TYPE) > { > struct lang_type *lt = TYPE_LANG_SPECIFIC (*type); > @@ -8887,6 +8893,10 @@ finish_struct (location_t loc, tree t, tree fieldlist, > tree attributes, > { > TREE_TYPE (field) > = c_build_bitfield_integer_type (width, TYPE_UNSIGNED (type)); > + if (tree attr = c_hardbool_type_attr (type)) > + decl_attributes (&TREE_TYPE (field), > + copy_list (attr), > + 0, NULL_TREE); > SET_DECL_MODE (field, TYPE_MODE (TREE_TYPE (field))); > } > DECL_INITIAL (field) = NULL_TREE; > diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc > index fd0a7f81a7a92..1244241dd295b 100644 > --- a/gcc/c/c-typeck.cc > +++ b/gcc/c/c-typeck.cc > @@ -2172,6 +2172,35 @@ convert_lvalue_to_rvalue (location_t loc, struct > c_expr exp, > if (convert_p && !error_operand_p (exp.value) > && (TREE_CODE (TREE_TYPE (exp.value)) != ARRAY_TYPE)) > exp.value = convert (build_qualified_type (TREE_TYPE (exp.value), > TYPE_UNQUALIFIED), exp.value); > + > + { > + tree false_value, true_value; > + if (convert_p && !error_operand_p (exp.value) > + && c_hardbool_type_attr (TREE_TYPE (exp.value), > + &false_value, &true_value)) > + { > + tree t = save_expr (exp.value); > + > + mark_exp_read (exp.value); > + > + tree trapfn = builtin_decl_explicit (BUILT_IN_TRAP); > + tree expr = build_call_expr_loc (loc, trapfn, 0); > + expr = build_compound_expr (loc, expr, boolean_true_node); > + expr = fold_build3_loc (loc, COND_EXPR, boolean_type_node, > + fold_build2_loc (loc, NE_EXPR, > + boolean_type_node, > + t, true_value), > + expr, boolean_true_node); > + expr = fold_build3_loc (loc, COND_EXPR, boolean_type_node, > + fold_build2_loc (loc, NE_EXPR, > + boolean_type_node, > + t, false_value), > + expr, boolean_false_node); > + > + exp.value = expr; > + } > + } > + > return exp; > } > > @@ -8167,7 +8196,7 @@ digest_init (location_t init_loc, tree type, tree init, > tree origtype, > } > } > > - if (code == VECTOR_TYPE) > + if (code == VECTOR_TYPE || c_hardbool_type_attr (type)) > /* Although the types are compatible, we may require a > conversion. */ > inside_init = convert (type, inside_init); > diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi > index dfbe33ac652f8..7b514354263f4 100644 > --- a/gcc/doc/extend.texi > +++ b/gcc/doc/extend.texi > @@ -8654,6 +8654,43 @@ initialization will result in future breakage. > GCC emits warnings based on this attribute by default; use > @option{-Wno-designated-init} to suppress them. > > +@item hardbool > +@itemx hardbool (@var{false_value}) > +@itemx hardbool (@var{false_value}, @var{true_value}) > +@cindex @code{hardbool} type attribute > +This attribute may only be applied to integral types in C, to introduce > +hardened boolean types. It turns the integral type into a boolean-like > +type with the same size and precision, that uses the specified values as > +representations for @code{false} and @code{true}. Underneath, it is > +actually an enumerate type, but its observable behavior is like that of > +@code{_Bool}, except for the strict internal representations, verified > +by runtime checks. > + > +If @var{true_value} is omitted, the bitwise negation of > +@var{false_value} is used. If @var{false_value} is omitted, zero is > +used. The named representation values must be different when converted > +to the original integral type. Narrower bitfields are rejected if the > +representations become indistinguishable. > + > +Values of such types automatically decay to @code{_Bool}, at which > +point, the selected representation values are mapped to the > +corresponding @code{_Bool} values. When the represented value is not > +determined, at compile time, to be either @var{false_value} or > +@var{true_value}, runtime verification calls @code{__builtin_trap} if it > +is neither. This is what makes them hardened boolean types. > + > +When converting scalar types to such hardened boolean types, implicitly > +or explicitly, behavior corresponds to a conversion to @code{_Bool}, > +followed by a mapping from @code{false} and @code{true} to > +@var{false_value} and @var{true_value}, respectively. > + > +@smallexample > +typedef char __attribute__ ((__hardbool__ (0x5a))) hbool; > +hbool first = 0; /* False, stored as (char)0x5a. */ > +hbool second = !first; /* True, stored as ~(char)0x5a. */ > +@end smallexample > + > + > @item may_alias > @cindex @code{may_alias} type attribute > Accesses through pointers to types with this attribute are not subject > diff --git a/gcc/testsuite/gcc.dg/hardbool-err.c > b/gcc/testsuite/gcc.dg/hardbool-err.c > new file mode 100644 > index 0000000000000..634feaed4deef > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/hardbool-err.c > @@ -0,0 +1,28 @@ > +/* { dg-do compile } */ > +/* { dg-options "" } */ > + > +typedef _Bool __attribute__ ((__hardbool__)) > +hbbl; /* { dg-error "integral types" } */ > + > +typedef double __attribute__ ((__hardbool__)) > +hbdbl; /* { dg-error "integral types" } */ > + > +enum x; > +typedef enum x __attribute__ ((__hardbool__)) > +hbenum; /* { dg-error "integral types" } */ > + > +struct s; > +typedef struct s __attribute__ ((__hardbool__)) > +hbstruct; /* { dg-error "integral types" } */ > + > +typedef int __attribute__ ((__hardbool__ (0, 0))) > +hb00; /* { dg-error "different values" } */ > + > +typedef int __attribute__ ((__hardbool__ (4, 16))) hb4x; > +struct s { > + hb4x m:2; > +}; /* { dg-error "is a GCC extension|different values" } */ > +/* { dg-warning "changes value" "warning" { target *-*-* } .-1 } */ > + > +hb4x __attribute__ ((vector_size (4 * sizeof (hb4x)))) > +vvar; /* { dg-error "invalid vector type" } */ > diff --git a/gcc/testsuite/gcc.dg/hardbool-trap.c > b/gcc/testsuite/gcc.dg/hardbool-trap.c > new file mode 100644 > index 0000000000000..2eebd0ef64fff > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/hardbool-trap.c > @@ -0,0 +1,13 @@ > +/* { dg-do compile } */ > +/* { dg-options "-fdump-tree-optimized" } */ > + > +typedef char __attribute__ ((__hardbool__ (1))) hbool; > + > +hbool var; > + > +int main () { > + __builtin_memset (&var, 0, sizeof (var)); > + (void)var; > +} > + > +/* { dg-final { scan-tree-dump-times "__builtin_trap" 1 "optimized" } } */ > diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-5a.c > b/gcc/testsuite/gcc.dg/torture/hardbool-5a.c > new file mode 100644 > index 0000000000000..a03887cfbecc5 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/torture/hardbool-5a.c > @@ -0,0 +1,6 @@ > +/* { dg-do run } */ > +/* { dg-options "-w" } */ > + > +#define falseval 0x5a > + > +#include "hardbool.c" > diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-i-5a.c > b/gcc/testsuite/gcc.dg/torture/hardbool-i-5a.c > new file mode 100644 > index 0000000000000..c0ba2a8b9148e > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/torture/hardbool-i-5a.c > @@ -0,0 +1,6 @@ > +/* { dg-do run } */ > +/* { dg-options "-w" } */ > + > +#define falseval 0xa53cc35a > + > +#include "hardbool-i.c" > diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-i.c > b/gcc/testsuite/gcc.dg/torture/hardbool-i.c > new file mode 100644 > index 0000000000000..39214d28c5627 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/torture/hardbool-i.c > @@ -0,0 +1,5 @@ > +/* { dg-do run } */ > + > +#define basetype int > + > +#include "hardbool.c" > diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-ll-5a.c > b/gcc/testsuite/gcc.dg/torture/hardbool-ll-5a.c > new file mode 100644 > index 0000000000000..14438c5104f07 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/torture/hardbool-ll-5a.c > @@ -0,0 +1,6 @@ > +/* { dg-do run } */ > +/* { dg-options "-w" } */ > + > +#define falseval 0x781ee187a53cc35all > + > +#include "hardbool-ll.c" > diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-ll.c > b/gcc/testsuite/gcc.dg/torture/hardbool-ll.c > new file mode 100644 > index 0000000000000..d4d498c6f2af1 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/torture/hardbool-ll.c > @@ -0,0 +1,5 @@ > +/* { dg-do run } */ > + > +#define basetype long long > + > +#include "hardbool.c" > diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-s-5a.c > b/gcc/testsuite/gcc.dg/torture/hardbool-s-5a.c > new file mode 100644 > index 0000000000000..e38a56b5deb05 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/torture/hardbool-s-5a.c > @@ -0,0 +1,6 @@ > +/* { dg-do run } */ > +/* { dg-options "-w" } */ > + > +#define falseval 0x5aa5 > + > +#include "hardbool-s.c" > diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-s.c > b/gcc/testsuite/gcc.dg/torture/hardbool-s.c > new file mode 100644 > index 0000000000000..942300be2072a > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/torture/hardbool-s.c > @@ -0,0 +1,5 @@ > +/* { dg-do run } */ > + > +#define basetype short > + > +#include "hardbool.c" > diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-ul-5a.c > b/gcc/testsuite/gcc.dg/torture/hardbool-ul-5a.c > new file mode 100644 > index 0000000000000..7beec578ff89c > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/torture/hardbool-ul-5a.c > @@ -0,0 +1,6 @@ > +/* { dg-do run } */ > +/* { dg-options "-w" } */ > + > +#define falseval 0xa53cc35a > + > +#include "hardbool-ul.c" > diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-ul.c > b/gcc/testsuite/gcc.dg/torture/hardbool-ul.c > new file mode 100644 > index 0000000000000..841c1d4bc2ec8 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/torture/hardbool-ul.c > @@ -0,0 +1,5 @@ > +/* { dg-do run } */ > + > +#define basetype unsigned long > + > +#include "hardbool.c" > diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-us-5a.c > b/gcc/testsuite/gcc.dg/torture/hardbool-us-5a.c > new file mode 100644 > index 0000000000000..5bfc922795d3d > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/torture/hardbool-us-5a.c > @@ -0,0 +1,6 @@ > +/* { dg-do run } */ > +/* { dg-options "-w" } */ > + > +#define falseval 0xa55a > + > +#include "hardbool-us.c" > diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-us.c > b/gcc/testsuite/gcc.dg/torture/hardbool-us.c > new file mode 100644 > index 0000000000000..e9feec681c41e > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/torture/hardbool-us.c > @@ -0,0 +1,5 @@ > +/* { dg-do run } */ > + > +#define basetype unsigned short > + > +#include "hardbool.c" > diff --git a/gcc/testsuite/gcc.dg/torture/hardbool.c > b/gcc/testsuite/gcc.dg/torture/hardbool.c > new file mode 100644 > index 0000000000000..01684952a2a9f > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/torture/hardbool.c > @@ -0,0 +1,118 @@ > +/* { dg-do run } */ > + > +#include <assert.h> > + > +#ifndef basetype > +#define basetype char > +#endif > + > +#ifndef falseval > +#define falseval 0 > +#endif > + > +#ifndef trueval > +#define trueval ~falseval > +#endif > + > +/* hardbool may be #defined so as to drop parms in other tests. */ > +typedef basetype __attribute__ ((hardbool (falseval, trueval))) hbool; > + > +typedef unsigned char __attribute__ ((__hardbool__ (1, 0))) zbool; > + > +struct hs { > + hbool a[2]; > + hbool x:2; > + hbool y:5; > + zbool z:1; > +}; > + > +hbool var = 0; > + > +struct hs x = { { 1, 0 }, 2, 0, 2 }; > + > +int f(hbool v) { > + return !v; > +} > + > +int g(int i) { > + return f(i); > +} > + > +hbool h(hbool x) { > + return x; > +} > + > +hbool h2(hbool x) { > + return h(x); > +} > + > +int hsx(struct hs v) { > + return v.x; > +} > + > +int ghs(hbool s) { > + struct hs v = { {s, !s}, s, !s, s }; > + return hsx (v); > +} > + > +int t = (hbool)2; > + > +void check_pfalse (hbool *p) > +{ > + assert (!*p); > + assert (*(basetype*)p == (basetype)falseval); > + assert (!(int)(hbool)*p); > +} > + > +void check_ptrue (hbool *p) > +{ > + assert (*p); > + assert (*(basetype*)p == (basetype)trueval); > + assert ((int)(hbool)*p); > +} > + > +void check_vfalse (hbool v) > +{ > + check_pfalse (&v); > +} > + > +void check_vtrue (hbool v) > +{ > + check_ptrue (&v); > +} > + > +int main () { > + check_pfalse (&var); > + var = !(int)(hbool)(_Bool)var; > + check_ptrue (&var); > + var = (zbool)var; > + check_ptrue (&var); > + > + check_ptrue (&x.a[0]); > + check_pfalse (&x.a[1]); > + check_vtrue (x.x); > + check_vfalse (x.y); > + check_vtrue (x.z); > + > + check_vtrue (t); > + > + check_vtrue (var && t); > + check_vfalse (!var || x.y); > + > + check_vfalse (f (2)); > + check_vfalse (f (1)); > + check_vtrue (f (0)); > + > + check_vfalse (g (2)); > + check_vfalse (g (1)); > + check_vtrue (g (0)); > + > + check_vtrue (h (2)); > + check_vtrue (h (1)); > + check_vfalse (h (0)); > + > + check_vtrue (h2 (2)); > + check_vtrue (h2 (1)); > + check_vfalse (h2 (0)); > +} > + > > > -- > Alexandre Oliva, happy hacker https://FSFLA.org/blogs/lxo/ > Free Software Activist GNU Toolchain Engineer > Disinformation flourishes because many people care deeply about injustice > but very few check the facts. Ask me about <https://stallmansupport.org>