July 25, 2017
On 07/25/2017 02:20 PM, Steven Schveighoffer wrote:
> On 7/25/17 2:14 PM, Andrei Alexandrescu wrote:
>> On 07/25/2017 12:14 PM, Kagamin wrote:
>>> While we're at it, check this: https://github.com/dlang/druntime/blob/master/src/core/stdc/stdio.d#L1047 
>>>
>>
>> That might be a mistake. Is fclose(f); getc(f); defined? -- Andrei
> 
> fclose is not @safe.

Ah, nice. Thanks! -- Andrei


July 25, 2017
On Tuesday, 25 July 2017 at 18:07:06 UTC, Steven Schveighoffer wrote:
> On 7/25/17 12:14 PM, Kagamin wrote:
>> While we're at it, check this: https://github.com/dlang/druntime/blob/master/src/core/stdc/stdio.d#L1047
>
> Looks fine to me. That's not an array of FILE, it's a single pointer.

fgetc cannot be @trusted the same way fclose cannot be @trusted.
If you pass either of them `null` - which constitutes a legal @safe context - the behaviour is undefined, which contradicts @trusted definition:
<Trusted functions are guaranteed by the programmer to not exhibit any undefined behavior if called by a safe function.>
July 25, 2017
On 7/25/17 2:36 PM, Moritz Maxeiner wrote:
> On Tuesday, 25 July 2017 at 18:07:06 UTC, Steven Schveighoffer wrote:
>> On 7/25/17 12:14 PM, Kagamin wrote:
>>> While we're at it, check this: https://github.com/dlang/druntime/blob/master/src/core/stdc/stdio.d#L1047 
>>>
>>
>> Looks fine to me. That's not an array of FILE, it's a single pointer.
> 
> fgetc cannot be @trusted the same way fclose cannot be @trusted.
> If you pass either of them `null` - which constitutes a legal @safe context - the behaviour is undefined, which contradicts @trusted definition:
> <Trusted functions are guaranteed by the programmer to not exhibit any undefined behavior if called by a safe function.>

The behavior is defined. It will crash with a segfault. This is par for the course in @safe land -- dereferencing null pointers is OK.

What is not defined is to fclose a file, and then use that FILE * in any way afterwards without reassigning.

Note that @safe functions don't make any guarantees once you pass in an invalid (except for null) or dangling pointer. However, if you are using only @safe code, you shouldn't be able to make one of these either. Hence fclose is not @safe or @trusted.

The one case where this fails is for a null pointer to a very very large struct that has a way to reference data outside the protected page. I have proposed in the past a way to protect against this, but it didn't gain any traction.

-Steve
July 25, 2017
On Tuesday, 25 July 2017 at 20:16:41 UTC, Steven Schveighoffer wrote:
> On 7/25/17 2:36 PM, Moritz Maxeiner wrote:
>> On Tuesday, 25 July 2017 at 18:07:06 UTC, Steven Schveighoffer wrote:
>>> On 7/25/17 12:14 PM, Kagamin wrote:
>>>> While we're at it, check this: https://github.com/dlang/druntime/blob/master/src/core/stdc/stdio.d#L1047
>>>>
>>>
>>> Looks fine to me. That's not an array of FILE, it's a single pointer.
>> 
>> fgetc cannot be @trusted the same way fclose cannot be @trusted.
>> If you pass either of them `null` - which constitutes a legal @safe context - the behaviour is undefined, which contradicts @trusted definition:
>> <Trusted functions are guaranteed by the programmer to not exhibit any undefined behavior if called by a safe function.>
>
> The behavior is defined. It will crash with a segfault.

In C land that behaviour is a platform (hardware/OS/libc) specific implementation detail (it's what you generally expect to happen, but AFAIK it isn't defined in official ISO/IEC C).

> This is par for the course in @safe land -- dereferencing null pointers  is OK.

In D land we require null dereferences to crash.

That means - from a strict, pedantic standpoint - that while it's OK to attribute D functions with null dereferences as @trusted, the same can't be said for C functions with null dereferences.
July 25, 2017
On 7/25/17 5:23 PM, Moritz Maxeiner wrote:
> On Tuesday, 25 July 2017 at 20:16:41 UTC, Steven Schveighoffer wrote:
>> The behavior is defined. It will crash with a segfault.
> 
> In C land that behaviour is a platform (hardware/OS/libc) specific implementation detail (it's what you generally expect to happen, but AFAIK it isn't defined in official ISO/IEC C).

In cases where C does not crash when dereferencing null, then D would not crash when dereferencing null. D depends on the hardware doing this (Walter has said so many times), so if C doesn't do it, then D won't. So those systems would have to be treated specially, and you'd have to work out your own home-grown mechanism for memory safety.

Optionally, one can redefine @safe *on those platforms* to say all dereferences will be checked against null, and then it could work on such platforms (and of course, you'd have to remove the @trusted marks from low-level C calls).

Either way, we can mark these as @trusted for all current D platforms.

-Steve
July 26, 2017
On 26.07.2017 02:35, Steven Schveighoffer wrote:
> On 7/25/17 5:23 PM, Moritz Maxeiner wrote:
>> On Tuesday, 25 July 2017 at 20:16:41 UTC, Steven Schveighoffer wrote:
>>> The behavior is defined. It will crash with a segfault.
>>
>> In C land that behaviour is a platform (hardware/OS/libc) specific implementation detail (it's what you generally expect to happen, but AFAIK it isn't defined in official ISO/IEC C).
> 
> In cases where C does not crash when dereferencing null, then D would not crash when dereferencing null. D depends on the hardware doing this (Walter has said so many times), so if C doesn't do it, then D won't. So those systems would have to be treated specially, and you'd have to work out your own home-grown mechanism for memory safety.

What Moritz is saying is that the following implementation of fclose is correct according to the C standard:

int fclose(FILE *stream){
    if(stream == NULL){
        go_wild_and_corrupt_all_the_memory();
    }else{
        actually_close_the_file(stream);
    }
}
July 26, 2017
On 26.07.2017 02:45, Timon Gehr would have liked to have written:
> ...
> What Moritz is saying is that the following implementation of fclose is correct according to the C standard:
> 
> int fclose(FILE *stream){
>      if(stream == NULL){
>          return go_wild_and_corrupt_all_the_memory();
>      }else{
>          return actually_close_the_file(stream);
>      }
> }

(Forgot the returns.)
July 25, 2017
On 07/25/2017 08:45 PM, Timon Gehr wrote:
> On 26.07.2017 02:35, Steven Schveighoffer wrote:
>> On 7/25/17 5:23 PM, Moritz Maxeiner wrote:
>>> On Tuesday, 25 July 2017 at 20:16:41 UTC, Steven Schveighoffer wrote:
>>>> The behavior is defined. It will crash with a segfault.
>>>
>>> In C land that behaviour is a platform (hardware/OS/libc) specific implementation detail (it's what you generally expect to happen, but AFAIK it isn't defined in official ISO/IEC C).
>>
>> In cases where C does not crash when dereferencing null, then D would not crash when dereferencing null. D depends on the hardware doing this (Walter has said so many times), so if C doesn't do it, then D won't. So those systems would have to be treated specially, and you'd have to work out your own home-grown mechanism for memory safety.
> 
> What Moritz is saying is that the following implementation of fclose is correct according to the C standard:
> 
> int fclose(FILE *stream){
>      if(stream == NULL){
>          go_wild_and_corrupt_all_the_memory();
>      }else{
>          actually_close_the_file(stream);
>      }
> }

I'd think that would be the case, but failed to find a fgetc implementation that mentions it's undefined for a null FILE*. Is there a link? Thx. -- Andrei
July 25, 2017
On 7/25/17 8:45 PM, Timon Gehr wrote:
> On 26.07.2017 02:35, Steven Schveighoffer wrote:
>> On 7/25/17 5:23 PM, Moritz Maxeiner wrote:
>>> On Tuesday, 25 July 2017 at 20:16:41 UTC, Steven Schveighoffer wrote:
>>>> The behavior is defined. It will crash with a segfault.
>>>
>>> In C land that behaviour is a platform (hardware/OS/libc) specific implementation detail (it's what you generally expect to happen, but AFAIK it isn't defined in official ISO/IEC C).
>>
>> In cases where C does not crash when dereferencing null, then D would not crash when dereferencing null. D depends on the hardware doing this (Walter has said so many times), so if C doesn't do it, then D won't. So those systems would have to be treated specially, and you'd have to work out your own home-grown mechanism for memory safety.
> 
> What Moritz is saying is that the following implementation of fclose is correct according to the C standard:
> 
> int fclose(FILE *stream){
>      if(stream == NULL){
>          go_wild_and_corrupt_all_the_memory();
>      }else{
>          actually_close_the_file(stream);
>      }
> }

I think we can correctly assume no fclose implementations exist that do anything but access data pointed at by stream. Which means a segfault on every platform we support.

On platforms that may not segfault, you'd be on your own.

In other words, I think we can assume for any C functions that are passed pointers that dereference those pointers, passing null is safely going to segfault.

Likewise, because D depends on hardware flagging of dereferencing null as a segfault, any platforms that *don't* have that for C also won't have it for D. And then @safe doesn't even work in D code either.

As we have good support for different prototypes for different platforms, we could potentially unmark those as @trusted in those cases.

-Steve
July 25, 2017
On 7/25/2017 8:26 AM, Andrei Alexandrescu wrote:
> A suite of safe wrappers on OS primitives might be useful.

The idea of fixing the operating system interface(s) has come up now and then. I've tried to discourage that on the following grounds:


* We are not in the operating system business.

* Operating system APIs grow like weeds. We'd set ourselves an impossible task.

* It's a huge job simply to provide accurate declarations for the APIs.

* We'd have to write our own documentation for the operating system APIs. It's hard enough writing such for Phobos.

* A lot are fundamentally unfixable, like free() and strlen().

* The API import files should be focused solely on direct access to the APIs, not adding a translation layer. The user of them will expect this.

* We already have safe wrappers for the commonly used APIs. For read(), there is std.stdio.


It is worthwhile, however, to augment the APIs with the appropriate attributes like @nogc, scope, nothrow, @safe (for the ones that are), etc.