April 03, 2012
On 2012-04-03 11:08:38 +0000, Don Clugston <dac@nospam.com> said:

> On 03/04/12 12:32, Timon Gehr wrote:
>> On 04/03/2012 10:27 AM, Don Clugston wrote:
>>> This is asking for a complicated special case. In global scope, order of
>>> declarations doesn't matter. In function scope, order of declarations
>>> always matters.
>>> If you have type inference of function returns, things can get nasty:
>>> 
>>> void foo()
>>> {
>>> auto b() { return a(); }
>>> X x = whatever;
>>> auto a() { return x; }
>>> }
>>> Now b actually depends on the declaration of x. So it's not enough to
>>> say that only function declarations are immune to ordering rules.
>> 
>> I come to a different conclusion. If only function declarations are
>> immune to ordering rules, the above example is simply illegal. The
>> example cannot be used to demonstrate incompleteness of the approach.
> 
> I don't see a way to just declare it as "illegal". How would you detect that situation in the general case?
> It's not easy.
> 
> Y b() { ... }
> Y y = b();
> X x = ...
> 
> Prove that y doesn't depend on x.

You're right Don, it shouldn't be illegal. But it could be part of the constrain. Let's rephrase Timon's idea like this:

If two or more functions declarations are following each other with no other statement in between, then these two functions can call one another. That could allow overloading too, as long as the declarations are following each other directly.

In fact, that'd probably be generalizable to templates, struct, class, and enum declarations too. Adjacent declarations would create some kind of island in the function's code where order of declaration does not matter. The island ends at the first non-declaration statement or local variable declaration, and a new island begins when new declarations are encountered.

-- 
Michel Fortin
michel.fortin@michelf.com
http://michelf.com/

April 03, 2012
On 04/03/2012 01:08 PM, Don Clugston wrote:
>
> Y b() { ... }
> Y y = b();
> X x = ...
>
> Prove that y doesn't depend on x.

Since only function declarations are immune to ordering rules, b cannot forward reference x.
April 03, 2012
"Don Clugston" <dac@nospam.com> wrote in message news:jlelnn$tm4$1@digitalmars.com...
>
> I don't see a way to just declare it as "illegal". How would you detect
> that situation in the general case?
> It's not easy.
>
> Y b() { ... }
> Y y = b();
> X x = ...
>
> Prove that y doesn't depend on x.

We don't *have* to go the route of allowing nested functions to access forward-referenced variables. We could just limit it to forward-referenced nested functions. So this code could be illegal for exactly the same reason it's currently illegal:

Y b() { /+ use the var 'x' +/ }
Y y = b();
X x = ...

If 'x' were a nested function instead of a variable, then *that* we could decide to start allowing:

Y b() { /+ call function 'x' +/ }
Y y = b();
X x() { ... } // b can access this because this is a nested func, not a var

Of course, as you already pointed out and I replied to elsewhere in this thread, we'd still have to do something about this scenario:

X b() { return a(); }
X w = b();
X x = whatever;
X a() { return x; }

Alternatively, if I understand Timon right, I think he's suggesting that we could treat "groups" of nested function declarations as "unordered", and any one other statement/declaration would simply break the "group":

// These three can all reference each other freely,
// but they cannot reference f, x, y or z.
A a() {...}
B b() {...}
C c() {...}

// Something other than a nested function declaration.
// This ends the "grouping" of a, b, and c.
F f = ...;

// These three can all reference each other freely.
// Naturally, these can also reference a, b, c, and f
X x() {...}
Y y() {...}
Z z() {...}

That would allow mutual recursion while neatly avoiding any problems.


April 03, 2012
On 03/04/12 13:35, Timon Gehr wrote:
> On 04/03/2012 01:08 PM, Don Clugston wrote:
>>
>> Y b() { ... }
>> Y y = b();
>> X x = ...
>>
>> Prove that y doesn't depend on x.
>
> Since only function declarations are immune to ordering rules, b cannot
> forward reference x.

But there could be another function a() which is below x, and which b() calls.
April 03, 2012
"Timon Gehr" <timon.gehr@gmx.ch> wrote in message news:jlej27$mvi$1@digitalmars.com...
>
> This is the right way to work around this issue. It works now and does not imply any kind of overhead at runtime:
>
> void foo(){
>     void a()(){ ... }
>     void b()  { ... }
> }
>

That's a very simple workaround (albiet unintuitive - unless I'm just too tired right now). It leads me to two questions:

1. How the heck does that work?

2. What, if any, problems would arise from just automatically doing that behind-the-scenes? Ie, to just automatically turn all non-templated nested functions into no-parameter templated nested functions? Would that be a feasable solution?


April 03, 2012
On 04/03/2012 01:55 PM, Don Clugston wrote:
> On 03/04/12 13:35, Timon Gehr wrote:
>> On 04/03/2012 01:08 PM, Don Clugston wrote:
>>>
>>> Y b() { ... }
>>> Y y = b();
>>> X x = ...
>>>
>>> Prove that y doesn't depend on x.
>>
>> Since only function declarations are immune to ordering rules, b cannot
>> forward reference x.
>
> But there could be another function a() which is below x, and which b()
> calls.

This scenario can be forbidden conservatively.
April 03, 2012
On 03/04/12 13:51, Nick Sabalausky wrote:
> "Don Clugston"<dac@nospam.com>  wrote in message
> news:jlelnn$tm4$1@digitalmars.com...
>>
>> I don't see a way to just declare it as "illegal". How would you detect
>> that situation in the general case?
>> It's not easy.
>>
>> Y b() { ... }
>> Y y = b();
>> X x = ...
>>
>> Prove that y doesn't depend on x.
>
> We don't *have* to go the route of allowing nested functions to access
> forward-referenced variables. We could just limit it to forward-referenced
> nested functions.

But it's the same. If you can forward reference a function, you can also access a variable.

So this code could be illegal for exactly the same reason
> it's currently illegal:

>
> Y b() { /+ use the var 'x' +/ }
> Y y = b();
> X x = ...
>
> If 'x' were a nested function instead of a variable, then *that* we could
> decide to start allowing:
>
> Y b() { /+ call function 'x' +/ }
> Y y = b();
> X x() { ... } // b can access this because this is a nested func, not a var
>
> Of course, as you already pointed out and I replied to elsewhere in this
> thread, we'd still have to do something about this scenario:
>
> X b() { return a(); }
> X w = b();
> X x = whatever;
> X a() { return x; }

And I don't think there's any easy solution to that.
I think it's a dead end.

> Alternatively, if I understand Timon right, I think he's suggesting that we
> could treat "groups" of nested function declarations as "unordered", and any
> one other statement/declaration would simply break the "group":
>
> // These three can all reference each other freely,
> // but they cannot reference f, x, y or z.
> A a() {...}
> B b() {...}
> C c() {...}
>
> // Something other than a nested function declaration.
> // This ends the "grouping" of a, b, and c.
> F f = ...;
>
> // These three can all reference each other freely.
> // Naturally, these can also reference a, b, c, and f
> X x() {...}
> Y y() {...}
> Z z() {...}
>
> That would allow mutual recursion while neatly avoiding any problems.

Yeah, that'd work. It's a funky rule though.
April 03, 2012
On 03/04/12 13:58, Timon Gehr wrote:
> On 04/03/2012 01:55 PM, Don Clugston wrote:
>> On 03/04/12 13:35, Timon Gehr wrote:
>>> On 04/03/2012 01:08 PM, Don Clugston wrote:
>>>>
>>>> Y b() { ... }
>>>> Y y = b();
>>>> X x = ...
>>>>
>>>> Prove that y doesn't depend on x.
>>>
>>> Since only function declarations are immune to ordering rules, b cannot
>>> forward reference x.
>>
>> But there could be another function a() which is below x, and which b()
>> calls.
>
> This scenario can be forbidden conservatively.

How?
April 03, 2012
On 04/03/2012 02:00 PM, Nick Sabalausky wrote:
> "Timon Gehr"<timon.gehr@gmx.ch>  wrote in message
> news:jlej27$mvi$1@digitalmars.com...
>>
>> This is the right way to work around this issue. It works now and does not
>> imply any kind of overhead at runtime:
>>
>> void foo(){
>>      void a()(){ ... }
>>      void b()  { ... }
>> }
>>
>
> That's a very simple workaround (albiet unintuitive - unless I'm just too
> tired right now). It leads me to two questions:
>
> 1. How the heck does that work?
>

Symbol lookup is done upon the first instantiation of the local template.

> 2. What, if any, problems would arise from just automatically doing that
> behind-the-scenes? Ie, to just automatically turn all non-templated nested
> functions into no-parameter templated nested functions? Would that be a
> feasable solution?
>
>

Your proposal is basically to allow a local function to forward reference any symbol that is declared before the local function is first referenced.

This still has the following problem:

void a(){/*cannot reference c*/}
void b(){/*because this references a*/}
void c(){/*references a and b*/ }

It would work by reordering the code like so:

void b(){/*references a*/}
void a(){/*references c*/}
void c(){/*references a and c*/}

That would be extremely unintuitive.
April 03, 2012
Nick Sabalausky:

> I submit that nested functions should be exempt from the usual sequential
> visibility rules. (Therefore, mutually recursive nested functions would
> become possible.)

What about static nested functions? This seems a simpler
enhancement request:

import std.stdio;
void main() {
   static int male(in int n) pure nothrow {
     return n ? (n - female(male(n - 1))) : 0;
   }
   static int female(in int n) pure nothrow {
     return n ? (n - male(female(n - 1))) : 1;
   }
   writeln(female(15));
}

Bye,
bearophile