digitalmars.D - Should templates have the instantiating scope's protection access
- Tofu Ninja (33/33) Jul 05 2016 Sorry if this has been posted before.
- Steven Schveighoffer (11/16) Jul 05 2016 Pretty recently actually:
- Tofu Ninja (15/23) Jul 05 2016 Ah that is a really good point, I knew it would muck something
- Tofu Ninja (8/14) Jul 05 2016 Actually scratch that, it would have to match on access rights to
- ZombineDev (7/21) Jul 05 2016 Using mixin templates you can get the behavior you want to some
- Tofu Ninja (21/27) Jul 06 2016 I thought about that, but the problem affects things like emplace
- Lodovico Giaretta (12/30) Jul 06 2016 Thinking about this, maybe the choice about attributes should be
- Tofu Ninja (35/46) Jul 06 2016 That is an option too. However there would still need to be some
- ZombineDev (14/61) Jul 06 2016 If mixin could work with normal templates, as well as mixin
Sorry if this has been posted before. One thing that constantly bugs me about D is how the protection system works when using templates. Currently the a template instantiation has the protection rights from the declaration scope, but this is not really congruent with how a lot of templates are used. For example in phobos it is common to see something like: module bar; void foo(alias pred, R)(R range) { /* somehow uses pred on the range elements */ } Its pretty common throughout std.algorithm. However, because of the way the protection system works, the pred that is passed in MUST be public. For example the following code won't work: private void mypred(RangeElementType x){ /* do something with x */ } ... import bar : foo; foo!mypred(someRange); Because foo is declared in another module, mypred MUST be public. I am passing mypred explicitly, there is no way I didn't intend for foo to have access to it. This causes lots of unnecessary public functions. An even more annoying consequence of this is that emplace can never be used with private constructors, this fundamentally puts it in a weaker position than new. This is actually a serious problem for std.experimental.allocator. You can't use an allocator in the same way you use new. One possible solution could be that templates have the access rights of the instantiating scope. This is honestly what I expect most of the time but there are probably cases where this would muck things up. Maybe a more case by case solution would be better. Thoughts?
Jul 05 2016
On 7/5/16 1:46 PM, Tofu Ninja wrote:Sorry if this has been posted before.Pretty recently actually: https://forum.dlang.org/post/rduvpfuzfzmzhoknsrkr forum.dlang.orgOne possible solution could be that templates have the access rights of the instantiating scope. This is honestly what I expect most of the time but there are probably cases where this would muck things up. Maybe a more case by case solution would be better.The clear problem with this solution is that this means you must use the instantiating module as part of the template definition. A template instantiation with exactly the same parameters must behave exactly the same, no matter where it was instantiated from. What this means, is that each instantiation is now tied to the module instantiating it, with no code reuse. I think at the very least, this should be opt-in if it even makes sense to do. -Steve
Jul 05 2016
On Tuesday, 5 July 2016 at 18:04:31 UTC, Steven Schveighoffer wrote:The clear problem with this solution is that this means you must use the instantiating module as part of the template definition. A template instantiation with exactly the same parameters must behave exactly the same, no matter where it was instantiated from. What this means, is that each instantiation is now tied to the module instantiating it, with no code reuse. I think at the very least, this should be opt-in if it even makes sense to do.Ah that is a really good point, I knew it would muck something up. But I suppose in a lot of cases this doesn't really matter, if the template is accessing private things then it already couldn't be reused outside of the module. I think it would make a difference in cases where the template does some kind of reflection or conditional compilation based on if it has access or not. Still opt-in I think. Also there could be code re-use wherever the access rights match. So if an argument is marked as opt-in, only the instantiation scope's access to that argument would need to be tied to the template instantiation. I suppose that means there would be 4 possible instantiations, one for private, package, protected, and public access.
Jul 05 2016
On Tuesday, 5 July 2016 at 18:48:05 UTC, Tofu Ninja wrote:Also there could be code re-use wherever the access rights match. So if an argument is marked as opt-in, only the instantiation scope's access to that argument would need to be tied to the template instantiation. I suppose that means there would be 4 possible instantiations, one for private, package, protected, and public access.Actually scratch that, it would have to match on access rights to the argument and any symbol accessible through the argument. It would get even crazier on recursive instantiations, so I think you are right, the template would have to be tied to the original instantiation module. If the template instantiated any other templates that had this behavior it would also have to be tied to the original module. Sounds complicated.
Jul 05 2016
On Tuesday, 5 July 2016 at 19:00:14 UTC, Tofu Ninja wrote:On Tuesday, 5 July 2016 at 18:48:05 UTC, Tofu Ninja wrote:Using mixin templates you can get the behavior you want to some extent, with exception that you need to type "mixin" in front when you're instantiating them. I wonder if reusing the mixin statement for normal templates would be a good idea to reuse existing code while allowing access to private members.Also there could be code re-use wherever the access rights match. So if an argument is marked as opt-in, only the instantiation scope's access to that argument would need to be tied to the template instantiation. I suppose that means there would be 4 possible instantiations, one for private, package, protected, and public access.Actually scratch that, it would have to match on access rights to the argument and any symbol accessible through the argument. It would get even crazier on recursive instantiations, so I think you are right, the template would have to be tied to the original instantiation module. If the template instantiated any other templates that had this behavior it would also have to be tied to the original module. Sounds complicated.
Jul 05 2016
On Wednesday, 6 July 2016 at 06:43:13 UTC, ZombineDev wrote:Using mixin templates you can get the behavior you want to some extent, with exception that you need to type "mixin" in front when you're instantiating them. I wonder if reusing the mixin statement for normal templates would be a good idea to reuse existing code while allowing access to private members.I thought about that, but the problem affects things like emplace as well and I somehow doubt that we are going to turn emplace into a mixin template. I think there needs to be some kind of opt-in attribute to indicate that the template should use the instantiating module's access rights. There are three things it would need to do... 1) It would need to cause the template instantiation to have the same access rights as the instantiating module. 2) It would need to tie the instantiation to the instantiating module, different module will have different access rights so the templates won't match. 3) It would need to transmit these access rights through other templates if they also have the opt-in attribute. All the templates would have the instantiating scopes access rights. It would be needed in cases like allocator.make which ends up calling emplace eventually. The access rights would need to be transmitted all the way to emplace. The attribute could be argument specific or applied to the whole template, I am not sure which would be better but they could both work.
Jul 06 2016
On Wednesday, 6 July 2016 at 07:47:18 UTC, Tofu Ninja wrote:I think there needs to be some kind of opt-in attribute to indicate that the template should use the instantiating module's access rights. There are three things it would need to do... 1) It would need to cause the template instantiation to have the same access rights as the instantiating module. 2) It would need to tie the instantiation to the instantiating module, different module will have different access rights so the templates won't match. 3) It would need to transmit these access rights through other templates if they also have the opt-in attribute. All the templates would have the instantiating scopes access rights. It would be needed in cases like allocator.make which ends up calling emplace eventually. The access rights would need to be transmitted all the way to emplace. The attribute could be argument specific or applied to the whole template, I am not sure which would be better but they could both work.Thinking about this, maybe the choice about attributes should be made by the user, so that if I want to give emplace access to private ctors of my structs, I have to explicitly declare inside my module that I'm going to give emplace those rights (which in turn are propagated to whatever templates emplace uses internally). This way the template writer doesn't have to bother whether his template needs those rights, it is clear which templates have them without looking at their declarations, and users can decide not to grant these rights (bonus point: this way nothing changes behaviour unless the user wants so).
Jul 06 2016
On Wednesday, 6 July 2016 at 08:05:38 UTC, Lodovico Giaretta wrote:Thinking about this, maybe the choice about attributes should be made by the user, so that if I want to give emplace access to private ctors of my structs, I have to explicitly declare inside my module that I'm going to give emplace those rights (which in turn are propagated to whatever templates emplace uses internally). This way the template writer doesn't have to bother whether his template needs those rights, it is clear which templates have them without looking at their declarations, and users can decide not to grant these rights (bonus point: this way nothing changes behaviour unless the user wants so).That is an option too. However there would still need to be some way for the rights to get carried through multiple levels of templates. I suppose retransmission could be done in two ways, explicitly or implicitly. I am not sure which way would be better. If it was explicit, things like allocator.make would need to explicitly retransmit the access rights down to emplace. Something like : auto make(T, A, ARGS...)(A alloc, ARGS args) { // ... emplace!( ShareAccess T)(/* ... */); //... } // Some other module class myClass{ private this(int x){} } // ... auto x = Mallocator.make!( ShareAccess myClass)(5); // Access rights get transmitted into make and then make retransmits them into emplace If it was implicit the ShareAccess in make would not be necessary, the access rights would carry through to emplace implicitly. I am not sure it would be a good option though because access might get accidentally granted somewhere it shouldn't. Having it be opt-in by the user also resolves the range function example I had earlier where it needed access to mypred. In this case I think the implicit retransmission is probably better, as it would allow any range function to not care about retransmitting the rights if they pass the argument to other templates.
Jul 06 2016
On Wednesday, 6 July 2016 at 08:45:25 UTC, Tofu Ninja wrote:On Wednesday, 6 July 2016 at 08:05:38 UTC, Lodovico Giaretta wrote:If mixin could work with normal templates, as well as mixin templates, the following would work: auto make(T, A, ARGS...)(A alloc, ARGS args) { // ... mixin emplace!T(/* ... */); //... } // Some other module class myClass{ private this(int x){} } // ... auto x = mixin Mallocator.make!myClass(5);Thinking about this, maybe the choice about attributes should be made by the user, so that if I want to give emplace access to private ctors of my structs, I have to explicitly declare inside my module that I'm going to give emplace those rights (which in turn are propagated to whatever templates emplace uses internally). This way the template writer doesn't have to bother whether his template needs those rights, it is clear which templates have them without looking at their declarations, and users can decide not to grant these rights (bonus point: this way nothing changes behaviour unless the user wants so).That is an option too. However there would still need to be some way for the rights to get carried through multiple levels of templates. I suppose retransmission could be done in two ways, explicitly or implicitly. I am not sure which way would be better. If it was explicit, things like allocator.make would need to explicitly retransmit the access rights down to emplace. Something like : auto make(T, A, ARGS...)(A alloc, ARGS args) { // ... emplace!( ShareAccess T)(/* ... */); //... } // Some other module class myClass{ private this(int x){} } // ... auto x = Mallocator.make!( ShareAccess myClass)(5); // Access rights get transmitted into make and then make retransmits them into emplace If it was implicit the ShareAccess in make would not be necessary, the access rights would carry through to emplace implicitly. I am not sure it would be a good option though because access might get accidentally granted somewhere it shouldn't. Having it be opt-in by the user also resolves the range function example I had earlier where it needed access to mypred. In this case I think the implicit retransmission is probably better, as it would allow any range function to not care about retransmitting the rights if they pass the argument to other templates.
Jul 06 2016