Bug 15335 – getSymbolsByUDA fails if type has private members
Status
RESOLVED
Resolution
FIXED
Severity
enhancement
Priority
P1
Component
phobos
Product
D
Version
D2
Platform
All
OS
All
Creation time
2015-11-14T12:18:58Z
Last change time
2020-03-21T03:56:39Z
Assigned to
No Owner
Creator
ryan
Comments
Comment #0 by ryan — 2015-11-14T12:18:58Z
getSymbolsByUDA will fail on a type that has _any_ private members (even if they are not marked with an attribute).
Case:
---
import std.traits;
struct mark { }
struct S {
@mark int a;
private int b;
}
static assert(getSymbolsByUDA!(S, mark).length == 1);
---
Error:
---
Press ENTER or type command to continue
/usr/include/dlang/dmd/std/traits.d(6701): Error: struct type.S member b is not accessible
/usr/include/dlang/dmd/std/meta.d(546): Error: template instance std.traits.getSymbolsByUDA!(S, mark).F!"b" error instantiating
/usr/include/dlang/dmd/std/meta.d(553): instantiated from here: staticMap!(StringToSymbol, "b")
/usr/include/dlang/dmd/std/traits.d(6703): instantiated from here: staticMap!(StringToSymbol, "a", "b")
/tmp/type.d(10): instantiated from here: getSymbolsByUDA!(S, mark)
Failed: ["dmd", "-Isrc", "-main", "-unittest", "-debug", "-g", "-v", "-o-", "/tmp/type.d", "-I/tmp"]
---
Comment #1 by b2.temp — 2015-11-16T17:43:25Z
I've encountered a similar issue yesterday. It looks like the only way to solve this is to make `getSymbolsByUDA` a mixin template containing the current function. Then when the template is mixed in the local scope, there's no more fake warning in case when a private member is accessed (even in "friends" classes or struct, so inside the same module.
For example in my case I finished with this:
---
mixin template ScopedReachability()
{
bool isMemberReachable(T, string member)()
if (is(T==class) || is(T==struct))
{
return __traits(compiles, __traits(getMember, T, member));
}
}
---
If I mix ScopedReachability where `isMemberReachable` has to be used it works. Previously, calling the function template was leading to the same error message that's reported here...
...So, back to real topic, by analogy:
---
mixin template ScopedgetSymbolsByUDA
{
template getSymbolsByUDA(alias symbol, alias attribute)
{
import std.typetuple : Filter, staticMap, TypeTuple;
static enum hasSpecificUDA(alias S) = hasUDA!(S, attribute);
alias StringToSymbol(alias Name) = Identity!(__traits(getMember, symbol, Name));
alias getSymbolsByUDA = Filter!(hasSpecificUDA, TypeTuple!(symbol,
staticMap!(StringToSymbol, __traits(allMembers, symbol))));
}
}
---
should work. Instead of calling the template, you first mix `ScopedgetSymbolsByUDA` and then you call the `getSymbolsByUDA` that's local. Of course this is not a workaround that I propose here. I think this should be done like this in phobos.
Comment #2 by ryan — 2015-11-18T03:15:00Z
(In reply to bb.temp from comment #1)
> I've encountered a similar issue yesterday. It looks like the only way to
> solve this is to make `getSymbolsByUDA` a mixin template containing the
> current function.
Yeah, its messy. I'm not sure I'd want it to require users to mix in something, but I'm at a loss for how else we can return actual symbols.
It _can_ be implemented if you just want to return member names:
template getMembersByUDA(T, alias attribute)
{
import std.meta : Filter;
enum hasSpecificUDA(string name) = mixin("hasUDA!(T."~name~", attribute)");
alias getMembersByUDA = Filter!(hasSpecificUDA, __traits(allMembers, T));
}
But then the user has to translate them to symbols using getMember on their end.
Comment #3 by b2.temp — 2015-11-21T09:49:55Z
I've looked at your PR on GH and obviously the first one is better since it just prevents the error.
The accessibility is a more global problem. Every time someone will put a function related to traits in a module and use this function in another module the same error will pop up. A mixin template allows to inject the function related to traits in the module it's used. So far it's ok, let's say for a private usage, (actually there's --0-- mixin template in phobos so I doubt they'll ever accept some new functions based on this, even if it works).
Maybe the real solution would be to make "__traits(getMember,...)", and more generally speaking all the traits verbs that might also block compilation because of this, able to return any member, regardless of the protection.
Then it would be up to the user to respect or not the protection using an additional "__traits(getProtection,...)" to filter an item in the traits code.
Comment #4 by ryan — 2015-11-23T13:19:45Z
(In reply to bb.temp from comment #3)
> (actually there's --0-- mixin template in phobos so
> I doubt they'll ever accept some new functions based on this, even if it
> works).
Actually, Proxy is a mixin template. Still, it actually makes sense there, whereas using a mixin template for this feels a bit hackier.
> Maybe the real solution would be to make "__traits(getMember,...)", and more
> generally speaking all the traits verbs that might also block compilation
> because of this, able to return any member, regardless of the protection.
Yeah, it would be nice if getMember could distinguish between 'referring' to a member and 'accessing' a member. As a matter of fact, you _can_ do this, as my getMembersByUDA example shows. You can inspect SomeType.privateMember by building the expression out of a mixin; I just don't know if you can translate that to a symbol without violating privacy.
Comment #5 by ryan — 2015-11-23T13:56:48Z
Here's another idea, this time with more mixins! Don't have time to test thoroughly right now but I _think_ its working with private members and giving back symbols rather than names:
template getMembersByUDA(T, alias attribute) {
import std.string : format;
import std.meta : Filter;
template toSymbols(names...) {
static if (names.length == 1)
mixin("alias toSymbols = AliasSeq!(T.%s);".format(names[0]));
else
mixin("alias toSymbols = AliasSeq!(toSymbols!(names[1..$]), %s);".format(names[0]));
}
enum hasSpecificUDA(string name) = mixin("hasUDA!(T."~name~", attribute)");
alias membersWithAttribute = Filter!(hasSpecificUDA, __traits(allMembers, T));
alias getMembersByUDA = toSymbols!(membersWithAttribute);
}
mixins are like violence ... if they don't work you're not using enough of them.
I'll test more later and put up a separate PR if it looks more promising.