December 04, 2014
On 12/4/2014 1:52 PM, H. S. Teoh via Digitalmars-d wrote:
> On Thu, Dec 04, 2014 at 01:10:31PM -0800, Walter Bright via Digitalmars-d wrote:
>> On 12/4/2014 11:41 AM, H. S. Teoh via Digitalmars-d wrote:
>>> Can we pretty please use the term "type qualifier" instead of "type
>>> constructor"? ;-)
>>
>> I think "type constructor" is the more pedantically correct term, but
>> you'll have to ask Andrei :-)
>
> Recently there was a bug filed by Andrei himself, and a bunch of merged
> PRs, that renamed "type constructor" to "type qualifier". The general
> sense I got was that we're deprecating "type constructor" in preference
> for "type qualifier".

Looks like Andrei Hath Spaketh!


>> Because there are inevitably cases where you'll need to wrap unsafe
>> code with a safe interface. By screwing this down too tightly for
>> @system code, you'll force people to use really ugly workarounds.
> Are there any use cases for bypassing scope semantics in @system code?

One is to interface with legacy code that does not use 'scope' properly in its declarations. There are probably more.

> How common are they?

I don't know.

> Maybe such code *should* look ugly -- to draw
> attention to the fact that something un-@safe is going on.

@system is ugly enough.

> Or perhaps we can make cast() strip away scope? I much rather require
> explicit annotation for potentially unsafe operations, than to have
> subtle bugs creep into the code just because I forgot to mark something
> as @safe.

Start your modules with:

  @safe:


> There are, though probably incomplete, and there's also an ongoing
> stream of Phobos PR's for marking things @safe that ought not to be
> @system.  There's a *lot* of Phobos code that needs to be cleaned up in
> this way before this can be workable. (Unless we abuse @trusted as a
> temporary workaround -- but that's not an approach I'd like to
> recommend!)

As long as there's ongoing progress!


> Still not sure how it would address the RC folks' request for compiler
> support, but I suppose that's a different topic.

Yup, different issue.


> I think it's a fairly important issue, since making this work correctly
> will also open up optimization opportunities for a good chunk of
> delegate-based code.

See my other reply with resolution.


> I was thinking more of what implications may hold in the loop body for
> references to (local) variables outside the loop, since that presents
> another potential optimization opportunity if we do it right.

Yes. I think there are a number of optimization properties we can exploit over time.


>> Overloaded operators are treated like calls to the functions that back
>> them.
> OK. You might want to state this explicitly in the DIP, just so it isn't
> left up to interpretation. :-)

Ok.

>>> Finally, the following isn't directly related to this DIP, since
>>> scope is intended to solve this problem, but I thought I should bring
>>> it up.  :-) In the section "escaping via return", 5 points are listed
>>> as sufficient for detecting the "return func(t);" case. The following
>>> section "scope ref" states that these 5 points are "correct" (in
>>> implementing escaping reference detection). However, isn't the
>>> following a loophole?
>>>
>>> 	struct S {
>>> 		int x;
>>> 	}
>>> 	ref int func(ref S s) {
>>> 		return s.x;
>>> 	}
>>> 	ref T foo() {
>>> 		S s;
>>> 		return func(s); // escaping reference
>>> 	}
>>>
>>> Since S cannot implicitly convert to int, it would appear that this
>>> circumvents escaping reference detection.
>>
>> No, because under this proposal, s.x is treated as if it were just s
>> as far a scope rules are concerned.
> [...]
>
> I'm not quite sure I see how this could be implemented. When the
> compiler is compiling func(), say it's in a different module, there's
> nothing about the function that says it's illegal to return a reference
> to the member of an incoming argument.

That's right, so the compiler assumes it does, unless the parameter is marked as scope.

> First of all, this isn't a
> template function so scope inference isn't in effect; secondly, even of
> it were in effect (say we write it as `func()(ref S s)` instead), it
> wouldn't be able to infer the return value as scope, since it's
> obviously escaping a reference to a function argument.

'scope ref' parameters cannot be returned by 'ref' or by 'scope ref'. The only distinction between 'ref' and 'scope ref' parameters is the latter cannot be returned by 'ref' or 'scope ref'.

> So, unless I'm
> missing something obvious, func() will be accepted by the compiler (as
> it should, since we didn't mark anything as scope here).

The compiler assumes func(s) returns a ref to s, and so it won't allow func(s) to be returned from foo().


> Now when the compiler compiles foo(), it can no longer see what's inside
> func(), so there's no way for it to know that the ref int being returned
> actually comes from the ref S parameter.

It assumes it does, unless 'ref S' is marked 'scope ref S'. It's the whole point of 'scope ref'.

December 04, 2014
On Thu, Dec 04, 2014 at 03:26:32PM -0800, Walter Bright via Digitalmars-d wrote:
> On 12/4/2014 1:52 PM, H. S. Teoh via Digitalmars-d wrote:
[...]
> >Are there any use cases for bypassing scope semantics in @system code?
> 
> One is to interface with legacy code that does not use 'scope' properly in its declarations. There are probably more.
> 
> >How common are they?
> 
> I don't know.
> 
> >Maybe such code *should* look ugly -- to draw attention to the fact that something un-@safe is going on.
> 
> @system is ugly enough.

Not when all code is @system by default!


> >Or perhaps we can make cast() strip away scope? I much rather require explicit annotation for potentially unsafe operations, than to have subtle bugs creep into the code just because I forgot to mark something as @safe.
> 
> Start your modules with:
> 
>   @safe:

I would, except that Phobos limitations preclude that. Someone else has already posted the list of bugs, so I won't repeat that here. Needless to say, it's still a ways off before putting @safe: at the top of your modules will solve the problem.


[...]
> >>>	struct S {
> >>>		int x;
> >>>	}
> >>>	ref int func(ref S s) {
> >>>		return s.x;
> >>>	}
> >>>	ref T foo() {
> >>>		S s;
> >>>		return func(s); // escaping reference
> >>>	}
> >>>
> >>>Since S cannot implicitly convert to int, it would appear that this circumvents escaping reference detection.
> >>
> >>No, because under this proposal, s.x is treated as if it were just s as far a scope rules are concerned.
> >[...]
> >
> >I'm not quite sure I see how this could be implemented. When the compiler is compiling func(), say it's in a different module, there's nothing about the function that says it's illegal to return a reference to the member of an incoming argument.
> 
> That's right, so the compiler assumes it does, unless the parameter is marked as scope.
> 
> >First of all, this isn't a template function so scope inference isn't
> >in effect; secondly, even of it were in effect (say we write it as
> >`func()(ref S s)` instead), it wouldn't be able to infer the return
> >value as scope, since it's obviously escaping a reference to a
> >function argument.
> 
> 'scope ref' parameters cannot be returned by 'ref' or by 'scope ref'. The only distinction between 'ref' and 'scope ref' parameters is the latter cannot be returned by 'ref' or 'scope ref'.
> 
> >So, unless I'm missing something obvious, func() will be accepted by
> >the compiler (as it should, since we didn't mark anything as scope
> >here).
> 
> The compiler assumes func(s) returns a ref to s, and so it won't allow
> func(s) to be returned from foo().

Ah, I see. So you're saying that under this DIP, `return func(...);`
from a ref function will be prohibited unless the arguments to func()
were all either scope or non-reference?

That sounds good, though it *is* pretty invasive, as any existing ref functions that do this will need to be revised, which potentially spreads 'scope' to many other places, depending on how deeply the chain of tail recursion / tail func calls go.


> >Now when the compiler compiles foo(), it can no longer see what's
> >inside func(), so there's no way for it to know that the ref int
> >being returned actually comes from the ref S parameter.
> 
> It assumes it does, unless 'ref S' is marked 'scope ref S'. It's the whole point of 'scope ref'.

Got it, thanks.


T

-- 
Shin: (n.) A device for finding furniture in the dark.
December 04, 2014
>> Hmmm, looks like a problem I didn't think of. darnit!
>
> Turns out, 'ref' has exactly the same issue. The resolution is the same:
>
>   alias ref int delegate() dg_t; // ref applies to return type

Was the goal of the post to represent the new alias syntax ?
If so, big fail, Walter,...


December 05, 2014
On 12/4/2014 1:24 PM, Steven Schveighoffer wrote:
> Hm... did you mean `alias this borrow`?


yes (hangs head in shame)
December 05, 2014
On 12/4/2014 1:54 PM, Martin Nowak wrote:
> On 12/04/2014 09:41 PM, Walter Bright wrote:
>>
>> Yes, it would be written:
>>
>>    scope ref T setVal(ref T t)
>>    {
>>       t.val = 12;
>>       return t;
>>    }
>
> But when there is no scope on the argument, I could not call setVal with a local
> T variable.

Actually, you can. The difference between 'ref' and 'scope ref' parameters is the latter cannot be returned by ref.
December 05, 2014
On 12/4/2014 3:58 PM, Basile Burg wrote:
> Was the goal of the post to represent the new alias syntax ?

??
December 05, 2014
Walter Bright:

> On 12/4/2014 1:24 PM, Steven Schveighoffer wrote:
>> Hm... did you mean `alias this borrow`?
>
>
> yes (hangs head in shame)

No need to be ashamed for similar things, you are doing your best.

Bye,
bearophile
December 05, 2014
On 12/4/2014 3:04 PM, deadalnix wrote:
> So as mentioned, there are various problem with this DIP :
>   - rvalue are defined as having a scope that goes to the end of the statement.
> That mean they can never be assigned to anything as per spec.

I don't believe this is correct. Rvalues can be assigned, just like:

   __gshared int x;
   { int i; x = i; }

i's scope ends at the } but it can still be assigned to x.


>   - It add more special casing with & (as if it wasn't enough of a mess with
> @property, optional () and the fact the function aren't first class). For
> instance *e has infinite lifetime when &(*e) is lifetime(e).

That's right. I know you're worried about that, but I still don't see it as an actual problem. (The optimizer makes use of this special case all the time.)


> Also, considering *e has infinite lifetime, (but not e[i] ???)

e[i] should be same as *e, unless e is a literal or tuple.

> what is the point of scope at all ? If all indirection goes to infinite lifetime (ie GC) then
> lifetime only apply to local variable and it should not be able to escape these,
> scope or not.

I originally had scope only apply to ref, but that made having scoped classes impossible.


> During discussion, I proposed to differentiate lifetime calculation between
> lvalues and rvalues (which are inherently different beasts with different
> lifetime) and carry (or not) the scope flag with each expression.

I'm not sure how that would be different from the DIP as it stands now.
December 05, 2014
On 12/4/2014 3:54 PM, H. S. Teoh via Digitalmars-d wrote:
>> The compiler assumes func(s) returns a ref to s, and so it won't allow
>> func(s) to be returned from foo().
>
> Ah, I see. So you're saying that under this DIP, `return func(...);`
> from a ref function will be prohibited unless the arguments to func()
> were all either scope or non-reference?

Right.

> That sounds good, though it *is* pretty invasive, as any existing ref
> functions that do this will need to be revised, which potentially
> spreads 'scope' to many other places, depending on how deeply the chain
> of tail recursion / tail func calls go.

I know:

1. that's why the extensive support for scope inference
2. I couldn't see any other way

December 05, 2014
On Thu, Dec 04, 2014 at 04:31:18PM -0800, Walter Bright via Digitalmars-d wrote:
> On 12/4/2014 3:04 PM, deadalnix wrote:
> >So as mentioned, there are various problem with this DIP :
> >  - rvalue are defined as having a scope that goes to the end of the statement.
> >That mean they can never be assigned to anything as per spec.
> 
> I don't believe this is correct. Rvalues can be assigned, just like:
> 
>    __gshared int x;
>    { int i; x = i; }
> 
> i's scope ends at the } but it can still be assigned to x.
[...]

Well, the "Expressions" section defines certain lifetimes to be infinity, however, it contradicts the definition of an rvalue's lifetime given under "Definitions" - "Lifetime", which states that an rvalue's lifetime lasts only to the end of the statement. This would imply that the following ought to be rejected:

	int x = 1 + 2; // lifetime(1 + 2) = end of statement
			// but lifetime(x) = infinity

Sounds like the Definitions section is not precise enough. :-)


T

-- 
Curiosity kills the cat. Moral: don't be the cat.