February 04, 2019
On Monday, 4 February 2019 at 01:39:20 UTC, Walter Bright wrote:
> https://issues.dlang.org/show_bug.cgi?id=19646

Hi Walter,

Thanks for the feedback. Given how terse the forum post is, I'm not sure how to interpret it. Are you suggesting this was always a bug, or are you changing D's @safe design to encompass expressions outside functions? If this was always a bug, then perhaps we should also add a bug report to include the fact that @safe is documented just as "Safe Functions", which could be misleading?

In any case, it's great to see this moving forward. I thought I was just asking a silly question because I was too tired!

Cheers,
Luís
February 04, 2019
I listed it as a "normal" bug rather than "enhancement". I believe that answers your question.
February 06, 2019
On Monday, 4 February 2019 at 20:36:23 UTC, Walter Bright wrote:
> I listed it as a "normal" bug rather than "enhancement". I believe that answers your question.

Are you confirming that the documentation in https://dlang.org/spec/memory-safe-d.html will be updated to refer to "safe code" instead of just safe functions?

Anyway, thinking about it, I think fixing this elegantly might require coming up with new semantics. How do you make sure that no @system code is called in your project without manually checking every single variable declaration in your dependencies? Checking that functions are @safe is easy because @system is infectious, so you only have to check for @trusted code, but there's no way to make global variable safety infectious, is there?
February 06, 2019
On 2/6/2019 2:02 AM, Olivier FAURE wrote:
> On Monday, 4 February 2019 at 20:36:23 UTC, Walter Bright wrote:
>> I listed it as a "normal" bug rather than "enhancement". I believe that answers your question.
> 
> Are you confirming that the documentation in https://dlang.org/spec/memory-safe-d.html will be updated to refer to "safe code" instead of just safe functions?
> 
> Anyway, thinking about it, I think fixing this elegantly might require coming up with new semantics. How do you make sure that no @system code is called in your project without manually checking every single variable declaration in your dependencies? Checking that functions are @safe is easy because @system is infectious, so you only have to check for @trusted code, but there's no way to make global variable safety infectious, is there?

I'm not seeing a problem with it.
February 07, 2019
On Wednesday, 6 February 2019 at 10:02:18 UTC, Olivier FAURE wrote:
>
> Anyway, thinking about it, I think fixing this elegantly might require coming up with new semantics. How do you make sure that no @system code is called in your project without manually checking every single variable declaration in your dependencies?

Isn't it enough that the same constraints have been in place when compiling the dependencies, if they are flagged as @safe? (Of course excepting @trusted code, that's always about trusting the human author's word that their unsafe code has no unsafe consequences; same as if you link to a C library.)

If @safe can be circumvented (in global initializations) then it's no longer a promise of safety, with all the security consequences, but rather an empty attribute.

If safety is indeed to be a "big thing" in computer science,[1] this is just a vulnerability bug that needs to be fixed.

[1] https://www.reddit.com/r/cpp/comments/6b4xrc/walter_bright_believes_memory_safety_will_kill_c/
February 07, 2019
On Thursday, 7 February 2019 at 01:04:44 UTC, Walter Bright wrote:
> I'm not seeing a problem with it.

Let me put this another way:

If you want to make sure only @safe code is ever run, you can slap @safe on your main function, and the program will only compile if every single other function is either @safe or @trusted.

However, @system variable declarations aren't infectious so far (and making them infectious would be a breaking change), which means the following code:

    import std.stdio;

    immutable int x = 1;
    int* y = cast(int*)&x;

    void main() @safe
    {
        *y = 2;
        writeln(x);
        writeln(*(&x));
        writeln(*y);
    }

compiles even though main(), a @safe function, ends up doing something unsafe (mutating a value declared as immutable).

One way to fix this would be to forbid using @system global variables in @safe functions, but this would definitely be a breaking change, unless global variable safety is determined by the compiler by default (which is its own can of worms).
February 07, 2019
On Thursday, 7 February 2019 at 17:33:01 UTC, Olivier FAURE wrote:
> [snip]
>
> One way to fix this would be to forbid using @system global variables in @safe functions, but this would definitely be a breaking change, unless global variable safety is determined by the compiler by default (which is its own can of worms).

