digitalmars.D - Updated DIP22 - Private symbol visibility
- Martin Nowak (4/4) Dec 20 2013 I updated DIP22 - Private symbol visibility with my findings from
- Adam D. Ruppe (3/3) Dec 20 2013 I don't have anything really substantial to say except that this
- Timon Gehr (13/17) Dec 21 2013 Looks mostly good to me, but the following snippets that both seem
- Martin Nowak (9/15) Dec 21 2013 Yes, this is understood, but it seems like a good tradeoff when
- Dicebot (5/9) Dec 21 2013 Still don't understand how intended and expected behavior can be
- Martin Nowak (8/13) Dec 21 2013 If I make a refactoring and move a function from module A to B, but the
- Timon Gehr (12/26) Dec 21 2013 Well, you pass an alias. Are you saying that for alias template
- Martin Nowak (7/14) Dec 21 2013 You can pass them and basically it behaves like public aliases, in the
- Timon Gehr (16/30) Dec 21 2013 I see. Then how are you going to handle the following:
- Martin Nowak (4/28) Dec 21 2013 This would say, Error: overload auto a.foo(int) is not accessible from
- Timon Gehr (3/27) Dec 21 2013 How is this going to work? Are you arguing for creating multiple
- Martin Nowak (6/9) Dec 22 2013 Ah sorry, I misread your example.
- Timon Gehr (15/25) Dec 22 2013 Therefore none of the following two calls will work?
- Dicebot (8/15) Dec 21 2013 If MUST have different overload resolution if B does not have
- Timon Gehr (2/6) Dec 21 2013 I think there is quite some overlap.
- Martin Nowak (13/20) Dec 21 2013 It's important for example to define a forwarding template in a
- Dicebot (7/13) Dec 21 2013 Yes.
- Timon Gehr (3/19) Dec 21 2013 You have a point there. :o)
- Martin Nowak (5/16) Dec 21 2013 Mixed protection in overload sets are pretty rare.
- H. S. Teoh (25/44) Dec 21 2013 I was pretty upset when I encountered the following situation:
- Martin Nowak (4/24) Dec 21 2013 Yes, this is the big issue that we try to address with DIP22.
I updated DIP22 - Private symbol visibility with my findings from implementing the 'Hide module members' pull request. https://github.com/D-Programming-Language/dmd/pull/739 I don't have much time to cope with the topic but any feedback is welcome.
Dec 20 2013
I don't have anything really substantial to say except that this looks good to me and i look forward to it being implemented/pulled.
Dec 20 2013
On 12/20/2013 09:48 PM, Martin Nowak wrote:I updated DIP22 - Private symbol visibility with my findings from implementing the 'Hide module members' pull request. https://github.com/D-Programming-Language/dmd/pull/739 I don't have much time to cope with the topic but any feedback is welcome.Looks mostly good to me, but the following snippets that both seem reasonable unfortunately contradict each other. "private is an encapsulation tool. If it is not intended to be used by "outsiders", it should not interfere with them at all." "* The least protected symbol determines the visibility for an overload set. After overload resolution an access check will be performed. Thereby overload resolution is independent of look-up origin." Adding a private symbol to an overload set can break 3rd party code with these rules. Since private symbols are usually excluded from di files I don't think this design is viable. I think it would be better to disallow overload sets with mixed protection.
Dec 21 2013
On Saturday, 21 December 2013 at 17:41:32 UTC, Timon Gehr wrote:Adding a private symbol to an overload set can break 3rd party code with these rules.Yes, this is understood, but it seems like a good tradeoff when compared to the alternative where overload resolution depends on look-up origin. The latter could easily break generic code and simple refactorings could introduce subtle bugs.Since private symbols are usually excluded from di files I don't think this design is viable.This, I don't have a good answer for, except that we have to put the private overload in the .di file as well.I think it would be better to disallow overload sets with mixed protection.Tried that, doesn't work. The most common counterexample is mixing private/public constructors.
Dec 21 2013
On Saturday, 21 December 2013 at 19:29:28 UTC, Martin Nowak wrote:Yes, this is understood, but it seems like a good tradeoff when compared to the alternative where overload resolution depends on look-up origin. The latter could easily break generic code and simple refactorings could introduce subtle bugs.Still don't understand how intended and expected behavior can be called "bug". (I don't like updated DIP at all and will be against anything that puts private symbols into overload sets)
Dec 21 2013
On 12/21/2013 08:54 PM, Dicebot wrote:On Saturday, 21 December 2013 at 19:29:28 UTC, Martin Nowak wrote: Still don't understand how intended and expected behavior can be called "bug".If I make a refactoring and move a function from module A to B, but the function calls an overload set in A it should not have a different overload resolution. Similar case, if I pass an overload set via alias to a traits template in module B, the template should follow the same overload resolution. It's very important that we preserve these properties.(I don't like updated DIP at all and will be against anything that puts private symbols into overload sets)I don't like them either, but they have some use-cases.
Dec 21 2013
On 12/21/2013 10:14 PM, Martin Nowak wrote:On 12/21/2013 08:54 PM, Dicebot wrote:Well, you pass an alias. Are you saying that for alias template arguments, the scope of the template declaration applies for accessibility checking? I.e. we cannot pass private symbols as alias arguments? I think this is an unreasonably high price to pay. Note that if we actually allow this, having any kind of differing behaviour when accessing an overload set from different modules is a consistency issue, since the same template with an alias parameter might be instantiated from both modules with the same overload set.On Saturday, 21 December 2013 at 19:29:28 UTC, Martin Nowak wrote: Still don't understand how intended and expected behavior can be called "bug".If I make a refactoring and move a function from module A to B, but the function calls an overload set in A it should not have a different overload resolution. Similar case, if I pass an overload set via alias to a traits template in module B, the template should follow the same overload resolution.It's very important that we preserve these properties.Basically just the constructor thing though? Note that constructors cannot be passed by alias due to a syntax restriction... :o)(I don't like updated DIP at all and will be against anything that puts private symbols into overload sets)I don't like them either, but they have some use-cases.
Dec 21 2013
On 12/21/2013 10:28 PM, Timon Gehr wrote:Well, you pass an alias. Are you saying that for alias template arguments, the scope of the template declaration applies for accessibility checking? I.e. we cannot pass private symbols as alias arguments? I think this is an unreasonably high price to pay.You can pass them and basically it behaves like public aliases, in the sense that the one who instantiates a template passes a public alias to a possibly private symbol. It's a question of transitivity what the template can do with the symbol. Currently it's not possible to call private functions or private methods of variables that are passed via alias parameter.Basically just the constructor thing though? Note that constructors cannot be passed by alias due to a syntax restriction... :o)
Dec 21 2013
On 12/21/2013 11:03 PM, Martin Nowak wrote:On 12/21/2013 10:28 PM, Timon Gehr wrote:I see. Then how are you going to handle the following: module a; private auto foo(int x){ } auto foo(double x){ } auto call(alias a){ a(0); } void x(){ call!foo(); // ok } module b; import a; void y(){ call!foo(); // _same_ symbol as in module 'a'. }Well, you pass an alias. Are you saying that for alias template arguments, the scope of the template declaration applies for accessibility checking? I.e. we cannot pass private symbols as alias arguments? I think this is an unreasonably high price to pay.You can pass them and basically it behaves like public aliases, in the sense that the one who instantiates a template passes a public alias to a possibly private symbol. ...It's a question of transitivity what the template can do with the symbol. Currently it's not possible to call private functions or private methods of variables that are passed via alias parameter. ...That's probably fine. (It is certainly nothing to touch while fixing the private symbol clash issue.)
Dec 21 2013
On 12/21/2013 11:17 PM, Timon Gehr wrote:On 12/21/2013 11:03 PM, Martin Nowak wrote:This will work.On 12/21/2013 10:28 PM, Timon Gehr wrote:I see. Then how are you going to handle the following: module a; private auto foo(int x){ } auto foo(double x){ } auto call(alias a){ a(0); } void x(){ call!foo(); // ok }Well, you pass an alias. Are you saying that for alias template arguments, the scope of the template declaration applies for accessibility checking? I.e. we cannot pass private symbols as alias arguments? I think this is an unreasonably high price to pay.You can pass them and basically it behaves like public aliases, in the sense that the one who instantiates a template passes a public alias to a possibly private symbol. ...module b; import a; void y(){ call!foo(); // _same_ symbol as in module 'a'. }This would say, Error: overload auto a.foo(int) is not accessible from module b.
Dec 21 2013
On 12/21/2013 11:56 PM, Martin Nowak wrote:On 12/21/2013 11:17 PM, Timon Gehr wrote:How is this going to work? Are you arguing for creating multiple instances of 'call'?... I see. Then how are you going to handle the following: module a; private auto foo(int x){ } auto foo(double x){ } auto call(alias a){ a(0); } void x(){ call!foo(); // ok }This will work.module b; import a; void y(){ call!foo(); // _same_ symbol as in module 'a'. }This would say, Error: overload auto a.foo(int) is not accessible from module b.
Dec 21 2013
On 12/22/2013 12:05 AM, Timon Gehr wrote:Ah sorry, I misread your example. The call template function is in module a, so it has access too.How is this going to work?Are you arguing for creating multiple instances of 'call'?So this is indeed not necessary, the function is called within the template, so only the template scope determines whether a function is accessible.
Dec 22 2013
On 12/22/2013 08:48 PM, Martin Nowak wrote:On 12/22/2013 12:05 AM, Timon Gehr wrote:Therefore none of the following two calls will work? module a; import b; auto call(alias a){ a(0); } void x(){ call!foo(); } module b; import a; private auto foo(int x){ } auto foo(double x){ } void y(){ call!foo(); }Ah sorry, I misread your example. The call template function is in module a, so it has access too. > Are you arguing for creating multiple instances of 'call'? So this is indeed not necessary, the function is called within the template, so only the template scope determines whether a function is accessible.How is this going to work?
Dec 22 2013
On Saturday, 21 December 2013 at 21:14:43 UTC, Martin Nowak wrote:If I make a refactoring and move a function from module A to B, but the function calls an overload set in A it should not have a different overload resolution.If MUST have different overload resolution if B does not have access to those A function. Anything else is damn broken module system. Why would anyone expect to move functions between modules and have accessibility rules magically persistent?Similar case, if I pass an overload set via alias to a traits template in module B, the template should follow the same overload resolution. It's very important that we preserve these properties.It is a major design issue with templates using declaration scope for access resolution instead of instantation scope. I think it was a mistake and is unrelated to general visibility rules.
Dec 21 2013
On 12/21/2013 10:36 PM, Dicebot wrote:I think there is quite some overlap.It is a major design issue with templates using declaration scope for access resolution instead of instantation scope. I think it was a mistake and is unrelated to general visibility rules.
Dec 21 2013
On 12/21/2013 10:36 PM, Dicebot wrote:If MUST have different overload resolution if B does not have access to those A function. Anything else is damn broken module system. Why would anyone expect to move functions between modules and have accessibility rules magically persistent?It's important for example to define a forwarding template in a different module. void call(alias funcs, Args...)(auto ref Args args) { funcs(args); // overload resolution happens here }It is a major design issue with templates using declaration scope for access resolution instead of instantation scope. I think it was a mistake and is unrelated to general visibility rules.What? It's a major design win that we don't have to rerun semantic for every identical instantiation, in the same sense that we don't have to reinclude header files. Or are you only talking about access checks? I don't think that handling access resolution separately from look-up resolution is desirable.
Dec 21 2013
On Saturday, 21 December 2013 at 22:14:51 UTC, Martin Nowak wrote:What? It's a major design win that we don't have to rerun semantic for every identical instantiation, in the same sense that we don't have to reinclude header files.I know that and completely agree with that.Or are you only talking about access checks?Yes.I don't think that handling access resolution separately from look-up resolution is desirable.That was my attitude initially when I have learned about this decision but it is root to certain class of problems in generic code and I have slowly moved to position that RAI for this exception is high enough.
Dec 21 2013
On 12/21/2013 08:29 PM, Martin Nowak wrote:On Saturday, 21 December 2013 at 17:41:32 UTC, Timon Gehr wrote:I think both options are similarly undesirable.Adding a private symbol to an overload set can break 3rd party code with these rules.Yes, this is understood, but it seems like a good tradeoff when compared to the alternative where overload resolution depends on look-up origin. The latter could easily break generic code and simple refactorings could introduce subtle bugs. ...You have a point there. :o)Since private symbols are usually excluded from di files I don't think this design is viable.This, I don't have a good answer for, except that we have to put the private overload in the .di file as well.I think it would be better to disallow overload sets with mixed protection.Tried that, doesn't work. The most common counterexample is mixing private/public constructors.
Dec 21 2013
On 12/21/2013 09:18 PM, Timon Gehr wrote:On 12/21/2013 08:29 PM, Martin Nowak wrote:Mixed protection in overload sets are pretty rare. So we just need to find an acceptable solution to deal with them without breaking valid use-cases. I can hardly come up with any use-case but the constructor example though.On Saturday, 21 December 2013 at 17:41:32 UTC, Timon Gehr wrote:I think both options are similarly undesirable.Adding a private symbol to an overload set can break 3rd party code with these rules.Yes, this is understood, but it seems like a good tradeoff when compared to the alternative where overload resolution depends on look-up origin. The latter could easily break generic code and simple refactorings could introduce subtle bugs. ...
Dec 21 2013
On Sat, Dec 21, 2013 at 10:09:44PM +0100, Martin Nowak wrote:On 12/21/2013 09:18 PM, Timon Gehr wrote:I was pretty upset when I encountered the following situation: ---std/regex.d--- ... private struct Stack { ... } ... ---mymodule.d--- ... /* public */ struct Stack { ... } ... ---main.d--- import std.regex; import mymodule; Stack s; // compile error: 'Stack' is ambiguous I find it unacceptable that introducing a *private* symbol to std.regex will break existing code (std.regex.Stack wasn't there in earlier versions of Phobos). Since std.regex.Stack is private, why would it even be in *any* overload set when compiling main.d??! This is leaky abstraction at its best: changing the implementation of a module without changing its API can randomly break user code due to newly-introduced private symbols clashing with user-defined symbols. T -- Those who've learned LaTeX swear by it. Those who are learning LaTeX swear at it. -- Pete BleackleyOn 12/21/2013 08:29 PM, Martin Nowak wrote:Mixed protection in overload sets are pretty rare. So we just need to find an acceptable solution to deal with them without breaking valid use-cases. I can hardly come up with any use-case but the constructor example though.On Saturday, 21 December 2013 at 17:41:32 UTC, Timon Gehr wrote:I think both options are similarly undesirable.Adding a private symbol to an overload set can break 3rd party code with these rules.Yes, this is understood, but it seems like a good tradeoff when compared to the alternative where overload resolution depends on look-up origin. The latter could easily break generic code and simple refactorings could introduce subtle bugs. ...
Dec 21 2013
On 12/21/2013 10:22 PM, H. S. Teoh wrote:I was pretty upset when I encountered the following situation: ---std/regex.d--- ... private struct Stack { ... } ... ---mymodule.d--- ... /* public */ struct Stack { ... } ... ---main.d--- import std.regex; import mymodule; Stack s; // compile error: 'Stack' is ambiguous I find it unacceptable that introducing a *private* symbol to std.regex will break existing code (std.regex.Stack wasn't there in earlier versions of Phobos). Since std.regex.Stack is private, why would it even be in *any* overload set when compiling main.d??! This is leaky abstraction at its best: changing the implementation of a module without changing its API can randomly break user code due to newly-introduced private symbols clashing with user-defined symbols.Yes, this is the big issue that we try to address with DIP22. Happened to me too when I added a private `abs` helper to core.time which then caused conflicts somewhere else in phobos.
Dec 21 2013