Issue 23124 - [dip1000] scope inference leads to implementatio-defined semantics for @trusted and @system.
Summary: [dip1000] scope inference leads to implementatio-defined semantics for @trust...
Status: REOPENED
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: x86_64 Linux
: P3 normal
Assignee: No Owner
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2022-05-19 07:18 UTC by Ate Eskola
Modified: 2022-12-17 10:37 UTC (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description Ate Eskola 2022-05-19 07:18:32 UTC
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/edtbjavjzkwogvutxpho@forum.dlang.org
Comment 1 Walter Bright 2022-08-26 17:44:03 UTC
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 Ate Eskola 2022-09-17 14:01:33 UTC
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 Ate Eskola 2022-09-17 14:20:51 UTC
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 Ate Eskola 2022-09-17 15:14:50 UTC
(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;}
}
---------------------