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

fix 14478: support non-copyable elements in many range algorithms #8721

Merged
merged 1 commit into from
May 1, 2023

Conversation

WebFreak001
Copy link
Member

@WebFreak001 WebFreak001 commented Mar 16, 2023

same as #8714

code not fully covered

@dlang-bot
Copy link
Contributor

Thanks for your pull request, @WebFreak001!

Bugzilla references

Auto-close Bugzilla Severity Description
14478 enhancement isInputRange should allow ranges of non-copyable elements

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#8721"

@RazvanN7
Copy link
Collaborator

RazvanN7 commented May 1, 2023

@WebFreak001 the testers are failing. Are you willing to push this to the finish line?

@dlang-bot dlang-bot merged commit d35dafb into dlang:master May 1, 2023
@WebFreak001 WebFreak001 deleted the fix-14478 branch May 1, 2023 16:18
@WebFreak001
Copy link
Member Author

thanks!

@RazvanN7
Copy link
Collaborator

RazvanN7 commented May 1, 2023

Thank you!

@JohanEngelen
Copy link
Contributor

JohanEngelen commented Apr 3, 2024

This PR results in issues at Weka with retro for a struct that has void opAssign()(auto ref const(typeof(this)) that) const defined, i.e. a struct that allows const assignment. The problem is that assignment is allowed, but move is not (because it would be a memcpy to const memory).

The cause of the problem: retro checks for hasAssignableElements, but then does a move rather than an assignment.
https://github.com/dlang/phobos/pull/8721/files#diff-705ed5d9f4ea4f9dadcb07d7116df8dc8397e851639a28538dc0b1d6fa446f56L312-R318

Testcase (https://d.godbolt.org/z/z6KMY6nYf):

import std.range;

struct Handle { 
    int[5] entries;
    void opAssign()(auto ref const(typeof(this)) that) const { }
}

const(Handle)[100] arr;
auto foo() {
    return arr[0..5].retro;
}

the problem is fixed by reverting the lines for retro.

@jmdavis
Copy link
Member

jmdavis commented Apr 3, 2024

This PR results in issues at Weka with retro for a struct that has void opAssign()(auto ref const(typeof(this)) that) const defined, i.e. a struct that allows const assignment. The problem is that assignment is allowed, but move is not (because it would be a memcpy to const memory).

Aside from anything to do with the specific changes here, what does a const opAssign even do? Conceptually, that seems like it would be illegal, because mutating const violates the type system (though without the compiler having extra checks to prevent it, it makes sense that opAssign would be treated like any other function in its ability to be const). Is this opAssign casting away const to mutate anyway? Or is it just mutating something outside the object instead of the object itself? Or is not actually doing any kind of assignment?

@JohanEngelen
Copy link
Contributor

Aside from anything to do with the specific changes here, what does a const opAssign even do?

It's used to enable assigning to a const(Handle!Block) as if it is a Handle!(const(Block)), and it's indeed writing to something outside the object. There could be better solutions, but "it worked" before...

@jmdavis
Copy link
Member

jmdavis commented Apr 3, 2024

Aside from anything to do with the specific changes here, what does a const opAssign even do?

It's used to enable assigning to a const(Handle!Block) as if it is a Handle!(const(Block)), and it's indeed writing to something outside the object. There could be better solutions, but "it worked" before...

I'm not trying to argue against changing the Phobos code. I'm just trying to understand why anyone would be doing anything with a const opAssign, since at a glance, it seems like something that's violating the type system.

Regardless of the details of your code, you've clearly shown that retro's template constraint is wrong, because it checks for one operation and then does another instead.

@JohanEngelen
Copy link
Contributor

Regardless of the details of your code, you've clearly shown that retro's template constraint is wrong, because it checks for one operation and then does another instead.

Then let's keep the discussion on topic and not derail it, thanks.

@WebFreak001
Copy link
Member Author

The hasAssignableElements is used in most of the range operations I touched in this PR, should all of them be changed as well?

@jmdavis
Copy link
Member

jmdavis commented Apr 4, 2024

https://issues.dlang.org/show_bug.cgi?id=24481

Regardless of the details of your code, you've clearly shown that retro's template constraint is wrong, because it checks for one operation and then does another instead.

Then let's keep the discussion on topic and not derail it, thanks.

It's very relevant, because understanding why users are doing weird edge-case stuff can have a big impact on what we test for in Phobos and the way that algorithms are written. And in this case, the Weka code is doing something that plenty of folks are just going to assume is illegal.

The hasAssignableElements is used in most of the range operations I touched in this PR, should all of them be changed as well?

As far as correctness goes, none of those functions should be using a move without verifying that it's legal first. They should probably all be changed to use a static if that checks whether a move compiles and use that if it is and do assignment otherwise.

@jmdavis
Copy link
Member

jmdavis commented Apr 4, 2024

I'll have a PR for this in a bit.

jmdavis added a commit to jmdavis/phobos that referenced this pull request Apr 4, 2024
In an attempt make it so that non-copyable types worked with some of the
functions in std/range/package.d, they were made to use moves instead of
assignment, which broke the code for types which work with assignment
but not moves (which affected the folks at Weka).

The code checked for assignment but not whether move could be used, and
that didn't change when the code was changed to use move, meaning that
the checks didn't match what the code was actually doing.

So, to support both the non-copyable types and the ones that can be
assigned to but not moved to, this changes the code so that it uses a
static if to check whether using move compiles, branching the code based
on whether move works on not. If move works, it's used. If it doesn't,
then assignment is used like used to be the case. So, in theory, the
code that worked previously works again, and the newer functionality of
being able to use non-copyable types with this code continues to work.

Discussion here: dlang#8721
@jmdavis
Copy link
Member

jmdavis commented Apr 4, 2024

#8969

@JohanEngelen
Copy link
Contributor

#8969

thank you!

jmdavis added a commit to jmdavis/phobos that referenced this pull request Apr 28, 2024
In an attempt make it so that non-copyable types worked with some of the
functions in std/range/package.d, they were made to use moves instead of
assignment, which broke the code for types which work with assignment
but not moves (which affected the folks at Weka).

The code checked for assignment but not whether move could be used, and
that didn't change when the code was changed to use move, meaning that
the checks didn't match what the code was actually doing.

So, to support both the non-copyable types and the ones that can be
assigned to but not moved to, this changes the code to use
core.lifetime.forward which will move the argument if it can and assign
otherwise. So ,the code that worked previously should work again, and
the newer functionality of being able to use non-copyable types with
this code should continue to work.

Discussion here: dlang#8721
dlang-bot pushed a commit that referenced this pull request Apr 29, 2024
In an attempt make it so that non-copyable types worked with some of the
functions in std/range/package.d, they were made to use moves instead of
assignment, which broke the code for types which work with assignment
but not moves (which affected the folks at Weka).

The code checked for assignment but not whether move could be used, and
that didn't change when the code was changed to use move, meaning that
the checks didn't match what the code was actually doing.

So, to support both the non-copyable types and the ones that can be
assigned to but not moved to, this changes the code to use
core.lifetime.forward which will move the argument if it can and assign
otherwise. So ,the code that worked previously should work again, and
the newer functionality of being able to use non-copyable types with
this code should continue to work.

Discussion here: #8721
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants