public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc(refs/users/aoliva/heads/testme)] Introduce hardbool attribute for C
@ 2023-11-20  9:03 Alexandre Oliva
  0 siblings, 0 replies; 19+ messages in thread
From: Alexandre Oliva @ 2023-11-20  9:03 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:8be7ee6b8787a968b6c8c52e7e1dc67b4e288d5c

commit 8be7ee6b8787a968b6c8c52e7e1dc67b4e288d5c
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Thu Oct 26 03:06:16 2023 -0300

    Introduce hardbool attribute for C
    
    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.
    
    
    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.
            * doc/invoke.texi (-ftrivial-auto-var-init): Document
            representation vs values.
    
    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.

Diff:
---
 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                           |  65 ++++++++++++++
 gcc/doc/invoke.texi                           |  21 +++++
 gcc/testsuite/gcc.dg/hardbool-err.c           |  31 +++++++
 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 ++++++++++++++++++++++++++
 22 files changed, 497 insertions(+), 3 deletions(-)

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index 461732f60f7..2b20e58c922 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -176,6 +176,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 *);
 static tree handle_fd_arg_attribute (tree *, tree, tree, int, bool *);
 static tree handle_null_terminated_string_arg_attribute (tree *, tree, tree, int, bool *);
@@ -294,6 +295,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 },
@@ -997,6 +1000,96 @@ 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);
+  ENUM_UNDERLYING_TYPE (*node) = orig;
+
+  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.  */
 
@@ -4408,7 +4501,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);
@@ -4421,6 +4515,7 @@ type_valid_for_vector_size (tree type, tree atname, tree args,
 	  && !ALL_SCALAR_FIXED_POINT_MODE_P (orig_mode))
       || !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type))
       || TREE_CODE (type) == BOOLEAN_TYPE
+      || hardbool_p
       || TREE_CODE (type) == BITINT_TYPE)
     {
       if (error_p)
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index 0ea0c4f4bef..491c1430519 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -1774,7 +1774,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;
@@ -9877,4 +9878,22 @@ c_strict_flex_array_level_of (tree array_field)
   return strict_flex_array_level;
 }
 
+/* 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 b57e83d7c5d..572520590f4 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -911,6 +911,7 @@ extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *);
 extern void c_common_finalize_early_debug (void);
 extern unsigned int c_strict_flex_array_level_of (tree);
 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);
@@ -1348,6 +1349,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 71e618cf3b6..918effd5ebb 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 64d3a941cb9..87b82752af8 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -6532,6 +6532,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);
@@ -9469,6 +9475,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 1dbb4471a88..fc2709e32ab 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -2065,6 +2065,35 @@ convert_lvalue_to_rvalue (location_t loc, struct c_expr exp,
     exp.value = convert (build_qualified_type (TREE_TYPE (exp.value), TYPE_UNQUALIFIED), exp.value);
   if (force_non_npc)
     exp.value = build1 (NOP_EXPR, TREE_TYPE (exp.value), 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;
 }
 
@@ -8396,7 +8425,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 3169acd8550..69ea9541887 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -8962,6 +8962,71 @@ initialization will result in future breakage.
 GCC emits warnings based on this attribute by default; use
 @option{-Wno-designated-init} to suppress them.
 
+@cindex @code{hardbool} type attribute
+@item hardbool
+@itemx hardbool (@var{false_value})
+@itemx hardbool (@var{false_value}, @var{true_value})
+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.  */
+
+static hbool zeroinit; /* False, stored as (char)0x5a.  */
+auto hbool uninit;     /* Undefined, may trap.  */
+@end smallexample
+
+When zero-initializing a variable or field of hardened boolean type
+(presumably held in static storage) the implied zero initializer gets
+converted to @code{_Bool}, and then to the hardened boolean type, so
+that the initial value is the hardened representation for @code{false}.
+Using that value is well defined.  This is @emph{not} the case when
+variables and fields of such types are uninitialized (presumably held in
+automatic or dynamic storage): their values are indeterminate, and using
+them invokes undefined behavior.  Using them may trap or not, depending
+on the bits held in the storage (re)used for the variable, if any, and
+on optimizations the compiler may perform on the grounds that using
+uninitialized values invokes undefined behavior.
+
+Users of @option{-ftrivial-auto-var-init} should be aware that the bit
+patterns used as initializers are @emph{not} converted to
+@code{hardbool} types, so using a @code{hardbool} variable that is
+implicitly initialized by the @option{-ftrivial-auto-var-init} may trap
+if the representations values chosen for @code{false} and @code{true} do
+not match the initializer.
+
+Since this is a language extension only available in C, interoperation
+with other languages may pose difficulties.  It should interoperate with
+Ada Booleans defined with the same size and equivalent representation
+clauses, and with enumerations or other languages' integral types that
+correspond to C's chosen integral type.
+
+
 @cindex @code{may_alias} type attribute
 @item may_alias
 Accesses through pointers to types with this attribute are not subject
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index bc9f4f70914..c2ab68a3fdc 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -13702,6 +13702,27 @@ The values used for pattern initialization might be changed in the future.
 
 The default is @samp{uninitialized}.
 
+Note that the initializer values, whether @samp{zero} or @samp{pattern},
+refer to data representation (in memory or machine registers), rather
+than to their interpretation as numerical values.  This distinction may
+be important in languages that support types with biases or implicit
+multipliers, and with such extensions as @samp{hardbool} (@pxref{Type
+Attributes}).  For example, a variable that uses 8 bits to represent
+(biased) quantities in the @code{range 160..400} will be initialized
+with the bit patterns @code{0x00} or @code{0xFE}, depending on
+@var{choice}, whether or not these representations stand for values in
+that range, and even if they do, the interpretation of the value held by
+the variable will depend on the bias.  A @samp{hardbool} variable that
+uses say @code{0X5A} and @code{0xA5} for @code{false} and @code{true},
+respectively, will trap with either @samp{choice} of trivial
+initializer, i.e., @samp{zero} initialization will not convert to the
+representation for @code{false}, even if it would for a @code{static}
+variable of the same type.  This means the initializer pattern doesn't
+generally depend on the type of the initialized variable.  One notable
+exception is that (non-hardened) boolean variables that fit in registers
+are initialized with @code{false} (zero), even when @samp{pattern} is
+requested.
+
 You can control this behavior for a specific variable by using the variable
 attribute @code{uninitialized} (@pxref{Variable Attributes}).
 
diff --git a/gcc/testsuite/gcc.dg/hardbool-err.c b/gcc/testsuite/gcc.dg/hardbool-err.c
new file mode 100644
index 00000000000..e5bf58e55de
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hardbool-err.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+typedef _Bool __attribute__ ((__hardbool__))
+hbbl; /* { dg-error "integral types" } */
+
+typedef double __attribute__ ((__hardbool__))
+hbdbl; /* { dg-error "integral types" } */
+
+typedef _Complex int __attribute__ ((__hardbool__))
+hbcplx; /* { 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 00000000000..2eebd0ef64f
--- /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 00000000000..a03887cfbec
--- /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 00000000000..c0ba2a8b914
--- /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 00000000000..39214d28c56
--- /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 00000000000..14438c5104f
--- /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 00000000000..d4d498c6f2a
--- /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 00000000000..e38a56b5deb
--- /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 00000000000..942300be207
--- /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 00000000000..7beec578ff8
--- /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 00000000000..841c1d4bc2e
--- /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 00000000000..5bfc922795d
--- /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 00000000000..e9feec681c4
--- /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 00000000000..01684952a2a
--- /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));
+}
+

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce hardbool attribute for C
@ 2023-11-29  4:35 Alexandre Oliva
  0 siblings, 0 replies; 19+ messages in thread
From: Alexandre Oliva @ 2023-11-29  4:35 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:7ae2b8a9f71c6add402b5f7dc2fc7ec7f0a120af

commit 7ae2b8a9f71c6add402b5f7dc2fc7ec7f0a120af
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Thu Oct 26 03:06:16 2023 -0300

    Introduce hardbool attribute for C
    
    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.
    
    
    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.
            * doc/invoke.texi (-ftrivial-auto-var-init): Document
            representation vs values.
    
    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.

Diff:
---
 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                           |  65 ++++++++++++++
 gcc/doc/invoke.texi                           |  21 +++++
 gcc/testsuite/gcc.dg/hardbool-err.c           |  31 +++++++
 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 ++++++++++++++++++++++++++
 22 files changed, 497 insertions(+), 3 deletions(-)

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index 461732f60f7..2b20e58c922 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -176,6 +176,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 *);
 static tree handle_fd_arg_attribute (tree *, tree, tree, int, bool *);
 static tree handle_null_terminated_string_arg_attribute (tree *, tree, tree, int, bool *);
@@ -294,6 +295,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 },
@@ -997,6 +1000,96 @@ 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);
+  ENUM_UNDERLYING_TYPE (*node) = orig;
+
+  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.  */
 
@@ -4408,7 +4501,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);
@@ -4421,6 +4515,7 @@ type_valid_for_vector_size (tree type, tree atname, tree args,
 	  && !ALL_SCALAR_FIXED_POINT_MODE_P (orig_mode))
       || !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type))
       || TREE_CODE (type) == BOOLEAN_TYPE
+      || hardbool_p
       || TREE_CODE (type) == BITINT_TYPE)
     {
       if (error_p)
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index ca7557c6b40..b2b70c99338 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -1826,7 +1826,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;
@@ -9988,4 +9989,22 @@ has_feature_p (const char *ident, bool strict_p)
   return !strict_p || *feat_p;
 }
 
+/* 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 dd4fe3aef38..3f772dbcca1 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -911,6 +911,7 @@ extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *);
 extern void c_common_finalize_early_debug (void);
 extern unsigned int c_strict_flex_array_level_of (tree);
 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);
@@ -1356,6 +1357,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 71e618cf3b6..918effd5ebb 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 439a3129f87..cf1df82c0f4 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -6532,6 +6532,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);
@@ -9469,6 +9475,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 17fdc9789b4..f37496cf687 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -2065,6 +2065,35 @@ convert_lvalue_to_rvalue (location_t loc, struct c_expr exp,
     exp.value = convert (build_qualified_type (TREE_TYPE (exp.value), TYPE_UNQUALIFIED), exp.value);
   if (force_non_npc)
     exp.value = build1 (NOP_EXPR, TREE_TYPE (exp.value), 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;
 }
 
@@ -8396,7 +8425,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 1ae589aeb29..e6de0815846 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -8962,6 +8962,71 @@ initialization will result in future breakage.
 GCC emits warnings based on this attribute by default; use
 @option{-Wno-designated-init} to suppress them.
 
+@cindex @code{hardbool} type attribute
+@item hardbool
+@itemx hardbool (@var{false_value})
+@itemx hardbool (@var{false_value}, @var{true_value})
+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.  */
+
+static hbool zeroinit; /* False, stored as (char)0x5a.  */
+auto hbool uninit;     /* Undefined, may trap.  */
+@end smallexample
+
+When zero-initializing a variable or field of hardened boolean type
+(presumably held in static storage) the implied zero initializer gets
+converted to @code{_Bool}, and then to the hardened boolean type, so
+that the initial value is the hardened representation for @code{false}.
+Using that value is well defined.  This is @emph{not} the case when
+variables and fields of such types are uninitialized (presumably held in
+automatic or dynamic storage): their values are indeterminate, and using
+them invokes undefined behavior.  Using them may trap or not, depending
+on the bits held in the storage (re)used for the variable, if any, and
+on optimizations the compiler may perform on the grounds that using
+uninitialized values invokes undefined behavior.
+
+Users of @option{-ftrivial-auto-var-init} should be aware that the bit
+patterns used as initializers are @emph{not} converted to
+@code{hardbool} types, so using a @code{hardbool} variable that is
+implicitly initialized by the @option{-ftrivial-auto-var-init} may trap
+if the representations values chosen for @code{false} and @code{true} do
+not match the initializer.
+
+Since this is a language extension only available in C, interoperation
+with other languages may pose difficulties.  It should interoperate with
+Ada Booleans defined with the same size and equivalent representation
+clauses, and with enumerations or other languages' integral types that
+correspond to C's chosen integral type.
+
+
 @cindex @code{may_alias} type attribute
 @item may_alias
 Accesses through pointers to types with this attribute are not subject
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 9f8a4ef3d5f..7e981dccd0c 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -13744,6 +13744,27 @@ The values used for pattern initialization might be changed in the future.
 
 The default is @samp{uninitialized}.
 
+Note that the initializer values, whether @samp{zero} or @samp{pattern},
+refer to data representation (in memory or machine registers), rather
+than to their interpretation as numerical values.  This distinction may
+be important in languages that support types with biases or implicit
+multipliers, and with such extensions as @samp{hardbool} (@pxref{Type
+Attributes}).  For example, a variable that uses 8 bits to represent
+(biased) quantities in the @code{range 160..400} will be initialized
+with the bit patterns @code{0x00} or @code{0xFE}, depending on
+@var{choice}, whether or not these representations stand for values in
+that range, and even if they do, the interpretation of the value held by
+the variable will depend on the bias.  A @samp{hardbool} variable that
+uses say @code{0X5A} and @code{0xA5} for @code{false} and @code{true},
+respectively, will trap with either @samp{choice} of trivial
+initializer, i.e., @samp{zero} initialization will not convert to the
+representation for @code{false}, even if it would for a @code{static}
+variable of the same type.  This means the initializer pattern doesn't
+generally depend on the type of the initialized variable.  One notable
+exception is that (non-hardened) boolean variables that fit in registers
+are initialized with @code{false} (zero), even when @samp{pattern} is
+requested.
+
 You can control this behavior for a specific variable by using the variable
 attribute @code{uninitialized} (@pxref{Variable Attributes}).
 
diff --git a/gcc/testsuite/gcc.dg/hardbool-err.c b/gcc/testsuite/gcc.dg/hardbool-err.c
new file mode 100644
index 00000000000..e5bf58e55de
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hardbool-err.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+typedef _Bool __attribute__ ((__hardbool__))
+hbbl; /* { dg-error "integral types" } */
+
+typedef double __attribute__ ((__hardbool__))
+hbdbl; /* { dg-error "integral types" } */
+
+typedef _Complex int __attribute__ ((__hardbool__))
+hbcplx; /* { 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 00000000000..2eebd0ef64f
--- /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 00000000000..a03887cfbec
--- /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 00000000000..c0ba2a8b914
--- /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 00000000000..39214d28c56
--- /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 00000000000..14438c5104f
--- /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 00000000000..d4d498c6f2a
--- /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 00000000000..e38a56b5deb
--- /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 00000000000..942300be207
--- /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 00000000000..7beec578ff8
--- /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 00000000000..841c1d4bc2e
--- /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 00000000000..5bfc922795d
--- /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 00000000000..e9feec681c4
--- /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 00000000000..01684952a2a
--- /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));
+}
+

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce hardbool attribute for C
@ 2023-11-23 11:46 Alexandre Oliva
  0 siblings, 0 replies; 19+ messages in thread
From: Alexandre Oliva @ 2023-11-23 11:46 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:89f0a0c26b831593c31b0270b5e93e548e03ac3c

commit 89f0a0c26b831593c31b0270b5e93e548e03ac3c
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Thu Oct 26 03:06:16 2023 -0300

    Introduce hardbool attribute for C
    
    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.
    
    
    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.
            * doc/invoke.texi (-ftrivial-auto-var-init): Document
            representation vs values.
    
    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.

Diff:
---
 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                           |  65 ++++++++++++++
 gcc/doc/invoke.texi                           |  21 +++++
 gcc/testsuite/gcc.dg/hardbool-err.c           |  31 +++++++
 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 ++++++++++++++++++++++++++
 22 files changed, 497 insertions(+), 3 deletions(-)

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index 461732f60f7..2b20e58c922 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -176,6 +176,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 *);
 static tree handle_fd_arg_attribute (tree *, tree, tree, int, bool *);
 static tree handle_null_terminated_string_arg_attribute (tree *, tree, tree, int, bool *);
@@ -294,6 +295,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 },
@@ -997,6 +1000,96 @@ 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);
+  ENUM_UNDERLYING_TYPE (*node) = orig;
+
+  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.  */
 
@@ -4408,7 +4501,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);
@@ -4421,6 +4515,7 @@ type_valid_for_vector_size (tree type, tree atname, tree args,
 	  && !ALL_SCALAR_FIXED_POINT_MODE_P (orig_mode))
       || !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type))
       || TREE_CODE (type) == BOOLEAN_TYPE
+      || hardbool_p
       || TREE_CODE (type) == BITINT_TYPE)
     {
       if (error_p)
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index 0ea0c4f4bef..491c1430519 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -1774,7 +1774,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;
@@ -9877,4 +9878,22 @@ c_strict_flex_array_level_of (tree array_field)
   return strict_flex_array_level;
 }
 
+/* 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 b57e83d7c5d..572520590f4 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -911,6 +911,7 @@ extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *);
 extern void c_common_finalize_early_debug (void);
 extern unsigned int c_strict_flex_array_level_of (tree);
 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);
@@ -1348,6 +1349,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 71e618cf3b6..918effd5ebb 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 64d3a941cb9..87b82752af8 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -6532,6 +6532,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);
@@ -9469,6 +9475,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 1dbb4471a88..fc2709e32ab 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -2065,6 +2065,35 @@ convert_lvalue_to_rvalue (location_t loc, struct c_expr exp,
     exp.value = convert (build_qualified_type (TREE_TYPE (exp.value), TYPE_UNQUALIFIED), exp.value);
   if (force_non_npc)
     exp.value = build1 (NOP_EXPR, TREE_TYPE (exp.value), 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;
 }
 
@@ -8396,7 +8425,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 3169acd8550..69ea9541887 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -8962,6 +8962,71 @@ initialization will result in future breakage.
 GCC emits warnings based on this attribute by default; use
 @option{-Wno-designated-init} to suppress them.
 
+@cindex @code{hardbool} type attribute
+@item hardbool
+@itemx hardbool (@var{false_value})
+@itemx hardbool (@var{false_value}, @var{true_value})
+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.  */
+
+static hbool zeroinit; /* False, stored as (char)0x5a.  */
+auto hbool uninit;     /* Undefined, may trap.  */
+@end smallexample
+
+When zero-initializing a variable or field of hardened boolean type
+(presumably held in static storage) the implied zero initializer gets
+converted to @code{_Bool}, and then to the hardened boolean type, so
+that the initial value is the hardened representation for @code{false}.
+Using that value is well defined.  This is @emph{not} the case when
+variables and fields of such types are uninitialized (presumably held in
+automatic or dynamic storage): their values are indeterminate, and using
+them invokes undefined behavior.  Using them may trap or not, depending
+on the bits held in the storage (re)used for the variable, if any, and
+on optimizations the compiler may perform on the grounds that using
+uninitialized values invokes undefined behavior.
+
+Users of @option{-ftrivial-auto-var-init} should be aware that the bit
+patterns used as initializers are @emph{not} converted to
+@code{hardbool} types, so using a @code{hardbool} variable that is
+implicitly initialized by the @option{-ftrivial-auto-var-init} may trap
+if the representations values chosen for @code{false} and @code{true} do
+not match the initializer.
+
+Since this is a language extension only available in C, interoperation
+with other languages may pose difficulties.  It should interoperate with
+Ada Booleans defined with the same size and equivalent representation
+clauses, and with enumerations or other languages' integral types that
+correspond to C's chosen integral type.
+
+
 @cindex @code{may_alias} type attribute
 @item may_alias
 Accesses through pointers to types with this attribute are not subject
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index a71fd524dfd..9d867cb13d2 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -13717,6 +13717,27 @@ The values used for pattern initialization might be changed in the future.
 
 The default is @samp{uninitialized}.
 
+Note that the initializer values, whether @samp{zero} or @samp{pattern},
+refer to data representation (in memory or machine registers), rather
+than to their interpretation as numerical values.  This distinction may
+be important in languages that support types with biases or implicit
+multipliers, and with such extensions as @samp{hardbool} (@pxref{Type
+Attributes}).  For example, a variable that uses 8 bits to represent
+(biased) quantities in the @code{range 160..400} will be initialized
+with the bit patterns @code{0x00} or @code{0xFE}, depending on
+@var{choice}, whether or not these representations stand for values in
+that range, and even if they do, the interpretation of the value held by
+the variable will depend on the bias.  A @samp{hardbool} variable that
+uses say @code{0X5A} and @code{0xA5} for @code{false} and @code{true},
+respectively, will trap with either @samp{choice} of trivial
+initializer, i.e., @samp{zero} initialization will not convert to the
+representation for @code{false}, even if it would for a @code{static}
+variable of the same type.  This means the initializer pattern doesn't
+generally depend on the type of the initialized variable.  One notable
+exception is that (non-hardened) boolean variables that fit in registers
+are initialized with @code{false} (zero), even when @samp{pattern} is
+requested.
+
 You can control this behavior for a specific variable by using the variable
 attribute @code{uninitialized} (@pxref{Variable Attributes}).
 
diff --git a/gcc/testsuite/gcc.dg/hardbool-err.c b/gcc/testsuite/gcc.dg/hardbool-err.c
new file mode 100644
index 00000000000..e5bf58e55de
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hardbool-err.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+typedef _Bool __attribute__ ((__hardbool__))
+hbbl; /* { dg-error "integral types" } */
+
+typedef double __attribute__ ((__hardbool__))
+hbdbl; /* { dg-error "integral types" } */
+
+typedef _Complex int __attribute__ ((__hardbool__))
+hbcplx; /* { 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 00000000000..2eebd0ef64f
--- /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 00000000000..a03887cfbec
--- /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 00000000000..c0ba2a8b914
--- /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 00000000000..39214d28c56
--- /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 00000000000..14438c5104f
--- /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 00000000000..d4d498c6f2a
--- /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 00000000000..e38a56b5deb
--- /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 00000000000..942300be207
--- /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 00000000000..7beec578ff8
--- /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 00000000000..841c1d4bc2e
--- /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 00000000000..5bfc922795d
--- /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 00000000000..e9feec681c4
--- /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 00000000000..01684952a2a
--- /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));
+}
+

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce hardbool attribute for C
@ 2023-10-21  3:35 Alexandre Oliva
  0 siblings, 0 replies; 19+ messages in thread
From: Alexandre Oliva @ 2023-10-21  3:35 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:9a8622235eec155c5c53b45b5f55aa9c2dcd15c1

commit 9a8622235eec155c5c53b45b5f55aa9c2dcd15c1
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Thu Oct 19 02:52:52 2023 -0300

    Introduce hardbool attribute for C
    
    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.
    
    
    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.
            * doc/invoke.texi (-ftrivial-auto-var-init): Document
            representation vs values.
    
    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.

Diff:
---
 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                           |  65 ++++++++++++++
 gcc/doc/invoke.texi                           |  21 +++++
 gcc/testsuite/gcc.dg/hardbool-err.c           |  31 +++++++
 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 ++++++++++++++++++++++++++
 22 files changed, 497 insertions(+), 3 deletions(-)

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index abf44d5426e8..232365d46e23 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -176,6 +176,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 *);
 static tree handle_fd_arg_attribute (tree *, tree, tree, int, bool *);
 
@@ -293,6 +294,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 },
@@ -992,6 +995,96 @@ 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);
+  ENUM_UNDERLYING_TYPE (*node) = orig;
+
+  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.  */
 
@@ -4403,7 +4496,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);
@@ -4416,6 +4510,7 @@ type_valid_for_vector_size (tree type, tree atname, tree args,
 	  && !ALL_SCALAR_FIXED_POINT_MODE_P (orig_mode))
       || !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type))
       || TREE_CODE (type) == BOOLEAN_TYPE
+      || hardbool_p
       || TREE_CODE (type) == BITINT_TYPE)
     {
       if (error_p)
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index f044db5b797b..a24b9ece9e3d 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -1772,7 +1772,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;
@@ -9808,4 +9809,22 @@ c_strict_flex_array_level_of (tree array_field)
   return strict_flex_array_level;
 }
 
+/* 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 1fdba7ef3ea0..53c3d3754f8c 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -911,6 +911,7 @@ extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *);
 extern void c_common_finalize_early_debug (void);
 extern unsigned int c_strict_flex_array_level_of (tree);
 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);
@@ -1348,6 +1349,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 71e618cf3b6d..918effd5ebba 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 7a145bed281f..134bae4c7653 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -6450,6 +6450,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);
@@ -9387,6 +9393,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 f7ce13ae7380..c436357a108e 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -2209,6 +2209,35 @@ convert_lvalue_to_rvalue (location_t loc, struct c_expr exp,
     exp.value = convert (build_qualified_type (TREE_TYPE (exp.value), TYPE_UNQUALIFIED), exp.value);
   if (force_non_npc)
     exp.value = build1 (NOP_EXPR, TREE_TYPE (exp.value), 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;
 }
 
@@ -8497,7 +8526,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 bf941e6b93a1..e1295898fc25 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -8832,6 +8832,71 @@ initialization will result in future breakage.
 GCC emits warnings based on this attribute by default; use
 @option{-Wno-designated-init} to suppress them.
 
+@cindex @code{hardbool} type attribute
+@item hardbool
+@itemx hardbool (@var{false_value})
+@itemx hardbool (@var{false_value}, @var{true_value})
+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.  */
+
+static hbool zeroinit; /* False, stored as (char)0x5a.  */
+auto hbool uninit;     /* Undefined, may trap.  */
+@end smallexample
+
+When zero-initializing a variable or field of hardened boolean type
+(presumably held in static storage) the implied zero initializer gets
+converted to @code{_Bool}, and then to the hardened boolean type, so
+that the initial value is the hardened representation for @code{false}.
+Using that value is well defined.  This is @emph{not} the case when
+variables and fields of such types are uninitialized (presumably held in
+automatic or dynamic storage): their values are indeterminate, and using
+them invokes undefined behavior.  Using them may trap or not, depending
+on the bits held in the storage (re)used for the variable, if any, and
+on optimizations the compiler may perform on the grounds that using
+uninitialized values invokes undefined behavior.
+
+Users of @option{-ftrivial-auto-var-init} should be aware that the bit
+patterns used as initializers are @emph{not} converted to
+@code{hardbool} types, so using a @code{hardbool} variable that is
+implicitly initialized by the @option{-ftrivial-auto-var-init} may trap
+if the representations values chosen for @code{false} and @code{true} do
+not match the initializer.
+
+Since this is a language extension only available in C, interoperation
+with other languages may pose difficulties.  It should interoperate with
+Ada Booleans defined with the same size and equivalent representation
+clauses, and with enumerations or other languages' integral types that
+correspond to C's chosen integral type.
+
+
 @cindex @code{may_alias} type attribute
 @item may_alias
 Accesses through pointers to types with this attribute are not subject
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index aebe9195ef0f..1a762bdcc480 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -13632,6 +13632,27 @@ The values used for pattern initialization might be changed in the future.
 
 The default is @samp{uninitialized}.
 
+Note that the initializer values, whether @samp{zero} or @samp{pattern},
+refer to data representation (in memory or machine registers), rather
+than to their interpretation as numerical values.  This distinction may
+be important in languages that support types with biases or implicit
+multipliers, and with such extensions as @samp{hardbool} (@pxref{Type
+Attributes}).  For example, a variable that uses 8 bits to represent
+(biased) quantities in the @code{range 160..400} will be initialized
+with the bit patterns @code{0x00} or @code{0xFE}, depending on
+@var{choice}, whether or not these representations stand for values in
+that range, and even if they do, the interpretation of the value held by
+the variable will depend on the bias.  A @samp{hardbool} variable that
+uses say @code{0X5A} and @code{0xA5} for @code{false} and @code{true},
+respectively, will trap with either @samp{choice} of trivial
+initializer, i.e., @samp{zero} initialization will not convert to the
+representation for @code{false}, even if it would for a @code{static}
+variable of the same type.  This means the initializer pattern doesn't
+generally depend on the type of the initialized variable.  One notable
+exception is that (non-hardened) boolean variables that fit in registers
+are initialized with @code{false} (zero), even when @samp{pattern} is
+requested.
+
 You can control this behavior for a specific variable by using the variable
 attribute @code{uninitialized} (@pxref{Variable Attributes}).
 
diff --git a/gcc/testsuite/gcc.dg/hardbool-err.c b/gcc/testsuite/gcc.dg/hardbool-err.c
new file mode 100644
index 000000000000..e5bf58e55de6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hardbool-err.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+typedef _Bool __attribute__ ((__hardbool__))
+hbbl; /* { dg-error "integral types" } */
+
+typedef double __attribute__ ((__hardbool__))
+hbdbl; /* { dg-error "integral types" } */
+
+typedef _Complex int __attribute__ ((__hardbool__))
+hbcplx; /* { 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 000000000000..2eebd0ef64ff
--- /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 000000000000..a03887cfbecc
--- /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 000000000000..c0ba2a8b9148
--- /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 000000000000..39214d28c562
--- /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 000000000000..14438c5104f0
--- /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 000000000000..d4d498c6f2af
--- /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 000000000000..e38a56b5deb0
--- /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 000000000000..942300be2072
--- /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 000000000000..7beec578ff89
--- /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 000000000000..841c1d4bc2ec
--- /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 000000000000..5bfc922795d3
--- /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 000000000000..e9feec681c41
--- /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 000000000000..01684952a2a9
--- /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));
+}
+

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce hardbool attribute for C
@ 2023-10-20  5:56 Alexandre Oliva
  0 siblings, 0 replies; 19+ messages in thread
From: Alexandre Oliva @ 2023-10-20  5:56 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:e327e565fddbb082037622fc3e60d0730f7f508d

commit e327e565fddbb082037622fc3e60d0730f7f508d
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Thu Oct 19 02:52:52 2023 -0300

    Introduce hardbool attribute for C
    
    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.
    
    
    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.
            * doc/invoke.texi (-ftrivial-auto-var-init): Document
            representation vs values.
    
    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.

Diff:
---
 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                           |  65 ++++++++++++++
 gcc/doc/invoke.texi                           |  21 +++++
 gcc/testsuite/gcc.dg/hardbool-err.c           |  31 +++++++
 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 ++++++++++++++++++++++++++
 22 files changed, 497 insertions(+), 3 deletions(-)

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index abf44d5426e8..232365d46e23 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -176,6 +176,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 *);
 static tree handle_fd_arg_attribute (tree *, tree, tree, int, bool *);
 
@@ -293,6 +294,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 },
@@ -992,6 +995,96 @@ 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);
+  ENUM_UNDERLYING_TYPE (*node) = orig;
+
+  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.  */
 
@@ -4403,7 +4496,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);
@@ -4416,6 +4510,7 @@ type_valid_for_vector_size (tree type, tree atname, tree args,
 	  && !ALL_SCALAR_FIXED_POINT_MODE_P (orig_mode))
       || !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type))
       || TREE_CODE (type) == BOOLEAN_TYPE
+      || hardbool_p
       || TREE_CODE (type) == BITINT_TYPE)
     {
       if (error_p)
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index f044db5b797b..a24b9ece9e3d 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -1772,7 +1772,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;
@@ -9808,4 +9809,22 @@ c_strict_flex_array_level_of (tree array_field)
   return strict_flex_array_level;
 }
 
+/* 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 1fdba7ef3ea0..53c3d3754f8c 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -911,6 +911,7 @@ extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *);
 extern void c_common_finalize_early_debug (void);
 extern unsigned int c_strict_flex_array_level_of (tree);
 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);
@@ -1348,6 +1349,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 71e618cf3b6d..918effd5ebba 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 7a145bed281f..134bae4c7653 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -6450,6 +6450,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);
@@ -9387,6 +9393,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 f7ce13ae7380..c436357a108e 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -2209,6 +2209,35 @@ convert_lvalue_to_rvalue (location_t loc, struct c_expr exp,
     exp.value = convert (build_qualified_type (TREE_TYPE (exp.value), TYPE_UNQUALIFIED), exp.value);
   if (force_non_npc)
     exp.value = build1 (NOP_EXPR, TREE_TYPE (exp.value), 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;
 }
 
@@ -8497,7 +8526,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 bf941e6b93a1..e1295898fc25 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -8832,6 +8832,71 @@ initialization will result in future breakage.
 GCC emits warnings based on this attribute by default; use
 @option{-Wno-designated-init} to suppress them.
 
+@cindex @code{hardbool} type attribute
+@item hardbool
+@itemx hardbool (@var{false_value})
+@itemx hardbool (@var{false_value}, @var{true_value})
+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.  */
+
+static hbool zeroinit; /* False, stored as (char)0x5a.  */
+auto hbool uninit;     /* Undefined, may trap.  */
+@end smallexample
+
+When zero-initializing a variable or field of hardened boolean type
+(presumably held in static storage) the implied zero initializer gets
+converted to @code{_Bool}, and then to the hardened boolean type, so
+that the initial value is the hardened representation for @code{false}.
+Using that value is well defined.  This is @emph{not} the case when
+variables and fields of such types are uninitialized (presumably held in
+automatic or dynamic storage): their values are indeterminate, and using
+them invokes undefined behavior.  Using them may trap or not, depending
+on the bits held in the storage (re)used for the variable, if any, and
+on optimizations the compiler may perform on the grounds that using
+uninitialized values invokes undefined behavior.
+
+Users of @option{-ftrivial-auto-var-init} should be aware that the bit
+patterns used as initializers are @emph{not} converted to
+@code{hardbool} types, so using a @code{hardbool} variable that is
+implicitly initialized by the @option{-ftrivial-auto-var-init} may trap
+if the representations values chosen for @code{false} and @code{true} do
+not match the initializer.
+
+Since this is a language extension only available in C, interoperation
+with other languages may pose difficulties.  It should interoperate with
+Ada Booleans defined with the same size and equivalent representation
+clauses, and with enumerations or other languages' integral types that
+correspond to C's chosen integral type.
+
+
 @cindex @code{may_alias} type attribute
 @item may_alias
 Accesses through pointers to types with this attribute are not subject
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index aebe9195ef0f..1a762bdcc480 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -13632,6 +13632,27 @@ The values used for pattern initialization might be changed in the future.
 
 The default is @samp{uninitialized}.
 
+Note that the initializer values, whether @samp{zero} or @samp{pattern},
+refer to data representation (in memory or machine registers), rather
+than to their interpretation as numerical values.  This distinction may
+be important in languages that support types with biases or implicit
+multipliers, and with such extensions as @samp{hardbool} (@pxref{Type
+Attributes}).  For example, a variable that uses 8 bits to represent
+(biased) quantities in the @code{range 160..400} will be initialized
+with the bit patterns @code{0x00} or @code{0xFE}, depending on
+@var{choice}, whether or not these representations stand for values in
+that range, and even if they do, the interpretation of the value held by
+the variable will depend on the bias.  A @samp{hardbool} variable that
+uses say @code{0X5A} and @code{0xA5} for @code{false} and @code{true},
+respectively, will trap with either @samp{choice} of trivial
+initializer, i.e., @samp{zero} initialization will not convert to the
+representation for @code{false}, even if it would for a @code{static}
+variable of the same type.  This means the initializer pattern doesn't
+generally depend on the type of the initialized variable.  One notable
+exception is that (non-hardened) boolean variables that fit in registers
+are initialized with @code{false} (zero), even when @samp{pattern} is
+requested.
+
 You can control this behavior for a specific variable by using the variable
 attribute @code{uninitialized} (@pxref{Variable Attributes}).
 
diff --git a/gcc/testsuite/gcc.dg/hardbool-err.c b/gcc/testsuite/gcc.dg/hardbool-err.c
new file mode 100644
index 000000000000..e5bf58e55de6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hardbool-err.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+typedef _Bool __attribute__ ((__hardbool__))
+hbbl; /* { dg-error "integral types" } */
+
+typedef double __attribute__ ((__hardbool__))
+hbdbl; /* { dg-error "integral types" } */
+
+typedef _Complex int __attribute__ ((__hardbool__))
+hbcplx; /* { 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 000000000000..2eebd0ef64ff
--- /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 000000000000..a03887cfbecc
--- /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 000000000000..c0ba2a8b9148
--- /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 000000000000..39214d28c562
--- /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 000000000000..14438c5104f0
--- /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 000000000000..d4d498c6f2af
--- /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 000000000000..e38a56b5deb0
--- /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 000000000000..942300be2072
--- /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 000000000000..7beec578ff89
--- /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 000000000000..841c1d4bc2ec
--- /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 000000000000..5bfc922795d3
--- /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 000000000000..e9feec681c41
--- /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 000000000000..01684952a2a9
--- /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));
+}
+

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce hardbool attribute for C
@ 2023-10-20  3:48 Alexandre Oliva
  0 siblings, 0 replies; 19+ messages in thread
From: Alexandre Oliva @ 2023-10-20  3:48 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:3805f1c6dabd70b5c7d4f06bc443b75cb4f845c2

commit 3805f1c6dabd70b5c7d4f06bc443b75cb4f845c2
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Thu Oct 19 02:52:52 2023 -0300

    Introduce hardbool attribute for C
    
    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.
    
    
    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.
            * doc/invoke.texi (-ftrivial-auto-var-init): Document
            representation vs values.
    
    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.

Diff:
---
 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                           |  65 ++++++++++++++
 gcc/doc/invoke.texi                           |  21 +++++
 gcc/testsuite/gcc.dg/hardbool-err.c           |  31 +++++++
 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 ++++++++++++++++++++++++++
 22 files changed, 497 insertions(+), 3 deletions(-)

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index abf44d5426e8..232365d46e23 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -176,6 +176,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 *);
 static tree handle_fd_arg_attribute (tree *, tree, tree, int, bool *);
 
@@ -293,6 +294,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 },
@@ -992,6 +995,96 @@ 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);
+  ENUM_UNDERLYING_TYPE (*node) = orig;
+
+  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.  */
 
@@ -4403,7 +4496,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);
@@ -4416,6 +4510,7 @@ type_valid_for_vector_size (tree type, tree atname, tree args,
 	  && !ALL_SCALAR_FIXED_POINT_MODE_P (orig_mode))
       || !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type))
       || TREE_CODE (type) == BOOLEAN_TYPE
+      || hardbool_p
       || TREE_CODE (type) == BITINT_TYPE)
     {
       if (error_p)
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index f044db5b797b..a24b9ece9e3d 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -1772,7 +1772,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;
@@ -9808,4 +9809,22 @@ c_strict_flex_array_level_of (tree array_field)
   return strict_flex_array_level;
 }
 
+/* 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 1fdba7ef3ea0..53c3d3754f8c 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -911,6 +911,7 @@ extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *);
 extern void c_common_finalize_early_debug (void);
 extern unsigned int c_strict_flex_array_level_of (tree);
 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);
@@ -1348,6 +1349,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 71e618cf3b6d..918effd5ebba 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 7a145bed281f..134bae4c7653 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -6450,6 +6450,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);
@@ -9387,6 +9393,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 f7ce13ae7380..c436357a108e 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -2209,6 +2209,35 @@ convert_lvalue_to_rvalue (location_t loc, struct c_expr exp,
     exp.value = convert (build_qualified_type (TREE_TYPE (exp.value), TYPE_UNQUALIFIED), exp.value);
   if (force_non_npc)
     exp.value = build1 (NOP_EXPR, TREE_TYPE (exp.value), 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;
 }
 
@@ -8497,7 +8526,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 bf941e6b93a1..e1295898fc25 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -8832,6 +8832,71 @@ initialization will result in future breakage.
 GCC emits warnings based on this attribute by default; use
 @option{-Wno-designated-init} to suppress them.
 
+@cindex @code{hardbool} type attribute
+@item hardbool
+@itemx hardbool (@var{false_value})
+@itemx hardbool (@var{false_value}, @var{true_value})
+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.  */
+
+static hbool zeroinit; /* False, stored as (char)0x5a.  */
+auto hbool uninit;     /* Undefined, may trap.  */
+@end smallexample
+
+When zero-initializing a variable or field of hardened boolean type
+(presumably held in static storage) the implied zero initializer gets
+converted to @code{_Bool}, and then to the hardened boolean type, so
+that the initial value is the hardened representation for @code{false}.
+Using that value is well defined.  This is @emph{not} the case when
+variables and fields of such types are uninitialized (presumably held in
+automatic or dynamic storage): their values are indeterminate, and using
+them invokes undefined behavior.  Using them may trap or not, depending
+on the bits held in the storage (re)used for the variable, if any, and
+on optimizations the compiler may perform on the grounds that using
+uninitialized values invokes undefined behavior.
+
+Users of @option{-ftrivial-auto-var-init} should be aware that the bit
+patterns used as initializers are @emph{not} converted to
+@code{hardbool} types, so using a @code{hardbool} variable that is
+implicitly initialized by the @option{-ftrivial-auto-var-init} may trap
+if the representations values chosen for @code{false} and @code{true} do
+not match the initializer.
+
+Since this is a language extension only available in C, interoperation
+with other languages may pose difficulties.  It should interoperate with
+Ada Booleans defined with the same size and equivalent representation
+clauses, and with enumerations or other languages' integral types that
+correspond to C's chosen integral type.
+
+
 @cindex @code{may_alias} type attribute
 @item may_alias
 Accesses through pointers to types with this attribute are not subject
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index aebe9195ef0f..1a762bdcc480 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -13632,6 +13632,27 @@ The values used for pattern initialization might be changed in the future.
 
 The default is @samp{uninitialized}.
 
+Note that the initializer values, whether @samp{zero} or @samp{pattern},
+refer to data representation (in memory or machine registers), rather
+than to their interpretation as numerical values.  This distinction may
+be important in languages that support types with biases or implicit
+multipliers, and with such extensions as @samp{hardbool} (@pxref{Type
+Attributes}).  For example, a variable that uses 8 bits to represent
+(biased) quantities in the @code{range 160..400} will be initialized
+with the bit patterns @code{0x00} or @code{0xFE}, depending on
+@var{choice}, whether or not these representations stand for values in
+that range, and even if they do, the interpretation of the value held by
+the variable will depend on the bias.  A @samp{hardbool} variable that
+uses say @code{0X5A} and @code{0xA5} for @code{false} and @code{true},
+respectively, will trap with either @samp{choice} of trivial
+initializer, i.e., @samp{zero} initialization will not convert to the
+representation for @code{false}, even if it would for a @code{static}
+variable of the same type.  This means the initializer pattern doesn't
+generally depend on the type of the initialized variable.  One notable
+exception is that (non-hardened) boolean variables that fit in registers
+are initialized with @code{false} (zero), even when @samp{pattern} is
+requested.
+
 You can control this behavior for a specific variable by using the variable
 attribute @code{uninitialized} (@pxref{Variable Attributes}).
 
diff --git a/gcc/testsuite/gcc.dg/hardbool-err.c b/gcc/testsuite/gcc.dg/hardbool-err.c
new file mode 100644
index 000000000000..e5bf58e55de6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hardbool-err.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+typedef _Bool __attribute__ ((__hardbool__))
+hbbl; /* { dg-error "integral types" } */
+
+typedef double __attribute__ ((__hardbool__))
+hbdbl; /* { dg-error "integral types" } */
+
+typedef _Complex int __attribute__ ((__hardbool__))
+hbcplx; /* { 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 000000000000..2eebd0ef64ff
--- /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 000000000000..a03887cfbecc
--- /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 000000000000..c0ba2a8b9148
--- /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 000000000000..39214d28c562
--- /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 000000000000..14438c5104f0
--- /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 000000000000..d4d498c6f2af
--- /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 000000000000..e38a56b5deb0
--- /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 000000000000..942300be2072
--- /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 000000000000..7beec578ff89
--- /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 000000000000..841c1d4bc2ec
--- /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 000000000000..5bfc922795d3
--- /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 000000000000..e9feec681c41
--- /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 000000000000..01684952a2a9
--- /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));
+}
+

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce hardbool attribute for C
@ 2023-09-30  4:14 Alexandre Oliva
  0 siblings, 0 replies; 19+ messages in thread
From: Alexandre Oliva @ 2023-09-30  4:14 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:926a6e7a464e89c756ba319ecef4977defbc4ec9

commit 926a6e7a464e89c756ba319ecef4977defbc4ec9
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Thu Jun 29 04:22:43 2023 -0300

    Introduce hardbool attribute for C
    
    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.
    
    
    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.
            * doc/invoke.texi (-ftrivial-auto-var-init): Document
            representation vs values.
    
    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.

Diff:
---
 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                           |  65 ++++++++++++++
 gcc/doc/invoke.texi                           |  21 +++++
 gcc/testsuite/gcc.dg/hardbool-err.c           |  31 +++++++
 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 ++++++++++++++++++++++++++
 22 files changed, 497 insertions(+), 3 deletions(-)

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index abf44d5426e..232365d46e2 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -176,6 +176,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 *);
 static tree handle_fd_arg_attribute (tree *, tree, tree, int, bool *);
 
@@ -293,6 +294,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 },
@@ -992,6 +995,96 @@ 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);
+  ENUM_UNDERLYING_TYPE (*node) = orig;
+
+  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.  */
 
@@ -4403,7 +4496,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);
@@ -4416,6 +4510,7 @@ type_valid_for_vector_size (tree type, tree atname, tree args,
 	  && !ALL_SCALAR_FIXED_POINT_MODE_P (orig_mode))
       || !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type))
       || TREE_CODE (type) == BOOLEAN_TYPE
+      || hardbool_p
       || TREE_CODE (type) == BITINT_TYPE)
     {
       if (error_p)
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index aae57260097..6b8698fcdc8 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -1772,7 +1772,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;
@@ -9808,4 +9809,22 @@ c_strict_flex_array_level_of (tree array_field)
   return strict_flex_array_level;
 }
 
+/* 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 1fdba7ef3ea..53c3d3754f8 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -911,6 +911,7 @@ extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *);
 extern void c_common_finalize_early_debug (void);
 extern unsigned int c_strict_flex_array_level_of (tree);
 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);
@@ -1348,6 +1349,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 71e618cf3b6..918effd5ebb 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 5822faf01b4..3ca868cf47e 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -6449,6 +6449,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);
@@ -9365,6 +9371,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 e55e887da14..1215f1a55ba 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -2209,6 +2209,35 @@ convert_lvalue_to_rvalue (location_t loc, struct c_expr exp,
     exp.value = convert (build_qualified_type (TREE_TYPE (exp.value), TYPE_UNQUALIFIED), exp.value);
   if (force_non_npc)
     exp.value = build1 (NOP_EXPR, TREE_TYPE (exp.value), 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;
 }
 
@@ -8493,7 +8522,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 097666bceb4..c9cb32a7383 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -8820,6 +8820,71 @@ initialization will result in future breakage.
 GCC emits warnings based on this attribute by default; use
 @option{-Wno-designated-init} to suppress them.
 
+@cindex @code{hardbool} type attribute
+@item hardbool
+@itemx hardbool (@var{false_value})
+@itemx hardbool (@var{false_value}, @var{true_value})
+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.  */
+
+static hbool zeroinit; /* False, stored as (char)0x5a.  */
+auto hbool uninit;     /* Undefined, may trap.  */
+@end smallexample
+
+When zero-initializing a variable or field of hardened boolean type
+(presumably held in static storage) the implied zero initializer gets
+converted to @code{_Bool}, and then to the hardened boolean type, so
+that the initial value is the hardened representation for @code{false}.
+Using that value is well defined.  This is @emph{not} the case when
+variables and fields of such types are uninitialized (presumably held in
+automatic or dynamic storage): their values are indeterminate, and using
+them invokes undefined behavior.  Using them may trap or not, depending
+on the bits held in the storage (re)used for the variable, if any, and
+on optimizations the compiler may perform on the grounds that using
+uninitialized values invokes undefined behavior.
+
+Users of @option{-ftrivial-auto-var-init} should be aware that the bit
+patterns used as initializers are @emph{not} converted to
+@code{hardbool} types, so using a @code{hardbool} variable that is
+implicitly initialized by the @option{-ftrivial-auto-var-init} may trap
+if the representations values chosen for @code{false} and @code{true} do
+not match the initializer.
+
+Since this is a language extension only available in C, interoperation
+with other languages may pose difficulties.  It should interoperate with
+Ada Booleans defined with the same size and equivalent representation
+clauses, and with enumerations or other languages' integral types that
+correspond to C's chosen integral type.
+
+
 @cindex @code{may_alias} type attribute
 @item may_alias
 Accesses through pointers to types with this attribute are not subject
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 40cf79873e9..30b9fabf3f7 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -13618,6 +13618,27 @@ The values used for pattern initialization might be changed in the future.
 
 The default is @samp{uninitialized}.
 
+Note that the initializer values, whether @samp{zero} or @samp{pattern},
+refer to data representation (in memory or machine registers), rather
+than to their interpretation as numerical values.  This distinction may
+be important in languages that support types with biases or implicit
+multipliers, and with such extensions as @samp{hardbool} (@pxref{Type
+Attributes}).  For example, a variable that uses 8 bits to represent
+(biased) quantities in the @code{range 160..400} will be initialized
+with the bit patterns @code{0x00} or @code{0xFE}, depending on
+@var{choice}, whether or not these representations stand for values in
+that range, and even if they do, the interpretation of the value held by
+the variable will depend on the bias.  A @samp{hardbool} variable that
+uses say @code{0X5A} and @code{0xA5} for @code{false} and @code{true},
+respectively, will trap with either @samp{choice} of trivial
+initializer, i.e., @samp{zero} initialization will not convert to the
+representation for @code{false}, even if it would for a @code{static}
+variable of the same type.  This means the initializer pattern doesn't
+generally depend on the type of the initialized variable.  One notable
+exception is that (non-hardened) boolean variables that fit in registers
+are initialized with @code{false} (zero), even when @samp{pattern} is
+requested.
+
 You can control this behavior for a specific variable by using the variable
 attribute @code{uninitialized} (@pxref{Variable Attributes}).
 
diff --git a/gcc/testsuite/gcc.dg/hardbool-err.c b/gcc/testsuite/gcc.dg/hardbool-err.c
new file mode 100644
index 00000000000..e5bf58e55de
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hardbool-err.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+typedef _Bool __attribute__ ((__hardbool__))
+hbbl; /* { dg-error "integral types" } */
+
+typedef double __attribute__ ((__hardbool__))
+hbdbl; /* { dg-error "integral types" } */
+
+typedef _Complex int __attribute__ ((__hardbool__))
+hbcplx; /* { 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 00000000000..2eebd0ef64f
--- /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 00000000000..a03887cfbec
--- /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 00000000000..c0ba2a8b914
--- /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 00000000000..39214d28c56
--- /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 00000000000..14438c5104f
--- /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 00000000000..d4d498c6f2a
--- /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 00000000000..e38a56b5deb
--- /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 00000000000..942300be207
--- /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 00000000000..7beec578ff8
--- /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 00000000000..841c1d4bc2e
--- /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 00000000000..5bfc922795d
--- /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 00000000000..e9feec681c4
--- /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 00000000000..01684952a2a
--- /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));
+}
+

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce hardbool attribute for C
@ 2023-09-29 22:11 Alexandre Oliva
  0 siblings, 0 replies; 19+ messages in thread
From: Alexandre Oliva @ 2023-09-29 22:11 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:a0564319b9e8871352b902a9c9daab8c712ed678

commit a0564319b9e8871352b902a9c9daab8c712ed678
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Thu Jun 29 04:22:43 2023 -0300

    Introduce hardbool attribute for C
    
    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.
    
    
    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.
            * doc/invoke.texi (-ftrivial-auto-var-init): Document
            representation vs values.
    
    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.

Diff:
---
 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                           |  65 ++++++++++++++
 gcc/doc/invoke.texi                           |  21 +++++
 gcc/testsuite/gcc.dg/hardbool-err.c           |  31 +++++++
 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 ++++++++++++++++++++++++++
 22 files changed, 497 insertions(+), 3 deletions(-)

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index abf44d5426e..232365d46e2 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -176,6 +176,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 *);
 static tree handle_fd_arg_attribute (tree *, tree, tree, int, bool *);
 
@@ -293,6 +294,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 },
@@ -992,6 +995,96 @@ 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);
+  ENUM_UNDERLYING_TYPE (*node) = orig;
+
+  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.  */
 
@@ -4403,7 +4496,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);
@@ -4416,6 +4510,7 @@ type_valid_for_vector_size (tree type, tree atname, tree args,
 	  && !ALL_SCALAR_FIXED_POINT_MODE_P (orig_mode))
       || !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type))
       || TREE_CODE (type) == BOOLEAN_TYPE
+      || hardbool_p
       || TREE_CODE (type) == BITINT_TYPE)
     {
       if (error_p)
diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
index aae57260097..6b8698fcdc8 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -1772,7 +1772,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;
@@ -9808,4 +9809,22 @@ c_strict_flex_array_level_of (tree array_field)
   return strict_flex_array_level;
 }
 
+/* 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 1fdba7ef3ea..53c3d3754f8 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -911,6 +911,7 @@ extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *);
 extern void c_common_finalize_early_debug (void);
 extern unsigned int c_strict_flex_array_level_of (tree);
 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);
@@ -1348,6 +1349,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 71e618cf3b6..918effd5ebb 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 5822faf01b4..3ca868cf47e 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -6449,6 +6449,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);
@@ -9365,6 +9371,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 e55e887da14..1215f1a55ba 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -2209,6 +2209,35 @@ convert_lvalue_to_rvalue (location_t loc, struct c_expr exp,
     exp.value = convert (build_qualified_type (TREE_TYPE (exp.value), TYPE_UNQUALIFIED), exp.value);
   if (force_non_npc)
     exp.value = build1 (NOP_EXPR, TREE_TYPE (exp.value), 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;
 }
 
@@ -8493,7 +8522,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 097666bceb4..c9cb32a7383 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -8820,6 +8820,71 @@ initialization will result in future breakage.
 GCC emits warnings based on this attribute by default; use
 @option{-Wno-designated-init} to suppress them.
 
+@cindex @code{hardbool} type attribute
+@item hardbool
+@itemx hardbool (@var{false_value})
+@itemx hardbool (@var{false_value}, @var{true_value})
+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.  */
+
+static hbool zeroinit; /* False, stored as (char)0x5a.  */
+auto hbool uninit;     /* Undefined, may trap.  */
+@end smallexample
+
+When zero-initializing a variable or field of hardened boolean type
+(presumably held in static storage) the implied zero initializer gets
+converted to @code{_Bool}, and then to the hardened boolean type, so
+that the initial value is the hardened representation for @code{false}.
+Using that value is well defined.  This is @emph{not} the case when
+variables and fields of such types are uninitialized (presumably held in
+automatic or dynamic storage): their values are indeterminate, and using
+them invokes undefined behavior.  Using them may trap or not, depending
+on the bits held in the storage (re)used for the variable, if any, and
+on optimizations the compiler may perform on the grounds that using
+uninitialized values invokes undefined behavior.
+
+Users of @option{-ftrivial-auto-var-init} should be aware that the bit
+patterns used as initializers are @emph{not} converted to
+@code{hardbool} types, so using a @code{hardbool} variable that is
+implicitly initialized by the @option{-ftrivial-auto-var-init} may trap
+if the representations values chosen for @code{false} and @code{true} do
+not match the initializer.
+
+Since this is a language extension only available in C, interoperation
+with other languages may pose difficulties.  It should interoperate with
+Ada Booleans defined with the same size and equivalent representation
+clauses, and with enumerations or other languages' integral types that
+correspond to C's chosen integral type.
+
+
 @cindex @code{may_alias} type attribute
 @item may_alias
 Accesses through pointers to types with this attribute are not subject
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 40cf79873e9..30b9fabf3f7 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -13618,6 +13618,27 @@ The values used for pattern initialization might be changed in the future.
 
 The default is @samp{uninitialized}.
 
+Note that the initializer values, whether @samp{zero} or @samp{pattern},
+refer to data representation (in memory or machine registers), rather
+than to their interpretation as numerical values.  This distinction may
+be important in languages that support types with biases or implicit
+multipliers, and with such extensions as @samp{hardbool} (@pxref{Type
+Attributes}).  For example, a variable that uses 8 bits to represent
+(biased) quantities in the @code{range 160..400} will be initialized
+with the bit patterns @code{0x00} or @code{0xFE}, depending on
+@var{choice}, whether or not these representations stand for values in
+that range, and even if they do, the interpretation of the value held by
+the variable will depend on the bias.  A @samp{hardbool} variable that
+uses say @code{0X5A} and @code{0xA5} for @code{false} and @code{true},
+respectively, will trap with either @samp{choice} of trivial
+initializer, i.e., @samp{zero} initialization will not convert to the
+representation for @code{false}, even if it would for a @code{static}
+variable of the same type.  This means the initializer pattern doesn't
+generally depend on the type of the initialized variable.  One notable
+exception is that (non-hardened) boolean variables that fit in registers
+are initialized with @code{false} (zero), even when @samp{pattern} is
+requested.
+
 You can control this behavior for a specific variable by using the variable
 attribute @code{uninitialized} (@pxref{Variable Attributes}).
 
diff --git a/gcc/testsuite/gcc.dg/hardbool-err.c b/gcc/testsuite/gcc.dg/hardbool-err.c
new file mode 100644
index 00000000000..e5bf58e55de
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hardbool-err.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+typedef _Bool __attribute__ ((__hardbool__))
+hbbl; /* { dg-error "integral types" } */
+
+typedef double __attribute__ ((__hardbool__))
+hbdbl; /* { dg-error "integral types" } */
+
+typedef _Complex int __attribute__ ((__hardbool__))
+hbcplx; /* { 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 00000000000..2eebd0ef64f
--- /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 00000000000..a03887cfbec
--- /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 00000000000..c0ba2a8b914
--- /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 00000000000..39214d28c56
--- /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 00000000000..14438c5104f
--- /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 00000000000..d4d498c6f2a
--- /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 00000000000..e38a56b5deb
--- /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 00000000000..942300be207
--- /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 00000000000..7beec578ff8
--- /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 00000000000..841c1d4bc2e
--- /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 00000000000..5bfc922795d
--- /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 00000000000..e9feec681c4
--- /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 00000000000..01684952a2a
--- /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));
+}
+

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce hardbool attribute for C
@ 2023-06-28 11:09 Alexandre Oliva
  0 siblings, 0 replies; 19+ messages in thread
From: Alexandre Oliva @ 2023-06-28 11:09 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:e77fb329a5dee39553c917079cb80e11ac34e081

commit e77fb329a5dee39553c917079cb80e11ac34e081
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Wed Jun 28 00:07:31 2023 -0300

    Introduce hardbool attribute for C
    
    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.
    
    
    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.
            * doc/invoke.texi (-ftrivial-auto-var-init): Document
            representation vs values.
    
    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.

Diff:
---
 gcc/c-family/c-attribs.cc                     |  98 ++++++++++++++++++++-
 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                           |  65 ++++++++++++++
 gcc/doc/invoke.texi                           |  21 +++++
 gcc/testsuite/gcc.dg/hardbool-err.c           |  31 +++++++
 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 ++++++++++++++++++++++++++
 22 files changed, 497 insertions(+), 4 deletions(-)

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index e2792ca6898..a556799ced7 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -175,6 +175,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 *);
 static tree handle_fd_arg_attribute (tree *, tree, tree, int, bool *);
 
@@ -292,6 +293,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 },
@@ -989,6 +992,96 @@ 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);
+  ENUM_UNDERLYING_TYPE (*node) = orig;
+
+  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.  */
 
@@ -4354,7 +4447,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);
@@ -4366,7 +4460,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 34566a342bd..c3b515609e9 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -1761,7 +1761,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;
@@ -9549,4 +9550,22 @@ c_strict_flex_array_level_of (tree array_field)
   return strict_flex_array_level;
 }
 
+/* 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 b5ef5ff6b2c..ead04b7b990 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -911,6 +911,7 @@ extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *);
 extern void c_common_finalize_early_debug (void);
 extern unsigned int c_strict_flex_array_level_of (tree);
 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);
@@ -1344,6 +1345,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 5754d00b30a..e73103c1884 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 1af51c4acfc..cc3ec900539 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -6414,6 +6414,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);
@@ -9304,6 +9310,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 22e240a3c2a..f9cc609b54d 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -2226,6 +2226,35 @@ convert_lvalue_to_rvalue (location_t loc, struct c_expr exp,
     exp.value = convert (build_qualified_type (TREE_TYPE (exp.value), TYPE_UNQUALIFIED), exp.value);
   if (force_non_npc)
     exp.value = build1 (NOP_EXPR, TREE_TYPE (exp.value), 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;
 }
 
@@ -8488,7 +8517,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 363d90746ed..c611f04f99c 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -8714,6 +8714,71 @@ initialization will result in future breakage.
 GCC emits warnings based on this attribute by default; use
 @option{-Wno-designated-init} to suppress them.
 
+@cindex @code{hardbool} type attribute
+@item hardbool
+@itemx hardbool (@var{false_value})
+@itemx hardbool (@var{false_value}, @var{true_value})
+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.  */
+
+static hbool zeroinit; /* False, stored as (char)0x5a.  */
+auto hbool uninit;     /* Undefined, may trap.  */
+@end smallexample
+
+When zero-initializing a variable or field of hardened boolean type
+(presumably held in static storage) the implied zero initializer gets
+converted to @code{_Bool}, and then to the hardened boolean type, so
+that the initial value is the hardened representation for @code{false}.
+Using that value is well defined.  This is @emph{not} the case when
+variables and fields of such types are uninitialized (presumably held in
+automatic or dynamic storage): their values are indeterminate, and using
+them invokes undefined behavior.  Using them may trap or not, depending
+on the bits held in the storage (re)used for the variable, if any, and
+on optimizations the compiler may perform on the grounds that using
+uninitialized values invokes undefined behavior.
+
+Users of @option{-ftrivial-auto-var-init} should be aware that the bit
+patterns used as initializers are @emph{not} converted to
+@code{hardbool} types, so using a @code{hardbool} variable that is
+implicitly initialized by the @option{-ftrivial-auto-var-init} may trap
+if the representations values chosen for @code{false} and @code{true} do
+not match the initializer.
+
+Since this is a language extension only available in C, interoperation
+with other languages may pose difficulties.  It should interoperate with
+Ada Booleans defined with the same size and equivalent representation
+clauses, and with enumerations or other languages' integral types that
+correspond to C's chosen integral type.
+
+
 @cindex @code{may_alias} type attribute
 @item may_alias
 Accesses through pointers to types with this attribute are not subject
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index efcf3bfb3d6..9b40ad822a2 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -13517,6 +13517,27 @@ The values used for pattern initialization might be changed in the future.
 
 The default is @samp{uninitialized}.
 
+Note that the initializer values, whether @samp{zero} or @samp{pattern},
+refer to data representation (in memory or machine registers), rather
+than to their interpretation as numerical values.  This distinction may
+be important in languages that support types with biases or implicit
+multipliers, and with such extensions as @samp{hardbool} (@pxref{Type
+Attributes}).  For example, a variable that uses 8 bits to represent
+(biased) quantities in the @code{range 160..400} will be initialized
+with the bit patterns @code{0x00} or @code{0xFE}, depending on
+@var{choice}, whether or not these representations stand for values in
+that range, and even if they do, the interpretation of the value held by
+the variable will depend on the bias.  A @samp{hardbool} variable that
+uses say @code{0X5A} and @code{0xA5} for @code{false} and @code{true},
+respectively, will trap with either @samp{choice} of trivial
+initializer, i.e., @samp{zero} initialization will not convert to the
+representation for @code{false}, even if it would for a @code{static}
+variable of the same type.  This means the initializer pattern doesn't
+generally depend on the type of the initialized variable.  One notable
+exception is that (non-hardened) boolean variables that fit in registers
+are initialized with @code{false} (zero), even when @samp{pattern} is
+requested.
+
 You can control this behavior for a specific variable by using the variable
 attribute @code{uninitialized} (@pxref{Variable Attributes}).
 
diff --git a/gcc/testsuite/gcc.dg/hardbool-err.c b/gcc/testsuite/gcc.dg/hardbool-err.c
new file mode 100644
index 00000000000..e5bf58e55de
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/hardbool-err.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+typedef _Bool __attribute__ ((__hardbool__))
+hbbl; /* { dg-error "integral types" } */
+
+typedef double __attribute__ ((__hardbool__))
+hbdbl; /* { dg-error "integral types" } */
+
+typedef _Complex int __attribute__ ((__hardbool__))
+hbcplx; /* { 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 00000000000..2eebd0ef64f
--- /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 00000000000..a03887cfbec
--- /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 00000000000..c0ba2a8b914
--- /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 00000000000..39214d28c56
--- /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 00000000000..14438c5104f
--- /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 00000000000..d4d498c6f2a
--- /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 00000000000..e38a56b5deb
--- /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 00000000000..942300be207
--- /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 00000000000..7beec578ff8
--- /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 00000000000..841c1d4bc2e
--- /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 00000000000..5bfc922795d
--- /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 00000000000..e9feec681c4
--- /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 00000000000..01684952a2a
--- /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));
+}
+

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce hardbool attribute for C
@ 2023-06-23 20:13 Alexandre Oliva
  0 siblings, 0 replies; 19+ messages in thread
From: Alexandre Oliva @ 2023-06-23 20:13 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:3eac4e94fb54dc094a36b117b4ea0bfb0a7870e2

commit 3eac4e94fb54dc094a36b117b4ea0bfb0a7870e2
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Fri Jun 23 17:10:15 2023 -0300

    Introduce hardbool attribute for C
    
    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.
    
    
    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.

Diff:
---
 gcc/c-family/c-attribs.cc                     |  98 ++++++++++++++++++++-
 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                           |  52 ++++++++++++
 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, 460 insertions(+), 4 deletions(-)

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index c12211cb4d4..365319e642b 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -176,6 +176,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 *);
 static tree handle_fd_arg_attribute (tree *, tree, tree, int, bool *);
 
@@ -293,6 +294,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 },
@@ -992,6 +995,96 @@ 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);
+  ENUM_UNDERLYING_TYPE (*node) = orig;
+
+  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.  */
 
@@ -4357,7 +4450,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);
@@ -4369,7 +4463,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 9c8eed5442a..1257760c426 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -1757,7 +1757,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;
@@ -9545,4 +9546,22 @@ c_strict_flex_array_level_of (tree array_field)
   return strict_flex_array_level;
 }
 
+/* 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 336a09f4a40..a5d3e77f121 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -909,6 +909,7 @@ extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *);
 extern void c_common_finalize_early_debug (void);
 extern unsigned int c_strict_flex_array_level_of (tree);
 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);
@@ -1342,6 +1343,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 5754d00b30a..e73103c1884 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 1af51c4acfc..cc3ec900539 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -6414,6 +6414,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);
@@ -9304,6 +9310,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 22e240a3c2a..f9cc609b54d 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -2226,6 +2226,35 @@ convert_lvalue_to_rvalue (location_t loc, struct c_expr exp,
     exp.value = convert (build_qualified_type (TREE_TYPE (exp.value), TYPE_UNQUALIFIED), exp.value);
   if (force_non_npc)
     exp.value = build1 (NOP_EXPR, TREE_TYPE (exp.value), 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;
 }
 
@@ -8488,7 +8517,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 a3b37337bd3..852f6b629be 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -8698,6 +8698,58 @@ initialization will result in future breakage.
 GCC emits warnings based on this attribute by default; use
 @option{-Wno-designated-init} to suppress them.
 
+@cindex @code{hardbool} type attribute
+@item hardbool
+@itemx hardbool (@var{false_value})
+@itemx hardbool (@var{false_value}, @var{true_value})
+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.  */
+
+static hbool zeroinit; /* False, stored as (char)0x5a.  */
+auto hbool uninit;     /* Undefined, may trap.  */
+@end smallexample
+
+When zero-initializing a variable or field of hardened boolean type
+(presumably held in static storage) the implied zero initializer gets
+converted to @code{_Bool}, and then to the hardened boolean type, so
+that the initial value is the hardened representation for @code{false}.
+Using that value is well defined.  This is @emph{not} the case when
+variables and fields of such types are uninitialized (presumably held in
+automatic or dynamic storage): their values are indeterminate, and using
+them invokes undefined behavior.  Using them may trap or not, depending
+on the bits held in the storage (re)used for the variable, if any, and
+on optimizations the compiler may perform on the grounds that using
+uninitialized values invokes undefined behavior.
+
+
 @cindex @code{may_alias} type attribute
 @item may_alias
 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 00000000000..634feaed4de
--- /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 00000000000..2eebd0ef64f
--- /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 00000000000..a03887cfbec
--- /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 00000000000..c0ba2a8b914
--- /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 00000000000..39214d28c56
--- /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 00000000000..14438c5104f
--- /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 00000000000..d4d498c6f2a
--- /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 00000000000..e38a56b5deb
--- /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 00000000000..942300be207
--- /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 00000000000..7beec578ff8
--- /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 00000000000..841c1d4bc2e
--- /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 00000000000..5bfc922795d
--- /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 00000000000..e9feec681c4
--- /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 00000000000..01684952a2a
--- /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));
+}
+

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce hardbool attribute for C
@ 2023-06-09  8:07 Alexandre Oliva
  0 siblings, 0 replies; 19+ messages in thread
From: Alexandre Oliva @ 2023-06-09  8:07 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:af9a4856cdc4aebf5c9b64572d4c2de7b3c8c0d6

commit af9a4856cdc4aebf5c9b64572d4c2de7b3c8c0d6
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Thu Jun 8 01:35:41 2023 -0300

    Introduce hardbool attribute for C
    
    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.
    
    
    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.

Diff:
---
 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                           |  52 ++++++++++++
 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, 459 insertions(+), 4 deletions(-)

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index d59acccb42a..f121ce737a4 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -176,6 +176,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 *);
 static tree handle_fd_arg_attribute (tree *, tree, tree, int, bool *);
 
@@ -293,6 +294,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 },
@@ -992,6 +995,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.  */
 
@@ -4357,7 +4449,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);
@@ -4369,7 +4462,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 2b4c82facf7..67ec87e23b2 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -1757,7 +1757,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;
@@ -9545,4 +9546,22 @@ c_strict_flex_array_level_of (tree array_field)
   return strict_flex_array_level;
 }
 
+/* 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 f96350b64af..ecb85e237f8 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -909,6 +909,7 @@ extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *);
 extern void c_common_finalize_early_debug (void);
 extern unsigned int c_strict_flex_array_level_of (tree);
 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);
@@ -1342,6 +1343,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 0f35dc4fe9a..8fd45be46ed 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 1b53f2d0785..09c6abd15f6 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -6425,6 +6425,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);
@@ -9302,6 +9308,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 a17879698ec..9d2f0515c59 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -2252,6 +2252,35 @@ convert_lvalue_to_rvalue (location_t loc, struct c_expr exp,
     exp.value = convert (build_qualified_type (TREE_TYPE (exp.value), TYPE_UNQUALIFIED), exp.value);
   if (force_non_npc)
     exp.value = build1 (NOP_EXPR, TREE_TYPE (exp.value), 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;
 }
 
@@ -8514,7 +8543,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 4834a8e0b40..e6e1236e338 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -8678,6 +8678,58 @@ initialization will result in future breakage.
 GCC emits warnings based on this attribute by default; use
 @option{-Wno-designated-init} to suppress them.
 
+@cindex @code{hardbool} type attribute
+@item hardbool
+@itemx hardbool (@var{false_value})
+@itemx hardbool (@var{false_value}, @var{true_value})
+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.  */
+
+static hbool zeroinit; /* False, stored as (char)0x5a.  */
+auto hbool uninit;     /* Undefined, may trap.  */
+@end smallexample
+
+When zero-initializing a variable or field of hardened boolean type
+(presumably held in static storage) the implied zero initializer gets
+converted to @code{_Bool}, and then to the hardened boolean type, so
+that the initial value is the hardened representation for @code{false}.
+Using that value is well defined.  This is @emph{not} the case when
+variables and fields of such types are uninitialized (presumably held in
+automatic or dynamic storage): their values are indeterminate, and using
+them invokes undefined behavior.  Using them may trap or not, depending
+on the bits held in the storage (re)used for the variable, if any, and
+on optimizations the compiler may perform on the grounds that using
+uninitialized values invokes undefined behavior.
+
+
 @cindex @code{may_alias} type attribute
 @item may_alias
 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 00000000000..634feaed4de
--- /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 00000000000..2eebd0ef64f
--- /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 00000000000..a03887cfbec
--- /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 00000000000..c0ba2a8b914
--- /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 00000000000..39214d28c56
--- /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 00000000000..14438c5104f
--- /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 00000000000..d4d498c6f2a
--- /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 00000000000..e38a56b5deb
--- /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 00000000000..942300be207
--- /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 00000000000..7beec578ff8
--- /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 00000000000..841c1d4bc2e
--- /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 00000000000..5bfc922795d
--- /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 00000000000..e9feec681c4
--- /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 00000000000..01684952a2a
--- /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));
+}
+

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce hardbool attribute for C
@ 2023-06-09  6:25 Alexandre Oliva
  0 siblings, 0 replies; 19+ messages in thread
From: Alexandre Oliva @ 2023-06-09  6:25 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:90036bbb47f14f7ed0b1d918a6d3b3cd43842053

commit 90036bbb47f14f7ed0b1d918a6d3b3cd43842053
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Thu Jun 8 01:35:41 2023 -0300

    Introduce hardbool attribute for C
    
    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.
    
    
    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.

Diff:
---
 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                           |  52 ++++++++++++
 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, 459 insertions(+), 4 deletions(-)

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index d59acccb42a..f121ce737a4 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -176,6 +176,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 *);
 static tree handle_fd_arg_attribute (tree *, tree, tree, int, bool *);
 
@@ -293,6 +294,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 },
@@ -992,6 +995,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.  */
 
@@ -4357,7 +4449,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);
@@ -4369,7 +4462,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 2b4c82facf7..67ec87e23b2 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -1757,7 +1757,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;
@@ -9545,4 +9546,22 @@ c_strict_flex_array_level_of (tree array_field)
   return strict_flex_array_level;
 }
 
+/* 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 f96350b64af..ecb85e237f8 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -909,6 +909,7 @@ extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *);
 extern void c_common_finalize_early_debug (void);
 extern unsigned int c_strict_flex_array_level_of (tree);
 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);
@@ -1342,6 +1343,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 0f35dc4fe9a..8fd45be46ed 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 1b53f2d0785..09c6abd15f6 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -6425,6 +6425,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);
@@ -9302,6 +9308,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 a17879698ec..9d2f0515c59 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -2252,6 +2252,35 @@ convert_lvalue_to_rvalue (location_t loc, struct c_expr exp,
     exp.value = convert (build_qualified_type (TREE_TYPE (exp.value), TYPE_UNQUALIFIED), exp.value);
   if (force_non_npc)
     exp.value = build1 (NOP_EXPR, TREE_TYPE (exp.value), 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;
 }
 
@@ -8514,7 +8543,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 4834a8e0b40..e6e1236e338 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -8678,6 +8678,58 @@ initialization will result in future breakage.
 GCC emits warnings based on this attribute by default; use
 @option{-Wno-designated-init} to suppress them.
 
+@cindex @code{hardbool} type attribute
+@item hardbool
+@itemx hardbool (@var{false_value})
+@itemx hardbool (@var{false_value}, @var{true_value})
+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.  */
+
+static hbool zeroinit; /* False, stored as (char)0x5a.  */
+auto hbool uninit;     /* Undefined, may trap.  */
+@end smallexample
+
+When zero-initializing a variable or field of hardened boolean type
+(presumably held in static storage) the implied zero initializer gets
+converted to @code{_Bool}, and then to the hardened boolean type, so
+that the initial value is the hardened representation for @code{false}.
+Using that value is well defined.  This is @emph{not} the case when
+variables and fields of such types are uninitialized (presumably held in
+automatic or dynamic storage): their values are indeterminate, and using
+them invokes undefined behavior.  Using them may trap or not, depending
+on the bits held in the storage (re)used for the variable, if any, and
+on optimizations the compiler may perform on the grounds that using
+uninitialized values invokes undefined behavior.
+
+
 @cindex @code{may_alias} type attribute
 @item may_alias
 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 00000000000..634feaed4de
--- /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 00000000000..2eebd0ef64f
--- /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 00000000000..a03887cfbec
--- /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 00000000000..c0ba2a8b914
--- /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 00000000000..39214d28c56
--- /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 00000000000..14438c5104f
--- /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 00000000000..d4d498c6f2a
--- /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 00000000000..e38a56b5deb
--- /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 00000000000..942300be207
--- /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 00000000000..7beec578ff8
--- /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 00000000000..841c1d4bc2e
--- /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 00000000000..5bfc922795d
--- /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 00000000000..e9feec681c4
--- /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 00000000000..01684952a2a
--- /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));
+}
+

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce hardbool attribute for C
@ 2023-06-09  6:17 Alexandre Oliva
  0 siblings, 0 replies; 19+ messages in thread
From: Alexandre Oliva @ 2023-06-09  6:17 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:1eac8267db189f830c25b7a99ffd6d22f8ae91f5

commit 1eac8267db189f830c25b7a99ffd6d22f8ae91f5
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Thu Jun 8 01:35:41 2023 -0300

    Introduce hardbool attribute for C
    
    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.
    
    
    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.

Diff:
---
 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                           |  52 ++++++++++++
 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, 459 insertions(+), 4 deletions(-)

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index d59acccb42a..f121ce737a4 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -176,6 +176,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 *);
 static tree handle_fd_arg_attribute (tree *, tree, tree, int, bool *);
 
@@ -293,6 +294,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 },
@@ -992,6 +995,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.  */
 
@@ -4357,7 +4449,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);
@@ -4369,7 +4462,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 2b4c82facf7..67ec87e23b2 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -1757,7 +1757,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;
@@ -9545,4 +9546,22 @@ c_strict_flex_array_level_of (tree array_field)
   return strict_flex_array_level;
 }
 
+/* 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 f96350b64af..ecb85e237f8 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -909,6 +909,7 @@ extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *);
 extern void c_common_finalize_early_debug (void);
 extern unsigned int c_strict_flex_array_level_of (tree);
 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);
@@ -1342,6 +1343,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 0f35dc4fe9a..8fd45be46ed 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 1b53f2d0785..09c6abd15f6 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -6425,6 +6425,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);
@@ -9302,6 +9308,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 a17879698ec..9d2f0515c59 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -2252,6 +2252,35 @@ convert_lvalue_to_rvalue (location_t loc, struct c_expr exp,
     exp.value = convert (build_qualified_type (TREE_TYPE (exp.value), TYPE_UNQUALIFIED), exp.value);
   if (force_non_npc)
     exp.value = build1 (NOP_EXPR, TREE_TYPE (exp.value), 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;
 }
 
@@ -8514,7 +8543,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 4834a8e0b40..e6e1236e338 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -8678,6 +8678,58 @@ initialization will result in future breakage.
 GCC emits warnings based on this attribute by default; use
 @option{-Wno-designated-init} to suppress them.
 
+@cindex @code{hardbool} type attribute
+@item hardbool
+@itemx hardbool (@var{false_value})
+@itemx hardbool (@var{false_value}, @var{true_value})
+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.  */
+
+static hbool zeroinit; /* False, stored as (char)0x5a.  */
+auto hbool uninit;     /* Undefined, may trap.  */
+@end smallexample
+
+When zero-initializing a variable or field of hardened boolean type
+(presumably held in static storage) the implied zero initializer gets
+converted to @code{_Bool}, and then to the hardened boolean type, so
+that the initial value is the hardened representation for @code{false}.
+Using that value is well defined.  This is @emph{not} the case when
+variables and fields of such types are uninitialized (presumably held in
+automatic or dynamic storage): their values are indeterminate, and using
+them invokes undefined behavior.  Using them may trap or not, depending
+on the bits held in the storage (re)used for the variable, if any, and
+on optimizations the compiler may perform on the grounds that using
+uninitialized values invokes undefined behavior.
+
+
 @cindex @code{may_alias} type attribute
 @item may_alias
 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 00000000000..634feaed4de
--- /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 00000000000..2eebd0ef64f
--- /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 00000000000..a03887cfbec
--- /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 00000000000..c0ba2a8b914
--- /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 00000000000..39214d28c56
--- /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 00000000000..14438c5104f
--- /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 00000000000..d4d498c6f2a
--- /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 00000000000..e38a56b5deb
--- /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 00000000000..942300be207
--- /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 00000000000..7beec578ff8
--- /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 00000000000..841c1d4bc2e
--- /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 00000000000..5bfc922795d
--- /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 00000000000..e9feec681c4
--- /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 00000000000..01684952a2a
--- /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));
+}
+

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce hardbool attribute for C
@ 2023-06-08 10:59 Alexandre Oliva
  0 siblings, 0 replies; 19+ messages in thread
From: Alexandre Oliva @ 2023-06-08 10:59 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:1bfbbba78e3b12d8e9188a1eb034c1f8df6e9ac5

commit 1bfbbba78e3b12d8e9188a1eb034c1f8df6e9ac5
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Thu Jul 7 11:38:14 2022 -0300

    Introduce hardbool attribute for C
    
    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.
    
    
    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.

Diff:
---
 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                           |  52 ++++++++++++
 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, 459 insertions(+), 4 deletions(-)

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index c12211cb4d4..b84b3de5638 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -176,6 +176,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 *);
 static tree handle_fd_arg_attribute (tree *, tree, tree, int, bool *);
 
@@ -293,6 +294,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 },
@@ -992,6 +995,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.  */
 
@@ -4357,7 +4449,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);
@@ -4369,7 +4462,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 9c8eed5442a..1257760c426 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -1757,7 +1757,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;
@@ -9545,4 +9546,22 @@ c_strict_flex_array_level_of (tree array_field)
   return strict_flex_array_level;
 }
 
+/* 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 336a09f4a40..a5d3e77f121 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -909,6 +909,7 @@ extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *);
 extern void c_common_finalize_early_debug (void);
 extern unsigned int c_strict_flex_array_level_of (tree);
 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);
@@ -1342,6 +1343,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 5754d00b30a..e73103c1884 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 1af51c4acfc..cc3ec900539 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -6414,6 +6414,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);
@@ -9304,6 +9310,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 22e240a3c2a..f9cc609b54d 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -2226,6 +2226,35 @@ convert_lvalue_to_rvalue (location_t loc, struct c_expr exp,
     exp.value = convert (build_qualified_type (TREE_TYPE (exp.value), TYPE_UNQUALIFIED), exp.value);
   if (force_non_npc)
     exp.value = build1 (NOP_EXPR, TREE_TYPE (exp.value), 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;
 }
 
@@ -8488,7 +8517,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 2de212c8c2d..7b559250273 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -8681,6 +8681,58 @@ initialization will result in future breakage.
 GCC emits warnings based on this attribute by default; use
 @option{-Wno-designated-init} to suppress them.
 
+@cindex @code{hardbool} type attribute
+@item hardbool
+@itemx hardbool (@var{false_value})
+@itemx hardbool (@var{false_value}, @var{true_value})
+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.  */
+
+static hbool zeroinit; /* False, stored as (char)0x5a.  */
+auto hbool uninit;     /* Undefined, may trap.  */
+@end smallexample
+
+When zero-initializing a variable or field of hardened boolean type
+(presumably held in static storage) the implied zero initializer gets
+converted to @code{_Bool}, and then to the hardened boolean type, so
+that the initial value is the hardened representation for @code{false}.
+Using that value is well defined.  This is @emph{not} the case when
+variables and fields of such types are uninitialized (presumably held in
+automatic or dynamic storage): their values are indeterminate, and using
+them invokes undefined behavior.  Using them may trap or not, depending
+on the bits held in the storage (re)used for the variable, if any, and
+on optimizations the compiler may perform on the grounds that using
+uninitialized values invokes undefined behavior.
+
+
 @cindex @code{may_alias} type attribute
 @item may_alias
 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 00000000000..634feaed4de
--- /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 00000000000..2eebd0ef64f
--- /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 00000000000..a03887cfbec
--- /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 00000000000..c0ba2a8b914
--- /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 00000000000..39214d28c56
--- /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 00000000000..14438c5104f
--- /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 00000000000..d4d498c6f2a
--- /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 00000000000..e38a56b5deb
--- /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 00000000000..942300be207
--- /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 00000000000..7beec578ff8
--- /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 00000000000..841c1d4bc2e
--- /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 00000000000..5bfc922795d
--- /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 00000000000..e9feec681c4
--- /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 00000000000..01684952a2a
--- /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));
+}
+

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce hardbool attribute for C
@ 2023-06-08 10:43 Alexandre Oliva
  0 siblings, 0 replies; 19+ messages in thread
From: Alexandre Oliva @ 2023-06-08 10:43 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:a3c03e2c4cfc53bae727a508f77b31eee792e9ce

commit a3c03e2c4cfc53bae727a508f77b31eee792e9ce
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Thu Jun 8 01:35:41 2023 -0300

    Introduce hardbool attribute for C
    
    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.
    
    
    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.

Diff:
---
 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                           |  52 ++++++++++++
 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, 459 insertions(+), 4 deletions(-)

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index c12211cb4d4..b84b3de5638 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -176,6 +176,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 *);
 static tree handle_fd_arg_attribute (tree *, tree, tree, int, bool *);
 
@@ -293,6 +294,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 },
@@ -992,6 +995,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.  */
 
@@ -4357,7 +4449,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);
@@ -4369,7 +4462,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 9c8eed5442a..1257760c426 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -1757,7 +1757,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;
@@ -9545,4 +9546,22 @@ c_strict_flex_array_level_of (tree array_field)
   return strict_flex_array_level;
 }
 
+/* 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 336a09f4a40..a5d3e77f121 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -909,6 +909,7 @@ extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *);
 extern void c_common_finalize_early_debug (void);
 extern unsigned int c_strict_flex_array_level_of (tree);
 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);
@@ -1342,6 +1343,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 5754d00b30a..e73103c1884 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 1af51c4acfc..cc3ec900539 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -6414,6 +6414,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);
@@ -9304,6 +9310,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 22e240a3c2a..f9cc609b54d 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -2226,6 +2226,35 @@ convert_lvalue_to_rvalue (location_t loc, struct c_expr exp,
     exp.value = convert (build_qualified_type (TREE_TYPE (exp.value), TYPE_UNQUALIFIED), exp.value);
   if (force_non_npc)
     exp.value = build1 (NOP_EXPR, TREE_TYPE (exp.value), 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;
 }
 
@@ -8488,7 +8517,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 2de212c8c2d..7b559250273 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -8681,6 +8681,58 @@ initialization will result in future breakage.
 GCC emits warnings based on this attribute by default; use
 @option{-Wno-designated-init} to suppress them.
 
+@cindex @code{hardbool} type attribute
+@item hardbool
+@itemx hardbool (@var{false_value})
+@itemx hardbool (@var{false_value}, @var{true_value})
+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.  */
+
+static hbool zeroinit; /* False, stored as (char)0x5a.  */
+auto hbool uninit;     /* Undefined, may trap.  */
+@end smallexample
+
+When zero-initializing a variable or field of hardened boolean type
+(presumably held in static storage) the implied zero initializer gets
+converted to @code{_Bool}, and then to the hardened boolean type, so
+that the initial value is the hardened representation for @code{false}.
+Using that value is well defined.  This is @emph{not} the case when
+variables and fields of such types are uninitialized (presumably held in
+automatic or dynamic storage): their values are indeterminate, and using
+them invokes undefined behavior.  Using them may trap or not, depending
+on the bits held in the storage (re)used for the variable, if any, and
+on optimizations the compiler may perform on the grounds that using
+uninitialized values invokes undefined behavior.
+
+
 @cindex @code{may_alias} type attribute
 @item may_alias
 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 00000000000..634feaed4de
--- /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 00000000000..2eebd0ef64f
--- /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 00000000000..a03887cfbec
--- /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 00000000000..c0ba2a8b914
--- /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 00000000000..39214d28c56
--- /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 00000000000..14438c5104f
--- /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 00000000000..d4d498c6f2a
--- /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 00000000000..e38a56b5deb
--- /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 00000000000..942300be207
--- /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 00000000000..7beec578ff8
--- /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 00000000000..841c1d4bc2e
--- /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 00000000000..5bfc922795d
--- /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 00000000000..e9feec681c4
--- /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 00000000000..01684952a2a
--- /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));
+}
+

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce hardbool attribute for C
@ 2023-06-08  9:17 Alexandre Oliva
  0 siblings, 0 replies; 19+ messages in thread
From: Alexandre Oliva @ 2023-06-08  9:17 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:1bfbbba78e3b12d8e9188a1eb034c1f8df6e9ac5

commit 1bfbbba78e3b12d8e9188a1eb034c1f8df6e9ac5
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Thu Jul 7 11:38:14 2022 -0300

    Introduce hardbool attribute for C
    
    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.
    
    
    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.

Diff:
---
 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                           |  52 ++++++++++++
 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, 459 insertions(+), 4 deletions(-)

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index c12211cb4d4..b84b3de5638 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -176,6 +176,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 *);
 static tree handle_fd_arg_attribute (tree *, tree, tree, int, bool *);
 
@@ -293,6 +294,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 },
@@ -992,6 +995,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.  */
 
@@ -4357,7 +4449,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);
@@ -4369,7 +4462,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 9c8eed5442a..1257760c426 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -1757,7 +1757,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;
@@ -9545,4 +9546,22 @@ c_strict_flex_array_level_of (tree array_field)
   return strict_flex_array_level;
 }
 
+/* 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 336a09f4a40..a5d3e77f121 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -909,6 +909,7 @@ extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *);
 extern void c_common_finalize_early_debug (void);
 extern unsigned int c_strict_flex_array_level_of (tree);
 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);
@@ -1342,6 +1343,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 5754d00b30a..e73103c1884 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 1af51c4acfc..cc3ec900539 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -6414,6 +6414,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);
@@ -9304,6 +9310,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 22e240a3c2a..f9cc609b54d 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -2226,6 +2226,35 @@ convert_lvalue_to_rvalue (location_t loc, struct c_expr exp,
     exp.value = convert (build_qualified_type (TREE_TYPE (exp.value), TYPE_UNQUALIFIED), exp.value);
   if (force_non_npc)
     exp.value = build1 (NOP_EXPR, TREE_TYPE (exp.value), 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;
 }
 
@@ -8488,7 +8517,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 2de212c8c2d..7b559250273 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -8681,6 +8681,58 @@ initialization will result in future breakage.
 GCC emits warnings based on this attribute by default; use
 @option{-Wno-designated-init} to suppress them.
 
+@cindex @code{hardbool} type attribute
+@item hardbool
+@itemx hardbool (@var{false_value})
+@itemx hardbool (@var{false_value}, @var{true_value})
+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.  */
+
+static hbool zeroinit; /* False, stored as (char)0x5a.  */
+auto hbool uninit;     /* Undefined, may trap.  */
+@end smallexample
+
+When zero-initializing a variable or field of hardened boolean type
+(presumably held in static storage) the implied zero initializer gets
+converted to @code{_Bool}, and then to the hardened boolean type, so
+that the initial value is the hardened representation for @code{false}.
+Using that value is well defined.  This is @emph{not} the case when
+variables and fields of such types are uninitialized (presumably held in
+automatic or dynamic storage): their values are indeterminate, and using
+them invokes undefined behavior.  Using them may trap or not, depending
+on the bits held in the storage (re)used for the variable, if any, and
+on optimizations the compiler may perform on the grounds that using
+uninitialized values invokes undefined behavior.
+
+
 @cindex @code{may_alias} type attribute
 @item may_alias
 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 00000000000..634feaed4de
--- /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 00000000000..2eebd0ef64f
--- /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 00000000000..a03887cfbec
--- /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 00000000000..c0ba2a8b914
--- /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 00000000000..39214d28c56
--- /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 00000000000..14438c5104f
--- /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 00000000000..d4d498c6f2a
--- /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 00000000000..e38a56b5deb
--- /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 00000000000..942300be207
--- /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 00000000000..7beec578ff8
--- /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 00000000000..841c1d4bc2e
--- /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 00000000000..5bfc922795d
--- /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 00000000000..e9feec681c4
--- /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 00000000000..01684952a2a
--- /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));
+}
+

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce hardbool attribute for C
@ 2023-06-08  4:48 Alexandre Oliva
  0 siblings, 0 replies; 19+ messages in thread
From: Alexandre Oliva @ 2023-06-08  4:48 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:6917e5cd532fdf788694b30752416fb8971f565b

commit 6917e5cd532fdf788694b30752416fb8971f565b
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Thu Jul 7 11:38:14 2022 -0300

    Introduce hardbool attribute for C
    
    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.
    
    
    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.

Diff:
---
 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                           |  52 ++++++++++++
 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, 459 insertions(+), 4 deletions(-)

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index c12211cb4d4..b84b3de5638 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -176,6 +176,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 *);
 static tree handle_fd_arg_attribute (tree *, tree, tree, int, bool *);
 
@@ -293,6 +294,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 },
@@ -992,6 +995,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.  */
 
@@ -4357,7 +4449,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);
@@ -4369,7 +4462,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 9c8eed5442a..1257760c426 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -1757,7 +1757,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;
@@ -9545,4 +9546,22 @@ c_strict_flex_array_level_of (tree array_field)
   return strict_flex_array_level;
 }
 
+/* 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 336a09f4a40..a5d3e77f121 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -909,6 +909,7 @@ extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *);
 extern void c_common_finalize_early_debug (void);
 extern unsigned int c_strict_flex_array_level_of (tree);
 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);
@@ -1342,6 +1343,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 5754d00b30a..e73103c1884 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 1af51c4acfc..cc3ec900539 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -6414,6 +6414,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);
@@ -9304,6 +9310,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 22e240a3c2a..f9cc609b54d 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -2226,6 +2226,35 @@ convert_lvalue_to_rvalue (location_t loc, struct c_expr exp,
     exp.value = convert (build_qualified_type (TREE_TYPE (exp.value), TYPE_UNQUALIFIED), exp.value);
   if (force_non_npc)
     exp.value = build1 (NOP_EXPR, TREE_TYPE (exp.value), 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;
 }
 
@@ -8488,7 +8517,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 17d6dd2c355..f8b0bb53ef5 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -8699,6 +8699,58 @@ initialization will result in future breakage.
 GCC emits warnings based on this attribute by default; use
 @option{-Wno-designated-init} to suppress them.
 
+@cindex @code{hardbool} type attribute
+@item hardbool
+@itemx hardbool (@var{false_value})
+@itemx hardbool (@var{false_value}, @var{true_value})
+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.  */
+
+static hbool zeroinit; /* False, stored as (char)0x5a.  */
+auto hbool uninit;     /* Undefined, may trap.  */
+@end smallexample
+
+When zero-initializing a variable or field of hardened boolean type
+(presumably held in static storage) the implied zero initializer gets
+converted to @code{_Bool}, and then to the hardened boolean type, so
+that the initial value is the hardened representation for @code{false}.
+Using that value is well defined.  This is @emph{not} the case when
+variables and fields of such types are uninitialized (presumably held in
+automatic or dynamic storage): their values are indeterminate, and using
+them invokes undefined behavior.  Using them may trap or not, depending
+on the bits held in the storage (re)used for the variable, if any, and
+on optimizations the compiler may perform on the grounds that using
+uninitialized values invokes undefined behavior.
+
+
 @cindex @code{may_alias} type attribute
 @item may_alias
 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 00000000000..634feaed4de
--- /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 00000000000..2eebd0ef64f
--- /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 00000000000..a03887cfbec
--- /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 00000000000..c0ba2a8b914
--- /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 00000000000..39214d28c56
--- /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 00000000000..14438c5104f
--- /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 00000000000..d4d498c6f2a
--- /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 00000000000..e38a56b5deb
--- /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 00000000000..942300be207
--- /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 00000000000..7beec578ff8
--- /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 00000000000..841c1d4bc2e
--- /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 00000000000..5bfc922795d
--- /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 00000000000..e9feec681c4
--- /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 00000000000..01684952a2a
--- /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));
+}
+

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce hardbool attribute for C
@ 2022-07-07 15:15 Alexandre Oliva
  0 siblings, 0 replies; 19+ messages in thread
From: Alexandre Oliva @ 2022-07-07 15:15 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:757141cc9134c3ff2e86219c2f08cd5cccdd2b58

commit 757141cc9134c3ff2e86219c2f08cd5cccdd2b58
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Thu Jul 7 11:38:14 2022 -0300

    Introduce hardbool attribute for C
    
    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.
    
    
    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.

Diff:
---
 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(-)

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index c8d96723f4c..e385d780c49 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 1b8e73f7bc5..9a8584a5fe0 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 c0900848965..a8d6691d081 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 18083d59618..1399cbf9d8d 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 ae8990c138f..e103582d677 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 fd0a7f81a7a..1244241dd29 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 dfbe33ac652..7b514354263 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 00000000000..634feaed4de
--- /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 00000000000..2eebd0ef64f
--- /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 00000000000..a03887cfbec
--- /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 00000000000..c0ba2a8b914
--- /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 00000000000..39214d28c56
--- /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 00000000000..14438c5104f
--- /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 00000000000..d4d498c6f2a
--- /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 00000000000..e38a56b5deb
--- /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 00000000000..942300be207
--- /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 00000000000..7beec578ff8
--- /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 00000000000..841c1d4bc2e
--- /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 00000000000..5bfc922795d
--- /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 00000000000..e9feec681c4
--- /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 00000000000..01684952a2a
--- /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));
+}
+


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [gcc(refs/users/aoliva/heads/testme)] Introduce hardbool attribute for C
@ 2022-05-06  7:19 Alexandre Oliva
  0 siblings, 0 replies; 19+ messages in thread
From: Alexandre Oliva @ 2022-05-06  7:19 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:bd4f52dff4ea17f204e2371bd8f8cc929291ce13

commit bd4f52dff4ea17f204e2371bd8f8cc929291ce13
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Thu Feb 17 03:12:34 2022 -0300

    Introduce hardbool attribute for C
    
    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.
    
    
    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.

Diff:
---
 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(-)

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index b1953a45f9b..414fecd4dd1 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 },
@@ -964,6 +967,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.  */
 
@@ -4288,7 +4380,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);
@@ -4300,7 +4393,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 730faa9e87f..1ac68008bd6 100644
--- a/gcc/c-family/c-common.cc
+++ b/gcc/c-family/c-common.cc
@@ -1781,7 +1781,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;
@@ -9477,4 +9478,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 52a85bfb783..1cc2dc00c1c 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -908,6 +908,7 @@ extern tree fold_for_warn (tree);
 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 tree c_hardbool_type_attr_1 (tree, tree *, tree *);
 
 /* Used by convert_and_check; in front ends.  */
 extern tree convert_init (tree, tree);
@@ -1286,6 +1287,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 18083d59618..1399cbf9d8d 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 c701f07befe..36a58fbc242 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -6142,6 +6142,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);
@@ -8863,6 +8869,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 e130196a3a7..dace37178cf 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;
 }
 
@@ -8154,7 +8183,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 84e6f6694ab..fe9f694ddeb 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 00000000000..634feaed4de
--- /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 00000000000..2eebd0ef64f
--- /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 00000000000..a03887cfbec
--- /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 00000000000..c0ba2a8b914
--- /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 00000000000..39214d28c56
--- /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 00000000000..14438c5104f
--- /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 00000000000..d4d498c6f2a
--- /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 00000000000..e38a56b5deb
--- /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 00000000000..942300be207
--- /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 00000000000..7beec578ff8
--- /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 00000000000..841c1d4bc2e
--- /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 00000000000..5bfc922795d
--- /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 00000000000..e9feec681c4
--- /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 00000000000..01684952a2a
--- /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));
+}
+


^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2023-11-29  4:35 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-11-20  9:03 [gcc(refs/users/aoliva/heads/testme)] Introduce hardbool attribute for C Alexandre Oliva
  -- strict thread matches above, loose matches on Subject: below --
2023-11-29  4:35 Alexandre Oliva
2023-11-23 11:46 Alexandre Oliva
2023-10-21  3:35 Alexandre Oliva
2023-10-20  5:56 Alexandre Oliva
2023-10-20  3:48 Alexandre Oliva
2023-09-30  4:14 Alexandre Oliva
2023-09-29 22:11 Alexandre Oliva
2023-06-28 11:09 Alexandre Oliva
2023-06-23 20:13 Alexandre Oliva
2023-06-09  8:07 Alexandre Oliva
2023-06-09  6:25 Alexandre Oliva
2023-06-09  6:17 Alexandre Oliva
2023-06-08 10:59 Alexandre Oliva
2023-06-08 10:43 Alexandre Oliva
2023-06-08  9:17 Alexandre Oliva
2023-06-08  4:48 Alexandre Oliva
2022-07-07 15:15 Alexandre Oliva
2022-05-06  7:19 Alexandre Oliva

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).