May 24, 2020
On Sunday, 24 May 2020 at 08:55:32 UTC, Walter Bright wrote:
> I can't see a practical difference between:
>
> @safe extern (C) void whatevs(parameters);
> @trusted extern (C) void whatevs(parameters);
>
> Both require that whatevs() provide a safe interface.

Remember that D has reflection. If we ever do a reflection check on these, @trusted stands out better there than @safe. Of course it could also just return null in reflection meaning "default applied" and that could be detected as well.

Otherwise it is the same, yes, but this reflection thing is a small nice benefit.
May 24, 2020
On Sunday, 24 May 2020 at 03:28:25 UTC, Walter Bright wrote:
> I'd like to emphasize:
>
> 1. It is not possible for the compiler to check any declarations where the implementation is not available. Not in D, not in any language. Declaring a declaration safe does not make it safe.

That's why Rust has gotten rid of declarations all together, except for C, which are implicitly marked unsafe, and they cannot be marked "safe" (there is no such feature).

> 2. If un-annotated declarations cause a compile time error, it is highly likely the programmer will resort to "greenwashing" - just slapping @safe on it. I've greenwashed code. Atila has. Bruce Eckel has. We've all done it. Sometimes even for good reasons.

Having extern(C) be @safe by default doesn't stop greenwashing. As others have pointed out, you are doing the greenwashing for the programmer. That's not better.

> 3. Un-annotated declarations are easily detectable in a code review.

Declarations annotated with @trusted are even easier to detect. And arguably declarations that aren't annotated aren't that easy to detect.

> 4. Greenwashing is not easily detectable in a code review.
>
> 5. Greenwashing doesn't fix anything. The code is not safer. It's an illusion, not a guarantee.
>
> 6. If someone cares to annotate declarations, it means he has at least thought about it, because he doesn't need to. Hence it's more likely to be correct than when greenwashed.
>
> 7. D should *not* make it worthwhile for people to greenwash code.
>
> It is, in a not-at-all obvious way, safer for C declarations to default to being safe.

This whole situation just makes it more obvious to me, you shouldn't be able to mark extern(C) declarations as @safe or @trusted at all. If that truly is your fear, it should just not be possible to mark them as @safe/@trusted.

8. D should make incorrect code *more* difficult, not easier. Or are you conveniently ignoring this ideology?

Code is going to break no matter what. That's the cost you are going to pay for making @safe default. You can't fix that. The difference is what the aftermath will look like.

May 24, 2020
On Sunday, 24 May 2020 at 01:26:02 UTC, ag0aep6g wrote:
> On 24.05.20 02:55, Arine wrote:
>> That works even if you make the static this() @safe, and remove the pointer incrementation.
>
> Sure. `*p = 13;` is perfectly @safe. The static constructor isn't needed for that part. You can just as well do the assignment in `main`. The static constructor is another feature that can smuggle unsafe code (the increment) into your program without the @trusted warning label.

It'd be no different than passing the pointer into @safe code as a parameter from @system code. Ultimately the error occurs in @system code and directly as a result of @system code. It is undefined behavior as well. No amount of safe code can save you from that.

>> You'd have to make the p initialization @safe.
>> 
>>      @safe:
>>          int* p = cast(int*) &x; // error
>> 
>> But note this doesn't work:
>> 
>>      @safe int* p = cast(int*) &x; // compiles
>> 
>> Having the default become @safe will help detect this, as I don't imagine that is a whole lot of usage of @safe: to begin with.
>
> The example compiles with `-preview=safedefault`. And even if that gets changed, it will probably still compile when marked @system. So we still won't find it when looking for "@trusted".

Then that is definitely a bug if that's the case. Someone should probably make a bug report, Walter? If you are still using @system with @safe, then that would still be somewhere you have to look for not memory safe code. @trusted should just mean that someone verified it. @system then would mean no one's verified it to be safe, that doesn't mean you don't have to check it.
May 24, 2020
On Sunday, 24 May 2020 at 14:39:50 UTC, Arine wrote:
[...]
> It'd be no different than passing the pointer into @safe code as a parameter from @system code. Ultimately the error occurs in @system code and directly as a result of @system code. It is undefined behavior as well. No amount of safe code can save you from that.

I think you're arguing against a point that wasn't made. I'm not saying that there's anything fundamentally unsound about an @system static constructor. As you say, it's the same any other @system function.

I'm just saying that it's another thing you have to check when you want to verify that a program is actually safe.

[...]
> Then that is definitely a bug if that's the case. Someone should probably make a bug report, Walter? If you are still using @system with @safe, then that would still be somewhere you have to look for not memory safe code. @trusted should just mean that someone verified it. @system then would mean no one's verified it to be safe, that doesn't mean you don't have to check it.

@system does indicate that you don't have to check a function. But its trumped by other indicators:

* @system entry points (`main`, static constructors, static initializers) - have to check those.

* Foreign prototypes (`extern (C)` and friends) - have to check those, whether they're @system or @safe or @trusted.

* @system functions that are being called by @trusted ones - have to check those. But I would say that's part of verifying @trusted functions.

Other than that (and maybe other special cases that I've missed), you can safely ignore @system functions, because your @safe program cannot possibly be calling them.
May 24, 2020
On Sunday, 24 May 2020 at 12:14:13 UTC, aliak wrote:
> On Sunday, 24 May 2020 at 11:30:53 UTC, Johannes Loher wrote:
>> On Sunday, 24 May 2020 at 11:25:06 UTC, aliak wrote:
>>> On Sunday, 24 May 2020 at 10:40:11 UTC, Johannes Loher wrote:
>>>> does not work). But I admit that it is still a bit weird to have 2 different defaults.
>>>
>>> Is that any more or less weirder than having functions inferred with different attributes based on context?
>>
>> What exactly are you referring to?
>
> Attribute inference by D, specifically template functions. The attributes are inferred based on context (I don't know the exact algorithm). So a function f(T)(T) when called can maybe be pure, maybe safe, maybe not?

From what I understand, it does not depend on the context but on the template parameters you pass to the template. I agree that it might be a bit confusing at first, but it makes sense if you realize that templates themselves are not functions but something that can generate functions (and other constructs) from compile time parameters. Why shouldn't the attributes of a generated function be able to depend on the parameters being passed to the template? Basically everything else can depend on them, too. Automatically inferring the attributes is just a very convenient way to do that. But yeah, it's not 100% consistent that they are not also inferred for regular functions (some people have been arguing for that).

However, at least for templates, there is a very good reason for the difference (or at least the fact that attributes are inferred for templates): if that was not the case, it would basically be impossible to write generic code that works with all the attribute combinations. However, the very purpose of templates is to enable writing generic code. They wouldn't make that much sense if that capability was strongly limited.

On the other hand, having different @safety defaults for bodiless function declarations and regular faction declarations does not have such a big benefit, especially when considering the fact, that we can have the same defaults but make @safe (default or explicit) bodiless function declarations a compiler error. If we ignore that option for some reason, it would only be dort of a necessity in order to prevent people from shooting them selves in the foot without even noticing. But there is no inherent value in the difference, it doesn't enable anything.

That said, I'd still prefer this variant over what DIP1028 does currently. It's just that I think the other option is even better because it is more consistent.
May 24, 2020
On 5/24/20 6:40 AM, Johannes Loher wrote:
> Steven actually made a proposal regarding creating 2 different manglings for extern(C) functions that are implemented in D. Regardless of which of the solutions  is taken, this could provide the same benefits that we have for extern(D) functions (linker errors if @safety does not match in the mangling). However, it sounds like a complicated solution (in an answer to him, you already mentioned that there might be technical difficulties regarding some object formats, debugging symbols, etc.) and I am not sure it's worth it. It also makes swapping out the libraries a bit weird: if you use an actual C library, it will always link but if swap to a library implemented in D, it only links if the @saftey mangling matches.

I don't think the technical difficulties make it impossible. If you can't point at the same address, point at a noop/jmp before the real address. This isn't hard.

In terms of actual C libraries, only the unsafe symbol will be defined (naturally), so marking the extern(C) function @safe (or assuming it's safe by default) is going to fail to link.

Note that "linker errors if @safety does not match" is not entirely accurate. This is ONLY the case if the prototype is @safe and the implementation is not. In all other cases, the program will link.

Next, I'd say that linker errors are reasonable for C functions -- they are not mangled, so the name is obvious (for extern(C) safe functions I would propose a really simple mangling like _d_safe_functionname). Plus, users who declare extern(C) prototypes already have to deal with linker errors because they have to name their prototypes correctly.

But I don't anticipate anyone taking up this idea (I lack the skills), it already seems DOA based on Walter's position.

-Steve
May 24, 2020
On Sunday, 24 May 2020 at 03:28:25 UTC, Walter Bright wrote:
> I'd like to emphasize:
>
> 1. It is not possible for the compiler to check any declarations where the implementation is not available. Not in D, not in any language. Declaring a declaration safe does not make it safe.
>
> 2. If un-annotated declarations cause a compile time error, it is highly likely the programmer will resort to "greenwashing" - just slapping @safe on it. I've greenwashed code. Atila has. Bruce Eckel has. We've all done it. Sometimes even for good reasons.
>
> 3. Un-annotated declarations are easily detectable in a code review.
>
> [...]

If we were designing a new language from scratch, I would agree 100% with your reasoning.

The problem is that there are un-annotated declarations in existing code that have already been reviewed, committed, and published under the assumption of @system-by-default. Those declarations need to be flagged for re-review in order to avoid introducing silent safety violations to existing D projects.

To address this issue, I'm working on a PR to have dmd emit a diagnostic message when it encounters an un-annotated external function declaration. Feel free to drop by and comment:

https://github.com/dlang/dmd/pull/11176
May 24, 2020
On Sunday, 24 May 2020 at 15:42:54 UTC, ag0aep6g wrote:
> On Sunday, 24 May 2020 at 14:39:50 UTC, Arine wrote:
>> Then that is definitely a bug if that's the case. Someone should probably make a bug report, Walter? If you are still using @system with @safe, then that would still be somewhere you have to look for not memory safe code. @trusted should just mean that someone verified it. @system then would mean no one's verified it to be safe, that doesn't mean you don't have to check it.
>
> @system does indicate that you don't have to check a function. But its trumped by other indicators:
>
> * @system entry points (`main`, static constructors, static initializers) - have to check those.
>
> * Foreign prototypes (`extern (C)` and friends) - have to check those, whether they're @system or @safe or @trusted.
>
> * @system functions that are being called by @trusted ones - have to check those. But I would say that's part of verifying @trusted functions.
>
> Other than that (and maybe other special cases that I've missed), you can safely ignore @system functions, because your @safe program cannot possibly be calling them.

You *have* to check @system code. That's where you are guarantee'd to have memory safety issues. If you are ignoring @system code because you think @safe code doesn't interact with it at all, then that's a problem you are creating for yourself. @system code can still call @safe code, and that @system code that is calling the @safe code can pass invalid information that causes the @safe code to misbehave. You have to check @system for memory safety issues. It seems Walter's comments about only have to review @trusted are being taken too literally.
May 24, 2020
On 24.05.20 19:44, Arine wrote:
> On Sunday, 24 May 2020 at 15:42:54 UTC, ag0aep6g wrote:
[...]
>> @system does indicate that you don't have to check a function. But its trumped by other indicators:
[...]
> You *have* to check @system code. That's where you are guarantee'd to have memory safety issues. If you are ignoring @system code because you think @safe code doesn't interact with it at all, then that's a problem you are creating for yourself. @system code can still call @safe code, and that @system code that is calling the @safe code can pass invalid information that causes the @safe code to misbehave. You have to check @system for memory safety issues.
You're right; it's not accurate that "@system does indicate that you don't have to check a function". That's only true under particular conditions:

When your entry points are @safe and you have already verified all @trusted functions (including their call graphs which might include @system functions), then you can ignore any other @system functions, because your program doesn't call them anyway. But that's true for any function. If your program doesn't call it, you don't need to check it.

So it's not a particularly meaningful thing to say about @system, and that's on me.
May 25, 2020
On 24.05.20 10:55, Walter Bright wrote:
> I infer your position is the idea that putting @trusted on the declarations isn't greenwashing, while @safe is.
> ...

It's only greenwashing if it's misleading. Putting @safe is a lie, putting @trusted is honest.

> I can't see a practical difference between:
> 
> @safe extern (C) void whatevs(parameters);
> @trusted extern (C) void whatevs(parameters);
> 
> Both require that whatevs() provide a safe interface. The difference between them is in the implementation of those functions, not the interface. Since the D compiler cannot see those implementations, they are immaterial to the compiler and user.

Sure, that's the point. Your @safe by default DIP in practice makes certain declarations @trusted by default. @safe is a fine default. @trusted is a horrible default. That's why your DIP claims it is for @safe by default (and not @trusted by default). Except in this one weird special case, where it introduces @trusted by default.