First Draft: Making printf @safe

Quirin Schroll qs.il.paperinik at gmail.com
Thu Jul 18 00:34:53 UTC 2024


On Wednesday, 17 July 2024 at 00:42:03 UTC, Walter Bright wrote:
> https://github.com/WalterBright/documents/blob/ed4f1b441e71b5ac5e23a54e7c93e68997981e9a/SafePrintf.md

Let’s say I have a `@safe`-annotated function. If I understand 
the DIP draft correctly, it’s proposed that I can call `printf` if
* the format is a compile-time constant (possibly derived through 
CTFE) and

plus the compiler:
* issues a hard error on incorrect use, e.g. number of arguments 
and specifiers mismatch,
* silently changes the format specifier if it’s meaningful, e.g. 
`%s` to `%d` for integers, making `%s` essentially universal,
* static array arguments are implicitly sliced, e.g. a `char[10]` 
argument becomes `char[]` argument,
* if a `%s` specifier lines up with `const(Char)[]` argument, 
silently changes the format specifier `%s` to `%.*s`/`%.*ls` and 
the corresponding argument `xs` is replaced by 
`cast(int)(xs.length & int.max` and `xs.ptr`.

My only issue is the `& int.max`, that should be a non-assert 
feature. With asserts enabled, just `assert(xs.length < int.max)`.

Otherwise, it’s a great idea. I’d make it `__printf`, though, and 
ideally, `__printf` becomes a new core-language keyword so that 
all the compiler-magic and special casing is appropriately 
justified. It should also not require any imports then, which 
would make it even easier to use. Changing `printf` in any shape 
or form will make some people unhappy. I could imagine people 
being much happier having a keyword that is guaranteed to lower 
to a `printf` call, with some checks and convenience added.

If we’re at it, `__printf` could also support slices of 
non-character type: When a non-character array is an argument 
type that lines up with some specifier, cut the format in half, 
loop over elements and print them individually comma-separated 
and using that specifier, then continue with the rest of the 
format:
```d
int a, b;
int[] xs;

int n = __printf("%d xyz %X abc %d", a, xs, b);
// lowers to:
int n = {
     int __result = __printf("%d xyz [", a);
     if (xs.length > 0)
     {
         __result += __printf("%X", xs[0]);
         foreach (__x; xs[1..$]) __result += __printf(", %X", __x);
     }
     return __result + __printf("] abc %d", a, xs, b);
}();
```
A similar approach would work for associative arrays as well. 
What’s so cool about it is that it would work with nested arrays! 
The cutting-and-loop approach also works for `struct` types, 
printing some header (the type name) and then the comma-separated 
`tupleof`, provided the members are of printf-friendly types.


More information about the dip.development mailing list