Inferring safety of global variables does seem tricky. You have to check every place it's used. I don't see what you could do other than forbid @system global variables in @safe functions. What your example shows is that it's broken already. At least adding the ability to mark them @safe is a step in making it possible to resolve the issue.
February 07, 2019
On Thursday, 7 February 2019 at 17:33:01 UTC, Olivier FAURE wrote:
> One way to fix this would be to forbid using @system global variables in @safe functions, but this would definitely be a breaking change, unless global variable safety is determined by the compiler by default (which is its own can of worms).

You are discussing evaluating the safety to the variable. Why not evaluate the safety of the variable's initialization expression? That is, @safe would always refer to code, just not necessarily functions.
February 07, 2019
On Thursday, February 7, 2019 3:33:45 PM MST Luís Marques via Digitalmars-d wrote:
> On Thursday, 7 February 2019 at 17:33:01 UTC, Olivier FAURE wrote:
> > One way to fix this would be to forbid using @system global variables in @safe functions, but this would definitely be a breaking change, unless global variable safety is determined by the compiler by default (which is its own can of worms).
>
> You are discussing evaluating the safety to the variable. Why not evaluate the safety of the variable's initialization expression? That is, @safe would always refer to code, just not necessarily functions.

Indeed. You don't check variables for @safety. You check code that runs. Initialization expressions are a bit weird in that they're a way for code that is run to exist outside of a function. In all other cases, any code that is run (as opposed to being a declaration) is inside a function of some kind. So, what we have here is simply a case of the small amount of runnable code that doesn't exist in a function being missed in the design of @safe. If you just think of all initialization expressions as being essentially lambdas that declared and called in place, then all that we need is for those lambdas to be marked as @safe when the section of code that they're in is marked with @safe.

It is possible that fixing this will cause code breakage, but it's likely to be rare, since you aren't normally going to get code like the original example had where an initialization expression outside of a function was taking an address. That code probably wasn't even legal until fairly recently, because it didn't used to be possible to directly initialize pointers when their value had to be known at compile time. As I understand it, being able to do something like

int* i = new int(42);

with a variable outside of a functions is a fairly recent improvement.

Regardless, I think that it's pretty clear that we need to fix this, and my inclination is to argue that initialization expressions should just be treated as if they were lowered to lambdas that were immediately called. e.g.

int* y = cast(int*) &x;

becomes something like

int* y = () { return cast(int*) &x; }();

Then when you have something like

@safe pure:
int* y = cast(int*) &x;

it's quite clear what should happen. But I don't know if there are any issues with that particular way of spec-ing it, and it's not like you want to _actually_ add any lambdas - though in most cases lambdas like that should be optimized out anywhere they're used anyway (though IIRC, that doesn't necessarily happen with dmd at present). However, it does make the attribute portion of things very straightforward.

- Jonathan M Davis




February 08, 2019
On 07.02.19 23:33, Luís Marques wrote:
> You are discussing evaluating the safety to the variable. Why not evaluate the safety of the variable's initialization expression? That is, @safe would always refer to code, just not necessarily functions.

Evaluating the safety of initializers without somehow marking the variables as @safe/@system would fix the issue you brought up, but not the one that Olivier is talking about.

His point is that you would still have to check manually if the globals you're using are safe or not. The compiler wouldn't see a difference between a safely initialized global and an unsafely initialized one.

But @safe is supposed to eliminate that kind of manual verification.

----
@safe:
int* x = /* ... this initializer would be checked for safety ... */;
void main()
{
    *x = 7; /* Guaranteed to be safe. */
    *y = 7; /* Might exhibit undefined behavior. */
}
@system:
int* y = /* ... this initializer would not be checked ... */;
----

If we'd apply the attributes to the variables, and forbid using @system variables in @safe code, then `*y = 7;` would be rejected.