www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - 'scope' confusion

reply Andy <andy.pj.hanson gmail.com> writes:
In the following code, I can pass a non-`scope` argument to a 
function expecting a `scope` parameter, but I can't pass a 
`scope` argument. That seems backwards?

```
 safe  nogc pure nothrow:

void main() {
	string[2] notScope = ["one", "two"];
	f(notScope);

	scope string[2] yesScope = ["one", "two"];
	f(yesScope);
}

void f(scope string[] expectingScope) {}
```

Output (of `dmd a.d -preview=dip1000`):

```
a.d(8): Error: cannot take address of `scope` local `yesScope` in 
` safe` function `main`
a.d(8): Error: cannot cast expression `yesScope` of type 
`string[2]` to `string[]`
```

The error only happens in dmd 2.099.0 -- it didn't happen in 
2.098.1 .

------

There is another even simpler case that doesn't work in either 
version:

```
 safe  nogc pure nothrow:

void main() {
	scope string[2] xs = ["one", "two"];
	foreach (scope immutable string x; xs) {}
}
```

Output (of `dmd a.d -preview=dip1000`):

```
a.d(5): Error: cannot take address of `scope` local `xs` in 
` safe` function `main`
```
Apr 07 2022
next sibling parent reply Dukc <ajieskola gmail.com> writes:
On Friday, 8 April 2022 at 05:52:09 UTC, Andy wrote:
 In the following code, I can pass a non-`scope` argument to a 
 function expecting a `scope` parameter, but I can't pass a 
 `scope` argument. That seems backwards?
I was already writing "these both should pass", but then I noticed what's wrong. The scope at `f` means you cannot escape the outer array. But on `yesScope`, it means that you cannot escape the strings! In other words, this fails for the same reason that this would fail: ``` safe void main() { scope immutable(char)[] a = "one"; scope immutable(char)[] b = "two"; auto array = [a, b]; } ``` It fails, because D does not have a concept of an array holding scope variables (or a pointer pointing to a scope variable). Only the outernmost array or pointer can be `scope`. That's with dynamic arrays. However, with static arrays and structs, `scope` means that each member of them is `scope`. Nothing else would make sense, because these contain their data in the variable itself. Assign a static array or a struct to new one, and it's contents becomes and independent copies of the original. Data escaping them is no more issue than `5` escaping an `int`.
Apr 08 2022
parent reply Andy <andy.pj.hanson gmail.com> writes:
On Friday, 8 April 2022 at 07:02:57 UTC, Dukc wrote:
 On Friday, 8 April 2022 at 05:52:09 UTC, Andy wrote:
 In the following code, I can pass a non-`scope` argument to a 
 function expecting a `scope` parameter, but I can't pass a 
 `scope` argument. That seems backwards?
I was already writing "these both should pass", but then I noticed what's wrong. The scope at `f` means you cannot escape the outer array. But on `yesScope`, it means that you cannot escape the strings! In other words, this fails for the same reason that this would fail: ``` safe void main() { scope immutable(char)[] a = "one"; scope immutable(char)[] b = "two"; auto array = [a, b]; } ``` It fails, because D does not have a concept of an array holding scope variables (or a pointer pointing to a scope variable). Only the outernmost array or pointer can be `scope`. That's with dynamic arrays. However, with static arrays and structs, `scope` means that each member of them is `scope`. Nothing else would make sense, because these contain their data in the variable itself. Assign a static array or a struct to new one, and it's contents becomes and independent copies of the original. Data escaping them is no more issue than `5` escaping an `int`.
Is there any way to do this safely? ``` safe nogc pure nothrow: void f() { scope immutable Node a = Node(1, null); g(a); } void g(scope immutable Node a) { scope immutable Node b = Node(2, &a); } struct Node { immutable int head; immutable Node* tail; } ``` I would think that getting `&a` in that context ought to be safe because it's only used in a `scope` variable, but apparently not. I'm using `immnutable` here because I know `a.tail = b` would be wrong; they're both `scope` but `a` is a parameter and thus outlives `b`. To put it more simply, why doesn't this work? ``` safe nogc pure nothrow: void f() { int x = 0; scope int* y = &x; } ```
Apr 14 2022
parent reply Paul Backus <snarwin gmail.com> writes:
On Friday, 15 April 2022 at 05:42:42 UTC, Andy wrote:
 Is there any way to do this safely?

 ```
  safe  nogc pure nothrow:

 void f() {
 	scope immutable Node a = Node(1, null);
 	g(a);
 }

 void g(scope immutable Node a) {
 	scope immutable Node b = Node(2, &a);
 }

 struct Node {
 	immutable int head;
 	immutable Node* tail;
 }
 ```
No, because `scope` isn't transitive. If the above example were allowed, then `b.tail.tail` would not be `scope`. Here's a simplified version, which makes the levels of indirection more obvious: ```d safe nogc pure nothrow: void f() { scope immutable int* a = null; g(a); } void g(scope immutable int* a) { scope immutable int** b = &a; // error } ```
 To put it more simply, why doesn't this work?

 ```d
  safe  nogc pure nothrow:

 void f() {
 	int x = 0;
 	scope int* y = &x;
 }
 ```
