digitalmars.D - Function pointers/delegates default args were stealth removed?
- Manu (20/20) Aug 26 2012 I just updated to 2.60 and found errors throughout my code where functio...
- Peter Alexander (5/5) Aug 26 2012 I don't think there's any cases where the change can't be worked
- Walter Bright (9/27) Aug 26 2012 The trouble is that, as 3866 shows, there is no design anyone could come...
- bearophile (5/9) Aug 26 2012 I think Ada is doing what D does now, it doesn't accept default
- Timon Gehr (2/4) Aug 26 2012 They could be made part of the variable declaration.
- Walter Bright (5/11) Aug 26 2012 You mean part of the function pointer variable?
- Timon Gehr (14/27) Aug 26 2012 If it is a template function, yes. But then you may as well pass the
- Walter Bright (5/35) Aug 26 2012 You pass the function declaration by alias, not the function pointer
- Chris Cain (47/47) Aug 26 2012 If I may, here's my suggestion on how everything could work (and
- Walter Bright (3/7) Aug 26 2012 What happens with the name mangling? What about overloading? template ty...
- Chris Cain (21/24) Aug 26 2012 Name mangling: Without knowing enough about compiler making, I'm
- Walter Bright (5/10) Aug 26 2012 The mangled names have a 1:1 correspondence with types. A mangled name c...
- Piotr Duda (11/27) Aug 26 2012 n,
- Walter Bright (6/10) Aug 27 2012 And then there's a list of other bugs that show up. Now you have two dif...
- Piotr Duda (8/17) Aug 27 2012 For linker these types should be identical, so there shouldn't be any
- Timon Gehr (9/24) Aug 27 2012 You said they should be part of the type for passing as template args:
- Artur Skawina (3/32) Aug 27 2012 Anonymous functions must be unique anyway.
- Timon Gehr (2/34) Aug 27 2012 What is the point?
- Walter Bright (3/4) Aug 27 2012 For that case, there is none, as you should be passing the lambda by ali...
- Artur Skawina (7/42) Aug 27 2012 I misunderstood, my fault, sorry.
- Manu (13/29) Aug 27 2012 This sounds like an implementation detail/dmd quirk is defining the
- Paulo Pinto (6/37) Aug 27 2012 For it sounds like constraining the language while keeping the
- Manu (3/10) Aug 27 2012 I don't see how the linker enters into it. Default args are irrelevant t...
- Walter Bright (5/7) Aug 27 2012 Consider name mangling and what it's for. Now consider two *different* t...
- Artur Skawina (39/46) Aug 27 2012 D or DMD? Because the relevant bug /was/ an implementation problem. I ca...
- Walter Bright (7/18) Aug 27 2012 Which revealed a design problem in the language.
- Walter Bright (3/5) Aug 27 2012 That's just one of the problems. And no, we can't go write our own linke...
- Jonathan M Davis (5/9) Aug 27 2012 Default args are essentially copy-pasted at the call point. It makes no ...
- Carl Sturtivant (3/16) Aug 27 2012 Agreed. So see my way of simulating these at run-time without
- Carl Sturtivant (28/33) Aug 27 2012 The following has other pervasive ramifications, to say the
- Carl Sturtivant (9/22) Aug 27 2012 That's a great idea Carl! You mean something like this:
- Simen Kjaeraas (5/19) Aug 27 2012 You *do* know you're talking to yourself, right?
- Carl Sturtivant (3/22) Aug 27 2012 My alter ego, you mean?
- Carl Sturtivant (5/15) Aug 27 2012 oops! last line was supposed to be:
- Manu (2/4) Aug 27 2012 If that were true, we wouldn't be having this discussion.
- Carl Sturtivant (28/36) Aug 27 2012 I think that we (at a minimum me and my alter ego) would find it
- Timon Gehr (9/55) Aug 27 2012 The function declaration is not necessarily available and the
- Carl Sturtivant (16/30) Aug 27 2012 This problem goes away if the defaults are always the same: the
- Carl Sturtivant (50/64) Aug 28 2012 I think this is the right behavior too. Default arguments are
- Timon Gehr (14/37) Aug 28 2012 This interpretation is simply wrong.
- Carl Sturtivant (10/57) Aug 28 2012 Function inlining or not in the presence of alloca calls and
- Carl Sturtivant (6/71) Aug 29 2012 Another, more conservative, approach would be for the compiler to
- Carl Sturtivant (22/90) Aug 29 2012 Here is an additional implementation possibility for the minimal
- Manu (50/89) Aug 26 2012 To be fair, it's not a very lively discussion. Spans 1 day, and only 3
- Walter Bright (14/59) Aug 26 2012 That's the way it used to work. The problem is, what are those types? Ar...
- Manu (18/52) Aug 27 2012 They are always equivalent. The default args are more like metadata.
- Walter Bright (7/12) Aug 27 2012 It was probably reduced from a larger scenario. Reduced bugs usually loo...
- Manu (82/91) Aug 27 2012 And how was that a problem? They never interacted in the example, the
- Carl Sturtivant (6/10) Aug 27 2012 If function pointers could be called with fewer than the
- Manu (9/18) Aug 27 2012 ... no.
- Carl Sturtivant (13/45) Aug 27 2012 Yes, I am suggesting that to get the default you want you define
- Carl Sturtivant (34/66) Aug 28 2012 More specifically, you can get the default initialization of e.g.
- Robert Clipsham (16/31) Aug 27 2012 I seem to recall I looked at this issue myself at one point. It
- Timon Gehr (5/29) Aug 27 2012 If the compiler design *requires* equal types to be stored uniquely in
- Walter Bright (6/25) Aug 27 2012 The language design requires a 1:1 mapping of mangling to types. Hence t...
- Jacob Carlborg (5/9) Aug 27 2012 How does this then work when the body of the anonymous functions are
- Walter Bright (2/4) Aug 28 2012 I don't know what you mean.
- Jacob Carlborg (20/21) Aug 28 2012 The original problem in the bug report looked like this:
- Robert Clipsham (2/12) Aug 28 2012 The body of the function has nothing to do with its type.
- Jacob Carlborg (4/5) Aug 29 2012 If I change the body of one of the functions the problem doesn't appear.
- Manu (5/19) Aug 27 2012 Cached in a hashmap! precisely what I suspected (without knowing anythin...
- foobar (9/41) Aug 27 2012 This discussion is all sorts of wrong. Whoever said that defargs
- Manu (3/10) Aug 27 2012 Yes, I suggested this as a possibility above, but nobody commented. It
- Andrei Alexandrescu (4/15) Aug 27 2012 Whilst I agree a metadata facility is an interesting topic to look into,...
- Timon Gehr (2/20) Aug 27 2012 +1.
- foobar (10/36) Aug 27 2012 The point was that there are _other_ motivating examples for
- Marco Leise (7/12) Aug 28 2012 Properties are currently implemented as "strip off the @ and
- foobar (13/23) Aug 29 2012 I was talking about complexity added to _the language_ and its
- Walter Bright (2/9) Aug 27 2012 The annotations design discussed was about annotating declarations, not ...
- Manu (3/19) Aug 27 2012 And that may be fine for default args too. I've personally only ever use...
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (4/18) Aug 27 2012 I had opened a related bug recently:
- Timon Gehr (2/18) Aug 27 2012 +1.
- Walter Bright (5/9) Aug 27 2012 Yes, I can make that example "work", but it would not make the general c...
- Walter Bright (8/26) Aug 27 2012 void delegate(int x) cppNonVirtualMethod;
- Manu (38/66) Aug 27 2012 I've actually considered this approach, and the overload isn't required ...
- Walter Bright (7/11) Aug 27 2012 These can all be handled in a straightforward (but admittedly wordy) met...
- Manu (17/30) Aug 27 2012 They can, see my prev post.
- Walter Bright (3/8) Aug 27 2012 Ok, thanks, it helps immensely to see just what you're trying to do. Let...
- kenji hara (32/47) Aug 27 2012 I think that the function type and its mangled name must not contain
- Timon Gehr (20/50) Aug 27 2012 I assume we'd like it to do that lest the user has to supply the type
- Steven Schveighoffer (62/90) Aug 27 2012 I think the type of the function pointer and the type (and mangling) of ...
- Manu (20/45) Aug 27 2012 I'd say, will.
- Andrei Alexandrescu (14/16) Aug 27 2012 I think the matter here is that we're looking at adding very significant...
- Manu (48/68) Aug 27 2012 ... what?
- Andrei Alexandrescu (25/65) Aug 27 2012 My understanding is that the bug revealed serious incompleteness in the
- Steven Schveighoffer (9/19) Aug 27 2012 That's not true, Kenji asked how the feature should behave in certain
- Andrei Alexandrescu (3/10) Aug 27 2012 What's missing is "simple".
- Steven Schveighoffer (7/17) Aug 27 2012 Simple as in simple to understand, or as in simple to implement? I thin...
- deadalnix (2/4) Aug 27 2012 You didn't, but I'd be foolish to assert otherwise.
- Jacob Carlborg (4/5) Aug 27 2012 No one really knows. It's probably a mix of dmd, dlang.org and TDPL.
- deadalnix (5/44) Aug 27 2012 That guy just have to rework 5 projects. This is quite a lot of work.
- Maxim Fomin (12/19) Aug 27 2012 I may be considered rude, but as I have understood from the first
- deadalnix (3/20) Aug 27 2012 The amount of corrected bug make the option of using an older compiler
- foobar (12/34) Aug 27 2012 All true, except one crucial fact: DMD gets critical bug fixes
- Jonathan M Davis (8/15) Aug 27 2012 Except that the change which is causing Manu problems _isn't_ a new feat...
- Andrei Alexandrescu (8/19) Aug 27 2012 Yah, this particular example is more of an example of the limitation of
- Jonathan M Davis (9/20) Aug 27 2012 Oh, I agree. I'm just saying that this is not a problem that a major.min...
- Jacob Carlborg (7/13) Aug 28 2012 The fix for this particular bug was to remove a language feature. I
- foobar (14/36) Aug 28 2012 I guess your definition of stable is different than mine.
- Manu (13/27) Aug 27 2012 Where is that stated? It appeared to be a perfectly good, working, and
- Jacob Carlborg (4/6) Aug 28 2012 Yes, they don't properly work in D.
- Jonathan M Davis (20/25) Aug 27 2012 You can't possibly really be using these functions with default argument...
- Daniel Murphy (14/29) Aug 28 2012 Yes, this. I looked into fixing issue 3866 earlier this year, and Kenji...
- Manu (28/61) Aug 28 2012 Well that's painful for a number of reasons.
- Andrei Alexandrescu (6/8) Aug 28 2012 Walter and Kenji think breaking meaningful existing code is an
-
bearophile
(6/8)
Aug 28 2012
Maybe as soon as the D1 branch stops getting bug fixes, - Carl Sturtivant (6/15) Aug 28 2012 I replied to Walter just now with a way to get a clean mechanism
- Manu (6/15) Aug 28 2012 Wow, I didn't see that coming.
- Jonathan M Davis (9/29) Aug 28 2012 The funny thing about that is that for the most part, language features ...
- Manu (6/39) Aug 29 2012 I've conceded that I don't mind changing my code, if a satisfactory
- kenji hara (7/17) Sep 06 2012 OK. I have finished working to implement it as a new pull request.
- Andrei Alexandrescu (3/9) Sep 06 2012 Thank you very much!
- Manu (3/8) Sep 12 2012 Awesome, thanks for this!
- Manu (19/40) Aug 27 2012 How do I use a function pointer 'like a function pointer' by your reason...
- Walter Bright (13/15) Aug 27 2012 Can you please post a canonical 99% use case that you use? I cannot sugg...
- Steven Schveighoffer (41/45) Aug 27 2012 OK, I've read all the posts on this, and this is what I think would solv...
- Don Clugston (13/29) Aug 28 2012 This sounds like sloppy thinking.
- Steven Schveighoffer (11/45) Aug 29 2012 First, I don't see IFTI anywhere in there.
- Maxim Fomin (22/22) Aug 27 2012 I think that D should be consistent with rules of types
- Jonathan M Davis (19/24) Aug 29 2012 Ideally, something which is going to be deprecated is first scheduled fo...
I just updated to 2.60 and found errors throughout my code where function pointers default args no longer work. *Every single project* I've written in D, 5 projects, don't work anymore, including my projects at work. I found this discussion: http://d.puremagic.com/issues/show_bug.cgi?id=3866 It seems the change was just decided and implemented with basically no discussion or argument at all :/ My use cases are dynamic linkage, and cross-language integration. I can't manually interact with DLL's containing API's that expect to have default arguments if function pointers no longer support them. Also when receiving foreign language function pointers, they frequently need to have default args too. I also integrate with many C style API's (rendering engines and the like), which involve registration of various callbacks, and lots of those have default args too. I find this particularly surprising, since I recently motivated implementation of new traits which could parse default args from parameter lists, and use that to generation function pointers in templates which auto-magically clone functions parameter lists verbatim, specifically including the default args...
Aug 26 2012
I don't think there's any cases where the change can't be worked around relatively easily. Correct me if I'm wrong. I was also surprised to see this removed without mention, but after reading the bug, it seems like the correct decision. I don't like the idea of adding default arguments to types.
Aug 26 2012
On 8/26/2012 3:26 PM, Manu wrote:I just updated to 2.60 and found errors throughout my code where function pointers default args no longer work. _Every single project_ I've written in D, 5 projects, don't work anymore, including my projects at work. I found this discussion: http://d.puremagic.com/issues/show_bug.cgi?id=3866 It seems the change was just decided and implemented with basically no discussion or argument at all :/ My use cases are dynamic linkage, and cross-language integration. I can't manually interact with DLL's containing API's that expect to have default arguments if function pointers no longer support them. Also when receiving foreign language function pointers, they frequently need to have default args too. I also integrate with many C style API's (rendering engines and the like), which involve registration of various callbacks, and lots of those have default args too. I find this particularly surprising, since I recently motivated implementation of new traits which could parse default args from parameter lists, and use that to generation function pointers in templates which auto-magically clone functions parameter lists verbatim, specifically including the default args...The trouble is that, as 3866 shows, there is no design anyone could come up with that worked in a consistent manner. The only consistent way out was to make default arguments a characteristic of the declaration, not of the type. The trouble for function pointers, is that any default args would need to be part of the type, not the declaration. I know it broke code (for many others, too), and I'm very sorry about that, but I don't see another way out. (Many uses of default arguments can be replaced with overloaded functions.)
Aug 26 2012
Walter Bright:The trouble is that, as 3866 shows, there is no design anyone could come up with that worked in a consistent manner. The only consistent way out was to make default arguments a characteristic of the declaration, not of the type.I think Ada is doing what D does now, it doesn't accept default arguments for things like function pointers/delegates. Bye, bearophile
Aug 26 2012
On 08/27/2012 12:41 AM, Walter Bright wrote:The trouble for function pointers, is that any default args would need to be part of the type, not the declaration.They could be made part of the variable declaration.
Aug 26 2012
On 8/26/2012 4:50 PM, Timon Gehr wrote:On 08/27/2012 12:41 AM, Walter Bright wrote:You mean part of the function pointer variable? Consider what you do with a function pointer - you pass it to someone else. That someone else gets it as a type, not a declaration. I.e. you lose the default argument information, since that is not attached to the type.The trouble for function pointers, is that any default args would need to be part of the type, not the declaration.They could be made part of the variable declaration.
Aug 26 2012
On 08/27/2012 02:44 AM, Walter Bright wrote:On 8/26/2012 4:50 PM, Timon Gehr wrote:Yes.On 08/27/2012 12:41 AM, Walter Bright wrote:You mean part of the function pointer variable?The trouble for function pointers, is that any default args would need to be part of the type, not the declaration.They could be made part of the variable declaration.Consider what you do with a function pointer - you pass it to someone else. That someone else gets it as a type, not a declaration.If it is a template function, yes. But then you may as well pass the function pointer variable per alias, which is common. Otherwise someone else gets nothing but a parameter declaration.I.e. you lose the default argument information, since that is not attached to the type.I think most valid existing use cases would still be supported: int execFunctionPointer(int function(int = 2) fun){ return fun(); } auto dg = (int x, int y=2){ return x+y; } writeln(range.map!dg()); int function(int=3)[] funs; funs[0](); It is up to you if it is worth the effort, of course.
Aug 26 2012
On 8/26/2012 6:55 PM, Timon Gehr wrote:On 08/27/2012 02:44 AM, Walter Bright wrote:You pass the function declaration by alias, not the function pointer declaration. In which case you will get the default arguments.On 8/26/2012 4:50 PM, Timon Gehr wrote:Yes.On 08/27/2012 12:41 AM, Walter Bright wrote:You mean part of the function pointer variable?The trouble for function pointers, is that any default args would need to be part of the type, not the declaration.They could be made part of the variable declaration.Consider what you do with a function pointer - you pass it to someone else. That someone else gets it as a type, not a declaration.If it is a template function, yes. But then you may as well pass the function pointer variable per alias, which is common.Otherwise someone else gets nothing but a parameter declaration.and it falls apart immediately once you try to transfer that function pointer anywhere.I.e. you lose the default argument information, since that is not attached to the type.I think most valid existing use cases would still be supported: int execFunctionPointer(int function(int = 2) fun){ return fun(); } auto dg = (int x, int y=2){ return x+y; } writeln(range.map!dg()); int function(int=3)[] funs; funs[0](); It is up to you if it is worth the effort, of course.
Aug 26 2012
If I may, here's my suggestion on how everything could work (and I think should be doable) ... basically, it would be part of the type, but functions with the same parameters regardless of defaults would be implicitly convertible to one another: ------ void efpBad( int function(int) fun ) { return fun(); // Fails to compile, as expected } void efp1( int function(int) fun ) { return fun(1); // Compiles fine, works as expected } void efp1Def1( int function(int = 1) fun ) { return fun(); // Compiles fine, equivalent to fun(1) which should be expected } auto fn1p = (int x) { return x; }; auto fn1pDef = (int x = 2) { return x; }; auto fn2p = (int x, int y) { return x + y; }; auto fn2pDef = (int x, int y = 2) { return x + y; }; don't match. (even though 1 has provided a "default") efp2(fn1p); // Success ... returns 1 as expected efp2(fn1pDef); // Ditto efp3(fn1p); // Compiles ... implicitly convertible, returns 1 (efp3 is providing the default above) efp3(fn1pDef); // Successfully compiles, returns 1 ... NOT 5 (because 1 is provided as the default in efp3 above) // Implicitly convertible between variables fn1p = fn1pDef; // Perfectly fine fn1p(); // Fails to compile, must provide an argument! fn1pDef = fn1p; // Again, fine. fn1pDef will allow the arg to have the default of 1 now. fn1pDef(); // Succeeds, returns 1 fn1p = fn2pDef; // Fails to compile, as expected fn1p = fn2p; // Nonsense, so it fails, of course // IsExpressions is(typeof(fn1p) == typeof(fn1pDef)); // False is(typeof(fn1p) : typeof(fn1pDef)); // True is(typeof(fn1pDef) : typeof(fn1p)); // True I hope that was rigorous enough to show the behavior when passing around and such. I feel like this behavior would be acceptable for almost every use. I'm pretty sure this is how people were expecting it to behave before, anyway. Is there a problem with it working this way? (Obviously, I'm no compiler expert)
Aug 26 2012
On 8/26/2012 8:49 PM, Chris Cain wrote:If I may, here's my suggestion on how everything could work (and I think should be doable) ... basically, it would be part of the type, but functions with the same parameters regardless of defaults would be implicitly convertible to one another:What happens with the name mangling? What about overloading? template type deduction? type specialization? type equivalence? type covariance?
Aug 26 2012
On Monday, 27 August 2012 at 04:01:10 UTC, Walter Bright wrote:What happens with the name mangling? What about overloading? template type deduction? type specialization? type equivalence? type covariance?Name mangling: Without knowing enough about compiler making, I'm not sure why it matters. I couldn't answer, sorry. Overloading: I'm making a guess, but do you mean something like this? void fn( int function(int) fp ) { ... } void fn( int function(int = 1) fp ) { ... } It should fail to compile. Type specialization: Templates? Bar!( int function(int) ) would have to be different than Bar!( int function(int = 1) ), I would think. Template type deduction: I don't really understand the question with this, actually. Type equivalence, type covariance: Like the is expressions I showed before? Or what do you mean? They should be implicitly convertible between each other. I'm just giving you the standpoint of a user of the language, not necessarily a compiler writer (I have an extremely basic understanding of compilers ... mostly, I just know lexing and parsing at the moment), so I'm having difficulty understanding what some of the problems with this is.
Aug 26 2012
On 8/26/2012 9:25 PM, Chris Cain wrote:On Monday, 27 August 2012 at 04:01:10 UTC, Walter Bright wrote:The mangled names have a 1:1 correspondence with types. A mangled name can, for example, be reversed into a type. If default args form part of the type, then they'll have to be mangled in, too. This causes a rather long list of substantial problems.What happens with the name mangling? What about overloading? template type deduction? type specialization? type equivalence? type covariance?Name mangling: Without knowing enough about compiler making, I'm not sure why it matters. I couldn't answer, sorry.
Aug 26 2012
2012/8/27 Walter Bright <newshound2 digitalmars.com>:On 8/26/2012 9:25 PM, Chris Cain wrote:eOn Monday, 27 August 2012 at 04:01:10 UTC, Walter Bright wrote:What happens with the name mangling? What about overloading? template type deduction? type specialization? type equivalence? type covariance?Name mangling: Without knowing enough about compiler making, I'm not sur=n,why it matters. I couldn't answer, sorry.The mangled names have a 1:1 correspondence with types. A mangled name ca=for example, be reversed into a type. If default args form part of the type, then they'll have to be mangled in=,too. This causes a rather long list of substantial problems.Default args should be part of types (for passing them as template args etc, implicity convertable if they differs only on defaults) but not mangled in (since mangling is revelant only for linking, where defaults doesn't matter). --=20 =E9=97=87=E3=81=AB=E9=9A=A0=E3=82=8C=E3=81=9F=E9=BB=92=E3=81=84=E5=8A=9B =E5=BC=B1=E3=81=84=E5=BF=83=E3=82=92=E6=93=8D=E3=82=8B
Aug 26 2012
On 8/26/2012 11:14 PM, Piotr Duda wrote:Default args should be part of types (for passing them as template args etc, implicity convertable if they differs only on defaults) but not mangled in (since mangling is revelant only for linking, where defaults doesn't matter).And then there's a list of other bugs that show up. Now you have two different types showing up as the same type (i.e. name) to the linker, and you've got weird collisions. Really, a type that is the same only different is a recipe for endless subtle and ugly problems. It's a nightmare.
Aug 27 2012
2012/8/27 Walter Bright <newshound2 digitalmars.com>:On 8/26/2012 11:14 PM, Piotr Duda wrote:dDefault args should be part of types (for passing them as template args etc, implicity convertable if they differs only on defaults) but not mangled in (since mangling is revelant only for linking, where defaults doesn't matter).And then there's a list of other bugs that show up. Now you have two different types showing up as the same type (i.e. name) to the linker, an=you've got weird collisions.For linker these types should be identical, so there shouldn't be any collisions, unless D handles default args fundamentally different than C++. --=20 =E9=97=87=E3=81=AB=E9=9A=A0=E3=82=8C=E3=81=9F=E9=BB=92=E3=81=84=E5=8A=9B =E5=BC=B1=E3=81=84=E5=BF=83=E3=82=92=E6=93=8D=E3=82=8B
Aug 27 2012
On 08/27/2012 10:48 AM, Piotr Duda wrote:2012/8/27 Walter Bright <newshound2 digitalmars.com>:You said they should be part of the type for passing as template args: auto foo(T)(T dg){ return dg(); } // therefore assert(foo((int x=2)=>x)==2); // this instantiation assert(foo((int x=3)=>x)==3); // must differ from this one // ergo, they cannot have the same mangled symbol name!On 8/26/2012 11:14 PM, Piotr Duda wrote:For linker these types should be identical, so there shouldn't be any collisions, unless D handles default args fundamentally different than C++.Default args should be part of types (for passing them as template args etc, implicity convertable if they differs only on defaults) but not mangled in (since mangling is revelant only for linking, where defaults doesn't matter).And then there's a list of other bugs that show up. Now you have two different types showing up as the same type (i.e. name) to the linker, and you've got weird collisions.
Aug 27 2012
On 08/27/12 12:54, Timon Gehr wrote:On 08/27/2012 10:48 AM, Piotr Duda wrote:Anonymous functions must be unique anyway. artur2012/8/27 Walter Bright <newshound2 digitalmars.com>:You said they should be part of the type for passing as template args: auto foo(T)(T dg){ return dg(); } // therefore assert(foo((int x=2)=>x)==2); // this instantiation assert(foo((int x=3)=>x)==3); // must differ from this one // ergo, they cannot have the same mangled symbol name!On 8/26/2012 11:14 PM, Piotr Duda wrote:For linker these types should be identical, so there shouldn't be any collisions, unless D handles default args fundamentally different than C++.Default args should be part of types (for passing them as template args etc, implicity convertable if they differs only on defaults) but not mangled in (since mangling is revelant only for linking, where defaults doesn't matter).And then there's a list of other bugs that show up. Now you have two different types showing up as the same type (i.e. name) to the linker, and you've got weird collisions.
Aug 27 2012
On 08/27/2012 01:16 PM, Artur Skawina wrote:On 08/27/12 12:54, Timon Gehr wrote:What is the point?On 08/27/2012 10:48 AM, Piotr Duda wrote:Anonymous functions must be unique anyway. artur2012/8/27 Walter Bright <newshound2 digitalmars.com>:You said they should be part of the type for passing as template args: auto foo(T)(T dg){ return dg(); } // therefore assert(foo((int x=2)=>x)==2); // this instantiation assert(foo((int x=3)=>x)==3); // must differ from this one // ergo, they cannot have the same mangled symbol name!On 8/26/2012 11:14 PM, Piotr Duda wrote:For linker these types should be identical, so there shouldn't be any collisions, unless D handles default args fundamentally different than C++.Default args should be part of types (for passing them as template args etc, implicity convertable if they differs only on defaults) but not mangled in (since mangling is revelant only for linking, where defaults doesn't matter).And then there's a list of other bugs that show up. Now you have two different types showing up as the same type (i.e. name) to the linker, and you've got weird collisions.
Aug 27 2012
On 8/27/2012 4:46 AM, Timon Gehr wrote:What is the point?For that case, there is none, as you should be passing the lambda by alias rather than by pointer.
Aug 27 2012
On 08/27/12 13:46, Timon Gehr wrote:On 08/27/2012 01:16 PM, Artur Skawina wrote:I misunderstood, my fault, sorry. A template instantiation with a parameter containing a default argument must of course reflect that argument in the mangled name. Are there other cases where def-args can't be ignored? (not where they are just "lost", that is not a problem) arturOn 08/27/12 12:54, Timon Gehr wrote:What is the point?On 08/27/2012 10:48 AM, Piotr Duda wrote:Anonymous functions must be unique anyway.2012/8/27 Walter Bright <newshound2 digitalmars.com>:You said they should be part of the type for passing as template args: auto foo(T)(T dg){ return dg(); } // therefore assert(foo((int x=2)=>x)==2); // this instantiation assert(foo((int x=3)=>x)==3); // must differ from this one // ergo, they cannot have the same mangled symbol name!On 8/26/2012 11:14 PM, Piotr Duda wrote:For linker these types should be identical, so there shouldn't be any collisions, unless D handles default args fundamentally different than C++.Default args should be part of types (for passing them as template args etc, implicity convertable if they differs only on defaults) but not mangled in (since mangling is revelant only for linking, where defaults doesn't matter).And then there's a list of other bugs that show up. Now you have two different types showing up as the same type (i.e. name) to the linker, and you've got weird collisions.
Aug 27 2012
On 27 August 2012 07:52, Walter Bright <newshound2 digitalmars.com> wrote:On 8/26/2012 9:25 PM, Chris Cain wrote:This sounds like an implementation detail/dmd quirk is defining the language spec... I generally agree with the usage proposal above, it should be metadata that doesn't affect type equivalence (although I'd probably expect is(f1 == f2def) should be true rather than false). If that info needs to be mangled into the name to reproduce the type later, fine, is that a problem? Perhaps stick it at the end of the mangled name in a new metadata suffix that is truncated prior to any comparisons for equality? This actually rather relates to the attribute/annotation proposals/conversations we were having some time back. If this is addressed, then it may enable attributes too.On Monday, 27 August 2012 at 04:01:10 UTC, Walter Bright wrote:The mangled names have a 1:1 correspondence with types. A mangled name can, for example, be reversed into a type. If default args form part of the type, then they'll have to be mangled in, too. This causes a rather long list of substantial problems.What happens with the name mangling? What about overloading? template type deduction? type specialization? type equivalence? type covariance?Name mangling: Without knowing enough about compiler making, I'm not sure why it matters. I couldn't answer, sorry.
Aug 27 2012
On Monday, 27 August 2012 at 07:54:12 UTC, Manu wrote:On 27 August 2012 07:52, Walter Bright <newshound2 digitalmars.com> wrote:For it sounds like constraining the language while keeping the C/C++ linker semantics, instead of using a D aware linker. Not trolling, just trying to understand the design constraints. -- PauloOn 8/26/2012 9:25 PM, Chris Cain wrote:This sounds like an implementation detail/dmd quirk is defining the language spec...On Monday, 27 August 2012 at 04:01:10 UTC, Walter Bright wrote:The mangled names have a 1:1 correspondence with types. A mangled name can, for example, be reversed into a type. If default args form part of the type, then they'll have to be mangled in, too. This causes a rather long list of substantial problems.What happens with the name mangling? What about overloading? template type deduction? type specialization? type equivalence? type covariance?Name mangling: Without knowing enough about compiler making, I'm not sure why it matters. I couldn't answer, sorry.
Aug 27 2012
On 27 August 2012 11:01, Paulo Pinto <pjmlp progtools.org> wrote:On Monday, 27 August 2012 at 07:54:12 UTC, Manu wrote:I don't see how the linker enters into it. Default args are irrelevant to the linker.This sounds like an implementation detail/dmd quirk is defining the language spec...For it sounds like constraining the language while keeping the C/C++ linker semantics, instead of using a D aware linker.
Aug 27 2012
On 8/27/2012 1:10 AM, Manu wrote:I don't see how the linker enters into it. Default args are irrelevant to the linker.Consider name mangling and what it's for. Now consider two *different* types mangling to the same name. D fundamentally depends on a 1:1 correspondence between types and name mangling, not 1:n or n:1.
Aug 27 2012
On 08/27/12 10:24, Walter Bright wrote:On 8/27/2012 1:10 AM, Manu wrote:D or DMD? Because the relevant bug /was/ an implementation problem. I can see how changing the implementation is not really practical short-term, hence didn't comment on it at the time, but even the examples given as "unsound" were fine and could be well defined. Type equivalence != type identity. Default args shouldn't be part of the mangled names. But having them (defargs) for function pointers can be very useful, esp. for generics ("void f(int=42); auto fp = &f; fp();" could be made to work), it reduces the amount of glue required and increases productivity - one of D's main strengths. In the mean time hacks such as this one can probably help sometimes, like in the automatically-generated-bindings cases: void f(int one, string two, double three) { import std.stdio; writeln(one, two, three); } void main() { auto fp = CWDA!(typeof(&f), "1", "\"two\"", "3.14")(&f); fp(1, "two", 3.14); fp(1, "two"); fp(1); fp(); fp(four/4); } int four = 4; static struct _CWDA(C, DA...) { C ptr; alias ptr this; import std.traits; static if (DA.length==3) // POC; handling other cases left as an exercise ;) auto ref opCall(ParameterTypeTuple!C[0..$-3] a, ParameterTypeTuple!C[$-3] da1 = mixin(DA[$-3]), ParameterTypeTuple!C[$-2] da2 = mixin(DA[$-2]), ParameterTypeTuple!C[$-1] da3 = mixin(DA[$-1])) { return ptr(a, da1, da2, da3); } } auto CWDA(C, DA...)(C c) { _CWDA!(C, DA) p; p.ptr = c; return p; } but that's not really an acceptable solution, obviously. arturI don't see how the linker enters into it. Default args are irrelevant to the linker.Consider name mangling and what it's for. Now consider two *different* types mangling to the same name. D fundamentally depends on a 1:1 correspondence between types and name mangling, not 1:n or n:1.
Aug 27 2012
On 8/27/2012 3:34 AM, Artur Skawina wrote:On 08/27/12 10:24, Walter Bright wrote:D.On 8/27/2012 1:10 AM, Manu wrote:D or DMD?I don't see how the linker enters into it. Default args are irrelevant to the linker.Consider name mangling and what it's for. Now consider two *different* types mangling to the same name. D fundamentally depends on a 1:1 correspondence between types and name mangling, not 1:n or n:1.Because the relevant bug /was/ an implementation problem.Which revealed a design problem in the language.but that's not really an acceptable solution, obviously.Right, and having a bunch of half-assed solutions is not a good long term solution. We all know that C++ is full of such, and they've caused endless grief as subsequent designers try to fix them, and pages and pages of impenetrable explanations in the C++ spec.
Aug 27 2012
On 8/27/2012 1:01 AM, Paulo Pinto wrote:For it sounds like constraining the language while keeping the C/C++ linker semantics, instead of using a D aware linker.That's just one of the problems. And no, we can't go write our own linker on all platforms.
Aug 27 2012
On Monday, August 27, 2012 08:14:41 Piotr Duda wrote:Default args should be part of types (for passing them as template args etc, implicity convertable if they differs only on defaults) but not mangled in (since mangling is revelant only for linking, where defaults doesn't matter).Default args are essentially copy-pasted at the call point. It makes no sense for them to be part of the type, and it makes no sense to use them with function pointers or function literals. - Jonathan M Davis
Aug 27 2012
On Monday, 27 August 2012 at 08:26:15 UTC, Jonathan M Davis wrote:On Monday, August 27, 2012 08:14:41 Piotr Duda wrote:Agreed. So see my way of simulating these at run-time without adverse type consequences or any overhead for existing functions.Default args should be part of types (for passing them as template args etc, implicity convertable if they differs only on defaults) but not mangled in (since mangling is revelant only for linking, where defaults doesn't matter).Default args are essentially copy-pasted at the call point. It makes no sense for them to be part of the type, and it makes no sense to use them with function pointers or function literals.
Aug 27 2012
The mangled names have a 1:1 correspondence with types. A mangled name can, for example, be reversed into a type. If default args form part of the type, then they'll have to be mangled in, too. This causes a rather long list of substantial problems.The following has other pervasive ramifications, to say the least. Worth thinking about IMHO nevertheless, even if to eventually say "no". So here's a completely different way that the effect of default arguments can be obtained without messing with the type system. Suppose a function pointer is always permitted be called with fewer actual arguments than parameters declared. I'm speculating uncritically here, so bear with me; this could be restricted to calling function pointers & delegate pointers, or with an avalanche of consequences permitted also for direct function calling where it will interact with the overloading rules, or ... there are many possible arrangements. So let's forge ahead for now with the basic idea for function pointers. Suppose a function pointer can be called with fewer actual arguments than the number of parameters in its declaration. Suppose that when such a call is made, the missing arguments will always be assigned the default initialization for their types (default default-arguments!). Now suppose that a language mechanism is provided so that code in the function body can determine how many actual arguments were supplied at the point of call. Now any function pointer can simulate other default arguments (non-default default arguments) by testing the actual number of arguments supplied and assigning defaults overtly to the remainder inside the function body. No need for new types: this is a run-time action. Now you can stamp on this with big logic boots, but let's see if I can escape from some of the trampling!
Aug 27 2012
Suppose a function pointer can be called with fewer actual arguments than the number of parameters in its declaration. Suppose that when such a call is made, the missing arguments will always be assigned the default initialization for their types (default default-arguments!). Now suppose that a language mechanism is provided so that code in the function body can determine how many actual arguments were supplied at the point of call. Now any function pointer can simulate other default arguments (non-default default arguments) by testing the actual number of arguments supplied and assigning defaults overtly to the remainder inside the function body. No need for new types: this is a run-time action.That's a great idea Carl! You mean something like this: int sum(int a, int b) { if( argc == 1 ) b = 1; //default for b if not supplied return a + b; } //... auto f = ∑ //... auto x = sum(y); //function pointer call, so fewer args permitted
Aug 27 2012
On Mon, 27 Aug 2012 11:57:45 +0200, Carl Sturtivant <sturtivant gmail.com> wrote:You *do* know you're talking to yourself, right? -- SimenSuppose a function pointer can be called with fewer actual arguments than the number of parameters in its declaration. Suppose that when such a call is made, the missing arguments will always be assigned the default initialization for their types (default default-arguments!). Now suppose that a language mechanism is provided so that code in the function body can determine how many actual arguments were supplied at the point of call. Now any function pointer can simulate other default arguments (non-default default arguments) by testing the actual number of arguments supplied and assigning defaults overtly to the remainder inside the function body. No need for new types: this is a run-time action.That's a great idea Carl!
Aug 27 2012
On Monday, 27 August 2012 at 10:12:28 UTC, Simen Kjaeraas wrote:On Mon, 27 Aug 2012 11:57:45 +0200, Carl Sturtivant <sturtivant gmail.com> wrote:My alter ego, you mean? Someone has to talk to the poor guy.You *do* know you're talking to yourself, right?Suppose a function pointer can be called with fewer actual arguments than the number of parameters in its declaration. Suppose that when such a call is made, the missing arguments will always be assigned the default initialization for their types (default default-arguments!). Now suppose that a language mechanism is provided so that code in the function body can determine how many actual arguments were supplied at the point of call. Now any function pointer can simulate other default arguments (non-default default arguments) by testing the actual number of arguments supplied and assigning defaults overtly to the remainder inside the function body. No need for new types: this is a run-time action.That's a great idea Carl!
Aug 27 2012
That's a great idea Carl! You mean something like this: int sum(int a, int b) { if( argc == 1 ) b = 1; //default for b if not supplied return a + b; } //... auto f = ∑ //... auto x = sum(y); //function pointer call, so fewer args permittedoops! last line was supposed to be: auto x = f(y); //function pointer call, so fewer args permitted And for the record, 'argc' in the above is a name that has a similar status to 'this' and is equal to the number of arguments actually passed. Probably there's a better choice of name.
Aug 27 2012
On 27 August 2012 11:12, Jonathan M Davis <jmdavisProg gmx.com> wrote:and it makes no sense to use them with function pointers or function literals.If that were true, we wouldn't be having this discussion.
Aug 27 2012
On Monday, 27 August 2012 at 08:39:01 UTC, Manu wrote:On 27 August 2012 11:12, Jonathan M Davis <jmdavisProg gmx.com> wrote:I think that we (at a minimum me and my alter ego) would find it helpful to acknowledge something along the following lines (in no particular order). 1. There is call for a default argument mechanism when calling function or delegate pointers. [Those who've used these extensively know they want them! Now we think about it, we want them too.] 2. The existing default argument mechanism which is to simply pad the call at compile time with the defaults read from the function definition is ill suited to function pointer calls. [Those who want sane compiler machinery know this.] 3. It's helpful to overtly acknowledge that function calling and function pointer calling are quite different. [They are already different in D because overloading is applied statically, just as default arguments have now become something only applied statically. A function pointer is to one function and overloading therefore doesn't exist.] 4. Function pointer calls being dynamical could perhaps have a dynamical mechanism to assign default arguments at run time, so as to go along with notions 1,2,3 above. [That way the function decides which arguments have been defaulted and assigns their default values at runtime, so the language merely needs a mechanism for the function body when run to find out which parameters it needs to assign defaults to i.e. how many arguments have actually been provided when the function pointer was called.] ---hence my earlier proposal. I'm feeling lonely ---please at least shoot it down!and it makes no sense to use them with function pointers or function literals.If that were true, we wouldn't be having this discussion.
Aug 27 2012
On 08/27/2012 05:05 AM, Walter Bright wrote:On 8/26/2012 6:55 PM, Timon Gehr wrote:The function declaration is not necessarily available and the pointer/delegate might not be a constant. This is the point of function pointers and delegates.On 08/27/2012 02:44 AM, Walter Bright wrote:You pass the function declaration by alias, not the function pointer declaration. In which case you will get the default arguments.On 8/26/2012 4:50 PM, Timon Gehr wrote:Yes.On 08/27/2012 12:41 AM, Walter Bright wrote:You mean part of the function pointer variable?The trouble for function pointers, is that any default args would need to be part of the type, not the declaration.They could be made part of the variable declaration.Consider what you do with a function pointer - you pass it to someone else. That someone else gets it as a type, not a declaration.If it is a template function, yes. But then you may as well pass the function pointer variable per alias, which is common.No it does not. The examples people have posted where it breaks their code that I am aware of all would work with this scheme. What possible use case do you think would have to be supported by function pointer/delegate default arguments that cannot be supported without serious implementation problems?Otherwise someone else gets nothing but a parameter declaration.and it falls apart immediately once you try to transfer that function pointer anywhere.I.e. you lose the default argument information, since that is not attached to the type.I think most valid existing use cases would still be supported: int execFunctionPointer(int function(int = 2) fun){ return fun(); } auto dg = (int x, int y=2){ return x+y; } writeln(range.map!dg()); int function(int=3)[] funs; funs[0](); It is up to you if it is worth the effort, of course.
Aug 27 2012
On Monday, 27 August 2012 at 00:44:54 UTC, Walter Bright wrote:On 8/26/2012 4:50 PM, Timon Gehr wrote:This problem goes away if the defaults are always the same: the .init defaults, and if function pointers (but not functions) can be called with fewer arguments than there are parameters in their definition. When the call is made, the remaining argument can be provided using the standard .init defaults. And because the standard .init defaults can be manipulated via defining new types for the parameters, it becomes the parameter's types that the defaults are found from, and defaults do not need to be encoded in the types of function pointers. And if you don't like all function pointers getting this treatment, then it could be extended only to those that are given a qualification indicating so: one more binary bit added to mangling of its type. This mechanism could be made to work equally well with extern(C) etc. functions.On 08/27/2012 12:41 AM, Walter Bright wrote:You mean part of the function pointer variable? Consider what you do with a function pointer - you pass it to someone else. That someone else gets it as a type, not a declaration. I.e. you lose the default argument information, since that is not attached to the type.The trouble for function pointers, is that any default args would need to be part of the type, not the declaration.They could be made part of the variable declaration.
Aug 27 2012
On Monday, 27 August 2012 at 00:44:54 UTC, Walter Bright wrote:On 8/26/2012 4:50 PM, Timon Gehr wrote:I think this is the right behavior too. Default arguments are IMHO just a compact way to write some simply related overloaded functions, e.g. thus: int sum(int x, int y = 1 ) { return x + y; } is just a compact way to write int sum(int x, int y) { return x + y; } int sum(int x) { return sum(x, 1); } and a function pointer refers to a single function, so overloading and default arguments are irrelevant to function pointers. It just so happens that the particularly simple overloading abbreviated by a function definition with default arguments is easily optimized by the compiler with only one function arriving at the linker: the most general one. And that's the one whose address is supplied if the unary & operator is applied to it. So that's the problem with 'int sum(int x, int y = 1 ) ...' --- it's not one function under the interpretation above. So how could we interpret int function(int x, int y = 1) sum; in the above interpretation? What's happening right now is that sum is considered to be a single function pointer, so we discard the default to get the most general function type much like the effect of unary & above. So the trouble with the supposed type 'int function(int x, int y = 1)' is that it is not one type, just as the function sum above is not one function. [Speculation mode starts here.] So what if function pointer declarations with defaults are considered to define several function pointers and not just one. e.g. thus: int function(int x, int y = 1) sum; could be taken by the compiler to mean int function(int x, int y) sum; //all the rest call back the general function pointer int function(int x) sum = function sum(int x) { return sum(x,1); } I haven't explored all of the ramifications of this, but it looks as if it solves quite a few problems without changing the type system, or affecting existing code. And there's a simple model of what's going on: overloading, just generalized slightly. This may not be clean enough or general enough. But the basic point that something like 'int function(int x, int y = 1)' is a sequence of types is worth attention. A declaration using a sequence of types should produce a sequence of function pointers under this interpretation. Exactly how "sequence of types" and "sequence of pointers" should be wrapped up for best effect is then the language design decision to make. There are several other interesting ways to go, some involving getting more general and allowing other kinds of overloading with function pointer sequences, not just the special kind that comes from default arguments, but I'll stop here for now.On 08/27/2012 12:41 AM, Walter Bright wrote:You mean part of the function pointer variable? Consider what you do with a function pointer - you pass it to someone else. That someone else gets it as a type, not a declaration. I.e. you lose the default argument information, since that is not attached to the type.The trouble for function pointers, is that any default args would need to be part of the type, not the declaration.They could be made part of the variable declaration.
Aug 28 2012
On 08/28/2012 10:33 PM, Carl Sturtivant wrote:On Monday, 27 August 2012 at 00:44:54 UTC, Walter Bright wrote:This interpretation is simply wrong. import std.stdio, std.c.stdlib; void* alloca20bytes(void* x = alloca(20)){ return x; } // your suggested transformation: /+void* alloca20bytes(void* x){ return x; } void* alloca20bytes(){ return alloca20bytes(alloca(20)); }+/ // would break the caller: void main(){ auto x = (cast(int*)alloca20bytes())[0..5]; x[] = 0; x[] += 2; writeln(x); }On 8/26/2012 4:50 PM, Timon Gehr wrote:I think this is the right behavior too. Default arguments are IMHO just a compact way to write some simply related overloaded functions, e.g. thus: int sum(int x, int y = 1 ) { return x + y; } is just a compact way to write int sum(int x, int y) { return x + y; } int sum(int x) { return sum(x, 1); } ...On 08/27/2012 12:41 AM, Walter Bright wrote:You mean part of the function pointer variable? Consider what you do with a function pointer - you pass it to someone else. That someone else gets it as a type, not a declaration. I.e. you lose the default argument information, since that is not attached to the type.The trouble for function pointers, is that any default args would need to be part of the type, not the declaration.They could be made part of the variable declaration.
Aug 28 2012
On Tuesday, 28 August 2012 at 21:40:01 UTC, Timon Gehr wrote:On 08/28/2012 10:33 PM, Carl Sturtivant wrote:Function inlining or not in the presence of alloca calls and similar using the existing stack frame are problematic. If the first call was inlined by the compiler, that would un-break the "problem". I suggest that we simply define default arguments via the transformation I suggested, and regard void* alloca20bytes(void* x = alloca(20)){ return x; } as broken. It's not compelling for a lot of reasons. Of course there may be something else wrong with the transformation! Fire away.On Monday, 27 August 2012 at 00:44:54 UTC, Walter Bright wrote:This interpretation is simply wrong. import std.stdio, std.c.stdlib; void* alloca20bytes(void* x = alloca(20)){ return x; } // your suggested transformation: /+void* alloca20bytes(void* x){ return x; } void* alloca20bytes(){ return alloca20bytes(alloca(20)); }+/ // would break the caller: void main(){ auto x = (cast(int*)alloca20bytes())[0..5]; x[] = 0; x[] += 2; writeln(x); }On 8/26/2012 4:50 PM, Timon Gehr wrote:I think this is the right behavior too. Default arguments are IMHO just a compact way to write some simply related overloaded functions, e.g. thus: int sum(int x, int y = 1 ) { return x + y; } is just a compact way to write int sum(int x, int y) { return x + y; } int sum(int x) { return sum(x, 1); } ...On 08/27/2012 12:41 AM, Walter Bright wrote:You mean part of the function pointer variable? Consider what you do with a function pointer - you pass it to someone else. That someone else gets it as a type, not a declaration. I.e. you lose the default argument information, since that is not attached to the type.The trouble for function pointers, is that any default args would need to be part of the type, not the declaration.They could be made part of the variable declaration.
Aug 28 2012
On Tuesday, 28 August 2012 at 23:34:54 UTC, Carl Sturtivant wrote:On Tuesday, 28 August 2012 at 21:40:01 UTC, Timon Gehr wrote:Another, more conservative, approach would be for the compiler to regard the non-general functions in such a group created by my transformation to be always inlined, so the default parameters are always evaluated before the call. Then your interesting example would work the way you expect.On 08/28/2012 10:33 PM, Carl Sturtivant wrote:Function inlining or not in the presence of alloca calls and similar using the existing stack frame are problematic. If the first call was inlined by the compiler, that would un-break the "problem". I suggest that we simply define default arguments via the transformation I suggested, and regard void* alloca20bytes(void* x = alloca(20)){ return x; } as broken. It's not compelling for a lot of reasons. Of course there may be something else wrong with the transformation! Fire away.On Monday, 27 August 2012 at 00:44:54 UTC, Walter Bright wrote:This interpretation is simply wrong. import std.stdio, std.c.stdlib; void* alloca20bytes(void* x = alloca(20)){ return x; } // your suggested transformation: /+void* alloca20bytes(void* x){ return x; } void* alloca20bytes(){ return alloca20bytes(alloca(20)); }+/ // would break the caller: void main(){ auto x = (cast(int*)alloca20bytes())[0..5]; x[] = 0; x[] += 2; writeln(x); }On 8/26/2012 4:50 PM, Timon Gehr wrote:I think this is the right behavior too. Default arguments are IMHO just a compact way to write some simply related overloaded functions, e.g. thus: int sum(int x, int y = 1 ) { return x + y; } is just a compact way to write int sum(int x, int y) { return x + y; } int sum(int x) { return sum(x, 1); } ...On 08/27/2012 12:41 AM, Walter Bright wrote:You mean part of the function pointer variable? Consider what you do with a function pointer - you pass it to someone else. That someone else gets it as a type, not a declaration. I.e. you lose the default argument information, since that is not attached to the type.The trouble for function pointers, is that any default args would need to be part of the type, not the declaration.They could be made part of the variable declaration.
Aug 29 2012
On Tuesday, 28 August 2012 at 20:33:50 UTC, Carl Sturtivant wrote:On Monday, 27 August 2012 at 00:44:54 UTC, Walter Bright wrote:Here is an additional implementation possibility for the minimal version where there are no new types. Parameters, e.g.: void fun(int x, int function(int x, int y = 1) fun) { //... j = fun(3); //alerts the compiler //... } could be analogously compiled to behave similarly, because the default arguments are 'nearby'. Here's what the compiler could in effect produce from the above. int fun(int x, int function(int x, int y) fun) { int function(int x) fun = function void(int x) { return fun(x, 1); } //... j = fun(3); //... } i.e. use local variables for the required specialized versions of the function. Of course then the question of "how near is nearby?" needs a design decision.On 8/26/2012 4:50 PM, Timon Gehr wrote:I think this is the right behavior too. Default arguments are IMHO just a compact way to write some simply related overloaded functions, e.g. thus: int sum(int x, int y = 1 ) { return x + y; } is just a compact way to write int sum(int x, int y) { return x + y; } int sum(int x) { return sum(x, 1); } and a function pointer refers to a single function, so overloading and default arguments are irrelevant to function pointers. It just so happens that the particularly simple overloading abbreviated by a function definition with default arguments is easily optimized by the compiler with only one function arriving at the linker: the most general one. And that's the one whose address is supplied if the unary & operator is applied to it. So that's the problem with 'int sum(int x, int y = 1 ) ...' --- it's not one function under the interpretation above. So how could we interpret int function(int x, int y = 1) sum; in the above interpretation? What's happening right now is that sum is considered to be a single function pointer, so we discard the default to get the most general function type much like the effect of unary & above. So the trouble with the supposed type 'int function(int x, int y = 1)' is that it is not one type, just as the function sum above is not one function. [Speculation mode starts here.] So what if function pointer declarations with defaults are considered to define several function pointers and not just one. e.g. thus: int function(int x, int y = 1) sum; could be taken by the compiler to mean int function(int x, int y) sum; //all the rest call back the general function pointer int function(int x) sum = function sum(int x) { return sum(x,1); } I haven't explored all of the ramifications of this, but it looks as if it solves quite a few problems without changing the type system, or affecting existing code. And there's a simple model of what's going on: overloading, just generalized slightly. This may not be clean enough or general enough. But the basic point that something like 'int function(int x, int y = 1)' is a sequence of types is worth attention. A declaration using a sequence of types should produce a sequence of function pointers under this interpretation. Exactly how "sequence of types" and "sequence of pointers" should be wrapped up for best effect is then the language design decision to make. There are several other interesting ways to go, some involving getting more general and allowing other kinds of overloading with function pointer sequences, not just the special kind that comes from default arguments, but I'll stop here for now.On 08/27/2012 12:41 AM, Walter Bright wrote:You mean part of the function pointer variable? Consider what you do with a function pointer - you pass it to someone else. That someone else gets it as a type, not a declaration. I.e. you lose the default argument information, since that is not attached to the type.The trouble for function pointers, is that any default args would need to be part of the type, not the declaration.They could be made part of the variable declaration.
Aug 29 2012
On 27 August 2012 01:41, Walter Bright <newshound2 digitalmars.com> wrote:On 8/26/2012 3:26 PM, Manu wrote:To be fair, it's not a very lively discussion. Spans 1 day, and only 3 people comment :) Is that really an exhaustive set of options? It seems the other possibility (including the default arg in the equivalence test) wasn't really considered. The part that I don't understand is how the situation manifests in the first place: auto foo = (int a = 1) { return a;}; auto bar = (int a) { return a;}; writeln(foo()); // writes '1' writeln(bar()); // writes '1' also! These are 2 distinct assignments. I would expect foo and bar *would* each take a type that is technically equivalent, but foo's type would have the bonus default arg in its type record, just as a function pointer that were declared explicitly. At what point does the compiler become confused? They never interact... The expected behaviour seems obvious to me, foo and bar should effectively be: int function(int a = 1) foo = ..; int function(int a) bar = ...; I presume if I typed that code, there would be no problem? The discussion is obviously regarding implementation details...? The trouble for function pointers, is that any default args would need toI just updated to 2.60 and found errors throughout my code where function pointers default args no longer work. _Every single project_ I've written in D, 5 projects, don't work anymore, including my projects at work. I found this discussion: http://d.puremagic.com/issues/** show_bug.cgi?id=3866 <http://d.puremagic.com/issues/show_bug.cgi?id=3866> It seems the change was just decided and implemented with basically no discussion or argument at all :/ My use cases are dynamic linkage, and cross-language integration. I can't manually interact with DLL's containing API's that expect to have default arguments if function pointers no longer support them. Also when receiving foreign language function pointers, they frequently need to have default args too. I also integrate with many C style API's (rendering engines and the like), which involve registration of various callbacks, and lots of those have default args too. I find this particularly surprising, since I recently motivated implementation of new traits which could parse default args from parameter lists, and use that to generation function pointers in templates which auto-magically clone functions parameter lists verbatim, specifically including the default args...The trouble is that, as 3866 shows, there is no design anyone could come up with that worked in a consistent manner. The only consistent way out was to make default arguments a characteristic of the declaration, not of the type.be part of the type, not the declaration.I thought they were part of the type already? I would have thought that's where they should be. Why is their new home more 'proper'? I know it broke code (for many others, too), and I'm very sorry about that,but I don't see another way out.It's a rather major breakage. I've seen you reject far more important changes solely on the grounds that they are a breaking change before... (Many uses of default arguments can be replaced with overloaded functions.)Can you suggest how? I can't think of a feasible approach. You can't overload function pointers (multiple instances of variables with the same name). And by definition of a function *pointer*, I don't own the target function to overload it. I can't reasonably produce static wrappers either, unless the wrapper receives the function pointer I intend to call as an arg, which is super nasty. I can probably address the global/static ones via wrappers, but I don't love redundant function calls, it reduces debug build performance (see: all my rants about __forceinline), but that would only address the problem in a few situations. This also undoes all that good work recently with the parameter list traits_<As I see it, calling a function is possibly the single most important thing a programming language does. Flexibility in this regard is valuable. Default args in function pointers was a HUGE selling point of D to me, I've made extremely liberal use of this feature to great benefit throughout all my projects. But perhaps most importantly, this change has a huge impact on my code at work... I don't really want to imagine telling my boss that I need to rewrite the engine bindings and integration code. I'll look like the biggest dick this side of the Baltic :/
Aug 26 2012
On 8/26/2012 5:06 PM, Manu wrote:To be fair, it's not a very lively discussion. Spans 1 day, and only 3 people comment :)If you want a lively discussion, start one up about what to name a keyword :-)Is that really an exhaustive set of options? It seems the other possibility (including the default arg in the equivalence test) wasn't really considered. The part that I don't understand is how the situation manifests in the first place: auto foo = (int a = 1) { return a;}; auto bar = (int a) { return a;}; writeln(foo()); // writes '1' writeln(bar()); // writes '1' also! These are 2 distinct assignments. I would expect foo and bar /would/ each take a type that is technically equivalent, but foo's type would have the bonus default arg in its type record, just as a function pointer that were declared explicitly.That's the way it used to work. The problem is, what are those types? Are they the same types or not? You're saying sometimes they are the same type, sometimes they are not. It's a mess.At what point does the compiler become confused? They never interact... The expected behaviour seems obvious to me, foo and bar should effectively be: int function(int a = 1) foo = ..; int function(int a) bar = ...;It seems obvious only in the trivial case. What if you passed foo to a function? Now what type is it? What is the mangling for the type?I presume if I typed that code, there would be no problem? The discussion is obviously regarding implementation details...?No.The trouble for function pointers, is that any default args would need to be part of the type, not the declaration. I thought they were part of the type already? I would have thought that's where they should be. Why is their new home more 'proper'?Because are the types the same or not?I know it broke code (for many others, too), and I'm very sorry about that, but I don't see another way out. It's a rather major breakage. I've seen you reject far more important changes solely on the grounds that they are a breaking change before...Unfortunately, it was already broken, as the bug report showed. It's a matter of where the crack shows up.(Many uses of default arguments can be replaced with overloaded functions.) Can you suggest how? I can't think of a feasible approach.I'd have to see what you were trying to do.You can't overload function pointers (multiple instances of variables with the same name). And by definition of a function /pointer/, I don't own the target function to overload it. I can't reasonably produce static wrappers either, unless the wrapper receives the function pointer I intend to call as an arg, which is super nasty. I can probably address the global/static ones via wrappers, but I don't love redundant function calls, it reduces debug build performance (see: all my rants about __forceinline), but that would only address the problem in a few situations. This also undoes all that good work recently with the parameter list traits >_< As I see it, calling a function is possibly the single most important thing a programming language does. Flexibility in this regard is valuable. Default args in function pointers was a HUGE selling point of D to me, I've made extremely liberal use of this feature to great benefit throughout all my projects. But perhaps most importantly, this change has a huge impact on my code at work... I don't really want to imagine telling my boss that I need to rewrite the engine bindings and integration code. I'll look like the biggest dick this side of the Baltic :/If you can show me a generic example of what you are trying to do, perhaps we can suggest a reasonable alternative.
Aug 26 2012
On 27 August 2012 03:51, Walter Bright <newshound2 digitalmars.com> wrote:On 8/26/2012 5:06 PM, Manu wrote:They are always equivalent. The default args are more like metadata. At what point does the compiler become confused? They never interact... Thetype that is technically equivalent, but foo's type would have the bonus defaultarg in its type record, just as a function pointer that were declaredexplicitly.That's the way it used to work. The problem is, what are those types? Are they the same types or not? You're saying sometimes they are the same type, sometimes they are not. It's a mess.Well the function you pass foo to will define the type is receives. If it receives by template, then it'll receive the default args, if it defines the function pointer type it receives explicitly (or uses another compatible definition), the default args may be lost (or different) in the new instance across the function call. I don't see why it needs to be mangled in. The linker doesn't care. The trouble for function pointers, is that any default args would needexpected behaviour seems obvious to me, foo and bar should effectively be: int function(int a = 1) foo = ..; int function(int a) bar = ...;It seems obvious only in the trivial case. What if you passed foo to a function? Now what type is it? What is the mangling for the type?They're equivalent, but not identical; one has some metadata the other doesn't have. I know it broke code (for many others, too), and I'm very sorry aboutto be part of the type, not the declaration. I thought they were part of the type already? I would have thought that's where they should be. Why is their new home more 'proper'?Because are the types the same or not?Does the bug report actually demonstrate how it was causing anybody any problems? It seemed a rather contrived scenario that just illustrated that there was a bug. Also, I think it could be fixed so the scenario in the bug report worked as expected (I still don't understand why it did't work in the first place).that, but I don't see another way out. It's a rather major breakage. I've seen you reject far more important changes solely on the grounds that they are a breaking change before...Unfortunately, it was already broken, as the bug report showed. It's a matter of where the crack shows up.
Aug 27 2012
On 8/27/2012 1:08 AM, Manu wrote:Does the bug report actually demonstrate how it was causing anybody any problems? It seemed a rather contrived scenario that just illustrated that there was a bug.It was probably reduced from a larger scenario. Reduced bugs usually look pretty twisted.Also, I think it could be fixed so the scenario in the bug report worked as expected (I still don't understand why it did't work in the first place).Because the two types were considered to be the same, only different. ---------------------------------- Please post a canonical example of how you use this, so we can think of an alternative.
Aug 27 2012
On 27 August 2012 11:28, Walter Bright <newshound2 digitalmars.com> wrote:On 8/27/2012 1:08 AM, Manu wrote:And how was that a problem? They never interacted in the example, the assignments were totally separate, they shouldn't have been confused. Just speculating, but it just looks like the type was misrepresented when it was looked up from a map by name or something, and matched the wrong cached definition... or something along those lines. It looks like a bug exposed from implementation detail, I can't see anything in the bug report that shouldn't theoretically work fine. ------------------------------**----Also, I think it could be fixed so the scenario in the bug report worked as expected (I still don't understand why it did't work in the first place).Because the two types were considered to be the same, only different.Please post a canonical example of how you use this, so we can think of an alternative.Well likewise, can you provide an example where, assuming that one bug were fixed, that the old approach actually caused a problem? The bug report shows a situation that has a clear presumed behaviour, and could surely have just been fixed. Almost all my API's are dynamically bound to foreign code, eg: extern(C) void function( ref const(Vector2) v0, ref const(Vector2) v1, ref const(Vector2) v2, ref const(Color) color = Color.white, BlendMode blendMode = BlendMode.Disabled ) fillTriangle2D; extern(C) void function( ref const(Vector3) v0, ref const(Vector3) v1, ref const(Vector3) v2, ref const(Color) color = Color.white, BlendMode blendMode = BlendMode.Disabled ) fillTriangle; extern(C) void function( ref const(Vector2) vPosition, ref const(Vector2) vSizeref, ref BaseTexture texture, ref const(Color) color = Color.white, BlendMode blendMode = BlendMode.Disabled, TextureMode textureMode = TextureMode.AllChannels ) fillSprite; extern(C) void function( ref const(Vector2) vMin, ref const(Vector2) vMax, ref const(Color) color = Color.white, BlendMode blendMode = BlendMode.Disabled ) fillRectangle; extern(C) void function( ref const(Vector3) vMin, ref const(Vector3) vMax, ref const(Matrix4x3) mLocalToWorld = Matrix4x3.identity, ref const(Color) color = Color.white, BlendMode blendMode = BlendMode.Disabled ) fillBox; extern(C) void function( ref const(Vector3) vCenter, float fRadius, ref const(Matrix4x3) mLocalToWorld = Matrix4x3.identity, ref const(Color) color = Color.white, BlendMode blendMode = BlendMode.Disabled, int iTessellationX = 12, int iTessellationY = 8 ) fillSphere; extern(C) void function( float fLength, float fRadius, ref const(Matrix4x3) mLocalToWorld = Matrix4x3.identity, ref const(Color) color = Color.white, BlendMode blendMode = BlendMode.Disabled, int iTessellation = 10 ) fillCone; extern(C) void function( float fLength, float fRadius1, float fRadius2, ref const(Matrix4x3) mLocalToWorld = Matrix4x3.identity, ref const(Color) color = Color.white, BlendMode blendMode = BlendMode.Disabled, int iTessellation = 10 ) fillCylinder; extern(C) void function( ref const(Matrix4) mClipToWorld, ref const(Color) color = Color.white, BlendMode blendMode = BlendMode.Disabled ) fillFrustum; Functions in structs is common, default args are often convenient: struct MFMaterialInterface { extern (C) int function(void* pPlatformData) registerMaterial; extern (C) void function() unregisterMaterial; extern (C) void function(MFMaterial* pMaterial, MFMaterialCreateParams *pCreateParams = null) createInstance; extern (C) void function(MFMaterial* pMaterial) destroyInstance; extern (C) int function() getNumParams; extern (C) MFMaterialParameterInfo* function(int parameterIndex) getParameterInfo; extern (C) void function(MFMaterial* pMaterial, int parameterIndex, int argIndex, size_t value) setParameter; extern (C) size_t function(MFMaterial* pMaterial, int parameterIndex, int argIndex, void* pValue = null) getParameter; } extern (C) void function(const(char)* pName, const(MFMaterialInterface)* pInterface) MFMaterial_RegisterMaterialType; This isn't the best demonstration of this pattern, just a compact one. But function pointers in structs/classes is not unusual. Here's an advanced trick I use a lot since D doesn't extern to static C++ methods (heavily simplified, this is way out of context): struct CPPClass { this() { // not my actual code, but effectively, write 'this' and the C++ method pointer into a delegate on initialisation [I wrap this process up using magic] void** pDelegate = cast(void**)&cppNonVirtualMethod; pDelegate[0] = this; pDelegate[1] = pCPPMethodPointer; } void delegate(int x = 0) cppNonVirtualMethod; // C++ methods often have default args private: // C++ method pointer received from foreign code during initialisation static void* pCPPMethodPointer; }
Aug 27 2012
extern(C) void function( ref const(Vector2) v0, ref const(Vector2) v1, ref const(Vector2) v2, ref const(Color) color = Color.white, BlendMode blendMode = BlendMode.Disabled ) fillTriangle2D;If function pointers could be called with fewer than the prototypical number of arguments, and the remaining arguments be always initialized to their .init defaults, you could perhaps make this sort of thing work without the default argument values by using struct defaults. How would that be deficient?
Aug 27 2012
On 27 August 2012 14:08, Carl Sturtivant <sturtivant gmail.com> wrote:... no. Color does not .init == Color.white. You're suggesting I define a new type, obscuring the API, every time I want a non-.init default arg? Also, I think it's correct that functions shouldn't be callable without explicit parameters. Default args are carefully selected, and they are always opt-in. 'v2' in this case shouldn't be allowed to default to [ NaN, NaN ] if I omit it.extern(C) void function( ref const(Vector2) v0, ref const(Vector2) v1, ref const(Vector2) v2, ref const(Color) color = Color.white, BlendMode blendMode = BlendMode.Disabled ) fillTriangle2D;If function pointers could be called with fewer than the prototypical number of arguments, and the remaining arguments be always initialized to their .init defaults, you could perhaps make this sort of thing work without the default argument values by using struct defaults. How would that be deficient?
Aug 27 2012
On Monday, 27 August 2012 at 12:14:30 UTC, Manu wrote:On 27 August 2012 14:08, Carl Sturtivant <sturtivant gmail.com> wrote:Yes, I am suggesting that to get the default you want you define a new (free at runtime) type. You need the D declaration of whichever C struct anyway, so this is that declaration with a default added. In a more complex situation aliasing can be used. For e.g. doubles, you can play a suitable struct game for once and for all, and use it repeatedly inside the D version of C structs. And I am suggesting the language definition be changed to allow calls of function pointers be made with some trailing arguments omitted. If default arguments for function pointers are not available, except for the default defaults (.init), then what of this proposal?... no. Color does not .init == Color.white. You're suggesting I define a new type, obscuring the API, every time I want a non-.init default arg? Also, I think it's correct that functions shouldn't be callable without explicit parameters. Default args are carefully selected, and they are always opt-in. 'v2' in this case shouldn't be allowed to default to [ NaN, NaN ] if I omit it.extern(C) void function( ref const(Vector2) v0, ref const(Vector2) v1, ref const(Vector2) v2, ref const(Color) color = Color.white, BlendMode blendMode = BlendMode.Disabled ) fillTriangle2D;If function pointers could be called with fewer than the prototypical number of arguments, and the remaining arguments be always initialized to their .init defaults, you could perhaps make this sort of thing work without the default argument values by using struct defaults. How would that be deficient?
Aug 27 2012
On Monday, 27 August 2012 at 12:14:30 UTC, Manu wrote:On 27 August 2012 14:08, Carl Sturtivant <sturtivant gmail.com> wrote:More specifically, you can get the default initialization of e.g. double to be different as follows. import std.stdio; struct dbl(double init) { double d = init; alias d this; } void main() { dbl!3.142 x; dbl!2.0 y; writeln(x*y); } In the case of an external(C) struct, the D definition can fix up its own default initialization without a new type name even. Color for example could be fixed up to initialize to Color.white by default. And, having the compiler fix up omitted trailing arguments with .init default values could be confined to "opt-in" parameters by a small change to the type system for function pointers as follows. The type of a function pointer could now include the number of trailing parameters that may be defaulted. This does not explode the size of a mangled name much. A syntactic mechanism to indicate which trailing parameters may be defaulted in a function prototype could be added to D. The default would be none with functions declared without that mechanism. This mechanism needs to be a part of the prototype so that it can be used on external(C) functions etcetera. Now only trailing parameters that you intend to be defaulted would would get defaulted with the .init treatment. Others would be compilation errors. Now what's wrong with this if there are no trailing argument defaults except for the .init defaults?... no. Color does not .init == Color.white. You're suggesting I define a new type, obscuring the API, every time I want a non-.init default arg? Also, I think it's correct that functions shouldn't be callable without explicit parameters. Default args are carefully selected, and they are always opt-in. 'v2' in this case shouldn't be allowed to default to [ NaN, NaN ] if I omit it.extern(C) void function( ref const(Vector2) v0, ref const(Vector2) v1, ref const(Vector2) v2, ref const(Color) color = Color.white, BlendMode blendMode = BlendMode.Disabled ) fillTriangle2D;If function pointers could be called with fewer than the prototypical number of arguments, and the remaining arguments be always initialized to their .init defaults, you could perhaps make this sort of thing work without the default argument values by using struct defaults. How would that be deficient?
Aug 28 2012
On Monday, 27 August 2012 at 10:32:28 UTC, Manu wrote:I seem to recall I looked at this issue myself at one point. It goes something like: ---- auto foo = (int a = 1) { return a; }; auto bar = (int a) { return a; }; ---- int function(int) is mangled exactly the same as int function(int = 1) as default args aren't used for mangling. dmd does semantic analysis on the type of foo, which returns int function(int = 1), which is mangled as int function(int) and stored in dmd's hashmap of types (default args aren't mangled). When the semantic analysis of bar is done it checks the hashmap, sees that the type is already there (has the same name mangling) and does not repeat semantic analysis. If you switch the order of declarations then the opposite happens - the default arg is ignored.Because the two types were considered to be the same, only different.And how was that a problem? They never interacted in the example, the assignments were totally separate, they shouldn't have been confused. Just speculating, but it just looks like the type was misrepresented when it was looked up from a map by name or something, and matched the wrong cached definition... or something along those lines. It looks like a bug exposed from implementation detail, I can't see anything in the bug report that shouldn't theoretically work fine.
Aug 27 2012
On 08/27/2012 02:48 PM, Robert Clipsham wrote:On Monday, 27 August 2012 at 10:32:28 UTC, Manu wrote:If the compiler design *requires* equal types to be stored uniquely in a hash map, then the default args shouldn't be stored as part of the type in the AST. I assume it is rather inconvenient to fix, but certainly possible.I seem to recall I looked at this issue myself at one point. It goes something like: ---- auto foo = (int a = 1) { return a; }; auto bar = (int a) { return a; }; ---- int function(int) is mangled exactly the same as int function(int = 1) as default args aren't used for mangling. dmd does semantic analysis on the type of foo, which returns int function(int = 1), which is mangled as int function(int) and stored in dmd's hashmap of types (default args aren't mangled). When the semantic analysis of bar is done it checks the hashmap, sees that the type is already there (has the same name mangling) and does not repeat semantic analysis. If you switch the order of declarations then the opposite happens - the default arg is ignored.Because the two types were considered to be the same, only different.And how was that a problem? They never interacted in the example, the assignments were totally separate, they shouldn't have been confused. Just speculating, but it just looks like the type was misrepresented when it was looked up from a map by name or something, and matched the wrong cached definition... or something along those lines. It looks like a bug exposed from implementation detail, I can't see anything in the bug report that shouldn't theoretically work fine.
Aug 27 2012
On 8/27/2012 6:05 AM, Timon Gehr wrote:On 08/27/2012 02:48 PM, Robert Clipsham wrote:This is correct.I seem to recall I looked at this issue myself at one point. It goes something like: ---- auto foo = (int a = 1) { return a; }; auto bar = (int a) { return a; }; ---- int function(int) is mangled exactly the same as int function(int = 1) as default args aren't used for mangling. dmd does semantic analysis on the type of foo, which returns int function(int = 1), which is mangled as int function(int) and stored in dmd's hashmap of types (default args aren't mangled). When the semantic analysis of bar is done it checks the hashmap, sees that the type is already there (has the same name mangling) and does not repeat semantic analysis. If you switch the order of declarations then the opposite happens - the default arg is ignored.If the compiler design *requires* equal types to be stored uniquely in a hash map, then the default args shouldn't be stored as part of the type in the AST. I assume it is rather inconvenient to fix, but certainly possible.The language design requires a 1:1 mapping of mangling to types. Hence the compiler design to use the mangling as a hashmap key of types. The failure of that approach in this case points to a problem in the language design, not a bug in the compiler.
Aug 27 2012
On 2012-08-27 22:53, Walter Bright wrote:The language design requires a 1:1 mapping of mangling to types. Hence the compiler design to use the mangling as a hashmap key of types. The failure of that approach in this case points to a problem in the language design, not a bug in the compiler.How does this then work when the body of the anonymous functions are different? How will they be identified? -- /Jacob Carlborg
Aug 27 2012
On 8/27/2012 11:53 PM, Jacob Carlborg wrote:How does this then work when the body of the anonymous functions are different? How will they be identified?I don't know what you mean.
Aug 28 2012
On 2012-08-28 09:16, Walter Bright wrote:I don't know what you mean.The original problem in the bug report looked like this: void main () { auto foo = (int a = 1) { return a; }; auto bar = (int a) { return a; }; writeln(foo()); writeln(bar()); } If I change one of the bodies of the anonymous functions to look like this: void main () { auto foo = (int a = 1) { return a; writeln("foo"); }; auto bar = (int a) { return a; }; writeln(foo()); writeln(bar()); } It behaves correctly and won't compile any more. -- /Jacob Carlborg
Aug 28 2012
On Tuesday, 28 August 2012 at 06:53:15 UTC, Jacob Carlborg wrote:On 2012-08-27 22:53, Walter Bright wrote:The body of the function has nothing to do with its type.The language design requires a 1:1 mapping of mangling to types. Hence the compiler design to use the mangling as a hashmap key of types. The failure of that approach in this case points to a problem in the language design, not a bug in the compiler.How does this then work when the body of the anonymous functions are different? How will they be identified?
Aug 28 2012
On 2012-08-28 21:27, Robert Clipsham wrote:The body of the function has nothing to do with its type.If I change the body of one of the functions the problem doesn't appear. -- /Jacob Carlborg
Aug 29 2012
On 27 August 2012 15:48, Robert Clipsham <robert octarineparrot.com> wrote:I seem to recall I looked at this issue myself at one point. It goes something like: ---- auto foo = (int a = 1) { return a; }; auto bar = (int a) { return a; }; ---- int function(int) is mangled exactly the same as int function(int = 1) as default args aren't used for mangling. dmd does semantic analysis on the type of foo, which returns int function(int = 1), which is mangled as int function(int) and stored in dmd's hashmap of types (default args aren't mangled). When the semantic analysis of bar is done it checks the hashmap, sees that the type is already there (has the same name mangling) and does not repeat semantic analysis. If you switch the order of declarations then the opposite happens - the default arg is ignored.Cached in a hashmap! precisely what I suspected (without knowing anything about it) ;) That explains why Walter keeps going on about the name mangling. It's all clear.
Aug 27 2012
On Monday, 27 August 2012 at 13:44:43 UTC, Manu wrote:On 27 August 2012 15:48, Robert Clipsham <robert octarineparrot.com> wrote:This discussion is all sorts of wrong. Whoever said that defargs are metadata (Manu?) was right. Therefore it would make sense to implement a general metadata facility for D and use *that* for defargs. C++11 has annotations, so that's the place to start looking at. D has already enough of those pesky special case features, let's not add yet another one at the expense of a more general metadata/annotation mechanism which eventually would be added anyway due to popular demand.I seem to recall I looked at this issue myself at one point. It goes something like: ---- auto foo = (int a = 1) { return a; }; auto bar = (int a) { return a; }; ---- int function(int) is mangled exactly the same as int function(int = 1) as default args aren't used for mangling. dmd does semantic analysis on the type of foo, which returns int function(int = 1), which is mangled as int function(int) and stored in dmd's hashmap of types (default args aren't mangled). When the semantic analysis of bar is done it checks the hashmap, sees that the type is already there (has the same name mangling) and does not repeat semantic analysis. If you switch the order of declarations then the opposite happens - the default arg is ignored.Cached in a hashmap! precisely what I suspected (without knowing anything about it) ;) That explains why Walter keeps going on about the name mangling. It's all clear.
Aug 27 2012
On 27 August 2012 17:03, foobar <foo bar.com> wrote:This discussion is all sorts of wrong. Whoever said that defargs are metadata (Manu?) was right. Therefore it would make sense to implement a general metadata facility for D and use *that* for defargs. C++11 has annotations, so that's the place to start looking at. D has already enough of those pesky special case features, let's not add yet another one at the expense of a more general metadata/annotation mechanism which eventually would be added anyway due to popular demand.Yes, I suggested this as a possibility above, but nobody commented. It seems like it might solve 2 problems with one stone ;)
Aug 27 2012
On 8/27/12 7:34 AM, Manu wrote:On 27 August 2012 17:03, foobar <foo bar.com <mailto:foo bar.com>> wrote: This discussion is all sorts of wrong. Whoever said that defargs are metadata (Manu?) was right. Therefore it would make sense to implement a general metadata facility for D and use *that* for defargs. C++11 has annotations, so that's the place to start looking at. D has already enough of those pesky special case features, let's not add yet another one at the expense of a more general metadata/annotation mechanism which eventually would be added anyway due to popular demand. Yes, I suggested this as a possibility above, but nobody commented. It seems like it might solve 2 problems with one stone ;)Whilst I agree a metadata facility is an interesting topic to look into, I think default arguments for functions is a poor motivating example. Andrei
Aug 27 2012
On 08/27/2012 04:53 PM, Andrei Alexandrescu wrote:On 8/27/12 7:34 AM, Manu wrote:+1.On 27 August 2012 17:03, foobar <foo bar.com <mailto:foo bar.com>> wrote: This discussion is all sorts of wrong. Whoever said that defargs are metadata (Manu?) was right. Therefore it would make sense to implement a general metadata facility for D and use *that* for defargs. C++11 has annotations, so that's the place to start looking at. D has already enough of those pesky special case features, let's not add yet another one at the expense of a more general metadata/annotation mechanism which eventually would be added anyway due to popular demand. Yes, I suggested this as a possibility above, but nobody commented. It seems like it might solve 2 problems with one stone ;)Whilst I agree a metadata facility is an interesting topic to look into, I think default arguments for functions is a poor motivating example. Andrei
Aug 27 2012
On Monday, 27 August 2012 at 14:53:24 UTC, Andrei Alexandrescu wrote:On 8/27/12 7:34 AM, Manu wrote:The point was that there are _other_ motivating examples for annotations hence there's already popular demand for it. Which is why it's worth the added complexity to the language. In fact part of that complexity *already* exists in D syntax via built-in annotations (e.g safe). This same feature (annotations) can be leveraged to solve this small corner case hence it shouldn't be implemented separately and complicate both D and DMD for little gain.On 27 August 2012 17:03, foobar <foo bar.com <mailto:foo bar.com>> wrote: This discussion is all sorts of wrong. Whoever said that defargs are metadata (Manu?) was right. Therefore it would make sense to implement a general metadata facility for D and use *that* for defargs. C++11 has annotations, so that's the place to start looking at. D has already enough of those pesky special case features, let's not add yet another one at the expense of a more general metadata/annotation mechanism which eventually would be added anyway due to popular demand. Yes, I suggested this as a possibility above, but nobody commented. It seems like it might solve 2 problems with one stone ;)Whilst I agree a metadata facility is an interesting topic to look into, I think default arguments for functions is a poor motivating example. Andrei
Aug 27 2012
Am Mon, 27 Aug 2012 19:46:57 +0200 schrieb "foobar" <foo bar.com>:The point was that there are _other_ motivating examples for annotations hence there's already popular demand for it. Which is why it's worth the added complexity to the language. In fact part of that complexity *already* exists in D syntax via built-in annotations (e.g safe).Properties are currently implemented as "strip off the and continue". They are mapped to storage class bit flags, like the others with no special code paths or data structures in place. -- Marco
Aug 28 2012
On Wednesday, 29 August 2012 at 07:00:06 UTC, Marco Leise wrote:Am Mon, 27 Aug 2012 19:46:57 +0200 schrieb "foobar" <foo bar.com>:I was talking about complexity added to _the language_ and its syntax - the part exposed to the user. I did not mean to imply anything about the compiler implementation of it. If what you say is true, it just shows that those features such as pure shouldn't have got the prefix in the first place. Anyway, my main point was: a. There already is popular demand for metadata. b. We already have some of the desired syntax for it in the language c. Once the facility is fully implemented and supports user defined annotations it could also be leveraged to implement default arguments as well.The point was that there are _other_ motivating examples for annotations hence there's already popular demand for it. Which is why it's worth the added complexity to the language. In fact part of that complexity *already* exists in D syntax via built-in annotations (e.g safe).Properties are currently implemented as "strip off the and continue". They are mapped to storage class bit flags, like the others with no special code paths or data structures in place.
Aug 29 2012
On 8/27/2012 7:03 AM, foobar wrote:This discussion is all sorts of wrong. Whoever said that defargs are metadata (Manu?) was right. Therefore it would make sense to implement a general metadata facility for D and use *that* for defargs. C++11 has annotations, so that's the place to start looking at. D has already enough of those pesky special case features, let's not add yet another one at the expense of a more general metadata/annotation mechanism which eventually would be added anyway due to popular demand.The annotations design discussed was about annotating declarations, not types.
Aug 27 2012
On 27 August 2012 23:54, Walter Bright <newshound2 digitalmars.com> wrote:On 8/27/2012 7:03 AM, foobar wrote:And that may be fine for default args too. I've personally only ever used them where I've explicitly defined them.This discussion is all sorts of wrong. Whoever said that defargs are metadata (Manu?) was right. Therefore it would make sense to implement a general metadata facility for D and use *that* for defargs. C++11 has annotations, so that's the place to start looking at. D has already enough of those pesky special case features, let's not add yet another one at the expense of a more general metadata/annotation mechanism which eventually would be added anyway due to popular demand.The annotations design discussed was about annotating declarations, not types.
Aug 27 2012
On 08/27/2012 05:48 AM, Robert Clipsham wrote:I seem to recall I looked at this issue myself at one point. It goes something like: ---- auto foo = (int a = 1) { return a; }; auto bar = (int a) { return a; }; ---- int function(int) is mangled exactly the same as int function(int = 1) as default args aren't used for mangling. dmd does semantic analysis on the type of foo, which returns int function(int = 1), which is mangled as int function(int) and stored in dmd's hashmap of types (default args aren't mangled). When the semantic analysis of bar is done it checks the hashmap, sees that the type is already there (has the same name mangling) and does not repeat semantic analysis. If you switch the order of declarations then the opposite happens - the default arg is ignored.I had opened a related bug recently: http://d.puremagic.com/issues/show_bug.cgi?id=8579 Ali
Aug 27 2012
On 08/27/2012 12:32 PM, Manu wrote:On 27 August 2012 11:28, Walter Bright <newshound2 digitalmars.com <mailto:newshound2 digitalmars.com>> wrote: On 8/27/2012 1:08 AM, Manu wrote: Also, I think it could be fixed so the scenario in the bug report worked as expected (I still don't understand why it did't work in the first place). Because the two types were considered to be the same, only different. And how was that a problem? They never interacted in the example, the assignments were totally separate, they shouldn't have been confused. Just speculating, but it just looks like the type was misrepresented when it was looked up from a map by name or something, and matched the wrong cached definition... or something along those lines. It looks like a bug exposed from implementation detail, I can't see anything in the bug report that shouldn't theoretically work fine. ...+1.
Aug 27 2012
On 8/27/2012 3:32 AM, Manu wrote:Well likewise, can you provide an example where, assuming that one bug were fixed, that the old approach actually caused a problem? The bug report shows a situation that has a clear presumed behaviour, and could surely have just been fixed.Yes, I can make that example "work", but it would not make the general case work. It would just kick the can down the road, and I'd get more bug reports on those. I'll look at your example separately.
Aug 27 2012
On 8/27/2012 3:32 AM, Manu wrote:Here's an advanced trick I use a lot since D doesn't extern to static C++ methods (heavily simplified, this is way out of context): struct CPPClass { this() { // not my actual code, but effectively, write 'this' and the C++ method pointer into a delegate on initialisation [I wrap this process up using magic] void** pDelegate = cast(void**)&cppNonVirtualMethod; pDelegate[0] = this; pDelegate[1] = pCPPMethodPointer; } void delegate(int x = 0) cppNonVirtualMethod; // C++ methods often have default argsvoid delegate(int x) cppNonVirtualMethod; void callCppNonVirtualMethod(int x) { (*cppNonVirtualMethod)(x); } void callCppNonVirtualMethod() { callCppNonVirtualMethod(0); } With inlining on, the calls to the second overload should disappear, and you have the same code generated as you would for the default arg method. You could probably reduce the typing with some sort of mixin template, but this is the basic idea.private: // C++ method pointer received from foreign code during initialisation static void* pCPPMethodPointer; }
Aug 27 2012
On 28 August 2012 00:03, Walter Bright <newshound2 digitalmars.com> wrote:On 8/27/2012 3:32 AM, Manu wrote:I've actually considered this approach, and the overload isn't required in this case, since the true method can have a default arg. The same pattern can apply at the global scope. There are 2 reasons I didn't go for it: Every api call is a double-call with inlining turned off (debug builds). Debug builds are already slow enough, I didn't want to base a design decision on that when it was perfectly syntactically reasonable to call through the function pointers directly. __forceinline would address this concern. (I really need this anyway for the simd api. simd intrinsics are all wrapped in templates; if these are not inlined, making a function call for every math opcode, it's gonna be waaaaay slower than using the fpu) And the second is forward declarations and function definitiona appearing within the same file (I have a feature request on this). The API is easily defined by the function pointer, but since the definition needs to define the default args, what now needs to be defined is a forward declaration/prototype. The trouble is, my module magic mixin can't produce the bodies for the stubs it finds because a prototype and definition can't appear in the same file. void functionDecl(int x = 10); // declaration by user (ideally with an custom attribute: engine_import, which would inform the magic generating mixin to produce a stub and linkage code for this) // **** magically generated by a mixin: **** void function(int) __functionDecl_ptr; __forceinline void functionDecl(int x) { __functionDecl_ptr(x); } // ************** static this() { __functionDecl_ptr = importExtern("functionDecl"); } // ************** Sadly, there's a lot missing from the language to be able to do that, but I hope we can reach that place one day.Here's an advanced trick I use a lot since D doesn't extern to static C++ methods (heavily simplified, this is way out of context): struct CPPClass { this() { // not my actual code, but effectively, write 'this' and the C++ method pointer into a delegate on initialisation [I wrap this process up using magic] void** pDelegate = cast(void**)&**cppNonVirtualMethod; pDelegate[0] = this; pDelegate[1] = pCPPMethodPointer; } void delegate(int x = 0) cppNonVirtualMethod; // C++ methods often have default argsvoid delegate(int x) cppNonVirtualMethod; void callCppNonVirtualMethod(int x) { (*cppNonVirtualMethod)(x); } void callCppNonVirtualMethod() { callCppNonVirtualMethod(0); } With inlining on, the calls to the second overload should disappear, and you have the same code generated as you would for the default arg method. You could probably reduce the typing with some sort of mixin template, but this is the basic idea.
Aug 27 2012
On 8/27/2012 3:32 AM, Manu wrote:Almost all my API's are dynamically bound to foreign code, eg: extern(C) void function( ref const(Vector2) v0, ref const(Vector2) v1, ref const(Vector2) v2, ref const(Color) color = Color.white, BlendMode blendMode = BlendMode.Disabled ) fillTriangle2D;These can all be handled in a straightforward (but admittedly wordy) method of wrapping the call to the function pointer in an overloaded function. I understand that you've probably got so many of these, it's drudgery to make the edits. Question: are the default values all of the form T.init? I.e. is Color.white == Color.init?
Aug 27 2012
On 28 August 2012 00:11, Walter Bright <newshound2 digitalmars.com> wrote:On 8/27/2012 3:32 AM, Manu wrote:They can, see my prev post. It's less readable, less maintainable, more obscure to those reading the code... the language had an awesome feature that addressed it directly. When mixins get involved, readability, syntax highlighting, and other basic IDE services all go out the window. Perhaps the most important one that I forgot; debugging becomes VERY annoying. Try stepping into an engine API when it's wrapped up with magic... F10 (jumps to a rubbish API file, cursor disappears, stub code doesn't actually exist). Continue pressing F10 for a while until it eventually finds it's way into the function pointer. Repeat when leaving the function >_< .. Again, __forceinline would address this. I understand that you've probably got so many of these, it's drudgery toAlmost all my API's are dynamically bound to foreign code, eg: extern(C) void function( ref const(Vector2) v0, ref const(Vector2) v1, ref const(Vector2) v2, ref const(Color) color = Color.white, BlendMode blendMode = BlendMode.Disabled ) fillTriangle2D;These can all be handled in a straightforward (but admittedly wordy) method of wrapping the call to the function pointer in an overloaded function.make the edits.It would be annoying. Question: are the default values all of the form T.init? I.e. isColor.white == Color.init?No, I don't think so. Color inits to black. Many enums also have an Unknown state. and float init's to NaN for some crazy reason ;)
Aug 27 2012
On 8/27/2012 2:37 PM, Manu wrote:They can, see my prev post. It's less readable, less maintainable, more obscure to those reading the code... the language had an awesome feature that addressed it directly. When mixins get involved, readability, syntax highlighting, and other basic IDE services all go out the window.Ok, thanks, it helps immensely to see just what you're trying to do. Let me think about it some more.
Aug 27 2012
I think that the function type and its mangled name must not contain the default args, then I can agree with Walter at the point. But, I think the variables of function pointers and delegates still can have default args as a part of their declarations. (It will be a part of VarDeclaration, not a part of TypeFunction) In following case, the default arg looks like a part of the type, but actually is a part of the declaration of fp. void function(int n = 10) fp; pragma(msg, typeof(fp)); // should print void function(int), because default args are not a part of type. fp(); // I think this can be allowed, because fp can remember that the first parameter has the default argument. But, it seems to me there are some corner cases. // fp will *inherit* default args from its initializer, or not? auto fp = (int n = 10){} // what is the actual default arg of fp? void function(int n = 10) fp1 = (int n = 20){} void function(int n) fp2 = (int n = 30){} void function(int n = 40) fp3 = (int n){} // fp has ambiguous default arg, or has no default arg? auto fp1 = some_runtime_condition ? (int n = 10){} : (int n = 20){} ; // more complicated case, first defarg is same, then it will be *inherited*? auto fp2 = some_runtime_condition ? (int n = 10, string s = "hello"){} : (int n = 10, string s = "world"){} ; int function(int n = 10) fp; // default arg of the first parameter is 10 fp = (int n = 20){ return n; } // function literal's default arg will be ignored (in my opinion), is this expected? // returning function pointer/delegate type can have default args? int delegate(int n = 10) foo(int x) { ... } If we can take agreements each other about them, it may be supported. Kenji Hara 2012/8/27 Walter Bright <newshound2 digitalmars.com>:On 8/27/2012 1:08 AM, Manu wrote:Does the bug report actually demonstrate how it was causing anybody any problems? It seemed a rather contrived scenario that just illustrated that there was a bug.It was probably reduced from a larger scenario. Reduced bugs usually look pretty twisted.Also, I think it could be fixed so the scenario in the bug report worked as expected (I still don't understand why it did't work in the first place).Because the two types were considered to be the same, only different. ---------------------------------- Please post a canonical example of how you use this, so we can think of an alternative.
Aug 27 2012
On 08/27/2012 03:23 PM, kenji hara wrote:I think that the function type and its mangled name must not contain the default args, then I can agree with Walter at the point. But, I think the variables of function pointers and delegates still can have default args as a part of their declarations. (It will be a part of VarDeclaration, not a part of TypeFunction) In following case, the default arg looks like a part of the type, but actually is a part of the declaration of fp. void function(int n = 10) fp; pragma(msg, typeof(fp)); // should print void function(int), because default args are not a part of type. fp(); // I think this can be allowed, because fp can remember that the first parameter has the default argument. But, it seems to me there are some corner cases. // fp will *inherit* default args from its initializer, or not? auto fp = (int n = 10){}I assume we'd like it to do that lest the user has to supply the type explicitly. It might lead to funny interactions though.// what is the actual default arg of fp? void function(int n = 10) fp1 = (int n = 20){} void function(int n) fp2 = (int n = 30){} void function(int n = 40) fp3 = (int n){}The declared default arg overrides. I'd suggest to make it an error when it can be fixed by leaving out the default argument from a function literal occurring within the expression.// fp has ambiguous default arg, or has no default arg? auto fp1 = some_runtime_condition ? (int n = 10){} : (int n = 20){} ; // more complicated case, first defarg is same, then it will be *inherited*? auto fp2 = some_runtime_condition ? (int n = 10, string s = "hello"){} : (int n = 10, string s = "world"){} ;I'd say, never attempt to combine default arguments in any way. It is not required to make it a hard error in all cases, because the error will occur when the user attempts to rely upon the non-existent default argument. By the argument from above, both of those should be in error.int function(int n = 10) fp; // default arg of the first parameter is 10 fp = (int n = 20){ return n; } // function literal's default arg will be ignored (in my opinion), is this expected?This specific expression should be in error, but it should probably be possible to do static int foo(int n=10){ return n; } int function(int = 20) fp = &foo; The reason why this should work is because adding a default argument should be as unlikely as possible to break existing code.// returning function pointer/delegate type can have default args? int delegate(int n = 10) foo(int x) { ... }Seems easy enough to support for consistency.If we can take agreements each other about them, it may be supported.I personally do not rely on them.
Aug 27 2012
On Mon, 27 Aug 2012 09:23:54 -0400, kenji hara <k.hara.pg gmail.com> wrote:I think that the function type and its mangled name must not contain the default args, then I can agree with Walter at the point. But, I think the variables of function pointers and delegates still can have default args as a part of their declarations. (It will be a part of VarDeclaration, not a part of TypeFunction) In following case, the default arg looks like a part of the type, but actually is a part of the declaration of fp.I think the type of the function pointer and the type (and mangling) of the function are two separate types. One is a function pointer, one is a function. You only need to mangle the function, not the pointer type. Consider this rough equivalent (not sure how to write the constraints): struct callWithN(F, int n) if(F is a function which takes an int as its last arg) { F funcptr; auto opCall(T...)(T t) if(T is equivalent to F's args) { return funcptr(t); } auto opCall(T...)(T t) if(T is equivalent to F's args, except for last int arg) { return funcptr(t, n); } } I think the above (with correct template constraints) is equivalent to defining a function pointer with a default integer argument. Note that the function it points to has *nothing* to do with default args, and so should be mangled like any other function. So I think the default args have to be part of the pointer type, just not the function type.void function(int n = 10) fp; pragma(msg, typeof(fp)); // should print void function(int), because default args are not a part of type.No. They are part of the function pointer type.// fp will *inherit* default args from its initializer, or not? auto fp = (int n = 10){}Yes, this should be equivalent to: void function(int = 10) fp = (int n){};// what is the actual default arg of fp? void function(int n = 10) fp1 = (int n = 20){}compiler error. Even if it should be able to reassign a default arg 10 to a default arg 20 function pointer, allowing the above to compile would be extremely confusing, and there is no real reason to allow it. Use the property of the function pointer that gets the function pointer type without the default params (see below).void function(int n) fp2 = (int n = 30){}30void function(int n = 40) fp3 = (int n){}40// fp has ambiguous default arg, or has no default arg? auto fp1 = some_runtime_condition ? (int n = 10){} : (int n = 20){} ;Compiler error. You can't define the type differently based on a runtime condition.// more complicated case, first defarg is same, then it will be *inherited*? auto fp2 = some_runtime_condition ? (int n = 10, string s = "hello"){} : (int n = 10, string s = "world"){} ;Again, two different types. Error.int function(int n = 10) fp; // default arg of the first parameter is 10 fp = (int n = 20){ return n; } // function literal's default arg will be ignored (in my opinion), is this expected?I think actually, this should be a compiler error, but it would be a valid choice to make this simply ignore the secondary default arg. Here is what I would like to happen: Given two sets of default parameter tuples X and Y, where X is not a prefix of Y, and Y is not a prefix of X, a function pointer with default args X cannot be assigned from a function pointer with default args Y. Given X and Y where X is a prefix of Y, a function pointer with default args Y can be assigned from a function pointer with default args X. However, the opposite will not be true. However, we should define a function pointer property (e.g. funcptr) which gives naked access to the underlying function pointer. So if you had fx with default args X, and fy with default args Y, and both pointed at functions with the same parameter types, then you could do: fx.funcptr = fy.funcptr; fy.funcptr = fx.funcptr; If this is too complicated, then just allowing all functions with the same underlying function parameter types to be freely assignable also is a valid (but IMO more confusing) choice. Given the above, I think it would be prudent to have the default args returnable via a property or a __traits command. e.g.: auto tupleOfDefaultArgs = fx.defargs;// returning function pointer/delegate type can have default args? int delegate(int n = 10) foo(int x) { ... }Of course. Default args are part of the pointer type. -Steve
Aug 27 2012
On 27 August 2012 16:23, kenji hara <k.hara.pg gmail.com> wrote:I think that the function type and its mangled name must not contain the default args, then I can agree with Walter at the point. But, I think the variables of function pointers and delegates still can have default args as a part of their declarations. (It will be a part of VarDeclaration, not a part of TypeFunction) In following case, the default arg looks like a part of the type, but actually is a part of the declaration of fp. void function(int n = 10) fp; pragma(msg, typeof(fp)); // should print void function(int), because default args are not a part of type. fp(); // I think this can be allowed, because fp can remember that the first parameter has the default argument. But, it seems to me there are some corner cases. // fp will *inherit* default args from its initializer, or not? auto fp = (int n = 10){}I'd say, will. // what is the actual default arg of fp?void function(int n = 10) fp1 = (int n = 20){}void function(int n) fp2 = (int n = 30){}void function(int n = 40) fp3 = (int n){}The declaration (on the left) is correct in all cases. It was explicitly stated when defining the variable. // fp has ambiguous default arg, or has no default arg?auto fp1 = some_runtime_condition ? (int n = 10){} : (int n = 20){} ;Finally, a REAL conundrum! :) .. Not sure what to tell you... Error? Warning, and remove the default arg since there's a disagreement?// more complicated case, first defarg is same, then it will be *inherited*? auto fp2 = some_runtime_condition ? (int n = 10, string s = "hello"){} : (int n = 10, string s = "world"){} ;Same problem as above. I'd say hard error, since this is a) extremely unlikely! And b) it's obvious there's no 'correct' conclusion anyway. The user writing that code clearly imagines that D is a dynamic language, and doesn't properly understand the nature of a compiled language. int function(int n = 10) fp; // default arg of the first parameter is 10fp = (int n = 20){ return n; } // function literal's default arg will be ignored (in my opinion), is this expected?Agree, the variable was already defined by the users explicit request. // returning function pointer/delegate type can have default args?int delegate(int n = 10) foo(int x) { ... }Sure, why not? If the recipient is auto, then it should inherit those properties. If we can take agreements each other about them, it may be supported.+1 for Kenji :)
Aug 27 2012
On 8/27/12 7:03 AM, Manu wrote:If we can take agreements each other about them, it may be supported. +1 for Kenji :)I think the matter here is that we're looking at adding very significant complexity to the language for a feature with at best minor, marginal benefits. Default arguments add no power to the language, you can quote me on that. A wrong was fixed with a simplifying solution. That broke some code. Granted, the code could be made to work with a much more complicated (and complicating) solution. Is it worth it? At this point in D's development, do we want to go full-bore about something that complicates the language to very little benefit, particularly considering we already have so many balls in the air? I think it's reasonable to look into fixing the code and calling it a day. Thanks, Andrei
Aug 27 2012
On 27 August 2012 17:51, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>wrote:On 8/27/12 7:03 AM, Manu wrote:... what? We're not talking about *adding* anything, we're talking about a bug. The scenario given in the bug report isn't even theoretically flawed, it should work fine under the existing design (hence the bug report), but it seems some design changes may a) make more sense, and also b) simplify implementation/fixing the bug. That seems fine, but rather than fixing said implementation bug, the feature was just removed from the language, which was coincidentally a major breaking change. Does the dmd implementation define the D language? I'm sure there's lots of bugs in various features that could be solved by removing the feature completely. I encounter problems using 'ref' and 'in' all the time... The feature was obviously intended, someone took the time to put it there in the first place, and people use it. It's implementation is apparently not perfect, and needs to be clarified, maybe that requires a design tweak. That seems more what Kenji is talking about to me. Default arguments add no power to the language, you can quote me on that.If we can take agreements each other about them, it may be supported. +1 for Kenji :)I think the matter here is that we're looking at adding very significant complexity to the language for a feature with at best minor, marginal benefits.Did I mention that *every project* I've written in D is broken? Why have I used a feature so much if it offers nothing? The fact the bug even exists implies that someone saw value in the feature in the first place... I'm not sure how you define 'power', but default args certainly offer convenience. It also allows the default to be changed universally in one place. I wouldn't want to find the location of all calls to the function, and then reason whether their manual placement of the default arg at each instance is as a conventional default (such that I should update it to the new default), or whether they explicitly intend that value in this particular case. A wrong was fixed with a simplifying solution. That broke some code.Granted, the code could be made to work with a much more complicated (and complicating) solution. Is it worth it? At this point in D's development, do we want to go full-bore about something that complicates the language to very little benefit, particularly considering we already have so many balls in the air? I think it's reasonable to look into fixing the code and calling it a day.A 'wrong'? What a subjective opinion. Default args are useful, and I absolutely appreciated the uniformity D seemed to offer in this respect (ie, all kinds of function definition supported them). How do you define 'fixing the code'? The 'fix' is a major breaking change. Usually people are extremely phobic of the possibility of breaking changes in even minor cases, yet this is relatively severe. Far more important changes have been rejected on the grounds that they're breaking changes alone... If a solid solution is too difficult to implement, or isn't worth the time right now, then just put the bug back. It was such a minor bug in a very contrived case, and better than the fix. On 27 August 2012 17:53, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:On 8/27/12 7:34 AM, Manu wrote:I agree. It was just an interesting parallel that I noticed.Yes, I suggested this as a possibility above, but nobody commented. It seems like it might solve 2 problems with one stone ;)Whilst I agree a metadata facility is an interesting topic to look into, I think default arguments for functions is a poor motivating example.
Aug 27 2012
On 8/27/12 8:46 AM, Manu wrote:We're not talking about /adding/ anything, we're talking about a bug. The scenario given in the bug report isn't even theoretically flawed, it should work fine under the existing design (hence the bug report), but it seems some design changes may a) make more sense, and also b) simplify implementation/fixing the bug.My understanding is that the bug revealed serious incompleteness in the underlying design of the feature; fixing them properly would be a major effort that (in my opinion) is not justified by the gains from the feature.That seems fine, but rather than fixing said implementation bug, the feature was just removed from the language, which was coincidentally a major breaking change.I agree that being a breaking change gives the issue a larger weight.Does the dmd implementation define the D language?No, and I wouldn't know why one would think I asserted otherwise.I'm sure there's lots of bugs in various features that could be solved by removing the feature completely. I encounter problems using 'ref' and 'in' all the time...The matter at hand here is the value of the feature in proportion to its difficulty of implementation, prevalence in code, etc. I don't think it would be e.g. realistic to drop ref for the language, although clearly it would simplify it.The feature was obviously intended, someone took the time to put it there in the first place, and people use it. It's implementation is apparently not perfect, and needs to be clarified, maybe that requires a design tweak. That seems more what Kenji is talking about to me.It does. Also, Kenji found a variety of issues that have no clear solution. I think there's no need for me to argue that features that "almost work" are not a good way to go, even though they are useful when they work.Default arguments add no power to the language, you can quote me on that. Did I mention that _every project_ I've written in D is broken? Why have I used a feature so much if it offers nothing?I agree that your use of the feature and the subsequent breakage are good arguments in favor of keeping the feature. Yet I know for experience the feature's benefits are just marginal, and I am appealing to you to look into alternative implementations using the rest of the D programming language. I am convince you should find some. (Again, I agree that having to change once working code remains a tall order.)A wrong was fixed with a simplifying solution. That broke some code. Granted, the code could be made to work with a much more complicated (and complicating) solution. Is it worth it? At this point in D's development, do we want to go full-bore about something that complicates the language to very little benefit, particularly considering we already have so many balls in the air? I think it's reasonable to look into fixing the code and calling it a day. A 'wrong'? What a subjective opinion.Actually, no. By "wrong" I meant the bug itself.Default args are useful, and I absolutely appreciated the uniformity D seemed to offer in this respect (ie, all kinds of function definition supported them). How do you define 'fixing the code'? The 'fix' is a major breaking change. Usually people are extremely phobic of the possibility of breaking changes in even minor cases, yet this is relatively severe. Far more important changes have been rejected on the grounds that they're breaking changes alone... If a solid solution is too difficult to implement, or isn't worth the time right now, then just put the bug back. It was such a minor bug in a very contrived case, and better than the fix.If experience with language design taught me anything, it is that contrived cases must be paid utmost attention. They are proofs that something is wrong. Andrei
Aug 27 2012
On Mon, 27 Aug 2012 13:19:11 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:On 8/27/12 8:46 AM, Manu wrote:That's not true, Kenji asked how the feature should behave in certain cases, and I answered every one of them. There is a clear and valid solution. Whether it's worth implementing or not is another question. I think if it's not implemented we can come up with a very clunky library solution, but I'd rather see the compiler do it. -SteveThe feature was obviously intended, someone took the time to put it there in the first place, and people use it. It's implementation is apparently not perfect, and needs to be clarified, maybe that requires a design tweak. That seems more what Kenji is talking about to me.It does. Also, Kenji found a variety of issues that have no clear solution. I think there's no need for me to argue that features that "almost work" are not a good way to go, even though they are useful when they work.
Aug 27 2012
On 8/27/12 11:08 AM, Steven Schveighoffer wrote:What's missing is "simple". AndreiIt does. Also, Kenji found a variety of issues that have no clear solution. I think there's no need for me to argue that features that "almost work" are not a good way to go, even though they are useful when they work.That's not true, Kenji asked how the feature should behave in certain cases, and I answered every one of them. There is a clear and valid solution.
Aug 27 2012
On Mon, 27 Aug 2012 14:45:54 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:On 8/27/12 11:08 AM, Steven Schveighoffer wrote:Simple as in simple to understand, or as in simple to implement? I think it's pretty simple to understand, especially if you understand default arguments in existing contexts. But that is a subjective measure. -SteveWhat's missing is "simple".It does. Also, Kenji found a variety of issues that have no clear solution. I think there's no need for me to argue that features that "almost work" are not a good way to go, even though they are useful when they work.That's not true, Kenji asked how the feature should behave in certain cases, and I answered every one of them. There is a clear and valid solution.
Aug 27 2012
Le 27/08/2012 19:19, Andrei Alexandrescu a écrit :You didn't, but I'd be foolish to assert otherwise.Does the dmd implementation define the D language?No, and I wouldn't know why one would think I asserted otherwise.
Aug 27 2012
On 2012-08-27 17:46, Manu wrote:Does the dmd implementation define the D language?No one really knows. It's probably a mix of dmd, dlang.org and TDPL. -- /Jacob Carlborg
Aug 27 2012
Le 27/08/2012 00:41, Walter Bright a écrit :On 8/26/2012 3:26 PM, Manu wrote:That guy just have to rework 5 projects. This is quite a lot of work. Obviously, that change have to be done. But you can't just impose such a change on users. Their agenda isn't synchronized with D's. D NEED a better versioning scheme, as explained MANY times here already.I just updated to 2.60 and found errors throughout my code where function pointers default args no longer work. _Every single project_ I've written in D, 5 projects, don't work anymore, including my projects at work. I found this discussion: http://d.puremagic.com/issues/show_bug.cgi?id=3866 It seems the change was just decided and implemented with basically no discussion or argument at all :/ My use cases are dynamic linkage, and cross-language integration. I can't manually interact with DLL's containing API's that expect to have default arguments if function pointers no longer support them. Also when receiving foreign language function pointers, they frequently need to have default args too. I also integrate with many C style API's (rendering engines and the like), which involve registration of various callbacks, and lots of those have default args too. I find this particularly surprising, since I recently motivated implementation of new traits which could parse default args from parameter lists, and use that to generation function pointers in templates which auto-magically clone functions parameter lists verbatim, specifically including the default args...The trouble is that, as 3866 shows, there is no design anyone could come up with that worked in a consistent manner. The only consistent way out was to make default arguments a characteristic of the declaration, not of the type. The trouble for function pointers, is that any default args would need to be part of the type, not the declaration. I know it broke code (for many others, too), and I'm very sorry about that, but I don't see another way out. (Many uses of default arguments can be replaced with overloaded functions.)
Aug 27 2012
On Monday, 27 August 2012 at 20:29:15 UTC, deadalnix wrote:That guy just have to rework 5 projects. This is quite a lot of work. Obviously, that change have to be done. But you can't just impose such a change on users. Their agenda isn't synchronized with D's. D NEED a better versioning scheme, as explained MANY times here already.I may be considered rude, but as I have understood from the first post Manu 1) decided to use a not fully designed language feature 2) used this feature in public API when working with DLLs (which is by itself a problematic area) 3) integrated this with other language in a a way that require substantial rewrite and possibly redesign 4) updated compiler which I consider a process of looking for problems and no excuse for making code-break change. Anyway, older version of compiler is available.
Aug 27 2012
Le 27/08/2012 23:08, Maxim Fomin a écrit :On Monday, 27 August 2012 at 20:29:15 UTC, deadalnix wrote:The amount of corrected bug make the option of using an older compiler unrealistic.That guy just have to rework 5 projects. This is quite a lot of work. Obviously, that change have to be done. But you can't just impose such a change on users. Their agenda isn't synchronized with D's. D NEED a better versioning scheme, as explained MANY times here already.I may be considered rude, but as I have understood from the first post Manu 1) decided to use a not fully designed language feature 2) used this feature in public API when working with DLLs (which is by itself a problematic area) 3) integrated this with other language in a a way that require substantial rewrite and possibly redesign 4) updated compiler which I consider a process of looking for problems and no excuse for making code-break change. Anyway, older version of compiler is available.
Aug 27 2012
On Monday, 27 August 2012 at 21:08:48 UTC, Maxim Fomin wrote:On Monday, 27 August 2012 at 20:29:15 UTC, deadalnix wrote:All true, except one crucial fact: DMD gets critical bug fixes incorporated with new features in the same release. This leaves a poor choice to the programmer, either he sticks with older compiler version and can't get any critical bug fixes, or he updates the compiler to latest version with all the bug fixes but risks breakage of code due to new features (which is _exactly_ what happened to manu). It's high time for D to split its dev version from the stable one. All major software have this, Debian, Linux, browsers (chrome, Firefox, etc), Windows, etc, etc. This is an ancient concept practiced for decades now in the software world.That guy just have to rework 5 projects. This is quite a lot of work. Obviously, that change have to be done. But you can't just impose such a change on users. Their agenda isn't synchronized with D's. D NEED a better versioning scheme, as explained MANY times here already.I may be considered rude, but as I have understood from the first post Manu 1) decided to use a not fully designed language feature 2) used this feature in public API when working with DLLs (which is by itself a problematic area) 3) integrated this with other language in a a way that require substantial rewrite and possibly redesign 4) updated compiler which I consider a process of looking for problems and no excuse for making code-break change. Anyway, older version of compiler is available.
Aug 27 2012
On Monday, August 27, 2012 23:22:39 foobar wrote:All true, except one crucial fact: DMD gets critical bug fixes incorporated with new features in the same release. This leaves a poor choice to the programmer, either he sticks with older compiler version and can't get any critical bug fixes, or he updates the compiler to latest version with all the bug fixes but risks breakage of code due to new features (which is _exactly_ what happened to manu).Except that the change which is causing Manu problems _isn't_ a new feature. It's a bug fix. So, better versioning wouldn't necessarily have helped him any at all. At best, if we had a more complex versioning scheme, it could be decided that the bug fix was potentially disruptive enough that it should only be fixed in a more major release, but _every_ bug fix risks breaking code, especially when code could be relying on the buggy behavior. - Jonathan M Davis
Aug 27 2012
On 8/27/12 2:28 PM, Jonathan M Davis wrote:On Monday, August 27, 2012 23:22:39 foobar wrote:Yah, this particular example is more of an example of the limitation of the stable/dev approach. Nevertheless, I do think we should switch to it. But more importantly we must first move to an integrally automated, scripted method of releasing software. The amount of manual drudgery that goes into today's release process makes my blood curd. To paraphrase a famous quote, we can't improve what we don't have. AndreiAll true, except one crucial fact: DMD gets critical bug fixes incorporated with new features in the same release. This leaves a poor choice to the programmer, either he sticks with older compiler version and can't get any critical bug fixes, or he updates the compiler to latest version with all the bug fixes but risks breakage of code due to new features (which is _exactly_ what happened to manu).Except that the change which is causing Manu problems _isn't_ a new feature. It's a bug fix. So, better versioning wouldn't necessarily have helped him any at all.
Aug 27 2012
On Monday, August 27, 2012 14:54:52 Andrei Alexandrescu wrote:On 8/27/12 2:28 PM, Jonathan M Davis wrote:Oh, I agree. I'm just saying that this is not a problem that a major.minor versioning system would solve. Such a versioning scheme could definitely be beneficial, but I think that many of those asking for it seriously overestimate how much it will reduce breaking changes. Aside from whatever deprecations occur (the number of which has been diminishing), they're almost exclusively caused by bug fixes.Except that the change which is causing Manu problems _isn't_ a new feature. It's a bug fix. So, better versioning wouldn't necessarily have helped him any at all.Yah, this particular example is more of an example of the limitation of the stable/dev approach. Nevertheless, I do think we should switch to it.But more importantly we must first move to an integrally automated, scripted method of releasing software. The amount of manual drudgery that goes into today's release process makes my blood curd. To paraphrase a famous quote, we can't improve what we don't have.Agreed. - Jonathan M Davis
Aug 27 2012
On 2012-08-27 23:28, Jonathan M Davis wrote:Except that the change which is causing Manu problems _isn't_ a new feature. It's a bug fix. So, better versioning wouldn't necessarily have helped him any at all. At best, if we had a more complex versioning scheme, it could be decided that the bug fix was potentially disruptive enough that it should only be fixed in a more major release, but _every_ bug fix risks breaking code, especially when code could be relying on the buggy behavior.The fix for this particular bug was to remove a language feature. I don't think it would be wise to put that change in a minor release. It doesn't matter why the feature was removed. If a feature is remove from the language it should only be put in a major release, period. -- /Jacob Carlborg
Aug 28 2012
On Monday, 27 August 2012 at 21:29:06 UTC, Jonathan M Davis wrote:On Monday, August 27, 2012 23:22:39 foobar wrote:I guess your definition of stable is different than mine. "Accepts invalid" bugs should never be fixed on the current stable branch as they introduce a breaking change unless it's a matter of a security problem. All compilers have a list of accepts invalid bugs and no one "fixes" them on the stable/release branch exactly because it will break existing code as evidently shown by manu's posts. Python introduced version 3 to handle this and Ruby had version 1.9, all the while both the stable and the next-version branches got bug-fixes. D was moved to Git long ago which trivially allows such branching schemes so there really no excuse to stick with the current flawed system except inertia.All true, except one crucial fact: DMD gets critical bug fixes incorporated with new features in the same release. This leaves a poor choice to the programmer, either he sticks with older compiler version and can't get any critical bug fixes, or he updates the compiler to latest version with all the bug fixes but risks breakage of code due to new features (which is _exactly_ what happened to manu).Except that the change which is causing Manu problems _isn't_ a new feature. It's a bug fix. So, better versioning wouldn't necessarily have helped him any at all. At best, if we had a more complex versioning scheme, it could be decided that the bug fix was potentially disruptive enough that it should only be fixed in a more major release, but _every_ bug fix risks breaking code, especially when code could be relying on the buggy behavior. - Jonathan M Davis
Aug 28 2012
On 28 August 2012 00:08, Maxim Fomin <maxim maxim-fomin.ru> wrote:On Monday, 27 August 2012 at 20:29:15 UTC, deadalnix wrote:Where is that stated? It appeared to be a perfectly good, working, and awesome(!) language feature. 2) used this feature in public API when working with DLLs (which is byThat guy just have to rework 5 projects. This is quite a lot of work. Obviously, that change have to be done. But you can't just impose such a change on users. Their agenda isn't synchronized with D's. D NEED a better versioning scheme, as explained MANY times here already.I may be considered rude, but as I have understood from the first post Manu 1) decided to use a not fully designed language featureitself a problematic area)Are you suggesting using DLL's is asking for trouble? Dynamic linkage is a fundamental part of software. 3) integrated this with other language in a a way that require substantialrewrite and possibly redesignD will interact with other languages in the real world, if anything, my doing so and talking about my approach and experience is a service. 4) updated compilerNo shit, D has loads of bugs which are rapidly fixed all the time. which I consider a process of looking for problems and no excuse for makingcode-break change. Anyway, older version of compiler is available.Whatever.
Aug 27 2012
On 2012-08-27 23:29, Manu wrote:Are you suggesting using DLL's is asking for trouble? Dynamic linkage is a fundamental part of software.Yes, they don't properly work in D. -- /Jacob Carlborg
Aug 28 2012
On Monday, August 27, 2012 11:38:52 Manu wrote:On 27 August 2012 11:12, Jonathan M Davis <jmdavisProg gmx.com> wrote:You can't possibly really be using these functions with default arguments unless you're not really using them like function pointers, otherwise you wouldn't have been using the default arguments in the first place. Sure, you could have something like auto func = (string a = "hello") { return a; } and then call it with one argument or no argument, but that's only because the function is completely local, and you could just as easily do string func(string a = "hello") { return a; } and get the same thing. As soon as you've really used it as a function pointer rather than a local function, you've lost the default argument. Default arguments just do not make sense with function pointers, because they don't follow the function pointer, because it's a _pointer_ and has no knowledge of what it's pointing to. It's only at the declaration point of the function that the default argument exists, and that has _nothing_ to do with the function pointer. You might as well ask a reference of type Object what the arguments used to construct the derived class that it actually refers to were as expect a function pointer to have any clue about default arguments to the function that it points to. - Jonathan M Davisand it makes no sense to use them with function pointers or function literals.If that were true, we wouldn't be having this discussion.
Aug 27 2012
"Jonathan M Davis" <jmdavisProg gmx.com> wrote in message news:mailman.1446.1346070222.31962.digitalmars-d puremagic.com...Default arguments just do not make sense with function pointers, because they don't follow the function pointer, because it's a _pointer_ and has no knowledge of what it's pointing to. It's only at the declaration point of the function that the default argument exists, and that has _nothing_ to do with the function pointer. You might as well ask a reference of type Object what the arguments used to construct the derived class that it actually refers to were as expect a function pointer to have any clue about default arguments to the function that it points to. - Jonathan M DavisYes, this. I looked into fixing issue 3866 earlier this year, and Kenji's list sounds very familiar. It comes down to - function pointers are not function declarations and therefore can't do everything function declarations do, such as overloading and default arguments. This is the price of being able to reassign them. Yes it is possible to make them work the way Manu has been using them, but the solution is messy and I seriously doubt it's worth it. I agree with everything Andrei has said about this. I don't consider putting default arguments back in the type a valid approach because of the reason above, so that leaves giving function pointer variables the ability to have default arguments. This seems to me like a really dumb corner case with the same problems.
Aug 28 2012
On 28 August 2012 16:50, Daniel Murphy <yebblies nospamgmail.com> wrote:"Jonathan M Davis" <jmdavisProg gmx.com> wrote in message news:mailman.1446.1346070222.31962.digitalmars-d puremagic.com...Well that's painful for a number of reasons. Other than the fact that I need to rewrite a bunch of code, I can't actually produce a solution that works as well in lieu of some feature requests. A language feature should be deprecated for some time before it is surprise-removed like that... that would allow some time to discuss and possibly implement support for work-arounds. Since I'll need to create wrapper functions and stubs for *everything*, I really need __forceinline, or debug builds will really suffer. (That said, I need it anyway for heaps of other stuff (SIMD in particular), but this really seals the deal.) Without it, all function calls require call-through stubs, all my API calls become double-calls, and debugging becomes really tedious (stepping into and out of stubs generated by magic that don't really exist every time you press F11 >_<). Also, in my case, I really need to be able to forward declare a function in the same file as it is defined. The user needs to write the prototypes, and then magic can scan for the prototypes and generate the stub its self, but they can't currently appear in the same file like in C/C++. Without both of those things, my new solutions will be worse. More verbose, difficult to read&maintain, slower, and really annoying to debug. Why are you so against default arguments associating with a declaration? Why is the solution messy? Default arguments simplify code. If a default is used frequently, it's better to define it in one place, that is easy to change, and appears with the definition. It's self-documenting, and very convenient.Default arguments just do not make sense with function pointers, because they don't follow the function pointer, because it's a _pointer_ and has no knowledge of what it's pointing to. It's only at the declaration point of the function that the default argument exists, and that has _nothing_ to do with the function pointer. You might as well ask a reference of type Object what the arguments used to construct the derived class that it actually refers to were as expect a function pointer to have any clue about defaultargumentsto the function that it points to. - Jonathan M DavisYes, this. I looked into fixing issue 3866 earlier this year, and Kenji's list sounds very familiar. It comes down to - function pointers are not function declarations and therefore can't do everything function declarations do, such as overloading and default arguments. This is the price of being able to reassign them. Yes it is possible to make them work the way Manu has been using them, but the solution is messy and I seriously doubt it's worth it. I agree with everything Andrei has said about this. I don't consider putting default arguments back in the type a valid approach because of the reason above, so that leaves giving function pointer variables the ability to have default arguments. This seems to me like a really dumb corner case with the same problems.
Aug 28 2012
On 8/28/12 8:23 AM, Manu wrote:Well that's painful for a number of reasons.. Other than the fact that I need to rewrite a bunch of code,Walter and Kenji think breaking meaningful existing code is an overriding concern, and I ended up agreeing with them. They will look into a solution that keeps your working code working. This change of wind may as well turn a new page in the history of D :o). Andrei
Aug 28 2012
Andrei Alexandrescu:This change of wind may as well turn a new page in the history of D :o).<humor>Maybe as soon as the D1 branch stops getting bug fixes, Walter&Co will start a D3 branch where this small breaking change happens.</humor> Bye, bearophile
Aug 28 2012
On Tuesday, 28 August 2012 at 18:52:13 UTC, Andrei Alexandrescu wrote:On 8/28/12 8:23 AM, Manu wrote:I replied to Walter just now with a way to get a clean mechanism to do this, that's consistent with the existing type system, and with function pointers working exactly as they do now. Carl.Well that's painful for a number of reasons.. Other than the fact that I need to rewrite a bunch of code,Walter and Kenji think breaking meaningful existing code is an overriding concern, and I ended up agreeing with them. They will look into a solution that keeps your working code working. This change of wind may as well turn a new page in the history of D :o).
Aug 28 2012
On 28 August 2012 21:52, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>wrote:On 8/28/12 8:23 AM, Manu wrote:Wow, I didn't see that coming. At very least, just put it on a deprecation schedule. I'm happy (perhaps even prefer) to use the alternative approach I've describe if those 2 issues are addressed in some way.Well that's painful for a number of reasons.. Other than the fact that I need to rewrite a bunch of code,Walter and Kenji think breaking meaningful existing code is an overriding concern, and I ended up agreeing with them. They will look into a solution that keeps your working code working. This change of wind may as well turn a new page in the history of D :o).
Aug 28 2012
On Wednesday, August 29, 2012 01:13:15 Manu wrote:On 28 August 2012 21:52, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>wrote:The funny thing about that is that for the most part, language features which are supposed to be deprecated tend to just stick around instead of getting deprecated, meaning that people keep on using them, and that by the time they're actually deprecated, they'll break that much more code... It's one thing to decide not to make a change becasue we don't want to break code. It's quite another to just keep putting it off to avoid breaking code. That just makes things worse when it finally happens. - Jonathan M DavisOn 8/28/12 8:23 AM, Manu wrote:Wow, I didn't see that coming. At very least, just put it on a deprecation schedule. I'm happy (perhaps even prefer) to use the alternative approach I've describe if those 2 issues are addressed in some way.Well that's painful for a number of reasons.. Other than the fact that I need to rewrite a bunch of code,Walter and Kenji think breaking meaningful existing code is an overriding concern, and I ended up agreeing with them. They will look into a solution that keeps your working code working. This change of wind may as well turn a new page in the history of D :o).
Aug 28 2012
On 29 August 2012 04:31, Jonathan M Davis <jmdavisProg gmx.com> wrote:On Wednesday, August 29, 2012 01:13:15 Manu wrote:I've conceded that I don't mind changing my code, if a satisfactory alternative exists (it doesn't currently). Also, I'd like to have some notice that I need to make some time to change the code, and the opportunity to work it into my schedule. Surely that's not unreasonable.On 28 August 2012 21:52, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>wrote:overridingOn 8/28/12 8:23 AM, Manu wrote:Well that's painful for a number of reasons.. Other than the fact that I need to rewrite a bunch of code,Walter and Kenji think breaking meaningful existing code is an:o).concern, and I ended up agreeing with them. They will look into a solution that keeps your working code working. This change of wind may as well turn a new page in the history of DWow, I didn't see that coming. At very least, just put it on a deprecation schedule. I'm happy (perhaps even prefer) to use the alternative approach I've describe if those 2 issues are addressed in some way.The funny thing about that is that for the most part, language features which are supposed to be deprecated tend to just stick around instead of getting deprecated, meaning that people keep on using them, and that by the time they're actually deprecated, they'll break that much more code... It's one thing to decide not to make a change becasue we don't want to break code. It's quite another to just keep putting it off to avoid breaking code. That just makes things worse when it finally happens.
Aug 29 2012
OK. I have finished working to implement it as a new pull request. https://github.com/D-Programming-Language/dmd/pull/1102 It contains several test suites that indicate which case is supported or not. https://github.com/D-Programming-Language/dmd/pull/1102/files#L6R1 Regards. Kenji Hara 2012/8/29 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>:On 8/28/12 8:23 AM, Manu wrote:Well that's painful for a number of reasons.. Other than the fact that I need to rewrite a bunch of code,Walter and Kenji think breaking meaningful existing code is an overriding concern, and I ended up agreeing with them. They will look into a solution that keeps your working code working. This change of wind may as well turn a new page in the history of D :o). Andrei
Sep 06 2012
On 9/6/12 11:15 AM, kenji hara wrote:OK. I have finished working to implement it as a new pull request. https://github.com/D-Programming-Language/dmd/pull/1102 It contains several test suites that indicate which case is supported or not. https://github.com/D-Programming-Language/dmd/pull/1102/files#L6R1 Regards. Kenji HaraThank you very much! Andrei
Sep 06 2012
On 6 September 2012 12:15, kenji hara <k.hara.pg gmail.com> wrote:OK. I have finished working to implement it as a new pull request. https://github.com/D-Programming-Language/dmd/pull/1102 It contains several test suites that indicate which case is supported or not. https://github.com/D-Programming-Language/dmd/pull/1102/files#L6R1Awesome, thanks for this! How far off is 2.61 likely to be?
Sep 12 2012
On 27 August 2012 13:54, Jonathan M Davis <jmdavisProg gmx.com> wrote:On Monday, August 27, 2012 11:38:52 Manu wrote:How do I use a function pointer 'like a function pointer' by your reasoning? Is dynamic linkage not a valid use of function pointers? It's possibly the *most* common use. Shared code is not exactly uncommon, just that it's often automated with compiler sugar these days. Manually binding dynamic code has become rare, but it's still valid. Default arguments just do not make sense with function pointers, becauseOn 27 August 2012 11:12, Jonathan M Davis <jmdavisProg gmx.com> wrote:You can't possibly really be using these functions with default arguments unless you're not really using them like function pointers, otherwise you wouldn't have been using the default arguments in the first place.and it makes no sense to use them with function pointers or function literals.If that were true, we wouldn't be having this discussion.they don't follow the function pointer, because it's a _pointer_ and has no knowledge of what it's pointing to. It's only at the declaration point of the function that the default argument exists, and that has _nothing_ to do with the function pointer.Clearly, since it's a function pointer, I don't own the function its self, or I'd just call that directly. However, as you say, at the declaration point of the function pointer, I can likewise inform the compiler of the default args. If the default args were worked into the variable declaration, that might work just as well as if it were in the type. I don't really care so much HOW it works, only that it does. I think 99% of my cases would be addressed by the def args living in the variable declaration. You might as well ask a reference of type Object whatthe arguments used to construct the derived class that it actually refers to were as expect a function pointer to have any clue about default arguments to the function that it points to.I agree, in principle, but I've still lost a thoroughly useful feature. Move it perhaps (to the declaration?), but don't *re-*move it (cough).
Aug 27 2012
On 8/27/2012 5:38 AM, Manu wrote:I don't really care so much HOW it works, only that it does. I think 99% of my cases would be addressed by the def args living in the variable declaration.Can you please post a canonical 99% use case that you use? I cannot suggest an alternative otherwise. I need to understand what coding problem you have been solving with default args. For example, the basic use case of default args is just a shorthand for doing function overloads: int foo(int a = 5) { ... } becomes: int foo(int a) { ... } int foo() { return foo(5); } Many use cases involving templates can be resolved by using alias template parameters. But I don't know what your use cases are.
Aug 27 2012
On Sun, 26 Aug 2012 18:26:40 -0400, Manu <turkeyman gmail.com> wrote:I just updated to 2.60 and found errors throughout my code where function pointers default args no longer work. *Every single project* I've written in D, 5 projects, don't work anymore, including my projects at work.OK, I've read all the posts on this, and this is what I think would solve the problem: 1. default parameters are part of the function pointer type, because a function pointer has a type. 2. The mangled name of the function that is assigned to that function pointer does *not* have default parameters mangled in. Only the function pointer type has them. 3. Since the default parameters are part of the type, but not defined by the function it points to, you can use interchangeably functions of the same type which define default parameters or not (or define different ones). The default parameters follow the function pointer variable. 4. If a template instantiates with a function type that takes a default parameter, that default parameter is mangled as part of the template type. There is no way around this. But the mangling of the default parameter is *separate* from the mangling of the function type. We can probably implement this as a library feature, but I lack the full template-fu skills to do it. It would be nice to have the compiler do this for us, but I don't know the work involved. Here is a pseudocode type we may be able to use: struct DefaultParamFuncPtr(F, alias T...) if(is(F == function) && isValues!T) // no idea how to define isValues { F funcptr; static T defaultparams; alias funcptr this; // allow this type to act as a straight F, or accept an F to rebind to a different function typeof(funcptr()) opCall(X...)(X params) if(X is a prefix of F's args) // no idea how to write this constraint :) { // construct the rest of the tuple for params given defaultparams typeof(F's args) realtuple = params ~ defaultparams[params.length..$]; // construct the full call parameters return funcptr(realtuple); } } Yeah, it would be really nice to do this in the compiler, but I think the above describes what I would consider to be an equivalent type. The default parameters live as metadata in the type of the function pointer, but are separate from the function type that it points to. -Steve
Aug 27 2012
On 27/08/12 16:16, Steven Schveighoffer wrote:On Sun, 26 Aug 2012 18:26:40 -0400, Manu <turkeyman gmail.com> wrote:This sounds like sloppy thinking. I _think_ what you are saying is that there should be implicit conversions from one function pointer type, to any other that has the same basic declaration, but different default arguments. But then you get problems with IFTI. void foo( void function(int x), double) {} void foo( void function(int x), int) {} void function(int x = 10) bar; foo(bar, 5); // fails -- ambiguous. Both overloads match with implicit conversions. But really, it seems to me that this whole feature is just syntax sugar for one special case of currying.I just updated to 2.60 and found errors throughout my code where function pointers default args no longer work. *Every single project* I've written in D, 5 projects, don't work anymore, including my projects at work.OK, I've read all the posts on this, and this is what I think would solve the problem: 1. default parameters are part of the function pointer type, because a function pointer has a type. 2. The mangled name of the function that is assigned to that function pointer does *not* have default parameters mangled in. Only the function pointer type has them. 3. Since the default parameters are part of the type, but not defined by the function it points to, you can use interchangeably functions of the same type which define default parameters or not (or define different ones). The default parameters follow the function pointer variable.
Aug 28 2012
On Tue, 28 Aug 2012 04:57:53 -0400, Don Clugston <dac nospam.com> wrote:On 27/08/12 16:16, Steven Schveighoffer wrote:First, I don't see IFTI anywhere in there. Second, why is this a problem? We already have oddities in terms of how implicitly converting one argument makes the interpretation of non-ambiguous arguments ambiguous. I don't think it's sloppy, my logic is: we can already (I think) do this in library code, so why is it impossible for the compiler to figure out?On Sun, 26 Aug 2012 18:26:40 -0400, Manu <turkeyman gmail.com> wrote:This sounds like sloppy thinking. I _think_ what you are saying is that there should be implicit conversions from one function pointer type, to any other that has the same basic declaration, but different default arguments. But then you get problems with IFTI. void foo( void function(int x), double) {} void foo( void function(int x), int) {} void function(int x = 10) bar; foo(bar, 5); // fails -- ambiguous. Both overloads match with implicit conversions.I just updated to 2.60 and found errors throughout my code where function pointers default args no longer work. *Every single project* I've written in D, 5 projects, don't work anymore, including my projects at work.OK, I've read all the posts on this, and this is what I think would solve the problem: 1. default parameters are part of the function pointer type, because a function pointer has a type. 2. The mangled name of the function that is assigned to that function pointer does *not* have default parameters mangled in. Only the function pointer type has them. 3. Since the default parameters are part of the type, but not defined by the function it points to, you can use interchangeably functions of the same type which define default parameters or not (or define different ones). The default parameters follow the function pointer variable.But really, it seems to me that this whole feature is just syntax sugar for one special case of currying.Probably. But it is what it is -- a poorly implemented/designed language feature. In most cases, we don't simply drop them without giving a replacement. Examples: octal literals, complex numbers, scope classes. -Steve
Aug 29 2012
I think that D should be consistent with rules of types derivation, in particular the pointer one. Two pointer types should be same if and only if types they pointer are the same. Breaking this rule would result in language complexity. In case when default arguments are part of the type, default arguments must be written in declaration of a function pointer, moreover function pointers with different default arguments would be different types. So, besides of getting convenient usage of a shorter function call a user would have carry a particular default value with every declaration/definition he writes. Also similar functions pointers with different default arguments would not be interchangeable because they are different types. I think that such constraints would make the proposal a net loss to D contribution. Also, making default arguments a part of a definition would break C/C++ legacy rules which claim that a function is characterized by its return value and number, order and types of its parameters. Although, it not necessarily mean that this rule cannot be broken, but it at least requires strong reasons, however as it is discussed, the proposal would result in a unclear usage, increased language complexity, subtle bugs and implementation issues.
Aug 27 2012
On Wednesday, August 29, 2012 12:40:45 Manu wrote:I've conceded that I don't mind changing my code, if a satisfactory alternative exists (it doesn't currently). Also, I'd like to have some notice that I need to make some time to change the code, and the opportunity to work it into my schedule. Surely that's not unreasonable.Ideally, something which is going to be deprecated is first scheduled for deprecation (and marked as such in both the changelog and documentation) and then later deprecated, giving programmer's time to change their code first. It almost always works that way with druntime and Phobos, but I don't think that we necessarily do as good a job with the compiler. Frequently the situation is that people on the newsgroup know fullwell that a feature is going to be deprecated (e.g. delete), but that's not well communicated outside of this group, and for some reason, the to-be-deprecated items just don't get deprecated even though it's been definitively decided that they will be at some point. In this particular case, it was viewed as a bug fix, and bug fixes break code all the time (especially accepts-invalid bugs), so no effort was made to indicate that a feature was going away (i.e. it was viewed as feature being fixed rather than a feature going away, so no notification beyond the bugfix in the changelog was given). Obviously, this was not handled in the best way, as this thread has shown. But stuff that's actually viewed as being deprecated generally is handled much better. - Jonathan M Davis
Aug 29 2012