-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
clean up Sized
checking
#122493
clean up Sized
checking
#122493
Conversation
rustbot has assigned @petrochenkov. Use r? to explicitly pick a reviewer |
Some changes occurred to the CTFE / Miri engine cc @rust-lang/miri Some changes occurred to the core trait solver cc @rust-lang/initiative-trait-system-refactor |
@bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
clean up `Sized` checking This PR cleans up `sized_constraint` and related functions to make them simpler and faster. This should not make more or less code compile, but it can change error output in some rare cases. ## enums and unions are `Sized`, even if they are not WF The previous code has some special handling for enums, which made them sized if and only if the last field of each variant is sized. For example given this definition (which is not WF) ```rust enum E<T1: ?Sized, T2: ?Sized, U1: ?Sized, U2: ?Sized> { A(T1, T2), B(U1, U2), } ``` the enum was sized if and only if `T2` and `U2` are sized, while `T1` and `T2` were ignored for `Sized` checking. After this PR this enum will always be sized. Unsized enums are not a thing in Rust and removing this special case allows us to return an `Option<Ty>` from `sized_constraint`, rather than a `List<Ty>`. Similarly, the old code made an union defined like this ```rust union Union<T: ?Sized, U: ?Sized> { head: T, tail: U, } ``` sized if and only if `U` is sized, completely ignoring `T`. This just makes no sense at all and now this union is always sized. ## apply the "perf hack" to all (non-error) types, instead of just type parameters This "perf hack" skips evaluating `sized_constraint(adt): Sized` if `sized_constraint(adt): Sized` exactly matches a predicate defined on `adt`, for example: ```rust // `Foo<T>: Sized` iff `T: Sized`, but we know `T: Sized` from a predicate of `Foo` struct Foo<T /*: Sized */>(T); ``` Previously this was only applied to type parameters and now it is applied to every type. This means that for example this type is now always sized: ```rust // Note that this definition is WF, but the type `S<T>` not WF in the global/empty ParamEnv struct S<T>([T]) where [T]: Sized; ``` I don't anticipate this to affect compile time of any real-world program, but it makes the code a bit nicer and it also makes error messages a bit more consistent if someone does write such a cursed type. ## tuples are sized if the last type is sized The old solver already has this behavior and this PR also implements it for the new solver and `is_trivially_sized`. This makes it so that tuples work more like a struct defined like this: ```rust struct TupleN<T1, T2, /* ... */ Tn: ?Sized>(T1, T2, /* ... */ Tn); ``` This might improve the compile time of programs with large tuples a little, but is mostly also a consistency fix. ## `is_trivially_sized` for more types This function is used post-typeck code (borrowck, const eval, codegen) to skip evaluating `T: Sized` in some cases. It will now return `true` in more cases, most notably `UnsafeCell<T>` and `ManuallyDrop<T>` where `T.is_trivially_sized`. I'm anticipating that this change will improve compile time for some real world programs.
r? @lcnr |
compiler/rustc_ty_utils/src/ty.rs
Outdated
|
||
let result = sized_constraint_for_ty(tcx, tail_ty); | ||
|
||
let result = result.filter(|&ty| { |
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.
instead of filter
and map
, use if ... { return None(..) }
.
question: why not return None
if the Sized
condition references error?
you should be able to assume that the sized_trait()
exists, using tcx.require_lang_item
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.
question: why not return
None
if theSized
condition references error?
Good question. I thought I tried this and it changed some diagnostics output in some unexpected way, but after trying it again that seems to not be the case. The same also applies to the representability check above: If the type is not representable we could just return None
.
👍 on throwing out filter
, we can just use let result = sized_constraint_for_ty(tcx, tail_ty)?;
(try operator) here.
|
||
ty::Tuple(tys) => tys.last().iter().all(|ty| is_very_trivially_sized(**ty)), | ||
ty::Tuple(tys) => tys.last().map_or(true, |&ty| is_very_trivially_sized(ty)), |
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.
I find map_or
pretty hard to understand, I have to basically inline it to make sense of it. That's why I used all()
.
is_none_or
would be the best way to express this, of course (Cc rust-lang/libs-team#212).
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.
Interesting. For me it's the exact opposite: I'm confused every time Option
is used as an iterator and it's not immediately obvious to me that this .iter().all(...)
checks for at most one element. On the other hand .map_or(true
already reads as is_none_or
to me, but that's probably just because I got used to it.
Will revert this change if you prefer.
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.
Yeah I think I'd prefer that.
☀️ Try build successful - checks-actions |
This comment has been minimized.
This comment has been minimized.
b5ef7bd
to
f0d157f
Compare
Finished benchmarking commit (c9b796a): comparison URL. Overall result: ✅ improvements - no action neededBenchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. While you can manually mark this PR as fit for rollup, we strongly recommend not doing so since this PR may lead to changes in compiler perf. @bors rollup=never Instruction countThis is a highly reliable metric that was used to determine the overall result at the top of this comment.
Max RSS (memory usage)ResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Binary sizeThis benchmark run did not return any relevant results for this metric. Bootstrap: 669.429s -> 671.833s (0.36%) |
☔ The latest upstream changes (presumably #122497) made this pull request unmergeable. Please resolve the merge conflicts. |
f0d157f
to
ee66acb
Compare
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.
r=me after some final nits
query adt_sized_constraint(key: DefId) -> ty::EarlyBinder<&'tcx ty::List<Ty<'tcx>>> { | ||
desc { |tcx| "computing `Sized` constraints for `{}`", tcx.def_path_str(key) } | ||
query adt_sized_constraint(key: DefId) -> Option<ty::EarlyBinder<Ty<'tcx>>> { | ||
desc { |tcx| "computing `Sized` constraint for `{}`", tcx.def_path_str(key) } |
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.
desc { |tcx| "computing `Sized` constraint for `{}`", tcx.def_path_str(key) } | |
desc { |tcx| "computing the `Sized` constraint for `{}`", tcx.def_path_str(key) } |
🤔 🤷
@@ -128,7 +128,7 @@ pub(super) trait GoalKind<'tcx>: | |||
goal: Goal<'tcx, Self>, | |||
) -> QueryResult<'tcx>; | |||
|
|||
/// A type is `Copy` or `Clone` if its components are `Sized`. |
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.
😊
// if the ADTs definition implies that it is sized by for all possible args. | ||
// In this case, the builtin impl will have no nested subgoals. This is a | ||
// "best effort" optimization and `sized_constraint` may return `Some`, even | ||
// if the ADT is sized for all possible args. | ||
ty::Adt(def, args) => { | ||
let sized_crit = def.sized_constraint(ecx.tcx()); |
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.
let sized_crit = def.sized_constraint(ecx.tcx()); | |
if let Some(ty) = def.sized_constraint(ecx.tcx()) { |
@@ -2120,11 +2120,9 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | |||
ty::Adt(def, args) => { | |||
let sized_crit = def.sized_constraint(self.tcx()); |
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.
also use if let
here
compiler/rustc_ty_utils/src/ty.rs
Outdated
let intermediate = adt.sized_constraint(tcx); | ||
intermediate.and_then(|intermediate| { |
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.
let intermediate = adt.sized_constraint(tcx); | |
intermediate.and_then(|intermediate| { | |
adt.sized_constraint(tcx).and_then(|intermediate| { |
very much a nit 😅
@bors delegate+ |
✌️ @lukas-code, you can now approve this pull request! If @lcnr told you to " |
@bors r=lcnr |
☀️ Test successful - checks-actions |
Finished benchmarking commit (196ff44): comparison URL. Overall result: ✅ improvements - no action needed@rustbot label: -perf-regression Instruction countThis is a highly reliable metric that was used to determine the overall result at the top of this comment.
Max RSS (memory usage)ResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Binary sizeThis benchmark run did not return any relevant results for this metric. Bootstrap: 667.92s -> 668.321s (0.06%) |
…errors Restore `pred_known_to_hold_modulo_regions` As requested by `@lcnr` in rust-lang#123275 (comment) this PR restores `pred_known_to_hold_modulo_regions` to fix that "unexpected unsized tail" beta regression. This also adds the reduced repro from rust-lang#123275 (comment) as a sub-optimal test is better than no test at all, and it'll also cover rust-lang#108721. It still ICEs on master, even though https://github.com/phlip9/rustc-warp-ice doesn't on nightly anymore, since rust-lang#122493. Fixes rust-lang#123275. r? `@compiler-errors` but feel free to close if you'd rather have a better test instead cc `@wesleywiser` who had signed up to do the revert Will need a backport if we go with this PR: `@rustbot` label +beta-nominated
Rollup merge of rust-lang#123578 - lqd:regression-123275, r=compiler-errors Restore `pred_known_to_hold_modulo_regions` As requested by `@lcnr` in rust-lang#123275 (comment) this PR restores `pred_known_to_hold_modulo_regions` to fix that "unexpected unsized tail" beta regression. This also adds the reduced repro from rust-lang#123275 (comment) as a sub-optimal test is better than no test at all, and it'll also cover rust-lang#108721. It still ICEs on master, even though https://github.com/phlip9/rustc-warp-ice doesn't on nightly anymore, since rust-lang#122493. Fixes rust-lang#123275. r? `@compiler-errors` but feel free to close if you'd rather have a better test instead cc `@wesleywiser` who had signed up to do the revert Will need a backport if we go with this PR: `@rustbot` label +beta-nominated
This PR cleans up
sized_constraint
and related functions to make them simpler and faster. This should not make more or less code compile, but it can change error output in some rare cases.enums and unions are
Sized
, even if they are not WFThe previous code has some special handling for enums, which made them sized if and only if the last field of each variant is sized. For example given this definition (which is not WF)
the enum was sized if and only if
T2
andU2
are sized, whileT1
andT2
were ignored forSized
checking. After this PR this enum will always be sized.Unsized enums are not a thing in Rust and removing this special case allows us to return an
Option<Ty>
fromsized_constraint
, rather than aList<Ty>
.Similarly, the old code made an union defined like this
sized if and only if
U
is sized, completely ignoringT
. This just makes no sense at all and now this union is always sized.apply the "perf hack" to all (non-error) types, instead of just type parameters
This "perf hack" skips evaluating
sized_constraint(adt): Sized
ifsized_constraint(adt): Sized
exactly matches a predicate defined onadt
, for example:Previously this was only applied to type parameters and now it is applied to every type. This means that for example this type is now always sized:
I don't anticipate this to affect compile time of any real-world program, but it makes the code a bit nicer and it also makes error messages a bit more consistent if someone does write such a cursed type.
tuples are sized if the last type is sized
The old solver already has this behavior and this PR also implements it for the new solver and
is_trivially_sized
. This makes it so that tuples work more like a struct defined like this:This might improve the compile time of programs with large tuples a little, but is mostly also a consistency fix.
is_trivially_sized
for more typesThis function is used post-typeck code (borrowck, const eval, codegen) to skip evaluating
T: Sized
in some cases. It will now returntrue
in more cases, most notablyUnsafeCell<T>
andManuallyDrop<T>
whereT.is_trivially_sized
.I'm anticipating that this change will improve compile time for some real world programs.