April 03, 2020
On Friday, April 3, 2020 3:50:39 AM MDT Walter Bright via Digitalmars-d wrote:
> On 3/27/2020 5:32 AM, aliak wrote:
> > So my understanding is if extern (C) is applied on a function declaration, then an annotation is explicitly required?
>
> No. Without an explicit annotation, it will be the default (@safe).

Which is really, really bad, because it means that the compiler is basically slapping @trusted on all extern(C) declarations. It's introducing a hole in @safe, and there is absolutely no reason to do so. IMHO, _nothing_ should ever be marked with @safe (explicitly or implicitly) unless the compiler has actually verified that it's memory safe. As such, non-extern(D) declarations should either be @system or @trusted, and @trusted needs to be done by the programmer, because it indicates that the programmer is saying that they verified that it was memory safe. It makes no sense for the compiler to ever treat extern(C) declarations as if they were marked with @safe. For the compiler to treat them as @safe means that it's is lying about what it's verified.

- Jonathan M Davis



April 03, 2020
On Thursday, 26 March 2020 at 02:58:26 UTC, Walter Bright wrote:
> @safe by default is going to improve your code.
This swayed my opinion about this dip. Now I am for it but please try to limit breaking changes to once a year.
April 03, 2020
On 4/3/2020 8:39 AM, Jonathan M Davis wrote:
> [...]

Yes, this has all been brought up and discussed before in this thread.
April 03, 2020
On 3/26/2020 9:02 AM, Atila Neves wrote:
> extern(C) doesn't necessarily mean the code is written in C and can't be verified by the compiler. It might just be written in D (or C++, or Rust, or...).

That's right. It means "use a C function call interface". For example, one might be writing a function meant to be called from C code.
April 03, 2020
On 4/3/20 4:22 PM, Walter Bright wrote:
> On 3/26/2020 9:02 AM, Atila Neves wrote:
>> extern(C) doesn't necessarily mean the code is written in C and can't be verified by the compiler. It might just be written in D (or C++, or Rust, or...).
> 
> That's right. It means "use a C function call interface". For example, one might be writing a function meant to be called from C code.

I want to make sure you understand that we are not talking about extern(C) functions that are written in D.

extern(C) void foo() {
import std.stdio;
writeln("hello world!");

}

can absolutely be assumed @safe. It has an implementation. The compiler can verify right there that it works. This should fail to compile in that case:

extern(C) void foo() {
*cast(int *)0xdeadbeef = 5;
}

But what should absolutely not compile is:

extern(C) int free(void *);

void foo(int *ptr) // now inferred @safe
{
   free(ptr);
}

Notice I didn't import core.stdc.stdlib. You cannot fix this code, it will break. Silently. Anything that depends on it will also break, cascading the error all the way through D code. Things that were @system will magically become @safe even though they are not. @safe will become a cruel joke.

There are multiple options:

1. extern(C) (or really anything without @safe name mangling) without implementation is assumed @system.
2. extern(C) (et. al.) without implementation must be marked @system, @safe, or @trusted explicitly.
3. option 1 or 2 PLUS such functions with implementation follow the same rules (for consistency).

There is no grey area -- @safe is COMPLETELY destroyed if we assume all unmarked extern(C) prototypes are @safe.

Even if the function is written in D, the fact that the prototype marking could be forgotten is going to cause huge issues. How many times has someone needed a function from druntime that's not public but is extern(C) and just threw in a prototype? All those would now be @safe!

The fact that we cannot control where/how people define their prototypes means we have to be firm on this. They need to opt-in to @safe with extern(C), it cannot be default.

-Steve
April 03, 2020
On Friday, 3 April 2020 at 09:50:39 UTC, Walter Bright wrote:
> On 3/27/2020 5:32 AM, aliak wrote:
>> So my understanding is if extern (C) is applied on a function declaration, then an annotation is explicitly required?
>
> No. Without an explicit annotation, it will be the default (@safe).

That sounds risky, and is exactly the opposite of how Rust handles extern functions:
https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#using-extern-functions-to-call-external-code

"Functions declared within `extern` blocks are always unsafe to call from Rust code. The reason is that other languages don’t enforce Rust’s rules and guarantees, and Rust can’t check them, so responsibility falls on the programmer to ensure safety."

Now I'm not suggesting that D should do it because Rust does ;-)  But their reasoning seems sound, and I don't see an obvious reason for assuming @safe-ty of functions that the D compiler cannot verify.
April 03, 2020
On Fri, Apr 03, 2020 at 05:06:28PM -0400, Steven Schveighoffer via Digitalmars-d wrote: [...]
> extern(C) int free(void *);
> 
> void foo(int *ptr) // now inferred @safe
> {
>    free(ptr);
> }
[...]

To drive home the point even more:

	// ----- mymod.d -----
	extern(C) void dealloc(void* p) @system {
		import std.stdc.stdio : free;
		free(p);
	}


	// ----- main.d -----
	// N.B.: does not import mymod directly
	extern(C) void dealloc(void* p); // assumed @safe by proposed rules
	void main() @safe {
		void* p;
		dealloc(p);	// oops
	}

Just because an extern(C) function is written in D, guarantees NOTHING, because the mangling name does not encode @safety.  Notice above that the prototype is assumed @safe, but this does not match the actual D implementation, which is @system.  However, this will not be caught by the linker because of extern(C): 'dealloc' will bind to the @system function even though main() thought it was @safe.

So yes, if this DIP gets implemented as-is, @safe becomes a joke, and we might as well stop playing now.


T

-- 
Never trust an operating system you don't have source for! -- Martin Schulze
April 03, 2020
On 4/3/20 5:06 PM, Steven Schveighoffer wrote:
> Even if the function is written in D, the fact that the prototype marking could be forgotten is going to cause huge issues.

e.g. I don't want to see the day subtle bugs due to forgetting to go and mark this prototype as @system crop up:

https://github.com/dlang/phobos/blob/cd2b75560b089beaaf757011d308894dbe44dc93/std/stdio.d#L261

Consider it. On systems with HAS_GETDELIM, stdin.byLine which was previously inferred @system now is inferred @safe. All because of stuff like this was missed. What a fun bug to track down and fix.

-Steve
April 03, 2020
On Friday, April 3, 2020 2:18:54 PM MDT Walter Bright via Digitalmars-d wrote:
> On 4/3/2020 8:39 AM, Jonathan M Davis wrote:
> > [...]
>
> Yes, this has all been brought up and discussed before in this thread.

Well, I don't think that you've actually acknowledged any of it, and what responses you do have make it seem like you're not aware of it or are ignoring it.

- Jonathan M Davis



April 03, 2020
On 4/3/2020 2:06 PM, Steven Schveighoffer wrote:
> I want to make sure you understand that we are not talking about extern(C) functions that are written in D.

I understand totally what you are talking about.


> But what should absolutely not compile is:
> 
> extern(C) int free(void *);
> 
> void foo(int *ptr) // now inferred @safe
> {
>     free(ptr);
> }

I understand your proposal. You want C functions without bodies to be @system.


> The fact that we cannot control where/how people define their prototypes means we have to be firm on this. They need to opt-in to @safe with extern(C), it cannot be default.

On the other hand, special cases like this tend to cause unexpected problems in the future. Experience pretty much guarantees it. It's likely to be tricky to implement as well.

People remember simple rules. They don't remember rules with odd exceptions to them, that always winds up with trouble and bug reports. Simple rules applied evenly lead to a compiler that works and is reliable. I'm afraid the weight of all the special rules will crush D.

Now, as to what to do. I spent a few minutes and added `@system:` in to the C headers in druntime for windows, posix, and linux. Done. I hope someone else will do it for freebsd, etc., if not I'll go do that to.

> is going to cause huge issues.

I doubt that for the simple reason that @system by default has not caused huge issues.

The rule is simple:

"For a D module with a bunch of C declarations in it, start it with `@system:`."

It's not a hard rule to check. It's one line. D's C interface has always relied on the user to get right. Recall that C doesn't do any name mangling at all:

  ----- mylib.di
    extern (C) int addOne(int i);

  ----- mylib.c
    double addOne(double d) { return d + 1; }


That'll link fine and yet fail at runtime. Oops! Calling it @system will not help at all. If the C implementation is bad, there's not a damn thing D can do about it, with or without @system. It's always going to be this way.