63

In kernel.h min is defined as:

#define min(x, y) ({                \
    typeof(x) _min1 = (x);          \
    typeof(y) _min2 = (y);          \
    (void) (&_min1 == &_min2);      \
    _min1 < _min2 ? _min1 : _min2; })

I don't understand what the line (void) (&_min1 == &_min2); does. Is it some kind of type checking or something?

Mateusz Piotrowski
  • 8,029
  • 10
  • 53
  • 79
Robert S. Barnes
  • 39,711
  • 30
  • 131
  • 179
  • Curious. It seems to me that the address comparison would force _min1 and _min2, and therefore x and y, to actually be computed and stored, but shouldn't that happen anyway when _min1 is compared against _min2 in the next line? – michel-slm Apr 08 '11 at 13:21
  • FYI, the question [Macro with an unusual line in linux kernel?](http://stackoverflow.com/questions/26717636/macro-with-an-unusual-line-in-linux-kernel) was merged with this one, so you have a a few new answers now. – Shafik Yaghmour Nov 14 '14 at 01:52

6 Answers6

64

The statement

(void) (&_min1 == &_min2);

is a guaranteed "no-op". So the only reason it's there is for its side effects.

But the statement has no side effects!

However: it forces the compiler to issue a diagnostic when the types of x and y are not compatible.
Note that testing with _min1 == _min2 would implicitly convert one of the values to the other type.

So, that is what it does. It validates, at compile time, that the types of x and y are compatible.

pmg
  • 106,608
  • 13
  • 126
  • 198
  • 3
    Maybe it's better to say that it has no _runtime_ side effects, but rather _compilation_ side effects. – J. Polfer Apr 08 '11 at 13:28
  • That doesn't sound right to me for some reason. If the types are incompatible, then the macro won't work anyways. I.e., if you pass the macro a struct foo and an int you'll get a compile time error anyways, even without that line. – Robert S. Barnes Apr 08 '11 at 14:11
  • 2
    @Robert: try, for instance, `m = min(42, 43.0);`. Both with and without the statement in question. – pmg Apr 08 '11 at 14:18
  • @pmg: So the point isn't incompatible types, it's that they want to ensure that both arguments are of the exact same type? – Robert S. Barnes Apr 08 '11 at 14:21
  • 2
    `int` and `volatile const int` are distinct, but compatible, types! – pmg Apr 08 '11 at 14:29
  • @pmg: Good catch. The macro should probably be using `typeof((x)+0)` rather than `typeof(x)`... – R.. GitHub STOP HELPING ICE Apr 08 '11 at 14:52
  • @R..: the sane version has my vote! Anyway, adding `+0` would make `char`, `short`, and `int` all compatible :) – pmg Apr 08 '11 at 15:24
20

The code in include/linux/kernel.h refers to this as an "unnecessary" pointer comparison. This is in fact a strict type check, ensuring that the types of x and y are the same.

A type mismatch here will cause a compilation error or warning.

Hasturkun
  • 35,395
  • 6
  • 71
  • 104
12

This provides for type checking, equality between pointers shall be between compatible types and gcc will provide a warning for cases where this is not so.

We can see that equality between pointers requires that the pointers be of compatible types from the draft C99 standard section 6.5.9 Equality operators which says:

One of the following shall hold:

and includes:

both operands are pointers to qualified or unqualified versions of compatible types;

and we can find what a compatible type is from section 6.2.7 Compatible type and composite type which says:

Two types have compatible type if their types are the same

This discussion on osnews also covers this and it was inspired by the GCC hacks in the Linux kernel article which has the same code sample. The answer says:

has to do with typechecking.

Making a simple program:

int x = 10; 
long y = 20;
long r = min(x, y);

Gives the following warning: warning: comparison of distinct pointer types lacks a cast

HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • Merged from http://stackoverflow.com/questions/26717636/macro-with-an-unusual-line-in-linux-kernel – Shog9 Nov 13 '14 at 21:26
7

See http://www.osnews.com/comments/20566 which explains:

It has to do with typechecking.

Making a simple program:

int x = 10; 
long y = 20; 
long r = min(x, y); 

Gives the following warning: warning: comparison of distinct pointer types lacks a cast

Emil Sit
  • 22,894
  • 7
  • 53
  • 75
4

Found answer here

"It has to do with typechecking. Making a simple program:

int x = 10; 
long y = 20; 
long r = min(x, y); 

Gives the following warning: warning: comparison of distinct pointer types lacks a cast"

incogn1to
  • 429
  • 4
  • 17
  • Merged from http://stackoverflow.com/questions/26717636/macro-with-an-unusual-line-in-linux-kernel – Shog9 Nov 13 '14 at 21:26
0

The Linux kernel is full of stuff like this (gratuitous gcc-specific hacks for the sake of "type safety" and other similar considerations), and I would consider it very bad practice and urge you not to follow it unless someone requires you to.

pmg is right about the purpose of the hack, but any sane person would define min as ((x)<(y)?(x):(y)).

Note that the kernel definition precludes many correct usages, e.g. where one argument is int and another is long. I suspect what they really wanted to preclude is signedness mismatches, where for example min(-1,1U) is 1. A better way to assert this would be to use a compile-time assertion for ((1?-1:(x))<0)==((1?-1:(y))<0). Note that this does not require any gcc-specific hacks.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • 4
    But ((x)<(y)?(x):(y)) breaks if either x or y have side-effects. In fairness to the kernel they specify GCC as their compiler so they're allowed GCC-specific stuff. – Rup Apr 08 '11 at 13:51
  • 1
    Everyone **knows** you don't pass expressions with side effects to a min/max macro. This is one of the first things you learn learning C. And specifying GCC as the only supported compiler is a hindrance to progress. – R.. GitHub STOP HELPING ICE Apr 08 '11 at 14:03
  • 4
    Sure, but I learned it the other way around: you don't write min / max as a macro like that. – Rup Apr 08 '11 at 14:10
  • 1
    Macro is the only way to make it work with multiple types (float, pointers, integers, signed or unsigned). There *are* ways to make it avoid evaluating them more than once, but they're sufficiently painful and make the macro gigantic and unreadable that I don't think they're worth it. A good C programmer will be cautious about side effects here anyway, and it's much more important that the macro is simple and clear and obviously correct to somebody who reads it. – R.. GitHub STOP HELPING ICE Apr 08 '11 at 14:37
  • 2
    The kernel requires various other things (`asm` blocks, linker section annotations) that are not part of standard C anyway, so specifying GNU C isn't really a loss. – caf Apr 11 '11 at 12:47
  • @caf: There's a big difference between requiring gcc/at&t style inline assembler support for small amounts of machine-specific code, and requiring a completely different GNU variant of the C language. The former is supported by almost any C compiler for unix-like systems. The latter... well it depends on which extension you're using. – R.. GitHub STOP HELPING ICE Apr 11 '11 at 14:22
  • @R.. when I evaluate `((1?-1:(x))<0)==((1?-1:(y))<0)` in my mind it always evaluates to true (-1 == -1). Is the trick here to provoke a compiler warning? – Aktau Jun 16 '14 at 08:14
  • 2
    @Aktau: No. The "trick" is that it can easily be false. The result of the `?:` operator has a type whose rules depend on the types of both the second and third operand. If one of `x` or `y` has an unsigned type of rank greater than that of `int` but the other does not, the equality will be false; one will be a large positive value and the other will be -1. These are exactly the cases where `min` and `max` macros could behave in an unexpected and possibly-dangerous manner. – R.. GitHub STOP HELPING ICE Jun 16 '14 at 15:01
  • Excellent, another thing learned. I've heard of crazy things with the `?:` operator being used for the implementation of `tgmath.h` on some toolchains. Now I know a bit better why/how. Thanks! – Aktau Jun 16 '14 at 15:02