Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Breaking change: Rebindable is always a struct with get. #8735

Conversation

FeepingCreature
Copy link
Contributor

@FeepingCreature FeepingCreature commented Apr 18, 2023

Previously, Rebindable!T aliases itself away to T if T is an array. This is bad, because it means we cannot reliably get rid of Rebindable with r.get again, forcing an additional cryptic static if in all code where the callee should never be Rebindable.

Let's just get rid of it.

See #8734 for a PR made awkward by Rebindable!T sometimes aliasing itself to T.

Note: This PR is largely to run Buildkite to see if this breaks anything.

Seems surprisingly green!

Okay, what do I need to do to get this merged?

@dlang-bot
Copy link
Contributor

Thanks for your pull request and interest in making D better, @FeepingCreature! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please verify that your PR follows this checklist:

  • My PR is fully covered with tests (you can see the coverage diff by visiting the details link of the codecov check)
  • My PR is as minimal as possible (smaller, focused PRs are easier to review than big ones)
  • I have provided a detailed rationale explaining my changes
  • New or modified functions have Ddoc comments (with Params: and Returns:)

Please see CONTRIBUTING.md for more information.


If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment.

Bugzilla references

Your PR doesn't reference any Bugzilla issue.

If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog.

Testing this PR locally

If you don't have a local development environment setup, you can use Digger to test this PR:

dub run digger -- build "master + phobos#8735"

@FeepingCreature FeepingCreature changed the title [TESTING] Breaking change: Rebindable is always a struct with get. Breaking change: Rebindable is always a struct with get. Apr 18, 2023
@ghost
Copy link

ghost commented Apr 18, 2023

what do I need to do to get this merged?

At first glance the ddox failure seems unrelated, you need to bring the attention of the author on that failure however.

@FeepingCreature
Copy link
Contributor Author

FeepingCreature commented Apr 18, 2023

Right, I've seen the ddox thing before on other PRs, I suspect it's some build timeout or memory usage, especially since it doesn't happen locally.

Ping @s-ludwig

edit: I mean, what do I need to do unrelated to the ddox issue? This is a pretty big change, and not amenable to deprecated.

@s-ludwig
Copy link
Member

unrelated ddox fail: dlang/ddox#239

@atilaneves
Copy link
Contributor

I didn't understand the explanation for why the current behaviour is bad.

@FeepingCreature
Copy link
Contributor Author

FeepingCreature commented Apr 18, 2023

When you have Rebindable!T foo, due to alias get this, you can always do T bar = foo. However, if you're chain-calling a function that does IFTI, there is no way to say "I want foo but without Rebindable" even if you know T - there is no inverse operation to rebindable that always gets you the original type back. (D does not have an "implicit conversion expression".) foo.get could be that operation, if only Rebindable didn't alias itself away for some types. That's why #8734 for instance has to static if detect whether the Rebindable actually had an effect.

@FeepingCreature
Copy link
Contributor Author

I guess I should investigate why "alias to T for arrays" was done in the first place.

@atilaneves
Copy link
Contributor

I think investigating would be wise. I don't know either.

@FeepingCreature
Copy link
Contributor Author

Looks like "that's just how Andrei wrote it back in 2008."

Ping @andralex ?

@andralex
Copy link
Member

Honestly I forgot the particulars, but probably that alias has to do with qualified arrays having an obvious way to rebind (dropping the top-level qualifier and passing it to the element type).

Rebindable has had a nice intent but its practicality didn't go too far. If there are more ideas on how to improve this type, I think the nicest thing to do would be to redo Rebindable under a different name and quietly undocumment Rebindable.

@FeepingCreature
Copy link
Contributor Author

FeepingCreature commented Apr 20, 2023

I mean, we do have "struct Rebindable" now! I think it's salvageable. Since pretty much every other type now results in a struct with .get, do you think arrays are worth keeping as an alias? Upside is I don't think pretty much anyone else used Rebindable since it was never fully generic until now. So if we want to fix this, now's the time.

@FeepingCreature FeepingCreature force-pushed the fix/breaking-rebindable-always-is-a-struct branch from c477427 to d20ad40 Compare May 3, 2023 12:02
@FeepingCreature
Copy link
Contributor Author

ping <3

@dkorpel
Copy link
Contributor

dkorpel commented May 3, 2023

@atilaneves

@FeepingCreature
Copy link
Contributor Author

FeepingCreature commented May 13, 2023

hi

bump btw :))

(Srsly, if you're not gonna merge, just reject, this silent waiting is just bad project management practice.)

@dkorpel
Copy link
Contributor

dkorpel commented May 13, 2023

@atilaneves

@dkorpel
Copy link
Contributor

dkorpel commented May 13, 2023

This needs a changelog entry

@dkorpel
Copy link
Contributor

dkorpel commented May 13, 2023

this silent waiting is just bad project management practice

I'm sorry, I'll address this stalling of Phobos PRs in the next meeting

@FeepingCreature FeepingCreature force-pushed the fix/breaking-rebindable-always-is-a-struct branch from d20ad40 to 6bd4f98 Compare May 15, 2023 06:19
@FeepingCreature
Copy link
Contributor Author

FeepingCreature commented May 15, 2023

Changelog entry added.

@FeepingCreature
Copy link
Contributor Author

FeepingCreature commented May 15, 2023

Wait, why did I misremember that this was mostly about arrays? :stares at the diff in confusion: I mean, the change makes sense but ... I should add classes to the changelog as well.

Okay, updated: Everything that uses Rebindable!X, classes and arrays, gets a struct Rebindable with X get(). I forgot that classes had hacky "alias away Rebindable" magic as well.

This type is messed up, yo.

@FeepingCreature FeepingCreature force-pushed the fix/breaking-rebindable-always-is-a-struct branch from 6bd4f98 to e7fd9b5 Compare May 15, 2023 06:30
@FeepingCreature
Copy link
Contributor Author

FeepingCreature commented May 15, 2023

Wait. No. How did this used to work for classes? The tests say that Rebindable!C just is a C. But the code only checks for isDynamicArray.

Wait.

Wait, no. The code used to alias away Rebindable if the type was not const, or rather, not qualified const or immutable. Okay, this makes slightly more sense.

Out with it anyways. Being able to always write T.get is simply more useful in generic code. Consistency beats corner-case convenience.

@FeepingCreature
Copy link
Contributor Author

FeepingCreature commented May 15, 2023

Update: I have tried to get rid of RebindableCommon entirely (inline it in UnqualRef), but this has run face-first into https://issues.dlang.org/show_bug.cgi?id=18755 . I'm not sure what Rebindable is actually supposed to do in that case. The struct Rebindable tries to use cast() if it is available (to avoid the union cast hack that doesn't work in CTFE), but opCast can be declared to not work on certain constnesses. Should I just turn off the cast() path if an opCast overload is detected, on the basis of "surely it is not intended for us"?

edit: Seems to work. 3 files changed, 127 insertions(+), 147 deletions(-) Should I push it?

edit: Hm, unittests don't compile. If somebody can sanity check me on this? std.datetime.systime contains this function

    pragma(inline, true) @property void _timezone(return scope immutable TimeZone tz) \
        @safe pure nothrow @nogc scope
    {
        _timezoneStorage = tz;
    }

I had to remove the return scope. And... this is nonsense, right? tz outlives _timezone in _timezoneStorage. This is exactly not "return scope", it's practically the definition of an escaping value. But if I do that, a bunch of other code breaks.

Previously, `Rebindable!T` aliases itself away to `T` if `T` is an array. This is bad, because it means we cannot reliably get rid of `Rebindable` with `r.get` again, forcing an additional cryptic `static if` in all code where the callee should never be `Rebindable`.
Let's just get rid of it.
@FeepingCreature FeepingCreature force-pushed the fix/breaking-rebindable-always-is-a-struct branch from e7fd9b5 to 553453a Compare May 15, 2023 07:30
@dkorpel
Copy link
Contributor

dkorpel commented May 15, 2023

This is exactly not "return scope", it's practically the definition of an escaping value.

Since the function returns void, return scope in this case means it may be assigned to the first parameter this, so this._timezoneStorage = tz; is okay.

@FeepingCreature
Copy link
Contributor Author

FeepingCreature commented May 16, 2023

Ah, so any (sensible) opAssign may take its first parameter return scope?

Excellent, seems that made it work. Since you clicked Approve, I'll make a separate PR.

edit: nevermind, more errors...

edit: Yeah that attempt fails in cryptic ways further in. I'll make a separate PR if I can make it work, it shouldn't hold up this one.

@dkorpel
Copy link
Contributor

dkorpel commented May 18, 2023

Ah, so any (sensible) opAssign may take its first parameter return scope?

Only when it returns void. opAssign usually returns this though.

@FeepingCreature
Copy link
Contributor Author

hi ping

@FeepingCreature
Copy link
Contributor Author

FeepingCreature commented Jun 15, 2023

Since it's been "an issue" lately (I disagree, but whatever): I'm entirely fine with renaming this to Rebindable2! (Or std.v2.typecons.Rebindable.) I just want a Rebindable with sane behavior in Phobos, I don't care what it's called.

Ie. I want to use it in my own Phobos std.algorithm PRs, I don't care who else uses it.

@dkorpel
Copy link
Contributor

dkorpel commented Jun 15, 2023

Ie. I want to use it in my own Phobos std.algorithm PRs, I don't care who else uses it.

In that case, you can make a private / package(std) Rebindable2 template, so we can defer the breaking change to Phobos v2 or something.

Although I don't mind the PR as is, since I don't think it breaks much.

@atilaneves

@FeepingCreature
Copy link
Contributor Author

FeepingCreature commented Jun 19, 2023

In case this (against expectations) actually gets merged, let me document my rationale of why it's okay to break behavior here.

I don't think anybody was actually using Rebindable for arrays. The first big usecase for it is generic code, but in generic code it didn't support structs, making it extremely marginal. If you know you have an array, you can make it rebindable by Unqual, and probably did. Rebindable was useful for classes, but that behavior is unaffected by this change.

In order for the Rebindable array case to be useful, you had to be writing generic code that took either arrays or objects, but not structs. Feel free to point out my folly if you actually did, but my naive expectation is that this has literally never happened.

Of course, now that Rebindable does support structs, this rationale becomes less true by the day. That's why I was kind of hoping to get this in before 2.104.

(Note: part of the problem is that it is impossible to deprecate this behavior.)

@FeepingCreature
Copy link
Contributor Author

FeepingCreature commented Jun 19, 2023

Correction: I just saw that this change affects classes as well. Nevermind this, I'm making a Rebindable2.

Be right back.

@andralex
Copy link
Member

Given that the name Rebindable was not very inspired to begin with, this may be a good opportunity to find a better name and leave Rebindable on the deprecation path.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants