www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Recursion and attribute inference

reply Dukc <ajieskola gmail.com> writes:
While writing about attribute inference to a D blog post draft, I 
encountered an ambiguous case.

```D
float recurse()(float val, float till)
{
     return val < till? recurse(val * 2, till): val;
}

 safe void main()
{
     import std;

     writeln(recurse(1.5, 40.0));
     writeln(typeid(&recurse!()));
}
```

This compiles and prints

```
48
float function(float, float) pure nothrow  nogc  safe*
```

. However, the [spec 
says](https://dlang.org/spec/function.html#function-attribute-inference) that a
function that recurses will not be inferred as ` safe`, `pure` or `nothrow`.

Which is right here, the implementation or the spec?
Nov 02 2022
parent reply Paul Backus <snarwin gmail.com> writes:
On Wednesday, 2 November 2022 at 19:17:10 UTC, Dukc wrote:
 Which is right here, the implementation or the spec?
I'm inclined to say the implementation is right, since the code does not actually violate memory safety. According to [the latest D Foundation meeting summary][1], Dennis Korpel is looking into making ` safe` inference work properly for recursive functions, so it's possible that this limitation will be lifted entirely in the future. [1]: https://forum.dlang.org/thread/jzqabfjmdwveeatpdklj forum.dlang.org
Nov 02 2022
next sibling parent reply zjh <fqbqrr 163.com> writes:
On Wednesday, 2 November 2022 at 22:01:24 UTC, Paul Backus wrote:

 Dennis Korpel is looking into making ` safe` inference work 
 properly for recursive functions,
Is there any plan to deduce `functions properties` ? I think that if we can deduce functions attributes .It will greatly reduce the `attribute soup`, make D code more `concise`, and greatly increase the `adoption` of D.
Nov 02 2022
parent Paul Backus <snarwin gmail.com> writes:
On Thursday, 3 November 2022 at 01:20:11 UTC, zjh wrote:
 On Wednesday, 2 November 2022 at 22:01:24 UTC, Paul Backus 
 wrote:

 Dennis Korpel is looking into making ` safe` inference work 
 properly for recursive functions,
Is there any plan to deduce `functions properties` ? I think that if we can deduce functions attributes .It will greatly reduce the `attribute soup`, make D code more `concise`, and greatly increase the `adoption` of D.
I believe it's being considered as an option, but there are no concrete plans yet. Personally, I think it's a good idea.
Nov 02 2022
prev sibling next sibling parent Dukc <ajieskola gmail.com> writes:
On Wednesday, 2 November 2022 at 22:01:24 UTC, Paul Backus wrote:
 On Wednesday, 2 November 2022 at 19:17:10 UTC, Dukc wrote:
 Which is right here, the implementation or the spec?
I'm inclined to say the implementation is right, since the code does not actually violate memory safety.
I'm going to assume that. Thanks.
Nov 03 2022
prev sibling parent reply Dennis <dkorpel gmail.com> writes:
On Wednesday, 2 November 2022 at 22:01:24 UTC, Paul Backus wrote:
 I'm inclined to say the implementation is right, since the code 
 does not actually violate memory safety.

 According to [the latest D Foundation meeting summary][1], 
 Dennis Korpel is looking into making ` safe` inference work 
 properly for recursive functions, so it's possible that this 
 limitation will be lifted entirely in the future.

 [1]: 
 https://forum.dlang.org/thread/jzqabfjmdwveeatpdklj forum.dlang.org
Right. When a function calls itself directly, attribute checks for that call are ignored. However, when there's one or more functions in between, the reasoning goes "I'm calling a function that can't finish attribute inference right now, so conservatively assume all attributes failed to infer to be on the safe side." It seems the specification was modified to document this shortcoming of the implementation. I wish we could simply assume all attributes were inferred, which would make this work: ```D void fun1()() { fun2(); } void fun2()() { fun1(); } void main() safe pure nothrow nogc { fun1(); // currently fails } ``` But here's a more tricky case: ```D system void systemFunc(); void fun1()() { alias T = typeof(fun2()); systemFunc(); } void fun2()() { fun1(); } void main0() system { fun1(); } void main() safe { fun2(); // not system! } ``` The compiler analyzes `fun1`, then `fun2`, and then needs to decide if the call to `fun1` is safe. If it assumes 'yes', then later when it sees the call to `systemFunc()`, it would need to go back and make `fun2()` system, and re-analyze the function body of `fun1` where `T` is now a system function. DMD is not suited to do this kind of backtracking, and that would potentially be very slow as well, so this probably won't be fixed in the general case. However, in the absence of typeof() and other function type dependencies, a list of callees for each function can be maintained to solve the most common case of mutual function dependencies through simple function calls.
Nov 03 2022
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 11/3/22 17:42, Dennis wrote:
 
 The compiler analyzes `fun1`, then `fun2`, and then needs to decide if 
 the call to `fun1` is safe.
 If it assumes 'yes', then later when it sees the call to `systemFunc()`, 
 it would need to go back and make `fun2()`  system, and re-analyze the 
 function body of `fun1` where `T` is now a  system function.
 
 DMD is not suited to do this kind of backtracking, and that would 
 potentially be very slow as well, so this probably won't be fixed in the 
 general case.
 ...
I agree. I don't think there should be backtracking as it won't terminate in the general case even if there are consistent solutions to all constraints. I guess in this case it's just annoying that there is no obvious default to choose if you are pressed to decide the attributes while still inferring them. I guess you could defer resolving them in derived types as well and couple inference appropriately, but I don't know if it is worth it, seems a bit tricky to get right/efficient with template instantiations because argument types might change after the fact.
 However, in the absence of typeof() and other function type 
 dependencies, a list of callees for each function can be maintained to 
 solve the most common case of mutual function dependencies through 
 simple function calls.
+1.
Nov 03 2022