Bug 59852 - Support sparse-style __attribute__((bitwise)) (type attribute)
Summary: Support sparse-style __attribute__((bitwise)) (type attribute)
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: c (show other bugs)
Version: unknown
: P3 enhancement
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-01-17 05:32 UTC by H. Peter Anvin
Modified: 2018-04-15 16:54 UTC (History)
6 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description H. Peter Anvin 2014-01-17 05:32:48 UTC
The sparse static C language checker contains a type attribute extension:

__attribute__((bitwise))

The bitwise attribute modifies an arithmetic type so that the only arithmetic options permitted are the ones that are strictly bitwise.  This is primarily used for data items with a specific endianness, such as the "network" side of the htonX() and ntohX() functions.

The sparse documentation describes this as:

              Warn about unsupported operations or type mismatches with restricted integer types.

              Sparse supports an extended attribute, __attribute__((bitwise)), which creates a new restricted integer type from a base integer type, distinct from the base integer type and from  any
              other  restricted  integer type not declared in the same declaration or typedef.  For example, this allows programs to create typedefs for integer types with specific endianness.  With
              -Wbitwise, Sparse will warn on any use of a restricted type in arithmetic operations other than bitwise operations, and on any conversion of one restricted type  into  another,  except
              via a cast that includes __attribute__((force)).

              __bitwise ends up being a "stronger integer separation". That one doesn't allow you to mix with non-bitwise integers, so now it's much harder to lose the type by mistake.

              __bitwise  is  for  *unique types* that cannot be mixed with other types, and that you'd never want to just use as a random integer (the integer 0 is special, though, and gets silently
              accepted iirc - it's kind of like "NULL" for pointers). So "gfp_t" or the "safe endianness" types would be __bitwise: you can only operate on them by  doing  specific  operations  that
              know about *that* particular type.

              Generally, you want bitwise if you are looking for type safety. Sparse does not issue these warnings by default.
Comment 1 Josh Triplett 2014-01-17 09:39:48 UTC
Note in particular the bit about typedefs.  Two identical declarations both using __attribute__((bitwise)) create two variables with different types, but two declarations both usin the same typedef declared with __attribute__((bitwise)) create two variables with the same type.  In code:

__attribute__((bitwise)) unsigned a, b;
__attribute__((bitwise)) unsigned c, d;
typedef __attribute__((bitwise)) unsigned foo_t;
foo_t e, f;
foo_t g, h;

a and b have the same type.
c and d have the same type.
e, f, g, and h have the same type.
Comment 2 Tom Tromey 2014-01-20 23:46:13 UTC
Suppose 'x' is of bitwise type.
Is "x == x" an allowable operation?
On the one hand, it isn't obvious whether "==" is "strictly bitwise".
On the other hand, this seems well-defined, at least in
cases where no promotion is needed.
This applies to "!=" as well.
Comment 3 H. Peter Anvin 2014-01-21 00:00:10 UTC
a == b and a != b are permitted.
~a is permitted iff sizeof(a) >= sizeof(int).
!a and other booleanizing operations are permitted but produces integer (non-bitwise) results.
Comment 4 Josh Triplett 2014-01-21 00:39:41 UTC
Also note that arithmetic operations between a bitwise and a known-zero value do not warn.

The warning on ~ of a value smaller than int only occurs if the value is not subsequently stuffed back into the same bitwise type.  For instance, this does not warn:

typedef unsigned short __attribute__((bitwise)) le16;

le16 i, j;

le16 k = ~i | j;
Comment 5 Tom Tromey 2014-01-21 02:34:47 UTC
(In reply to Josh Triplett from comment #4)
> Also note that arithmetic operations between a bitwise and a known-zero
> value do not warn.

I'm curious about this too.
If it means that the warnings should be deferred until after
optimization, then it seems like maybe a difficult problem.
On the other extreme, if this just refers to constant expressions,
making this a purely front-end feature, then it seems more tractable.
Comment 6 Josh Triplett 2014-01-21 02:45:29 UTC
(In reply to Tom Tromey from comment #5)
> (In reply to Josh Triplett from comment #4)
> > Also note that arithmetic operations between a bitwise and a known-zero
> > value do not warn.
> 
> I'm curious about this too.
> If it means that the warnings should be deferred until after
> optimization, then it seems like maybe a difficult problem.
> On the other extreme, if this just refers to constant expressions,
> making this a purely front-end feature, then it seems more tractable.

Sparse does it for constant expressions only, as far as I can tell.
Comment 7 Josh Triplett 2014-01-21 02:54:13 UTC
(In reply to Josh Triplett from comment #4)
> Also note that arithmetic operations between a bitwise and a known-zero
> value do not warn.
> 
> The warning on ~ of a value smaller than int only occurs if the value is not
> subsequently stuffed back into the same bitwise type.  For instance, this
> does not warn:
> 
> typedef unsigned short __attribute__((bitwise)) le16;
> 
> le16 i, j;
> 
> le16 k = ~i | j;

To elaborate on this with some implementation details of Sparse: applying ~ to a bitwise type smaller than an int produces a value of a corresponding bitwise type with the added attribute "fouled".  Bitwise operations propagate the fouled bit if either operand has it, without warning.  == and != will warn about fouled types.  Assignments or conversions to the original unfouled bitwise type will work without warning, discarding the fouled bit.  And any arithmetic operation that would warn about a bitwise type will warn about a fouled type, complaining that the type degraded to "int".
Comment 8 Josh Triplett 2014-01-21 03:08:09 UTC
(In reply to Josh Triplett from comment #7)
> (In reply to Josh Triplett from comment #4)
> > Also note that arithmetic operations between a bitwise and a known-zero
> > value do not warn.
> > 
> > The warning on ~ of a value smaller than int only occurs if the value is not
> > subsequently stuffed back into the same bitwise type.  For instance, this
> > does not warn:
> > 
> > typedef unsigned short __attribute__((bitwise)) le16;
> > 
> > le16 i, j;
> > 
> > le16 k = ~i | j;
> 
> To elaborate on this with some implementation details of Sparse: applying ~
> to a bitwise type smaller than an int produces a value of a corresponding
> bitwise type with the added attribute "fouled".  Bitwise operations
> propagate the fouled bit if either operand has it, without warning.  == and
> != will warn about fouled types.  Assignments or conversions to the original
> unfouled bitwise type will work without warning, discarding the fouled bit. 
> And any arithmetic operation that would warn about a bitwise type will warn
> about a fouled type, complaining that the type degraded to "int".

One more detail: bitwise '&' of two fouled bitwise types will work and produce the same fouled type; but bitwise '&' of a bitwise type and the corresponding fouled bitwise type will produce the unfouled bitwise type.  For details, see commit d24967cb847b7a04920698a9053ea8195046a831 in Sparse by Al Viro:

    Basically, we delay reporting an error on ~<short bitwise> for as long as
    possible in hope that taint will be cleansed later.  Exact rules follow:
    
            * ~short_bitwise => corresponding fouled
            * any arithmetics that would be banned for bitwise => same warning
    as if we would have bitwise
            * if t1 is bitwise type and t2 - its fouled analog, then
    t1 & t2 => t1, t1 | t2 => t2, t1 ^ t2 => t2.
            * conversion of t2 to t1 is silent (be it passing as argument
    or assignment).  Other conversions are banned.
            * x ? t1 : t2 => t2
            * ~t2 => t2 (_not_ t1; something like ~(x ? y : ~y) is still fouled)
            * x ? t2 : t2 => t2, t2 {&,|,^} t2 => t2 (yes, even ^ - same as before).
            * x ? t2 : constant_valid_for_t1 => t2
            * !t2 => warning, ditto for comparisons involving t2 in any way.
            * wrt casts t2 acts exactly as t1 would.
            * for sizeof, typeof and alignof t2 acts as promoted t1.  Note that
    fouled can never be an lvalue or have types derived from it - can't happen.