Understanding typecheck in linux kernel - c

The following code is found from include/linux/typecheck.h:
/*
* Check at compile time that something is of a particular type.
* Always evaluates to 1 so you may use it easily in comparisons.
*/
#define typecheck(type,x) \
({ type __dummy; \
typeof(x) __dummy2; \
(void)(&__dummy == &__dummy2); \
1; \\ <---- Why here is a 1;?
})
/*
* Check at compile time that 'function' is a certain type, or is a pointer
* to that type (needs to use typedef for the function type.)
*/
#define typecheck_fn(type,function) \
({ typeof(type) __tmp = function; \
(void)__tmp; \
})
Does the 1; make any difference? Also, how can a block "evaluates to 1"?

The macro is a statement expression: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
The value of the final expression is the value "returned" by the macro.
The typecheck macro causes a compile time warning if x is not of type. Let's say I declared char *a and then tried typecheck(char, a) and compiled with gcc -Wall:
1.c: In function 'main':
1.c:5:21: warning: comparison of distinct pointer types lacks a cast [enabled by default]
(void)(&__dummy == &__dummy2); \
^
1.c:14:2: note: in expansion of macro 'typecheck'
typecheck(char, a);
^

Related

Can't understand statement (void)(x == y) and 1; in linux typecheck macro

In linux 5.4.21 source code include/linux/irqflags.h,
#define raw_local_save_flags(flags) \
do { \
typecheck(unsigned long, flags); \
flags = arch_local_save_flags(); \
} while (0)
and in include/linux/typecheck.h
#define typecheck(type,x) \
({ type __dummy; \
typeof(x) __dummy2; \
(void)(&__dummy == &__dummy2); \
1; \
})
I can't understand how the typecheck macro works. So the typecheck macro makes a variable __dummy having type type, and another variable __dummy2 with the same type with x. It then compares the address of those two variables. I guess putting (void) makes the comparison statement not make error.. but what does this comparison do? How can two variables have same address? and what is the last state 1; ? And what effect does this macro have in the raw_local_save_flags macro? (related to how it is called..) Can somebody explain it to me please? Thanks!
The comparison &__dummy == &__dummy2 is only permitted if the objects are of the same type, so a compiler will complain if they are not.
See this post about Compound Statements.
You can supply a brace-surrounded block (a compound statement) as a valid expression whose value is the last expression in the block.
So in ({ int x = c; x; }) the value of the expression is evaluated as x;

Can c11's 'Generic' keyword be used within gcc _Static_assert

I understand what one would use C11's 'Generic' for, and I would like to use it within a static assertion to guarantee that two user-defined types (typedefs) are the same primative type.
I have made a macro that maps each primative type to an enumerated value, and verified that it works as desired. However, when I try to compare the equality of two resultant macros from two types in a static assert, I get a compiler error. When you comment out the static assert, the code works as expected.
It almost seems as if the static assert is being evaluated by the compiler BEFORE the generic expansion is evaluated. Could this be the case? And where can I go to verify the behavior of this?
Example Code:
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
typedef enum
{
UTIL_TYPE_ENUM_BOOL,
UTIL_TYPE_ENUM_CHAR,
UTIL_TYPE_ENUM_SCHAR,
UTIL_TYPE_ENUM_UCHAR,
UTIL_TYPE_ENUM_SHORT,
UTIL_TYPE_ENUM_USHORT,
UTIL_TYPE_ENUM_INT,
UTIL_TYPE_ENUM_UINT,
UTIL_TYPE_ENUM_LONG,
UTIL_TYPE_ENUM_ULONG,
UTIL_TYPE_ENUM_LONG_LONG,
UTIL_TYPE_ENUM_ULONG_LONG,
UTIL_TYPE_ENUM_FLOAT,
UTIL_TYPE_ENUM_DOUBLE,
UTIL_TYPE_ENUM_LONG_DOUBLE,
UTIL_TYPE_ENUM_OTHER,
} UtilTypeEnum_t;
// returns the enumerated value representing a primitive type
#define UTIL_TYPE_GET_TYPE_ENUM(x) _Generic((x), \
_Bool: UTIL_TYPE_ENUM_BOOL, \
char: UTIL_TYPE_ENUM_CHAR, \
signed char: UTIL_TYPE_ENUM_SCHAR, \
unsigned char: UTIL_TYPE_ENUM_UCHAR, \
short int: UTIL_TYPE_ENUM_SHORT, \
unsigned short int: UTIL_TYPE_ENUM_USHORT, \
int: UTIL_TYPE_ENUM_INT, \
unsigned int: UTIL_TYPE_ENUM_UINT, \
long int: UTIL_TYPE_ENUM_LONG, \
unsigned long int: UTIL_TYPE_ENUM_ULONG, \
long long int: UTIL_TYPE_ENUM_LONG_LONG, \
unsigned long long int: UTIL_TYPE_ENUM_ULONG_LONG, \
float: UTIL_TYPE_ENUM_FLOAT, \
double: UTIL_TYPE_ENUM_DOUBLE, \
long double: UTIL_TYPE_ENUM_LONG_DOUBLE, \
default: UTIL_TYPE_ENUM_OTHER)
typedef int32_t foo_t;
typedef float bar_t;
// IF YOU COMMENT OUT THE STATIC ASSERT, THE CODE WILL COMPILE AND WORKS AS EXPECTED
_Static_assert((UTIL_TYPE_GET_TYPE_ENUM(foo_t)==UTIL_TYPE_GET_TYPE_ENUM(bar_t)),"ERROR");
int main(void)
{
foo_t foo;
bar_t bar;
printf("foo's type = %d\n", UTIL_TYPE_GET_TYPE_ENUM(foo));
printf("bar's type = %d\n", UTIL_TYPE_GET_TYPE_ENUM(bar));
if (UTIL_TYPE_GET_TYPE_ENUM(foo) != UTIL_TYPE_GET_TYPE_ENUM(bar))
{
printf("Not the same type!\n");
}
else
{
printf("Same type!\n");
}
return 0;
}
#endif
Compiler Error:
$ gcc foo.c
foo.c:35:49: error: expected expression before ‘,’ token
#define UTIL_TYPE_GET_TYPE_ENUM(x) _Generic((x), \
^
foo.c:77:17: note: in expansion of macro ‘UTIL_TYPE_GET_TYPE_ENUM’
_Static_assert((UTIL_TYPE_GET_TYPE_ENUM(foo_t)==UTIL_TYPE_GET_TYPE_ENUM(bar_t)),"ERROR");
^~~~~~~~~~~~~~~~~~~~~~~
foo.c:35:49: error: expected expression before ‘,’ token
#define UTIL_TYPE_GET_TYPE_ENUM(x) (_Generic((x), \
^
foo.c:77:49: note: in expansion of macro ‘UTIL_TYPE_GET_TYPE_ENUM’
_Static_assert((UTIL_TYPE_GET_TYPE_ENUM(foo_t)==UTIL_TYPE_GET_TYPE_ENUM(bar_t)),"ERROR");
^~~~~~~~~~~~~~~~~~~~~~~
foo.c:77:16: error: expression in static assertion is not an integer
_Static_assert((UTIL_TYPE_GET_TYPE_ENUM(foo_t)==UTIL_TYPE_GET_TYPE_ENUM(bar_t)),"ERROR");
The argument to a _Generic selection must be a valid C expression, the type of which is then examined. You provide a type name, which simply isn't an expression.
To get an expression of the types you are after, you can use a compound literal:
_Static_assert((UTIL_TYPE_GET_TYPE_ENUM((foo_t){0})==UTIL_TYPE_GET_TYPE_ENUM((bar_t){0})),"ERROR");
(foo_t){0} and (bar_t){0} are now expressions of the types you want to compare, and so may be used in the generic selection.
You can achieve what you want, or at least something close, with
#define SAME_TYPE(t1,t2) _Generic((t1){0}, t2: 1, default: 0)
_Static_assert(SAME_TYPE(foo_t, bar_t));
This does not involve enumerations that assume a finite set of types (not supporting struct types, etc.) and does not depend on the "GNU C" typeof extension (which is not part of the C language).
This works for types, not expressions. It can easily be extended to the case where one argument is a type and the other is an expression. If you need to assert that two expressions have the same type, that's at least somewhat harder to do purely in C, and might not be possible. If you're in the special case where they're variables, the expression
1 ? &var1 : &var2
is a constraint violation if var1 and var2 have different types, but sadly GCC treats this as a warning by default rather than an error. I'm not aware of a way to make it into an error without a full -Werror because it doesn't seem to be in its own warning group, just the anonymous on-by-default warnings...
As already noted in another answer, you cannot pass a type as the first argument to _Generic. You need to provide an expression, and the compiler will deduce if the type of the expression matches against the list.
It is possible to simplify your code. You do not need to use those enumerations. gcc supports the typeof extension, which allows you to do something like:
#define EXPR_HAVE_SAME_TYPE(E1, E2) \
_Generic((E1), typeof(E2): 1, default: 0)
_Static_assert(EXPR_HAVE_SAME_TYPE(foo, foo), "...");
_Static_assert(!EXPR_HAVE_SAME_TYPE(foo, bar), "...");
You can use this for types if you create expressions:
#define SAME_TYPE(T1, T2) \
EXPR_HAVE_SAME_TYPE(*(T1 *)0, *(T2 *)0)
_Static_assert(SAME_TYPE(foo_t, foo_t), "...");
_Static_assert(!SAME_TYPE(foo_t, bar_t), "...");
A similar notion was mentioned in R.'s comment and answer.

Macro ternary comma expression causes warning when used as a statement

I have defined a macro to log NULL pointers. Because I have NULL checks in my code, the macro is a ternary that always returns the argument and calls a function if it is NULL.
This however causes a compile warning (gcc: right-hand operand of comma expression has no effect) if the macro is used outside of an if statement.
Curiously, clang doesn't produce a warning, even with -Weverything.
The solution I have come up so far is defining another macro that wraps the expression in an if without a body.
#define NULLPRINT(X) \
((X) ? (X) \
: (fprintf(stderr, \
"%s:%u: %s: %s is NULL.\n", \
__FILE__, \
__LINE__, \
__func__, \
#X), \
stacktrace(), \
(X)))
#define IF_NULLPRINT(X) do {if(NULLPRINT(X)){}} while(0)
Is there a way to do this without macro duplication and avoiding the compiler warning?
Obviously without turning off the warning.
Try casting it to void:
(void) NULLPRINT(...);
This generally suppresses warnings about unused values of expressions.
You could define another macro that hides this.

Typechecking in gcc macros and type decay

This:
#define MC_tpchk(X,Y) \
({ __typeof(X) _x; __typeof(Y) _y; (void)(&_x == &_y); 1; })
is stolen (with renames) from the Linux source.
I'd like to use it to typecheck some stuff in some of my generic macros (can't use inline functions there).
Unfortunately, it has some issues with string literals:
This all works:
char c_a, c_b;
int i_a, i_b;
char *s_a, *s_b;
MC_tpchk(c_a,c_b);
MC_tpchk(i_a,i_b);
MC_tpchk(s_a,s_b);
char *foo = "foo";
MC_tpchk(s_a,foo);
But this is a compile error:
MC_tpchk(s_a, "foo");
The error:
error: comparison of distinct pointer types lacks a cast [-Werror]
is just a warning if I compile without -Werror, but I would like to keep the -Werror there.
How else can I deal with this error?
My guess is that the detected type is probably char[]. How can I make the compiler show this? Can I make the macro arguments decay (in this case to char*) so this is no longer an error?
It does appear to be because the string literal is being detected as an array type. A pointer to an array type does not match a pointer to a pointer type.
I wrote a different macro:
#define MC_tpchk(X,Y) \
do { \
break; \
void (*CHK) (__typeof(X) *); \
__typeof(Y) _y; \
CHK(&_y); \
} while (0)
This seems to provide a more descriptive error:
error: passing argument 1 of 'CHK' from incompatible pointer type
note: expected 'const char **' but argument is of type 'char (*)[4]'
I can't think of a way to decay the array within the macro to properly detect its compatibility with a pointer type. However, the caller of your macro could be conditioned to handle an array differently. So, this would work:
MC_tpchk(s_a, &*"foo");

C preprocessor - comparing macro arguments

wrt:
C preprocessor macro specialisation based on an argument
I am trying to use this technique to compare to 'void', however it also
matches 'void *'.
Has anybody else seen this problem ?
Is there a known solution ?
The source contains thousands of lines with preprocessor macros, so it is not easily readable. However it is like this:
#define GIVE_A_FUNCTION(RetType,Name,ArgType1)\
RetType Name(ArgType1 ArgName1) \
{ \
SWITCH_ON_VOID( \
RetType,\
,\
RetType value =)\
GetValue(); \
PostProcessing(); \
SWITCH_ON_VOID( \
RetType,\
,\
return value;)\
}
This expands to nothing if type is 'void *', hence I do not include the
return statement.
I can solve the problem manually but would prefer not to.
Regards,

Resources