View mode: basic / threaded / horizontal-split · Log in · Help
January 07, 2013
Re: ref is unsafe
On Friday, 4 January 2013 at 20:20:08 UTC, Jonathan M Davis wrote:
> On Friday, January 04, 2013 17:26:59 Zach the Mystic wrote:
>> > Honestly though, I'm inclined to argue that functions which
>> > return by ref and
>> > have a ref parameter of that same type just be considered
>> > @system.
>> 
>> Structs mess that up as well:
>> struct S { int i; }
>> ref int d(ref S s)
>> {
>> return s.i;
>> }
>
> Yes. That's a function which takes a ref and returns by ref 
> just like I said.
> It's just that in this case, the ref returned isn't the full 
> object that was
> passed by ref but just a portion of it. What that means is that 
> you can't
> assume that the ref being returned is safe just because the 
> type of the
> parameter and the return type aren't the same. But it doesn't 
> change the
> statement that a function which takes a parameter by ref and 
> returns by ref
> can't be considered @safe without additional constraints of 
> some kind. It just
> shows why you don't have an easy way out to make many of them 
> @safe based on
> the differing types involved.
>
> - Jonathan M Davis

Why this won't work?
1. If the function code is available at ct, we can check for 
escaping locals.
2. Otherwise, we want to statically say to the compiler that the 
returned ref is safe exactly in these lines in which the 
particular function argument, from which the ref has been 
extracted, has not yet gone out of scope. So the returned ref 
safety guarantee tracks the argument safety guarantee.
Something like this:

ref int f(@infer_safe_from ref int a);

With such an annotation on an argument, the compiler will be able 
to infer the safety of a function when used in cases in which a 
is in scope whenever the returned ref is referenced.

Now, if f is used in this manner:
{
January 09, 2013
Re: ref is unsafe
I felt confident enough about my proposal to submit it as 
enhancement request:

http://d.puremagic.com/issues/show_bug.cgi?id=9283
January 09, 2013
Re: ref is unsafe
On Wednesday, 9 January 2013 at 04:33:21 UTC, Zach the Mystic 
wrote:
> I felt confident enough about my proposal to submit it as 
> enhancement request:
>
> http://d.puremagic.com/issues/show_bug.cgi?id=9283

I like it. One issue though, like you also indicated by putting 
question marks on it:

ref T get(T)()
{
  T local;
  return cast(out) local; // This shouldn't compile
}

Because, wouldn't returning a local variable as a reference be a 
dangling reference in all cases? No matter if the programmer 
claims it's correct by saying cast(out)... it just can't be 
correct.

And T can be a type that has reference semantics or value 
semantics, it doesn't matter. That function would always return a 
dangling reference, were it allowed to compile.
January 09, 2013
Re: ref is unsafe
On Wednesday, 9 January 2013 at 04:33:21 UTC, Zach the Mystic 
wrote:
> I felt confident enough about my proposal to submit it as 
> enhancement request:
>
> http://d.puremagic.com/issues/show_bug.cgi?id=9283

By the way, what do you propose is the correct placement of this 
"new" out keyword:

#1: out ref int get(ref int a);
#2: ref out int get(ref int a);
#3: ref int get(ref int a) out;

I wouldn't allow #3.
January 09, 2013
Re: ref is unsafe
On Sunday, 30 December 2012 at 22:02:16 UTC, Jonathan M Davis 
wrote:
>  But that's
> very different from any attribute that we currently have. It 
> would be like
> having a throw attribute instead of a nothrow attribute. I 
> suppose that it is
> a possible solution though. I could also see an argument that 
> the attribute
> should go on the parameter rather than the function, in which 
> case you could
> have more fine-grained control over it, but it does complicate 
> things further.

I think this is the most reasonable thing to do and I can argue 
that the complications are not a valid argument against this. 
I've came out with roughly the same idea some days ago. Comparing 
this with nothrow is a nice point, but I don't see it as an 
argument against it. This is the most logical thing to do, and 
solves problems.

So, the general notion that we want to (statically) express is 
that the ref result of a function __can__ depend on one (or more 
- depending on a condition for example) ref function parameters. 
Now, if the result is used when all the annotated arguments are 
still in scope, that usage can be considered @safe.

So a function declaration will (conceptually) look like this:
ref int min(@result_tracks_scope_of ref int a, 
@result_tracks_scope_of ref int b) {
    return a < b ? a : b;
}

Now the interface provides enough information for itself to infer 
when the usage is safe and when not.

This will work equally well when we refer to members of the ref 
parameters.

ref int a(@result_tracks_scope_of A a) {
    return a.la.bala;
}

The crucial thing is that the compiler can simply infer these 
attributes when the implementation is available, so we won't have 
to issue errors when the user has not added them (if the code is 
available).
January 10, 2013
Re: ref is unsafe
On Thursday, 3 January 2013 at 21:56:22 UTC, David Nadlinger 
wrote:
> I must admit that I haven't read the rest of the thread yet, 
> but I think the obvious and correct solution is to disallow 
> passing locals (including non-ref parameters, which are 
> effectively locals in D) as non-scope ref arguments.
>
> The scope attribute, once properly implemented, would make sure 
> that the reference is not escaped. For now, we could just make 
> it behave overly conservative in @safe code.
>
> David

If you disallow passing local variables as non-scope ref 
arguments, then you effectively disallow all method calls on 
local variables. My reasoning is as follows:

struct T
{
    int get(int v) const;
    void set(int v);
}

Those methods of T can be thought of as free functions with these 
signatures:

int get(ref const T obj, int v);
void set(ref T obj, int v);

And these kinds of method calls:

T obj;
int n = obj.get(v);
obj.set(n);

...can be thought of as being converted to these free function 
calls:

T obj;
int n = .get(obj, v);
.set(obj, n);

I don't know what the compiler does or doesn't do, but it is 
*as_if* the compiler did this conversion from method calls to 
free functions.

Now it's obvious, given those free function signatures, that if 
you disallow passing function-local variables as non-scope 
references, you also disallow this code:

void func()
{
    T obj;
    obj.set(123);
}

Because that would effectively be the same as:

void func()
{
    T obj;          // obj is a local variable
    .set(obj, 123); // obj is passed as non-scope ref
}

Then, you might ask, why don't those methods of T correspond to 
these free function signatures:

int get(scope ref const T obj, int v);
void set(scope ref T obj, int v);

And the answer is obviously that it would prevent these kinds of 
methods:

struct T
{
    int v;

    ref T increment()
    {
        v++;
        return this;
    }
}

...because that would then convert to this free function 
signature:

ref T increment(scope ref T obj)
{
    obj.v++;
    return obj; // Can't return a reference to a scope argument
}
January 10, 2013
Re: ref is unsafe
...Although, I should add that my analogy between methods and 
free functions seems to break when the object is an rvalue. Like 
in:

struct T
{
    int v;

    this(int a)
    {
        v = a;
    }

    int get()
    {
        return v;
    }
}

int v = T(4).get();

Given my analogy, the method get() should be able to be thought 
of as a free function:

int gget(ref T obj)
{
    return obj.v;
}

But then the above method call should be able to thought of as:

int v = gget(T(4));

...which won't compile because T(4) is an rvalue, and according 
to D, rvalues can't be passed as ref (nor const ref). I don't 
know which one is flawed, my analogy, or the logic of how D is 
designed.
January 10, 2013
Re: ref is unsafe
On Thursday, 10 January 2013 at 16:42:09 UTC, Tommi wrote:
> ...which won't compile because T(4) is an rvalue, and according 
> to D, rvalues can't be passed as ref (nor const ref). I don't 
> know which one is flawed, my analogy, or the logic of how D is 
> designed.

My analogy is a bit broken in the sense that methods actually see 
their designated object as a reference to lvalue even if it is an 
rvalue. But I don't think that affects the logic of my main 
argument about scope arguments.

A more strict language logic would be inconvenient.

But, this logic does introduce a discrepancy between non-member 
operators and member operators in C++ (which D actually 
side-steps by disallowing non-member operators... and then 
re-introduces by providing UFCS):

// C++:

struct T
{
    int val = 10;

    T& operator--()
    {
        --val;
        return *this;
    }
};

T& operator++(T& t)
{
    ++t.val;
    return t;
}

int main()
{
    _cprintf("%d\n", (--T()).val); // Prints: 9
    _cprintf("%d\n", (++T()).val); // Error: no known conversion
                                   // from 'T' to 'T&'
    return 0;
}

// D:

import std.stdio;

struct T
{
    int val;

    int get()
    {
        ++val;
        return val;
    }
}

int het(ref T t)
{
    ++t.val;
    return t.val;
}

void main()
{
    writeln(T().get());
    writeln(T().het()); // Error: T(0) is not an lvalue
}
Next ›   Last »
3 4 5 6 7
Top | Discussion index | About this forum | D home