Bug 24516 – qualifiers lost when tupleof is aliased

Status
NEW
Severity
normal
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2024-04-21T04:16:39Z
Last change time
2024-12-13T19:34:48Z
Assigned to
No Owner
Creator
Jonathan M Davis
Moved to GitHub: dmd#20444 →

Comments

Comment #0 by issues.dlang — 2024-04-21T04:16:39Z
When tupleof is used on a const type, the types of the resulting symbols are const. However, if the result of tupleof is aliased (either directly within a function or inside an eponymous template), then the constness is lost. --- void main() { import std.meta : AliasSeq; static struct S { int i; int* ptr; string str; } static assert(is(typeof(S.tupleof) == AliasSeq!(int, int*, string))); static assert(is(typeof(const(S).tupleof) == AliasSeq!(const int, const int*, const string))); alias CS = const S; static assert(is(typeof(CS.tupleof) == AliasSeq!(const int, const int*, const string))); alias Symbols = S.tupleof; alias ConstSymbols = CS.tupleof; static assert(is(typeof(Symbols) == AliasSeq!(int, int*, string))); // fails static assert(is(typeof(ConstSymbols) == AliasSeq!(const int, const int*, const string))); static assert(is(typeof(FieldSymbols!S) == AliasSeq!(int, int*, string))); // fails static assert(is(typeof(FieldSymbols!(const S)) == AliasSeq!(const int, const int*, const string))); } template FieldSymbols(T) { alias FieldSymbols = T.tupleof; } --- The lines that are marked with // fails result in --- test.d(22): Error: static assert: `is((int, int*, string) == (const(int), const(int*), const(string)))` is false --- indicating that the constness was lost when the result of tupleof was aliased.
Comment #1 by nick — 2024-05-01T14:43:24Z
> S.tupleof I don't think it makes sense to allow `tupleof` to be a type property. It should be instance only. The spec does not mention its use on a type. https://dlang.org/spec/class.html#tupleof Also in dmd/typesem.d, dotExp: ```d else if (ident == Id._tupleof) { if (e.isTypeExp()) { error(e.loc, "`.tupleof` cannot be used on type `%s`", mt.toChars); return ErrorExp.get(); } ``` For some reason that is not being triggered.
Comment #2 by issues.dlang — 2024-05-01T22:56:04Z
(In reply to Nick Treleaven from comment #1) > > S.tupleof > > I don't think it makes sense to allow `tupleof` to be a type property. It > should be instance only. The spec does not mention its use on a type. > https://dlang.org/spec/class.html#tupleof The spec uses tupleof on a type right there in the example that you're linking to even before it uses it on a variable: --- class Foo { int x; long y; } static assert(__traits(identifier, Foo.tupleof[0]) == "x"); static assert(is(typeof(Foo.tupleof)[1] == long)); --- And both druntime and Phobos having been using tupleof on types for years - e.g. std.traits.Fields. It would break a lot of existing code if tupleof stopped working on types. Also, in most cases, it's less error-prone to do type introspection on types rather than values or symbols. Some kinds of type introspection have to be done on values or symbols, but pretty much any time that a trait takes an alias rather than a type, it makes it far trickier to write it correctly, and corner cases are frequently a problem. If anything, the fact that type qualifers are being lost when aliasing the result of tupleof is an example of why aliasing symbols when doing type introspection (as opposed to aliasing types) tends to be error-prone. We unfortunately have variety of bugs where weird stuff happens like type qualifiers disappearing when operating on symbols rather than on types, and it's why some of the traits in std.traits are overloaded so that they can explicitly take types or use an alias parameter. Using tupleof on an instance has some uses, since then you can do stuff like --- foo.tupleof[0] = 1; --- but when doing type introspection, it makes far more sense to be operating on the type, and tupleof has worked that way for many years. The problem in this bug report is that for some reason, aliasing the result of tupleof results in symbols that behave differently from the direct result of tupleof, and the qualifiers are lost. I see no reason why it should be reasonable for that to happen.
Comment #3 by b2.temp — 2024-05-01T23:35:51Z
(In reply to Nick Treleaven from comment #1) > > S.tupleof > > I don't think it makes sense to allow `tupleof` to be a type property. It > should be instance only. The spec does not mention its use on a type. > https://dlang.org/spec/class.html#tupleof > > Also in dmd/typesem.d, dotExp: > ```d > else if (ident == Id._tupleof) > { > if (e.isTypeExp()) > { > error(e.loc, "`.tupleof` cannot be used on type `%s`", > mt.toChars); > return ErrorExp.get(); > } > ``` > For some reason that is not being triggered. The reason must be that `S` in `S.tupleof` is not a TypeExp it's a DeclExp.
Comment #4 by nick — 2024-05-02T17:16:34Z
> The reason must be that `S` in `S.tupleof` is not a TypeExp it's a DeclExp. No, the quoted code is for static array tupleof. I tried adding it for classes and structs but dotExp seems to have a phantom expression e which is a VarExp.
Comment #5 by nick — 2024-05-02T17:27:34Z
> The spec uses tupleof on a type right there in the example Sorry, yes. We shouldn't break code then.
Comment #6 by b2.temp — 2024-05-02T17:56:31Z
(In reply to Nick Treleaven from comment #4) > > The reason must be that `S` in `S.tupleof` is not a TypeExp it's a DeclExp. > > No, the quoted code is for static array tupleof. I see, the compiler code you quoted is not for `S.tupleof` > I tried adding it for > classes and structs but dotExp seems to have a phantom expression e which is a VarExp. I'm not sure if that helps but, as you might already know ?, the D front-end rewrites the DotExp lhs to what it resolves to e.g either a VarExp, a TypeExp, a DeclExp, etc. If it's not that then maybe that VarExp is used to extract a side-effects or to create a lvalue. but I dont see why this would be done on a type, that would be a bug.
Comment #7 by robert.schadek — 2024-12-13T19:34:48Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/20444 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB