August 14, 2016
On 8/14/2016 7:37 AM, Dicebot wrote:
> struct Container
> {
>     int data;
>
>     static struct Range
>     {
>         int* pdata;
>         // range methods skipped for clarity ..
>     }
>
>     Range asRange ( )
>     {
>         return Range(&this.data);
>     }
> }
>
> void main ( )
> {
>     Container container;
>
>     import std.stdio;
>     writeln(container.asRange()); // case 1, OK
>
>     scope r = container.asRange(); // case 2, OK
>     auto r = container.asRange(); // case 3, not OK
> }


A great example. Let's analyze by peeling back the syntactic sugar:

----
'container' is a stack variable

'container.int' is a stack variable

'asRange()' returns an instance of 'Range' which is a stack variable, call it 'range'

'range.pdata' is a stack variable

Range range.pdata = &container.data; // range is inferred as 'scope' because
                                     // container.data is a stack variable

auto r = range; // r is inferred as 'scope' because 'range' is 'scope'
----

It's a bit advanced, but it falls entirely in the DIP rules and is workable with some implementation effort (not sure how much).

The idea is, if something is ultimately a stack variable, the scope rules apply.

August 15, 2016
On 08/14/2016 11:02 PM, Walter Bright wrote:
> It's a bit advanced, but it falls entirely in the DIP rules and is workable with some implementation effort (not sure how much).
> 
> The idea is, if something is ultimately a stack variable, the scope rules apply.

That is an unexpected application of described rules - I feel like I need some time to fit it into my head before I can respond :)




August 15, 2016
On 08/14/2016 10:50 PM, Walter Bright wrote:
> On 8/14/2016 7:42 AM, Dicebot wrote:
>> Two another more nitpicky comments:
>>
>> 1) Deprecation process proposal right now says this:
>>
>> - remove -scope, issue warning when errors are detected
>> - replace warnings with deprecation messages
>>
>> PLEASE FOR THE SAKE OF ALL DEAR NEVER EVER DO THIS
>>
>> Deprecations must always come first. Warnings may or may not follow later but any deprecation process must mandatory start with deprecation message, not exceptions and no excuses.
> 
> Hmm, but we've always done warning => deprecation => error

Yes, and it was a major disaster because most sensible projects compile with warnings as errors enabled (it is also the default in dub) and thus putting a warning breaks a many more times more code than putting a deprecation. We have already talked that through before with Martin and it seemed acknowledged that pattern has to be changed to "deprecation -> (optional warning) -> error".



August 14, 2016
On 8/14/2016 2:12 PM, Dicebot wrote:
> On 08/14/2016 10:50 PM, Walter Bright wrote:
>> Hmm, but we've always done warning => deprecation => error
> Yes, and it was a major disaster because most sensible projects compile
> with warnings as errors enabled (it is also the default in dub) and thus
> putting a warning breaks a many more times more code than putting a
> deprecation. We have already talked that through before with Martin and
> it seemed acknowledged that pattern has to be changed to "deprecation ->
> (optional warning) -> error".

Ok, that makes sense. Thanks for the explanation!
August 15, 2016
On Sunday, 14 August 2016 at 20:02:35 UTC, Walter Bright wrote:
> auto r = range;

You said that if a value (range in this case) is returned by scope, its lifetime is restricted to its expression, hence it can't be assigned to a variable, because the variable outlives the expression.
August 15, 2016
On 08/15/2016 12:32 PM, Kagamin wrote:
> On Sunday, 14 August 2016 at 20:02:35 UTC, Walter Bright wrote:
>> auto r = range;
> 
> You said that if a value (range in this case) is returned by scope, its lifetime is restricted to its expression, hence it can't be assigned to a variable, because the variable outlives the expression.

As far as I understood Walter, his explained behaviour should happen if function is NOT annottated with scope and will be a default out-of-box one when trying to "borrow" stack-allocated data. Not sure yet how it should work with more complex cases (i.e. container stores a slice / pointer internally and not just int).



August 15, 2016
On 8/15/2016 2:32 AM, Kagamin wrote:
> On Sunday, 14 August 2016 at 20:02:35 UTC, Walter Bright wrote:
>> auto r = range;
>
> You said that if a value (range in this case) is returned by scope, its lifetime
> is restricted to its expression, hence it can't be assigned to a variable,
> because the variable outlives the expression.

A very good question. If the functions parameters are 'return scope', that means the returned value has the (smallest) scope if its 'return scope' parameters. If there are no 'return scope' parameters, the scope of the return value is limited to the expression.

This is why things like this will work:

  scope int* foo(return scope int* p) { return p; }

  int i;
  int* q = foo(&i); // ok

because the compiler knows that the address of i is what is being returned, and q has a shorter lifetime than i.

Also:

  scope int* foo(scope int* p) { return p; }

would be an error, and:

  scope int* foo(int* p) { return p; }
  int* q = foo();  // error
August 15, 2016
On 08/15/2016 02:52 PM, Walter Bright wrote:
> On 8/15/2016 2:32 AM, Kagamin wrote:
>> On Sunday, 14 August 2016 at 20:02:35 UTC, Walter Bright wrote:
>>> auto r = range;
>>
>> You said that if a value (range in this case) is returned by scope,
>> its lifetime
>> is restricted to its expression, hence it can't be assigned to a
>> variable,
>> because the variable outlives the expression.
> 
> A very good question. If the functions parameters are 'return scope', that means the returned value has the (smallest) scope if its 'return scope' parameters. If there are no 'return scope' parameters, the scope of the return value is limited to the expression.

Does that mean that a scope return in aggregate method will bind to aggregate lifetime if method itself is annotated with scope too? For example:

struct Container
{
    scope Range asRange ( ) scope
    {
        return Range(&this.data);
    }
}




August 15, 2016
On 8/15/2016 5:17 AM, Dicebot wrote:
> On 08/15/2016 02:52 PM, Walter Bright wrote:
>> On 8/15/2016 2:32 AM, Kagamin wrote:
>>> On Sunday, 14 August 2016 at 20:02:35 UTC, Walter Bright wrote:
>>>> auto r = range;
>>>
>>> You said that if a value (range in this case) is returned by scope,
>>> its lifetime
>>> is restricted to its expression, hence it can't be assigned to a
>>> variable,
>>> because the variable outlives the expression.
>>
>> A very good question. If the functions parameters are 'return scope',
>> that means the returned value has the (smallest) scope if its 'return
>> scope' parameters. If there are no 'return scope' parameters, the scope
>> of the return value is limited to the expression.
>
> Does that mean that a scope return in aggregate method will bind to
> aggregate lifetime if method itself is annotated with scope too? For
> example:
>
> struct Container
> {
>     scope Range asRange ( ) scope
>     {
>         return Range(&this.data);
>     }
> }

If the method is annotated with scope, the scope applies to the 'this' pointer.

August 15, 2016
On Mon, 15 Aug 2016 06:36:00 -0700, Walter Bright wrote:
> If the method is annotated with scope, the scope applies to the 'this' pointer.

Oh god, the attribute explosion is getting worse.

I think at this point the proper way to use attribute-based D features is to write your code without them, then write a tool that will add all the attributes to your source code that it can while not breaking anything. Like now it's sensible to write a method:

public final void foo() scope inout @nogc nothrow @safe pure {}

I think the solution is to turn every function into a no-args template, but then you can't use virtual methods.