-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Switch builtins.sort to a custom stable PeekSort #12623
base: master
Are you sure you want to change the base?
Conversation
Unlike std::sort and std::stable_sort, this implementation does not lead to out-of-bounds memory reads or other undefined behavior when the predicate is not strict weak ordering. This makes it possible to use this function in libexpr for builtins.sort, where an incorrectly implemented comparator in the user nix code currently can crash and burn the evaluator by invoking C++ UB.
This prevents C++ level undefined behavior from affecting the evaluator. Stdlib implementation details should not affect eval, regardless of the build platform. Even erroneous usage of `builtins.sort` should not make it possible to crash the evaluator or produce results that depend on the host platform.
…16 elements libstdc++'s std::stable_sort and new builtins.sort implementation special-case ranges with length less than or equal to 16 and delegate to insertionsort. Having a larger e2e test would allow catching sort stability issues at functional level as well.
comparator a b && comparator b c -> comparator a c | ||
``` | ||
|
||
1. Irreflexivity |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forgot to add that the relation must also be asymmetric.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if mentioning it here explicitly would be preferable. Irreflexivity
together with transitivity
already imply that the relation is asymmetric: a < b && b < a -> (a < a = false)
. Maybe including this in the doc is beneficial, even if it's somewhat redundant from the mathematics point of view?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW when I think about this stuff, I tend to start with a "total preorder" for <= (which is also a total order on equivalence classes for <=), and then define a total order from it.
for (auto innerBegin = begin; innerBegin < innerEnd; ++innerBegin) { | ||
ASSERT_LE(prevId, innerBegin->second); | ||
prevId = innerBegin->second; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be simplified with std::is_sorted
.
Motivation
Unlike
std::sort
andstd::stable_sort
, this implementationdoes not lead to out-of-bounds memory reads or other undefined
behavior when the predicate is not strict weak ordering.
This makes it possible to use this function in libexpr for
builtins.sort, where an incorrectly implemented comparator
in the user nix code currently can crash and burn the evaluator
by invoking C++ UB.
This prevents C++ level undefined behavior from affecting
the evaluator. Stdlib implementation details should not affect
eval, regardless of the build platform. Even erroneous usage
of
builtins.sort
should not make it possible to crash theevaluator or produce results that depend on the host platform.
Context
#12106
Add 👍 to pull requests you find important.
The Nix maintainer team uses a GitHub project board to schedule and track reviews.