I think it's a fine design. Obviously, the C++ people who invented
virtual void fun() = 0;
were blind to whatever mistake it embodies.
When I gained the proper understanding that 0 is a null pointer constant, I immediatelly stopped using NULL. It's so much nicer.
- It works fine in a typeless language in which every value is a machine word, and integers and the null pointer share exactly the same representation.
- It works fine in a strongly typed language, where context indicates what zero means, even in the context of different types of different sizes and representations.
Unfortunately C (and also C++) contains some untyped areas, like variadic argument lists.
I would have designed it differently: only the token 0 would have the special overloaded meaning, and not all constant zero-valued expressions of any integer type. So (0 + 0) would not be a null pointer.
In unsafe contexts, the use of a 0 token would be diagnosed.
printf("...", ... 0, ...); // diagnosed
printf("...", ... 0L, ...); // not diagnosed: argument of long type
printf("...", ... 0 + 0, ...); // not diagnosed: argument of int type
printf("...", ... (void *) 0, ...); // not diagnosed: null pointer of (void *) type.
I think I would also not have the hexadecimal or octal zero tokens be the null pointer constant:
char *p = 0; // null pointer, no diagnostic
char *q = 0x0; // error, integer to pointer with no cast
The downside is that the AST would have to retain that representation detail somewhere (possibly just in a single Boolean flag).