That example does work. It's only when you get to two levels of indirection that it fails; for example: ```d safe nogc pure nothrow: void f() { int x = 0; scope int* y = &x; scope int** z = &y; // error } ```
Apr 14 2022
parent reply Dom DiSc <dominikus scherkl.de> writes:
On Friday, 15 April 2022 at 06:12:06 UTC, Paul Backus wrote:
 No, because `scope` isn't transitive. [...]
 That example does work. It's only when you get to two levels of 
 indirection that it fails; for example:

 ```d
  safe  nogc pure nothrow:

 void f() {
 	int x = 0;
 	scope int* y = &x;
 	scope int** z = &y; // error
 }
 ```
If it's not transitive, perhaps we need something to make it explicit: ```d void f() { int x = 0; scope int* y = &x; scope int* scope* z = &y; // like const* in C++ } ```
Apr 15 2022
parent Loara <loara noreply.com> writes:
On Friday, 15 April 2022 at 10:31:31 UTC, Dom DiSc wrote:
 On Friday, 15 April 2022 at 06:12:06 UTC, Paul Backus wrote:
 No, because `scope` isn't transitive. [...]
 That example does work. It's only when you get to two levels 
 of indirection that it fails; for example:

 ```d
  safe  nogc pure nothrow:

 void f() {
 	int x = 0;
 	scope int* y = &x;
 	scope int** z = &y; // error
 }
 ```
If it's not transitive, perhaps we need something to make it explicit: ```d void f() { int x = 0; scope int* y = &x; scope int* scope* z = &y; // like const* in C++ } ```
Actually the documentation is a bit confusing about `scope` storage class and which operations are admitted for scoped variables/function parameters. I think the role of `scope` inside D is not well defined at this moment (and the original DIP1000 was written even worse) so until this feature will be fixed we should avoid use it in released cose (and use `const` instead of `in`)
Apr 15 2022
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
The trouble here is the scope array is a static array. This means the scope 
applies to the elements of the static array.

But when passing it to the function, the static array is converted to a dynamic 
array. A dynamic array is a pointer to the array's elements.

The scope protections are not transitive, so a scope dynamic array does not 
protect its elements as scope, hence the errors you're seeing.
Apr 15 2022
parent Salih Dincer <salihdb hotmail.com> writes:
On Friday, 15 April 2022 at 20:43:19 UTC, Walter Bright wrote:
 The trouble here is the scope array is a static array. This 
 means the scope applies to the elements of the static array.

 But when passing it to the function, the static array is 
 converted to a dynamic array. A dynamic array is a pointer to 
 the array's elements.

 The scope protections are not transitive, so a scope dynamic 
 array does not protect its elements as scope, hence the errors 
 you're seeing.
A very good explanation. Thank you very much for my own name. SDB 79
Apr 15 2022
prev sibling parent reply Andy <andy.pj.hanson gmail.com> writes:
I have another puzzle related to scoping. In this case it's about 
defining data structures that will be compatible with scope.

```
 safe  nogc pure nothrow:

extern(C) void main() {}

struct ArrayWrapper(T) {
	private T[] inner;

	ref inout(T) opIndex(immutable size_t index) inout {
		return inner[index];
	}
}

struct SomeValue {
	int* a;
}

int* f0(int[] xs) {
	return &xs[0];
}

SomeValue* f1(SomeValue[] xs) {
	return &xs[0];
}

int* g0(ArrayWrapper!int xs) {
	return &xs[0];
}

SomeValue* g1(ArrayWrapper!SomeValue xs) {
	return &xs[0];
}
```

Compiling this code results in an error in `g1` only:
```
a.d(30): Error: cannot take address of `ref return` of 
`xs.opIndex()` in ` safe` function `g1`
```


Writing `return &xs.inner[0];` would work .. which is exactly 
what `opIndex` is supposed to do. The question is how to get the 
compiler to understand this. How can I write `ArrayWrapper` so 
that it behaves the same way an array does?
Apr 30 2022
parent reply Paul Backus <snarwin gmail.com> writes:
On Sunday, 1 May 2022 at 04:04:04 UTC, Andy wrote:
 Compiling this code results in an error in `g1` only:
 ```
 a.d(30): Error: cannot take address of `ref return` of 
 `xs.opIndex()` in ` safe` function `g1`
 ```


 Writing `return &xs.inner[0];` would work .. which is exactly 
 what `opIndex` is supposed to do. The question is how to get 
 the compiler to understand this. How can I write `ArrayWrapper` 
 so that it behaves the same way an array does?
Simplified example: ```d int** p; ref int* get() safe { return *p; } int** g1() safe { return &get(); // error } ``` Looks like the compiler has a blanket rule against taking the address of a `ref` return value if the value's type contains indirections.
Apr 30 2022
parent Dennis <dkorpel gmail.com> writes:
On Sunday, 1 May 2022 at 05:05:47 UTC, Paul Backus wrote:
 Looks like the compiler has a blanket rule against taking the 
 address of a `ref` return value if the value's type contains 
 indirections.
True. I thought this wouldn't be a problem in my fix for [Issue 22519 - [dip1000] cannot take address of `ref return`](https://issues.dlang.org/show_bug.cgi?id=22519), but the `opIndex` of a dynamic array is a compelling case, so I'm working on it. https://issues.dlang.org/show_bug.cgi?id=23079 https://github.com/dlang/dmd/pull/14062
May 02 2022