Jump to page: 1 24  
Page
Thread overview
DIP1000 scope inference
Oct 25, 2022
Paul Backus
Oct 25, 2022
rikki cattermole
Oct 25, 2022
rikki cattermole
Oct 25, 2022
Paul Backus
Oct 25, 2022
Paul Backus
Oct 25, 2022
Quirin Schroll
Oct 25, 2022
Paul Backus
Oct 26, 2022
Walter Bright
Oct 26, 2022
rikki cattermole
Oct 26, 2022
Nick Treleaven
Oct 26, 2022
German Diago
Oct 26, 2022
Salih Dincer
Oct 26, 2022
tsbockman
Oct 27, 2022
Quirin Schroll
Oct 27, 2022
ag0aep6g
Oct 27, 2022
tsbockman
Oct 26, 2022
Dukc
Oct 27, 2022
Walter Bright
Re: DIP1000 scope inference proposal
Oct 27, 2022
Walter Bright
Oct 27, 2022
Walter Bright
Oct 27, 2022
German Diago
Oct 27, 2022
Quirin Schroll
Oct 27, 2022
Walter Bright
Oct 27, 2022
Dukc
October 24, 2022

Deprecation messages due to dip1000's imminent arrival are scheduled to happen on the next release of the compiler. I have some concerns about scope inference, and wanted to find out the answers here.

Let's say I have a scope array like this in a @trusted function:

int[] mkarr() @trusted {
   scope arr = [1, 2, 3];
   return arr;
}

Clearly, this is a bad idea. The compiler might put the array data actually on the stack (right?), and therefore return stack data when it shouldn't.

But what if you don't mark it scope? Let's try something here:

int[] mkarr() @safe {
   int[3] arr = [1, 2, 3];
   int[] other = arr[];

   other = [4, 5, 6];
   return other;
}

by the time other is returned, it should no longer be pointing at stack data. But because it was originally assigned to the static array, other is inferred as scope (as is proven by the code above failing to compile with dip1000 enabled with an error about returning scope data).

Let's switch that back to @trusted, and now it does compile, even with dip1000. BUT, let me ask this very crucial question:

Does the inferred scope make it so that the compiler is allowed to allocate the [4, 5, 6] literal on the stack? Keep in mind that I never put scope here, this is something the compiler did on its own.

In a @trusted function today, without dip1000, the above is perfectly reasonable and not invalid. Will dip1000 make it corrupt memory?

-Steve

October 25, 2022

On Tuesday, 25 October 2022 at 01:35:28 UTC, Steven Schveighoffer wrote:

>

Does the inferred scope make it so that the compiler is allowed to allocate the [4, 5, 6] literal on the stack? Keep in mind that I never put scope here, this is something the compiler did on its own.

No, it does not. This capability was added only for array literals, and only for variable initialization:

DMD PR: https://github.com/dlang/dmd/pull/14562
Spec PR (pending): https://github.com/dlang/dlang.org/pull/3442

However, this thread raises an important point: changing the way existing language constructs allocate memory in the presence of scope may cause @trusted code which relied on the original behavior to become unsound.

For example, the @trusted function below is memory safe when using the current compiler release, but will become unsafe when compiled with DMD 2.101:

@trusted int[] example()
{
    scope example = [1, 2, 3];
    return example;
}

The worst part is that the potential memory corruption is introduced silently. Users who upgrade to DMD 2.101 will have no idea that the ground has shifted beneath their feet until their code invokes UB at runtime.

October 25, 2022
On 25/10/2022 3:09 PM, Paul Backus wrote:
> For example, the `@trusted` function below is memory safe when using the current compiler release, but will become unsafe when compiled with DMD 2.101:
> 
> ```d
> @trusted int[] example()
> {
>      scope example = [1, 2, 3];
>      return example;
> }
> ```

Does this also apply to @safe?
October 25, 2022
On 25/10/2022 3:13 PM, rikki cattermole wrote:
> On 25/10/2022 3:09 PM, Paul Backus wrote:
>> For example, the `@trusted` function below is memory safe when using the current compiler release, but will become unsafe when compiled with DMD 2.101:
>>
>> ```d
>> @trusted int[] example()
>> {
>>      scope example = [1, 2, 3];
>>      return example;
>> }
>> ```
> 
> Does this also apply to @safe?

Apparently.

I can't find any checks in the PR.

REVERT REVERT REVERT (or ya know add the check for @safe).

#SuddenlyWorried lol
October 25, 2022

On Tuesday, 25 October 2022 at 02:13:20 UTC, rikki cattermole wrote:

>

On 25/10/2022 3:09 PM, Paul Backus wrote:

>

For example, the @trusted function below is memory safe when using the current compiler release, but will become unsafe when compiled with DMD 2.101:

@trusted int[] example()
{
     scope example = [1, 2, 3];
     return example;
}

Does this also apply to @safe?

No, because you are not allowed to return a scope variable in @safe code, even if you happen to know that it points to a heap allocation.

October 24, 2022

On 10/24/22 10:09 PM, Paul Backus wrote:

>

On Tuesday, 25 October 2022 at 01:35:28 UTC, Steven Schveighoffer wrote:

>

Does the inferred scope make it so that the compiler is allowed to allocate the [4, 5, 6] literal on the stack? Keep in mind that I never put scope here, this is something the compiler did on its own.

No, it does not. This capability was added only for array literals, and only for variable initialization:

DMD PR: https://github.com/dlang/dmd/pull/14562
Spec PR (pending): https://github.com/dlang/dlang.org/pull/3442

OK, what about this?

int[] mkarr() @trusted {
    int[3] arr = [1, 2, 3];
    int[] other = [4, 5, 6];

    auto foo = other;
    other = arr[];
    return foo;
}

other is inferred as scope (along with foo), because it touches arr[] later (but after it was pointing at what should have been heap memory). So does that count as possible for stack allocation, or is it still heap allocated?

-Steve

October 25, 2022

On Tuesday, 25 October 2022 at 02:38:02 UTC, Steven Schveighoffer wrote:

>

OK, what about this?

int[] mkarr() @trusted {
    int[3] arr = [1, 2, 3];
    int[] other = [4, 5, 6];

    auto foo = other;
    other = arr[];
    return foo;
}

other is inferred as scope (along with foo), because it touches arr[] later (but after it was pointing at what should have been heap memory). So does that count as possible for stack allocation, or is it still heap allocated?

When I compile the above with @safe and -preview=dip1000, I get

Error: reference to local variable `arr` assigned to non-scope `other`

...using both DMD 2.100.2 and DMD master. So scope is not actually being inferred here, and the array is allocated on the heap.

My expectation is that scope will probably never be inferred for other, because doing multi-step inference like this requires dataflow analysis in the general case, which is something Walter wants to avoid (see discussion in issue 20674). So I don't think you have anything to worry about.

Still, this is a good illustration of how silently changing the rules on people can have unintended consequences. If Walter ever does consider adding dataflow analysis, overly-aggressive "optimizations" like these could easily become obstacles in the way of that goal.

October 25, 2022

On 10/24/22 10:59 PM, Paul Backus wrote:

>

On Tuesday, 25 October 2022 at 02:38:02 UTC, Steven Schveighoffer wrote:

>

OK, what about this?

int[] mkarr() @trusted {
    int[3] arr = [1, 2, 3];
    int[] other = [4, 5, 6];

    auto foo = other;
    other = arr[];
    return foo;
}

other is inferred as scope (along with foo), because it touches arr[] later (but after it was pointing at what should have been heap memory). So does that count as possible for stack allocation, or is it still heap allocated?

When I compile the above with @safe and -preview=dip1000, I get

    Error: reference to local variable arr assigned to non-scope other

OK, I misread the error here, it's the same on run.dlang.io. But we did just go through an exercise where a struct not labeled scope is inferred scope not because of its declaration, but because of later things done with it. It doesn't seem to be the case here.

>

...using both DMD 2.100.2 and DMD master. So scope is not actually being inferred here, and the array is allocated on the heap.

My expectation is that scope will probably never be inferred for other, because doing multi-step inference like this requires dataflow analysis in the general case, which is something Walter wants to avoid (see discussion in [issue 20674][1]). So I don't think you have anything to worry about.

My biggest concern is that this inference takes priority over what is actually written, and then can cause memory problems to occur in code that seemingly reads like it shouldn't cause memory problems.

I'm trying to find a hole because I'm worried about that hole showing up without intention later (especially with the way the compiler can inline and rewrite code for optimization). The compiler doing things that are not checkable (I know of no way to introspect that something is scope inferred), hard to describe, and impossible to prevent makes things uncomfortable. Especially if the compiler might make disastrous decisions based on that inference.

It would be relieving to have some rule that says "any data inferred scope inside a @system or @trusted context without explicitly being declared scope shall not result in memory allocations hoisting to the stack". I can deal, begrudgingly, with compiler errors that are misguided. I can't deal with memory errors caused by the compiler knowing better than me.

-Steve

October 25, 2022

On 10/25/22 9:44 AM, Steven Schveighoffer wrote:

>

But we did just go through an exercise where a struct not labeled scope is inferred scope not because of its declaration, but because of later things done with it.

It's very curious. I can't get any indication that the struct is inferred scope except by throwing an exception contained in it. If I declare the function @safe, it won't let me assign the scope variable to the struct member. If I mark it as @trusted, that succeeds, but then it won't let me throw the exception out of the struct because it says the struct is scope.

Again, with no way to tell whether scope is inferred, it's hard to judge.

-Steve

October 25, 2022

On Tuesday, 25 October 2022 at 02:09:02 UTC, Paul Backus wrote:

>

For example, the @trusted function below is memory safe when using the current compiler release, but will become unsafe when compiled with DMD 2.101:

@trusted int[] example()
{
    scope example = [1, 2, 3];
    return example;
}

The worst part is that the potential memory corruption is introduced silently. Users who upgrade to DMD 2.101 will have no idea that the ground has shifted beneath their feet until their code invokes UB at runtime.

Asking curiously, wasn’t the function UB before, but the behavior changed?

« First   ‹ Prev
1 2 3 4