This should compile, but does not.
----------
struct MyType
{ private @system int* _content;
// This type is designed so that _content will never point to local data.
@trusted pure this(return ref int i){_content = new int(i);}
// Note, not marked RETURN scope. Should return an unscoped pointer.
@trusted pure scope content(){return _content;}
}
@safe void main()
{ int* outer;
if(true)
{ int inner;
// Inferred as scope, which is intended
auto myVar = MyType(inner);
// Should be allowed
outer = myVar.content;
}
}
----------
More through explaination: https://forum.dlang.org/thread/[email protected]
Comment #1 by bugzilla — 2022-08-26T17:44:03Z
The error is:
test.d(17): Error: scope variable `myVar` assigned to `outer` with longer lifetime
which is a correct error message. `outer` lives longer than the scope of `inner`, so it is an error to set `outer` to pointing at `inner`.
If you need it to compile, remove `@safe` from main().
It's a feature of D to do scope inference. Whether the use of a scope pointer is checked or not is dependent on whether the function using the scope pointer is @safe or not.
This is working as designed.
Comment #2 by Ajieskola — 2022-09-17T14:01:33Z
I did change my mind in the topic a bit. I said previously, that no `scope` inference should be done for a `@trusted` or `@system` function. Now I think that it's okay, good even, to infer, but if the inference changes the attributes it should be an error.
Why? Consider a hypothetical future spec-compliant D compiler. The language spec does not say where the inference must stop, so our future compiler infers `scope` only if the argument is directly returned. If it encounters any other expression than a plain symbol name, it stops inference there.
Now we have a function
------
import std.random : dice;
auto either(int* a, int* b) @trusted { return dice(1,1) ? a : b; }
------
What happens? DMD infers arguments a and b as `scope`, BUT our future compiler does not. What is a perfectly safe function with DMDFE just became a terrible footgun in another spec-abiding compiler!
So, if we don't want to disable inference in these cases, there are two options:
1: If inference detects need for adding `scope` or `return` to a `@system` or `@trusted` function, it must error.
2: The spec must unambiguously specify where `scope` and `return scope` should be inferred and where it should not.
I think the first option is simpler, but I'm happy with either one.
Comment #3 by Ajieskola — 2022-09-17T14:20:51Z
Example in my last comment was wrong. Posting a corrected one:
----------------
struct MyType
{ private int* wontEverPointToLocal;
int* mayPointToLocalIfScope;
// Safe in the future compiler because
// not callable with scope MyType.
// Dangerous in DMD because it infers
// scope for this reference.
@trusted fun(bool cond){return cond? wontEverPointToLocal: mayPointToLocalIfScope;}
}
----------------
Comment #4 by Ajieskola — 2022-09-17T15:14:50Z
(In reply to Ate Eskola from comment #3)
> Example in my last comment was wrong. Posting a corrected one:
>
> ----------------
> struct MyType
> { private int* wontEverPointToLocal;
> int* mayPointToLocalIfScope;
> // Safe in the future compiler because
> // not callable with scope MyType.
> // Dangerous in DMD because it infers
> // scope for this reference.
> @trusted fun(bool cond){return cond? wontEverPointToLocal:
> mayPointToLocalIfScope;}
> }
> ----------------
Nope, still not right. My brains are porridge. Third try:
---------------------
struct MyType
{ int* ptr;
// Safe in DMD because return scope is inferred.
// Dangerous in our future compiler because
// may return an unscoped pointer to local data,
// when MyType is scope.
@trusted scope getPtr(bool cond){return cond? ptr: null;}
}
---------------------
Comment #5 by robert.schadek — 2024-12-13T19:22:56Z