-
-
Notifications
You must be signed in to change notification settings - Fork 14.7k
Support assoc type defaults for assoc type constraints of trait objects #152959
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -234,7 +234,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { | |
| .filter(|item| item.is_type() || item.is_const()) | ||
| // Traits with RPITITs are simply not dyn compatible (for now). | ||
| .filter(|item| !item.is_impl_trait_in_trait()) | ||
| .map(|item| (item.def_id, trait_ref)), | ||
| .map(|item| (item, trait_ref)), | ||
| ); | ||
| } | ||
| ty::ClauseKind::Projection(pred) => { | ||
|
|
@@ -314,14 +314,24 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { | |
| let mut missing_assoc_items = FxIndexSet::default(); | ||
| let projection_bounds: Vec<_> = ordered_associated_items | ||
| .into_iter() | ||
| .filter_map(|key @ (def_id, _)| { | ||
| if let Some(&assoc) = projection_bounds.get(&key) { | ||
| .filter_map(|(item, trait_ref)| { | ||
| let def_id = item.def_id; | ||
| let key = (def_id, trait_ref); | ||
| if let Some(&(assoc, _)) = projection_bounds.get(&key) { | ||
| return Some(assoc); | ||
| } | ||
| if !tcx.generics_require_sized_self(def_id) { | ||
| let has_default = item.defaultness(tcx).has_value(); | ||
| if !has_default && !tcx.generics_require_sized_self(def_id) { | ||
| missing_assoc_items.insert(key); | ||
| } | ||
| None | ||
| if has_default { | ||
| Some(trait_ref.map_bound(|trait_ref| ty::ProjectionPredicate { | ||
| projection_term: ty::AliasTerm::new_from_args(tcx, def_id, trait_ref.args), | ||
| term: tcx.type_of(def_id).instantiate(tcx, trait_ref.args).into(), | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not entirely sure if your current approach can handle the following code that was suggested in similar form in the RFC: #![feature(associated_type_defaults)]
trait Trait {
type T = Self::U;
type U;
}
fn main() {
let _: dyn Trait<U = ()>; // `Trait::T` should default to `()`
}I have a hunch that this might not work right now but I might be mistaken. I think this will get rejected with the value of associated type trait Trait: SuperTrait<T = Self::U> { type U; }
trait SuperTrait { type T; }
fn main() {
let _: dyn Trait<U = ()>;
//~^ ERROR the value of the associated type `T` in `SuperTrait` must be specified
}In any case, encapsulating this as a UI test would be nice I think.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, right, I couldn't think of this case. Thanks! Yeah, this case is a bit irritating, as the defaulted
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah right, well, I'm actually questioning the feasibility of this idea. That's because you're not allowed to normalize in
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking of |
||
| })) | ||
| } else { | ||
| None | ||
| } | ||
| }) | ||
| .collect(); | ||
|
|
||
|
|
@@ -394,7 +404,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { | |
| }) | ||
| }); | ||
|
|
||
| let existential_projections = projection_bounds.into_iter().map(|(bound, _)| { | ||
| let existential_projections = projection_bounds.into_iter().map(|bound| { | ||
| bound.map_bound(|mut b| { | ||
| assert_eq!(b.projection_term.self_ty(), dummy_self); | ||
|
|
||
|
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you please also add a corresponding test for type-level associated constants (feature It should look something like: // Type associated consts needn't be specified in the trait object type
// if they have a default in which case we use said default.
//@ check-pass
#![feature(min_generic_const_args, associated_type_defaults)]
#![expect(incomplete_features)]
trait Trait {
type const N: usize = 1;
}
fn main() {
// `Trait::N` defaults to `1`.
let _: dyn Trait;
let _: [(); <dyn Trait as Trait>::N] = [(); 1];
}(The the phrasing of the description is admittedly rather awkward, feel free to improve it) (I've intentionally proactively added |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| #![feature(associated_type_defaults)] | ||
|
|
||
| trait Foo { | ||
| // This causes cycle, as being effectively `Box<dyn Foo<Assoc = Box<dyn Foo<..>>>>`. | ||
| type Assoc = Box<dyn Foo>; | ||
| //~^ ERROR: cycle detected when computing type of `Foo::Assoc` | ||
| } | ||
|
|
||
| trait Bar { | ||
| // This does not. | ||
| type Assoc = Box<dyn Bar<Assoc = ()>>; | ||
| } | ||
|
|
||
| fn main() {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| error[E0391]: cycle detected when computing type of `Foo::Assoc` | ||
| --> $DIR/defaults-on-trait-object-cycle.rs:5:5 | ||
| | | ||
| LL | type Assoc = Box<dyn Foo>; | ||
| | ^^^^^^^^^^ | ||
| | | ||
| = note: ...which immediately requires computing type of `Foo::Assoc` again | ||
| note: cycle used when checking that `Foo` is well-formed | ||
| --> $DIR/defaults-on-trait-object-cycle.rs:3:1 | ||
| | | ||
| LL | trait Foo { | ||
| | ^^^^^^^^^ | ||
| = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information | ||
|
|
||
| error: aborting due to 1 previous error | ||
|
|
||
| For more information about this error, try `rustc --explain E0391`. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| #![feature(associated_type_defaults)] | ||
|
|
||
| trait Foo { | ||
| type Assoc: Default = (); | ||
|
|
||
| fn foo(&self) -> Self::Assoc { | ||
| Default::default() | ||
| } | ||
| } | ||
|
|
||
| // The assoc type constraint can be omitted for the assoc type with a default. | ||
| type FooObj = Box<dyn Foo>; | ||
|
|
||
| trait Bar { | ||
| type Assoc1 = i32; | ||
| type Assoc2; | ||
| } | ||
|
|
||
| // We can't omit assoc type constraints for an assoc type without a default value. | ||
| type BarObj1 = Box<dyn Bar>; | ||
| //~^ ERROR: the value of the associated type `Assoc2` in `Bar` must be specified | ||
|
|
||
| type BarObj2 = Box<dyn Bar<Assoc2 = ()>>; | ||
|
|
||
| type BarObj3 = Box<dyn Bar<Assoc1 = u32, Assoc2 = i32>>; | ||
|
|
||
| fn check_projs(foo1: &dyn Foo, foo2: &dyn Foo<Assoc = bool>) { | ||
| let _: () = foo1.foo(); | ||
| let _: () = foo2.foo(); | ||
| //~^ ERROR: mismatched types | ||
| } | ||
|
|
||
| fn main() {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| error[E0191]: the value of the associated type `Assoc2` in `Bar` must be specified | ||
| --> $DIR/defaults-on-trait-object.rs:20:24 | ||
| | | ||
| LL | type Assoc2; | ||
| | ----------- `Assoc2` defined here | ||
| ... | ||
| LL | type BarObj1 = Box<dyn Bar>; | ||
| | ^^^ | ||
| | | ||
| help: specify the associated type | ||
| | | ||
| LL | type BarObj1 = Box<dyn Bar<Assoc2 = /* Type */>>; | ||
| | +++++++++++++++++++++ | ||
|
|
||
| error[E0308]: mismatched types | ||
| --> $DIR/defaults-on-trait-object.rs:29:17 | ||
| | | ||
| LL | let _: () = foo2.foo(); | ||
| | -- ^^^^^^^^^^ expected `()`, found `bool` | ||
| | | | ||
| | expected due to this | ||
|
|
||
| error: aborting due to 2 previous errors | ||
|
|
||
| Some errors have detailed explanations: E0191, E0308. | ||
| For more information about an error, try `rustc --explain E0191`. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,7 +6,8 @@ trait Hierarchy { | |
| type Value; | ||
| type ChildKey; | ||
| type Children = dyn Index<Self::ChildKey, Output = dyn Hierarchy>; | ||
| //~^ ERROR: the value of the associated types | ||
| //~^ ERROR: cycle detected when computing type of `Hierarchy::Children` | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The cycle here is unfortunate but I think this might be inevitable 🤔 |
||
| //~| ERROR: the value of the associated types | ||
|
|
||
| fn data(&self) -> Option<(Self::Value, Self::Children)>; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,33 @@ | ||
| error[E0191]: the value of the associated types `Value`, `ChildKey` and `Children` in `Hierarchy` must be specified | ||
| error[E0391]: cycle detected when computing type of `Hierarchy::Children` | ||
| --> $DIR/issue-23595-1.rs:8:5 | ||
| | | ||
| LL | type Children = dyn Index<Self::ChildKey, Output = dyn Hierarchy>; | ||
| | ^^^^^^^^^^^^^ | ||
| | | ||
| = note: ...which immediately requires computing type of `Hierarchy::Children` again | ||
| note: cycle used when checking that `Hierarchy` is well-formed | ||
| --> $DIR/issue-23595-1.rs:5:1 | ||
| | | ||
| LL | trait Hierarchy { | ||
| | ^^^^^^^^^^^^^^^ | ||
| = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information | ||
|
|
||
| error[E0191]: the value of the associated types `Value` and `ChildKey` in `Hierarchy` must be specified | ||
| --> $DIR/issue-23595-1.rs:8:60 | ||
| | | ||
| LL | type Value; | ||
| | ---------- `Value` defined here | ||
| LL | type ChildKey; | ||
| | ------------- `ChildKey` defined here | ||
| LL | type Children = dyn Index<Self::ChildKey, Output = dyn Hierarchy>; | ||
| | ------------- `Children` defined here ^^^^^^^^^ | ||
| | ^^^^^^^^^ | ||
| | | ||
| help: specify the associated types | ||
| | | ||
| LL | type Children = dyn Index<Self::ChildKey, Output = dyn Hierarchy<Value = /* Type */, ChildKey = /* Type */, Children = /* Type */>>; | ||
| | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | ||
| LL | type Children = dyn Index<Self::ChildKey, Output = dyn Hierarchy<Value = /* Type */, ChildKey = /* Type */>>; | ||
| | +++++++++++++++++++++++++++++++++++++++++++ | ||
|
|
||
| error: aborting due to 1 previous error | ||
| error: aborting due to 2 previous errors | ||
|
|
||
| For more information about this error, try `rustc --explain E0191`. | ||
| Some errors have detailed explanations: E0191, E0391. | ||
| For more information about an error, try `rustc --explain E0191`. |
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.
Could you please also update the large comment above. I'm referring to the part where it says The user has to constrain all associated types & consts via bindings unless the corresponding associated item has a
where Self: Sizedclause.. Could you update it to say sth. along the lines of […] unless […] has a […] clause or it has a default value which we would then pick.Thanks!