digitalmars.D - Recursion and attribute inference
- Dukc (22/22) Nov 02 2022 While writing about attribute inference to a D blog post draft, I
- Paul Backus (9/10) Nov 02 2022 I'm inclined to say the implementation is right, since the code
- zjh (5/7) Nov 02 2022 Is there any plan to deduce `functions properties` ?
- Paul Backus (3/11) Nov 02 2022 I believe it's being considered as an option, but there are no
- Dukc (2/7) Nov 03 2022 I'm going to assume that. Thanks.
- Dennis (54/62) Nov 03 2022 Right.
- Timon Gehr (11/26) Nov 03 2022 I agree. I don't think there should be backtracking as it won't
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
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
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
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:I believe it's being considered as an option, but there are no concrete plans yet. Personally, I think it's a good idea.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
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:I'm going to assume that. Thanks.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.
Nov 03 2022
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.orgRight. 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
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