D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 17567 - make shared methods callable on unshared objects (and covariant)
Summary: make shared methods callable on unshared objects (and covariant)
Status: RESOLVED WONTFIX
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: All All
: P3 enhancement
Assignee: No Owner
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2017-06-28 13:40 UTC by Martin Nowak
Modified: 2017-06-28 19:57 UTC (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description Martin Nowak 2017-06-28 13:40:42 UTC
cat > bug.d << CODE
struct S
{
  void func() shared {}
}

void bug()
{
    S s;
    s.func;
}
dmd -c bug
----
bug.d(9): Error: shared method bug.S.func is not callable using a non-shared object
----
cat > bug2.d << CODE
interface I
{
    void func();
}
class C : I
{
    void func() shared {}
}
CODE
dmd -c bug
----
bug.d(5): Error: class bug.C interface function 'void func()' is not implemented
----

Both of these use-cases should be supported. Shared on a methods guarantees thread-safety which is compatible with being used in an unshared manner. Therefor shared methods should be fully covariant to unshared methods.

This does not touch shared as type qualifier, `shared(T)` means the type is shared between threads, and hence is obviously not convertible to `T`.

Looks like this would be the first storage class where we'd support covariance and real overloading.
While overloading, e.g. `nothrow` and non-`nothrow` methods is not an error, the compiler always prefers `nothrow`, `pure`, `@safe`, or `@nogc` methods.
But overloading should prefer unshared methods over `shared` ones, e.g. because the methods might avoid using a mutex.
Comment 1 Andrei Alexandrescu 2017-06-28 14:06:15 UTC
This is not possible. Thanks Timon Gehr for explaining this to me. Consider:

shared int* p;

struct S
{
  int x;
  void func() shared { p = &x; }
}

void bug()
{
    auto p = new S;
    p.func;
    ++p.x;
}

At this point we have threads getting shared access to x, but the current thread believes x is unshared.
Comment 2 Martin Nowak 2017-06-28 14:23:11 UTC
(In reply to Andrei Alexandrescu from comment #1)
> At this point we have threads getting shared access to x, but the current
> thread believes x is unshared.

It already seemed too simple to work out :).
So this would only work with some inout flavor of shared which likely wouldn't be worth the trouble.
Comment 3 Walter Bright 2017-06-28 19:57:41 UTC
(In reply to Martin Nowak from comment #2)
> It already seemed too simple to work out :).

We worked out the rules for implicit conversions (used for covariance/contravariance) back when we came up with const/shared/etc. It's best to derive these in isolation, and then apply them without needing to work it out again.

(It's a lot like using the chain rule for derivatives. Derive the chain rule, prove it's correct, then just apply it as a given.)

I've gone through the same process with scope conversions. I kept confusing myself until I finally sat down and worked up a chart with the rules, convinced myself that that was correct, and then just applied them without worrying about it.