-
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
Refactor TraitObject to Slice<ExistentialPredicate> #37965
Refactor TraitObject to Slice<ExistentialPredicate> #37965
Conversation
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.
LGTM modulo comments. cc @nikomatsakis about Binder
discipline.
ty::TyAdt(def, _) => def.is_fundamental(), | ||
ty::TyDynamic(ref data, ..) => { | ||
match data.principal() { | ||
Some(ref principal) => tcx.has_attr(principal.def_id(), "fundamental"), |
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.
You shouldn't need ref
in positions by this, the match is on an rvalue anyway.
tt.principal.def_id().is_local() | ||
ty::TyDynamic(ref tt, ..) => { | ||
match tt.principal() { | ||
Some(ref p) => p.def_id().is_local(), |
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.
Same extraneous ref
. It might be worth using map_or
in cases like this, I'm not sure.
match self.tcx().lang_items.to_builtin_kind(obligation.predicate.def_id()) { | ||
Some(ty::BoundCopy) => { | ||
let def_id = obligation.predicate.def_id(); | ||
match obligation.predicate.def_id() { |
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 should be an if-else chain.
} | ||
|
||
data.principal.with_self_ty(this.tcx(), self_ty) | ||
match data.principal() { | ||
Some(ref p) => p.with_self_ty(this.tcx(), self_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.
Same extraneous ref
.
data_a.principal.def_id() == data_b.principal.def_id() && | ||
data_a.builtin_bounds.is_superset(&data_b.builtin_bounds) | ||
match (data_a.principal(), data_b.principal()) { | ||
(Some(ref a), Some(ref b)) => a.def_id() == b.def_id() && |
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.
Same extraneous ref
.
@@ -807,11 +807,11 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { | |||
} | |||
|
|||
/*From:*/ (_, | |||
/*To: */ &ty::TyTrait(ref obj)) => { | |||
/*To: */ &ty::TyDynamic(.., ref reg)) => { |
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.
Same odd reg
.
// This is something like impl Trait1 for Trait2. Illegal | ||
// if Trait1 is a supertrait of Trait2 or Trait2 is not object safe. | ||
|
||
if !self.tcx.is_object_safe(data.principal.def_id()) { | ||
if data.principal().is_none() || | ||
!self.tcx.is_object_safe(data.principal().unwrap().def_id()) { |
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 written using map_or
.
@@ -1958,7 +1958,7 @@ pub fn compute_bounds<'gcx: 'tcx, 'tcx>(astconv: &AstConv<'gcx, 'tcx>, | |||
|
|||
Bounds { | |||
region_bounds: region_bounds, | |||
builtin_bounds: builtin_bounds, | |||
auto_traits: auto_traits, |
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'm not sure this partitioning should even exist until a TyDynamic
is created.
|
||
let poly_trait_ref = data.principal.with_self_ty(self.tcx(), self.tcx().types.err); | ||
let poly_trait_ref = data.principal().unwrap().with_self_ty(self.tcx(), | ||
self.tcx().types.err); | ||
self.add_constraints_from_trait_ref(generics, poly_trait_ref.0, variance); |
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 wrapped in an if let Some
instead of using .unwrap()
.
v.extend(obj.principal.skip_binder().substs.regions()); | ||
TyDynamic(ref obj, region) => { | ||
let mut v = vec![region]; | ||
v.extend(obj.principal().unwrap().skip_binder().substs.regions()); |
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 shouldn't .unwrap()
.
b475ea0
to
2c1ec2f
Compare
☔ The latest upstream changes (presumably #37890) made this pull request unmergeable. Please resolve the merge conflicts. |
3c63d7c
to
8d76e7e
Compare
@eddyb Ready for the crater run, I think, but might want to review before? |
90d69cd
to
72f54e0
Compare
if let Some(principal) = trait_data.principal() { | ||
self.push_def_path(principal.def_id(), output); | ||
self.push_type_params(principal.skip_binder().substs, | ||
&trait_data.projection_bounds().collect::<Vec<_>>()[..], |
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.
Shouldn't allocate.
field.ty, | ||
ty::BoundSized, | ||
fcx.tcx.lang_items.require(lang_items::SizedTraitLangItem) | ||
.unwrap_or_else(|msg| fcx.tcx.sess.fatal(&msg[..])), |
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.
Replace with require_lang_item
.
@@ -371,15 +371,17 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { | |||
variance); | |||
} | |||
|
|||
ty::TyTrait(ref data) => { | |||
ty::TyDynamic(ref data, ref reg) => { |
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.
reg
fdb5b7b
to
c0eca6a
Compare
Expanded the description with discussion and elaboration on what this PR does. @eddyb Let me know what parts of that should be transferred into documentation comments in the code. |
c0eca6a
to
eb7bb54
Compare
Squashed the remaining fix commits. r? @eddyb |
Started crater run. |
@@ -1492,11 +1493,13 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { | |||
} | |||
} | |||
|
|||
let copy_lang_item = self.tcx.require_lang_item(lang_items::CopyTraitLangItem); |
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.
copy_trait
or copy_def_id
.
match tt.principal() { | ||
Some(p) => p.def_id().is_local(), | ||
None => false, | ||
} |
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 (and other places) could use map_or
for brevity.
} else if self.tcx().lang_items.unsize_trait() == Some(def_id) { | ||
self.assemble_candidates_for_unsizing(obligation, &mut candidates); | ||
} else { | ||
// For non-builtins and Send/Sync |
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.
Comment is unnecessary/misleading.
|
||
// If the type is `Foo+'a`, ensures that the type | ||
// being cast to `Foo+'a` outlives `'a`: | ||
let outlives = ty::OutlivesPredicate(source, data.region_bound); | ||
let outlives = ty::OutlivesPredicate(source, r); | ||
push(ty::Binder(outlives).to_predicate()); |
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 necessary in this PR, but these push
calls could be replaced with collecting an iterator to a Vec
.
self.mk_ty(TyTrait(box obj)) | ||
pub fn mk_trait(self, | ||
obj: ty::Binder<&'tcx Slice<ExistentialPredicate<'tcx>>>, | ||
reg: &'tcx ty::Region) |
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.
Maybe rename this to mk_dynamic
?
// If it could be sized, and is, add the sized predicate | ||
if self.implicitly_sized && tcx.lang_items.sized_trait().is_some() { | ||
let trait_ref = ty::TraitRef { | ||
def_id: tcx.lang_items.sized_trait().unwrap(), |
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.
Use if let Some
here.
} | ||
let sync_trait = ccx.tcx.require_lang_item(lang_items::SyncTraitLangItem); | ||
let send_trait = ccx.tcx.require_lang_item(lang_items::SendTraitLangItem); | ||
if trait_ref.def_id != sync_trait && trait_ref.def_id != send_trait { |
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.
Is this check necessary anymore? AFAIK trait_has_default_impl
returns true for Send
and Sync
.
@@ -1611,7 +1610,7 @@ fn add_unsized_bound<'gcx: 'tcx, 'tcx>(astconv: &AstConv<'gcx, 'tcx>, | |||
if let &hir::TraitTyParamBound(ref ptr, hir::TraitBoundModifier::Maybe) = ab { | |||
if unbound.is_none() { | |||
assert!(ptr.bound_lifetimes.is_empty()); | |||
unbound = Some(ptr.trait_ref.clone()); | |||
unbound = Some(ptr.trait_ref.ref_id.clone()); |
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.
No need to .clone()
.
@@ -1622,25 +1621,26 @@ fn add_unsized_bound<'gcx: 'tcx, 'tcx>(astconv: &AstConv<'gcx, 'tcx>, | |||
|
|||
let kind_id = tcx.lang_items.require(SizedTraitLangItem); | |||
match unbound { | |||
Some(ref tpb) => { | |||
Some(ref ref_id) => { |
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.
Unnecessary ref
.
Builtin traits are an exception to this rule: it's possible to have bounds of | ||
one non-builtin type, plus any number of builtin types. For example, the | ||
Send and Sync are an exception to this rule: it's possible to have bounds of | ||
one non-builtin type, plus either or both of Send and Sync. For example, the |
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.
s/type/trait
Crater report contains no legitimate regressions (ignoring uses of |
6b84098
to
17ac55e
Compare
☔ The latest upstream changes (presumably #37676) made this pull request unmergeable. Please resolve the merge conflicts. |
17ac55e
to
0ae852c
Compare
@eddyb Rebased, but haven't run tests--started those locally (and Travis will test too, of course). |
(ty::Substs::empty().types().rev(), None), | ||
}; | ||
|
||
substs.chain(opt_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.
My suggestion was to move the .types().rev()
here.
@@ -369,27 +365,30 @@ pub fn predicates_for_generics<'tcx>(cause: ObligationCause<'tcx>, | |||
/// `bound` or is not known to meet bound (note that this is | |||
/// conservative towards *no impl*, which is the opposite of the | |||
/// `evaluate` methods). | |||
pub fn type_known_to_meet_builtin_bound<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, | |||
pub fn type_known_to_meet_bound<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, | |||
ty: Ty<'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.
There's a few indentation aberrations, here and in a few other places. Let me know if you want a list.
projection_bounds: data_a.projection_bounds.clone(), | ||
}); | ||
let principal = data_a.principal(); | ||
let iter = principal.iter().map(|x| ty::ExistentialPredicate::Trait(x.0)) |
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.
Hmm, to make this nicer I think you should call .skip_binder
on data_a
and data_b
(with a comment that it's reintroduced below).
let mut v = iter::once(ty::ExistentialPredicate::Trait(existential_principal.0)) | ||
.chain(auto_traits.into_iter().map(|x| ty::ExistentialPredicate::AutoTrait(x))) | ||
.chain(existential_projections | ||
.map(|x| ty::ExistentialPredicate::Projection(x.0))) |
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.
At least use .skip_binder()
instead of .0
.
Renames TyTrait to TyDynamic.
Replaces instances of tcx.lang_items.require(..) with fatal unwrap with this method.
0ae852c
to
8b82fd7
Compare
Fixed review comments. r? @eddyb |
@bors r+ |
📌 Commit 8b82fd7 has been approved by |
…=eddyb Refactor TraitObject to Slice<ExistentialPredicate> For reference, the primary types changes in this PR are shown below. They may add in the understanding of what is discussed below, though they should not be required. We change `TraitObject` into a list of `ExistentialPredicate`s to allow for a couple of things: - Principal (ExistentialPredicate::Trait) is now optional. - Region bounds are moved out of `TraitObject` into `TyDynamic`. This permits wrapping only the `ExistentialPredicate` list in `Binder`. - `BuiltinBounds` and `BuiltinBound` are removed entirely from the codebase, to permit future non-constrained auto traits. These are replaced with `ExistentialPredicate::AutoTrait`, which only requires a `DefId`. For the time being, only `Send` and `Sync` are supported; this constraint can be lifted in a future pull request. - Binder-related logic is extracted from `ExistentialPredicate` into the parent (`Binder<Slice<EP>>`), so `PolyX`s are inside `TraitObject` are replaced with `X`. The code requires a sorting order for `ExistentialPredicate`s in the interned `Slice`. The sort order is asserted to be correct during interning, but the slices are not sorted at that point. 1. `ExistentialPredicate::Trait` are defined as always equal; **This may be wrong; should we be comparing them and sorting them in some way?** 1. `ExistentialPredicate::Projection`: Compared by `ExistentialProjection::sort_key`. 1. `ExistentialPredicate::AutoTrait`: Compared by `TraitDef.def_path_hash`. Construction of `ExistentialPredicate`s is conducted through `TyCtxt::mk_existential_predicates`, which interns a passed iterator as a `Slice`. There are no convenience functions to construct from a set of separate iterators; callers must pass an iterator chain. The lack of convenience functions is primarily due to few uses and the relative difficulty in defining a nice API due to optional parts and difficulty in recognizing which argument goes where. It is also true that the current situation isn't significantly better than 4 arguments to a constructor function; but the extra work is deemed unnecessary as of this time. ```rust // before this PR struct TraitObject<'tcx> { pub principal: PolyExistentialTraitRef<'tcx>, pub region_bound: &'tcx ty::Region, pub builtin_bounds: BuiltinBounds, pub projection_bounds: Vec<PolyExistentialProjection<'tcx>>, } // after pub enum ExistentialPredicate<'tcx> { // e.g. Iterator Trait(ExistentialTraitRef<'tcx>), // e.g. Iterator::Item = T Projection(ExistentialProjection<'tcx>), // e.g. Send AutoTrait(DefId), } ```
⌛ Testing commit 8b82fd7 with merge 8e373b4... |
Refactor ty::FnSig to contain a &'tcx Slice<Ty<'tcx>> We refactor this in order to achieve the following wins: - Decrease the size of `FnSig` (`Vec` + `bool`: 32, `&Slice` + `bool`: 24). - Potentially decrease total allocated memory due to arena-allocating `FnSig` inputs/output; since they are allocated in the type list arena, other users of type lists can reuse the same allocation for an equivalent type list. - Remove the last part of the type system which needs drop glue (#37965 removed the other remaining part). This makes arenas containing `FnSig` faster to drop (since we don't need to drop a Vec for each one), and makes reusing them without clearing/dropping potentially possible. r? @eddyb
@Mark-Simulacrum: Very late to the party, but I just ran into the the sort order while spelunking for #41444, and I'm curious about:
What does "correct" mean? I can see from the code that the only time the assertion runs is when it is passed something that has previously been sorted using the same
Why not just tie-break by |
Since we now represent I think I answered the second question as well. You do make a good point about possibly more efficient interning if we sorted with tie-breaking by Let me know if you have any more questions! |
You simply can't have more than one |
@Mark-Simulacrum: Thanks for the explanation. I have one follow-up question on the following (and it's not really a question about this code in particular any more):
The comment on
I guess it depends on what is meant by "same contents", but it would seem to me that at this point you could wind up interning both of the slices |
Slices can be compared by pointer and length because they're always interned, which means that |
For reference, the primary types changes in this PR are shown below. They may add in the understanding of what is discussed below, though they should not be required.
We change
TraitObject
into a list ofExistentialPredicate
s to allow for a couple of things:TraitObject
intoTyDynamic
. This permits wrapping only theExistentialPredicate
list inBinder
.BuiltinBounds
andBuiltinBound
are removed entirely from the codebase, to permit future non-constrained auto traits. These are replaced withExistentialPredicate::AutoTrait
, which only requires aDefId
. For the time being, onlySend
andSync
are supported; this constraint can be lifted in a future pull request.ExistentialPredicate
into the parent (Binder<Slice<EP>>
), soPolyX
s are insideTraitObject
are replaced withX
.The code requires a sorting order for
ExistentialPredicate
s in the internedSlice
. The sort order is asserted to be correct during interning, but the slices are not sorted at that point.ExistentialPredicate::Trait
are defined as always equal; This may be wrong; should we be comparing them and sorting them in some way?ExistentialPredicate::Projection
: Compared byExistentialProjection::sort_key
.ExistentialPredicate::AutoTrait
: Compared byTraitDef.def_path_hash
.Construction of
ExistentialPredicate
s is conducted throughTyCtxt::mk_existential_predicates
, which interns a passed iterator as aSlice
. There are no convenience functions to construct from a set of separate iterators; callers must pass an iterator chain. The lack of convenience functions is primarily due to few uses and the relative difficulty in defining a nice API due to optional parts and difficulty in recognizing which argument goes where. It is also true that the current situation isn't significantly better than 4 arguments to a constructor function; but the extra work is deemed unnecessary as of this time.