Remove interior mutability from TraitDef by turning fields into queries#41911
Remove interior mutability from TraitDef by turning fields into queries#41911bors merged 10 commits intorust-lang:masterfrom
Conversation
eddyb
left a comment
There was a problem hiding this comment.
I like the overall approach but have some doubts on the details.
src/librustc/traits/mod.rs
Outdated
There was a problem hiding this comment.
Since they're identical I'd use the same function twice.
There was a problem hiding this comment.
You think so? It's just a coincidence that they are identical at the moment.
There was a problem hiding this comment.
maybe have provide() call provide_extern()? I imagine that anything we provide for extern crates, we would also provide for locals, but maybe not the other way around
src/librustc/traits/project.rs
Outdated
There was a problem hiding this comment.
How does this work? Do we produce cycle errors in more cases?
There was a problem hiding this comment.
I'm not sure what you mean exactly. I don't see a potential cycle here.
There was a problem hiding this comment.
The !is_complete case is a fallback for cycles, that is, when normalization of e.g. <T as Foo>::Assoc is needed to compute Foo's specialization graph.
There was a problem hiding this comment.
Oh, so assoc_ty_def() might be called while still building the specialization graph?
There was a problem hiding this comment.
It needs to be used in the specialization graph of the same trait.
There was a problem hiding this comment.
It needs to be used in the specialization graph of the same trait.
Yes, that makes sense. And I thought the following example might trigger the condition, but it doesn't :(
#![feature(specialization)]
trait Assoc {
type Output;
}
impl<T> Assoc for T {
default type Output = u8;
}
impl Assoc for u32 {
type Output = u32;
}
impl Assoc for u64 {
// Shouldn't this cause a reentrant call?
type Output = <u32 as Assoc>::Output;
}
fn main() {
let _x: <u32 as Assoc>::Output = 0;
}There was a problem hiding this comment.
In the impl header - only Self/params are checked by coherence/specialization.
There was a problem hiding this comment.
So, the following gives me the error overflow evaluating the requirement <u32 as Assoc>::Output regardless of whether cycle detection is enabled:
#![feature(specialization)]
trait Assoc {
type Output;
}
impl Assoc for u32 {
type Output = u64;
}
impl Assoc for <u32 as Assoc>::Output {
type Output = u64;
}
fn main() {
let _x: <u64 as Assoc>::Output = 0;
}So that's not really what we are looking for, I guess?
There was a problem hiding this comment.
Hmm, I don't really expect you to be able to project <u32 as Assoc>::Output in an impl for Assoc. What I expect could work is if there is a DAG between the traits, sort of like this:
#![feature(specialization)]
use std::vec;
trait Assoc {
type Output;
}
impl Assoc for u32 {
type Output = u64;
}
impl Assoc for <vec::IntoIter<u64> as Iterator>::Item {
type Output = u64;
}
fn main() {
}Interestingly, coherence rejects this example, presumably because of RFC 1214 concerns. I'm not sure that's right, but this example does work today:
#![feature(specialization)]
use std::vec;
trait Assoc {
type Output;
}
impl Assoc for u32 {
type Output = u64;
}
impl Assoc for <u32 as Assoc2>::Output {
type Output = u64;
}
trait Assoc2 {
type Output;
}
impl Assoc2 for u32 {
type Output = u64;
}
fn main() {
}That said, I think that would also work in @michaelwoerister's branch, no?
src/librustc/ty/maps.rs
Outdated
There was a problem hiding this comment.
Wait, where are all the blanket impls?
There was a problem hiding this comment.
They are part of the list.
src/librustc/ty/trait_def.rs
Outdated
There was a problem hiding this comment.
This is potentially very bad, as N blanket impls and M non-blanket now use N*M space.
There was a problem hiding this comment.
Hm, OK, let me think about that. Maybe we could share the list of blanket impls?
|
☔ The latest upstream changes (presumably #41847) made this pull request unmergeable. Please resolve the merge conflicts. |
3abfdbe to
742ebc1
Compare
|
@eddyb I added the cycle check back in (40a6734) and blanket impl lists are now shared (742ebc1). But I didn't manage to come up with a test case that would cause a cycle (see #41911 (comment)), unfortunately. |
|
@michaelwoerister Yeah, I'm waiting on @aturon and/or @nikomatsakis for that. |
|
|
||
| pub fn trait_impls(&self, trait_did: DefId) -> &'hir [NodeId] { | ||
| self.dep_graph.read(DepNode::TraitImpls(trait_did)); | ||
| self.dep_graph.read(DepNode::AllLocalTraitImpls); |
There was a problem hiding this comment.
I'm curious why you converted this into a single dep-node, rather than keeping it per-trait. It seems like this is losing quite a bit of precision, no?
(That is, if any trait adds an impl, we will consider them all to have changed.)
There was a problem hiding this comment.
Mostly for ease of implementation and symmetry with the situation in metadata. DepNode::TraitImpls has been re-purposed to represent local and remote impls and, with red-green, will take care of stopping invalidation short.
Without red-green though you are right, it's losing precision. Want me to make it per-item?
There was a problem hiding this comment.
Probably not a big deal.
src/librustc/traits/project.rs
Outdated
There was a problem hiding this comment.
Hmm, I don't really expect you to be able to project <u32 as Assoc>::Output in an impl for Assoc. What I expect could work is if there is a DAG between the traits, sort of like this:
#![feature(specialization)]
use std::vec;
trait Assoc {
type Output;
}
impl Assoc for u32 {
type Output = u64;
}
impl Assoc for <vec::IntoIter<u64> as Iterator>::Item {
type Output = u64;
}
fn main() {
}Interestingly, coherence rejects this example, presumably because of RFC 1214 concerns. I'm not sure that's right, but this example does work today:
#![feature(specialization)]
use std::vec;
trait Assoc {
type Output;
}
impl Assoc for u32 {
type Output = u64;
}
impl Assoc for <u32 as Assoc2>::Output {
type Output = u64;
}
trait Assoc2 {
type Output;
}
impl Assoc2 for u32 {
type Output = u64;
}
fn main() {
}That said, I think that would also work in @michaelwoerister's branch, no?
| let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); | ||
| let impl_simple_self_ty = fast_reject::simplify_type(tcx, | ||
| impl_trait_ref.self_ty(), | ||
| false).unwrap(); |
There was a problem hiding this comment.
I just realized, this is stricter than it has to be. it should use tcx.type_of(impl_def_id) instead.
src/librustc/ty/trait_def.rs
Outdated
| .map(|&node_id| tcx.hir.local_def_id(node_id)); | ||
|
|
||
| for impl_def_id in local_impls.chain(remote_impls.into_iter()) { | ||
| let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); |
|
|
||
| // Formerly this ICEd with the following message: | ||
| // Tried to project an inherited associated type during coherence checking, | ||
| // which is currently not supported. |
There was a problem hiding this comment.
Where is that message in the source? Could that codepath be removed?
There was a problem hiding this comment.
It here:
rust/src/librustc/traits/project.rs
Line 989 in 4d09a0e
But it has this nice, long comment which made me reluctant to remove it. I could move the comment to assoc_ty_def(), make its return value non-optional, and then remove the branch though.
There was a problem hiding this comment.
Yeah, moving the comment should be fine.
| span_bug!(obligation.cause.span, | ||
| "Tried to project an inherited associated type during \ | ||
| coherence checking, which is currently not supported."); | ||
| None |
There was a problem hiding this comment.
Why did you remove this comment etc? Do we think this code-path is dead, or not?
There was a problem hiding this comment.
If we think it's unreachable, I'd rather see bug!(...), probably with an updated comment explaining that we expect this scenario to be prevented by cycle-errors.
There was a problem hiding this comment.
The diff is a bit confusion here because indentation has changed. The None belongs to let new_candidate = if !is_default {, for which nothing should have changed, I believe.
|
@bors r+ |
|
📌 Commit 08660af has been approved by |
…akis Remove interior mutability from TraitDef by turning fields into queries This PR gets rid of anything `std::cell` in `TraitDef` by - moving the global list of trait impls from `TraitDef` into a query, - moving the list of trait impls relevent for some self-type from `TraitDef` into a query - moving the specialization graph of trait impls into a query, and - moving `TraitDef::object_safety` into a query. I really like how querifying things not only helps with incremental compilation and on-demand, but also just plain makes the code cleaner `:)` There are also some smaller fixes in the PR. Commits can be reviewed separately. r? @eddyb or @nikomatsakis
|
☀️ Test successful - status-appveyor, status-travis |
A change in rust-lang#41911 had made `for_all_relevant_impls` do a linear scan over all impls, instead of using an HashMap. Use an HashMap again to avoid quadratic blowup when there is a large number of structs with impls. I think this fixes rust-lang#43141 completely, but I want better measurements in order to be sure. As a perf patch, please don't roll this up.
make `for_all_relevant_impls` O(1) again A change in #41911 had made `for_all_relevant_impls` do a linear scan over all impls, instead of using an HashMap. Use an HashMap again to avoid quadratic blowup when there is a large number of structs with impls. I think this fixes #43141 completely, but I want better measurements in order to be sure. As a perf patch, please don't roll this up. r? @eddyb beta-nominating because regression
A change in rust-lang#41911 had made `for_all_relevant_impls` do a linear scan over all impls, instead of using an HashMap. Use an HashMap again to avoid quadratic blowup when there is a large number of structs with impls. I think this fixes rust-lang#43141 completely, but I want better measurements in order to be sure. As a perf patch, please don't roll this up.
This PR gets rid of anything
std::cellinTraitDefbyTraitDefinto a query,TraitDefinto a queryTraitDef::object_safetyinto a query.I really like how querifying things not only helps with incremental compilation and on-demand, but also just plain makes the code cleaner
:)There are also some smaller fixes in the PR. Commits can be reviewed separately.
r? @eddyb or @nikomatsakis