dmd icon indicating copy to clipboard operation
dmd copied to clipboard

add scope const to ... parameters

Open WalterBright opened this issue 11 months ago • 22 comments

core.stdc.stdio.printf already does this.

WalterBright avatar Aug 24 '23 18:08 WalterBright

Thanks for your pull request, @WalterBright!

Bugzilla references

Your PR doesn't reference any Bugzilla issue.

If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog.

Testing this PR locally

If you don't have a local development environment setup, you can use Digger to test this PR:

dub run digger -- build "master + dmd#15556"

dlang-bot avatar Aug 24 '23 18:08 dlang-bot

Dautotest apparently cannot handle scope const ... parameters, it fails with:

src/dmd/errors.d(174): [1;33mWarning: [mDdoc: parameter count mismatch, expected 3, got 2
``

WalterBright avatar Aug 24 '23 20:08 WalterBright

core.stdc.stdio.printf already does this.

Which is wrong, because the %n specifier modifies a variadic argument, violating const

dkorpel avatar Aug 25 '23 09:08 dkorpel

I don't see much point in adding unchecked and wrong annotations to dirty old printf. If we want to move to something type safe / memory @safe / pure, we have to step away from printf and go for a D solution.

dkorpel avatar Aug 25 '23 09:08 dkorpel

Ha, I forgot about %n. But we never use it, and I've never seen it used. From a pedantic standpoint, you're quite correct. From a pragmatic point of view, I'm not so sure. Replacing printf just for that seems a lot of work for little payoff.

WalterBright avatar Aug 25 '23 13:08 WalterBright

From a pragmatic point of view, I'm not so sure.

I agree I'm being pedantic, but from a pragmatic point of view, there's little advantage of adding const. We're already passing const(char)* arguments to ... without const, and the compiler doesn't complain, so it only inaccurately documents what's already known.

Replacing printf just for that seems a lot of work for little payoff

It's not just const, it's also for @safe and getting rid of C strings, which is something you said you wanted.

dkorpel avatar Aug 25 '23 15:08 dkorpel

It's not just const, it's also for @safe and getting rid of C strings, which is something you said you wanted.

printf still works with D strings, we do it all the time. One of the impediments to getting rid of all the C strings, which I do want, is interfacing with the C++ gdc and ldc backends.

WalterBright avatar Aug 25 '23 17:08 WalterBright

I do want to make the dmd source code @safe and dip1000 compliant. The way we use printf is trustable and dip1000 compliant. It's better than the hack we use to get a pure malloc and free.

WalterBright avatar Aug 25 '23 17:08 WalterBright

The way we use printf is trustable and dip1000 compliant.

You can keep using printf deep down to do the string concatenation and wrap it in a safe interface with @trusted, but the fundamental problem is that the current interface of the error() family of functions (which this PR tries to improve) is not @safe, and cannot be @safe as long as it's a thin wrapper where the format string and arguments are C strings, and passed verbatim to printf.

void error(const ref Loc loc, const(char)* format, scope const ...)

This will only ever be called from a @system or @trusted context, so no scope checks will ever happen. And because C variadics throw type safety completely out the window, the const doesn't serve any purpose either. Only when we change it to something safe, for example:

void error(const ref Loc loc, string format, RootObject[] args...);
void error(T...)(const ref Loc loc, string format, T args);

Only then would it make sense to add const or scope (if scope is not inferred already).

dkorpel avatar Aug 25 '23 17:08 dkorpel

This will only ever be called from a @system or @trusted context

@safe code can call a @trusted function with scope parameters and scope will be checked. If error() is @trusted, it must provide a safe interface, and that implies it must properly use va_args to correctly get the types and values of arguments.

As for char* 0 terminated strings, they can be passed to @safe functions. It's just that the @safe function cannot do any pointer arithmetic on it, it can only look at the first character of the string. But it can pass the pointer along to an @trusted function, which can then do pointer arithmetic on it. Pointers-to-strings essentially become opaque objects to @safe code.

So I think we're good. Besides, if we can't do this, D cannot successfully interface with C code.

WalterBright avatar Aug 25 '23 20:08 WalterBright

it must properly use va_args to correctly get the types and values of arguments

How?

But it can pass the pointer along to an @trusted function, which can then do pointer arithmetic on it.

Only if the @trusted function can verify it is indeed a 0 terminated string and not e.g. a new char('a'). How is it going to do that?

dkorpel avatar Aug 25 '23 20:08 dkorpel

How?

That's why it's a trusted function! Not sure just what you're asking?

Only if the @trusted function can verify it is indeed a 0 terminated string and not e.g. a new char('a'). How is it going to do that?

Great question! The answer is, the @trusted function is indeed doing something unsafe with the pointer, but that's why it's @trusted.

WalterBright avatar Aug 25 '23 23:08 WalterBright

Only if the @trusted function can verify it is indeed a 0 terminated string and not e.g. a new char('a'). How is it going to do that?

Great question! The answer is, the @trusted function is indeed doing something unsafe with the pointer, but that's why it's @trusted.

No. @trusted functions must have safe interfaces. A function that assumes a null-terminated string behind a char* cannot be @trusted. It can only be @system. This is the very core idea of @trusted. I'm not being pedantic. Violating that rule is not being pragmatic.

aG0aep6G avatar Aug 26 '23 06:08 aG0aep6G

That's why it's a trusted function! Not sure just what you're asking?

I thought you were implying there was a way to use va_args and retrieve extra type information so it could be made type safe. Reading it again, it seems like you're saying "the caller must pass the variadic arguments with the right types in order for it to be safe". Like aG0aep6G said, that's not how @trusted works.

dkorpel avatar Aug 26 '23 09:08 dkorpel

@trusted does not mean: "this is unsafe but I don't care". It means: "this is safe, though the compiler might not understand". If you want @trusted functions that accept C strings, you need to have a "zero-terminated C string" type that is ABI-compatible with char*.

tgehr avatar Aug 26 '23 10:08 tgehr

In any case, you cannot trust printf. It will result in undefined behavior for bad argument combinations.

tgehr avatar Aug 26 '23 10:08 tgehr

It will result in undefined behavior for bad argument combinations.

We've added a checker for the arguments based on the printf format string.

WalterBright avatar Aug 27 '23 00:08 WalterBright

the caller must pass the variadic arguments with the right types in order for it to be safe

import core.stdc.stdio;
void test(int i) { printf("%p\n", i); }
test.d(3): Deprecation: argument `i` for format specification `"%p"` must be `void*`, not `int`

WalterBright avatar Aug 27 '23 00:08 WalterBright

@tgehr does that mean no opaque types are allowed in @safe code?

WalterBright avatar Aug 27 '23 00:08 WalterBright

We've added a checker for the arguments based on the printf format string.

Which is not sufficient for @trusted

printf("%s", new char('a')); // permitted

dkorpel avatar Aug 27 '23 10:08 dkorpel

Which is wrong, because the %n specifier modifies a variadic argument, violating const

https://issues.dlang.org/show_bug.cgi?id=24133

WalterBright avatar Sep 03 '23 09:09 WalterBright

The error seems to be a tester problem.

WalterBright avatar Nov 14 '23 06:11 WalterBright