Bug 24587 – Allow negated qualifiers in cast expressions

Status
NEW
Severity
enhancement
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2024-06-05T13:07:53Z
Last change time
2024-12-13T19:35:36Z
Assigned to
No Owner
Creator
Bolpat
Moved to GitHub: dmd#18245 →

Comments

Comment #0 by qs.il.paperinik — 2024-06-05T13:07:53Z
Allow a `!` in a cast expression to remove specific qualifiers from a type. Unlike `cast()`, notably `cast(!shared)` removes `shared` only and leaves `inout` and/or `const` intact if they’re present. More than one can be used and `!` negates all of them: `cast(!const shared)` removes `const` and `shared`, but leaves `inout` intact. Grammar: ```diff CastQual: cast ( TypeCtors? ) UnaryExpression + cast ( ! TypeCtors ) UnaryExpression ``` Note that the `TypeCtors` in the second rule one aren’t optional.
Comment #1 by razvan.nitu1305 — 2024-06-07T07:47:22Z
I don't find this very useful. It seems that we are complicating the type system only to obtain something that is actually more confusing. cast() is used to drop all qualifiers whereas if you want to keep some of them you can just cast to the appropriate combination of qualifiers (i.e. cast(const shared)). That way you know exactly what the type is at the cast site, whereas introducing the possibility to negate a single qualifier makes it ambiguous with regards to what the type at the cast site is. Also, can you provide a concrete code example where this is useful? It seems to me that this is a theoretical proposal without any actual practical usefulness.
Comment #2 by qs.il.paperinik — 2024-06-17T13:16:26Z
I just found out `cast(const)` not only adds `const` if it’s not present, it also removes any other qualifiers. (In reply to RazvanN from comment #1) > I don't find this very useful. Not an argument. > It seems that we are complicating the type > system only to obtain something that is actually more confusing. The type system remains unchanged. It only adds a concise syntax to cast away specific qualifiers without having to query the type. You can argue the syntax is confusing. I don’t think it is. > cast() is > used to drop all qualifiers whereas if you want to keep some of them you can > just cast to the appropriate combination of qualifiers (i.e. cast(const > shared)). Yes, but that’s not easy. One has to know which qualifiers are present. In a template-context, one has to tediously query them, and in a non-template context, one has to look them up and repeat them, and if they change, too bad, the cast now does something unintended. > That way [with `cast(Quals)`] you know exactly what the type is at the cast site, > whereas introducing the possibility to negate a single qualifier makes it > ambiguous with regards to what the type at the cast site is. When is that a use-case? When is “I don’t care what qualifiers a type has, make it XYZ” a use case? The only one I see is when you want to extract information of what template instance or whatnot something is, and qualifiers are in the way. I.e. in an unevaluated context. Of course, if you have a `const shared LongAndComplicated!(Template!Instance) x`, it’s easy to write `cast(const)x`, but here I have to look up its type, see that it’s `const` and repeat(!) that qualifier. What I want is not repeat anything and express intent, i.e. the intent to remove `shared`. What did a `cast(const)` intend? Add `const`? Remove something else and retain `const`? Who knows. > Also, can you provide a concrete code example where this is useful? I can’t because I don’t work professionally with D. Any example I’d provide would be dismissed as “toy project example” anyways. > It seems > to me that this is a theoretical proposal without any actual practical > usefulness. Among the negated qualifier casts, most definitely `cast(!shared)` would be useful. If you `cast()`, you might end up casting away `const` and mutate stuff that you shouldn’t. For `cast(!shared)` on a `const shared` type object, I see two uses: 1. You intend to read, then you lock a mutex, `cast(!shared)` and do the read while being assured by the compiler you’re not mutating anything as `const` (or anything else) goes nowhere. Also, `const` member functions are used instead of non-`const` ones. 2. You intend to write, and because `cast(!shared)` leaves `const` intact, you’ll get a compile-error instead of UB if you accidentally write to something that’s `const`. I don’t know where in run-time code you want to override all qualifiers. In general, I’d assume you always want to add or remove specific ones. E.g. template code can’t really use `cast(const)` not because it adds `const` (that’s obvious), but because it removes `shared` when it’s present and does so “silently”.
Comment #3 by robert.schadek — 2024-12-13T19:35:36Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/18245 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB