diff --git a/src/librustc_error_codes/error_codes.rs b/src/librustc_error_codes/error_codes.rs index fbcc976bd49..18d58d9d19e 100644 --- a/src/librustc_error_codes/error_codes.rs +++ b/src/librustc_error_codes/error_codes.rs @@ -116,6 +116,7 @@ E0211: include_str!("./error_codes/E0211.md"), E0214: include_str!("./error_codes/E0214.md"), E0220: include_str!("./error_codes/E0220.md"), E0221: include_str!("./error_codes/E0221.md"), +E0222: include_str!("./error_codes/E0222.md"), E0223: include_str!("./error_codes/E0223.md"), E0225: include_str!("./error_codes/E0225.md"), E0229: include_str!("./error_codes/E0229.md"), @@ -457,8 +458,6 @@ E0745: include_str!("./error_codes/E0745.md"), // E0217, // ambiguous associated type, defined in multiple supertraits // E0218, // no associated type defined // E0219, // associated type defined in higher-ranked supertrait -// E0222, // Error code E0045 (variadic function must have C or cdecl calling - // convention) duplicate E0224, // at least one non-builtin train is required for an object type E0226, // only a single explicit lifetime bound is permitted E0227, // ambiguous lifetime bound, explicit lifetime bound required diff --git a/src/librustc_error_codes/error_codes/E0222.md b/src/librustc_error_codes/error_codes/E0222.md new file mode 100644 index 00000000000..66b6c4d712b --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0222.md @@ -0,0 +1,51 @@ +An attempt was made to constrain an associated type. +For example: + +```compile_fail,E0222 +pub trait Vehicle { + type Color; +} + +pub trait Box { + type Color; +} + +pub trait BoxCar : Box + Vehicle {} + +fn dent_object(c: dyn BoxCar) {} // Invalid constraint +``` + +In this example, `BoxCar` has two super-traits: `Vehicle` and `Box`. Both of +these traits define an associated type `Color`. `BoxCar` inherits two types +with that name from both super-traits. Because of this, we need to use the +fully qualified path syntax to refer to the appropriate `Color` associated +type, either `::Color` or `::Color`, but this +syntax is not allowed to be used in a function signature. + +In order to encode this kind of constraint, a `where` clause and a new type +parameter are needed: + +``` +pub trait Vehicle { + type Color; +} + +pub trait Box { + type Color; +} + +pub trait BoxCar : Box + Vehicle {} + +// Introduce a new `CAR` type parameter +fn foo( + c: CAR, +) where + // Bind the type parameter `CAR` to the trait `BoxCar` + CAR: BoxCar, + // Further restrict `::Color` to be the same as the + // type parameter `COLOR` + CAR: Vehicle, + // We can also simultaneously restrict the other trait's associated type + CAR: Box +{} +``` diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index c3a5175abac..d89db403b15 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -804,6 +804,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { predicate.span, "equality constraints are not yet supported in `where` clauses", ) + .span_label(predicate.span, "not supported") .note( "for more information, see https://github.com/rust-lang/rust/issues/20041", ) diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 68625e71b04..f9854aee979 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -1267,7 +1267,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let candidate = if self.trait_defines_associated_type_named(trait_ref.def_id(), binding.item_name) { // Simple case: X is defined in the current trait. - Ok(trait_ref) + trait_ref } else { // Otherwise, we have to walk through the supertraits to find // those that do. @@ -1276,8 +1276,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &trait_ref.print_only_trait_path().to_string(), binding.item_name, path_span, - ) - }?; + match binding.kind { + ConvertedBindingKind::Equality(ty) => Some(ty.to_string()), + _ => None, + }, + )? + }; let (assoc_ident, def_scope) = tcx.adjust_ident_and_get_scope(binding.item_name, candidate.def_id(), hir_ref_id); @@ -1626,20 +1630,31 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } let mut suggestions_len = suggestions.len(); if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(sugg_span) { - if potential_assoc_types.is_empty() && trait_bounds.len() == 1 && + let assoc_types: Vec = associated_types + .iter() + .map(|item_def_id| { + let assoc_item = tcx.associated_item(*item_def_id); + format!("{} = Type", assoc_item.ident) + }) + .collect(); + let dedup = assoc_types.clone().drain(..).collect::>(); + + if dedup.len() != assoc_types.len() && trait_bounds.len() == 1 { + // If there are duplicates associated type names and a single trait bound do not + // use structured suggestion, it means that there are multiple super-traits with + // the same associated type name. + err.help( + "consider introducing a new type parameter, adding `where` constraints \ + using the fully-qualified path to the associated type", + ); + } else if dedup.len() == assoc_types.len() && + potential_assoc_types.is_empty() && + trait_bounds.len() == 1 && // Do not attempt to suggest when we don't know which path segment needs the // type parameter set. trait_bounds[0].trait_ref.path.segments.len() == 1 { - debug!("path segments {:?}", trait_bounds[0].trait_ref.path.segments); applicability = Applicability::HasPlaceholders; - let assoc_types: Vec = associated_types - .iter() - .map(|item_def_id| { - let assoc_item = tcx.associated_item(*item_def_id); - format!("{} = Type", assoc_item.ident) - }) - .collect(); let sugg = assoc_types.join(", "); if snippet.ends_with('>') { // The user wrote `Trait<'a>` or similar and we don't have a type we can @@ -1666,7 +1681,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .collect::>() .join(", "); err.span_label( - span, + sugg_span, format!( "associated type{} {} must be specified", pluralize!(associated_types.len()), @@ -1743,15 +1758,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ¶m_name.as_str(), assoc_name, span, + None, ) } + // Checks that `bounds` contains exactly one element and reports appropriate + // errors otherwise. fn one_bound_for_assoc_type( &self, all_candidates: impl Fn() -> I, ty_param_name: &str, assoc_name: ast::Ident, span: Span, + is_equality: Option, ) -> Result, ErrorReported> where I: Iterator>, @@ -1778,16 +1797,29 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { debug!("one_bound_for_assoc_type: bound2 = {:?}", bound2); let bounds = iter::once(bound).chain(iter::once(bound2)).chain(matching_candidates); - let mut err = struct_span_err!( - self.tcx().sess, - span, - E0221, - "ambiguous associated type `{}` in bounds of `{}`", - assoc_name, - ty_param_name - ); + let mut err = if is_equality.is_some() { + // More specific Error Index entry. + struct_span_err!( + self.tcx().sess, + span, + E0222, + "ambiguous associated type `{}` in bounds of `{}`", + assoc_name, + ty_param_name + ) + } else { + struct_span_err!( + self.tcx().sess, + span, + E0221, + "ambiguous associated type `{}` in bounds of `{}`", + assoc_name, + ty_param_name + ) + }; err.span_label(span, format!("ambiguous associated type `{}`", assoc_name)); + let mut where_bounds = vec![]; for bound in bounds { let bound_span = self .tcx() @@ -1807,17 +1839,26 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { bound.print_only_trait_path(), ), ); - err.span_suggestion( - span, - "use fully qualified syntax to disambiguate", - format!( - "<{} as {}>::{}", - ty_param_name, - bound.print_only_trait_path(), - assoc_name, - ), - Applicability::MaybeIncorrect, - ); + if let Some(constraint) = &is_equality { + where_bounds.push(format!( + " T: {trait}::{assoc} = {constraint}", + trait=bound.print_only_trait_path(), + assoc=assoc_name, + constraint=constraint, + )); + } else { + err.span_suggestion( + span, + "use fully qualified syntax to disambiguate", + format!( + "<{} as {}>::{}", + ty_param_name, + bound.print_only_trait_path(), + assoc_name, + ), + Applicability::MaybeIncorrect, + ); + } } else { err.note(&format!( "associated type `{}` could derive from `{}`", @@ -1826,9 +1867,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { )); } } + if !where_bounds.is_empty() { + err.help(&format!( + "consider introducing a new type parameter `T` and adding `where` constraints:\ + \n where\n T: {},\n{}", + ty_param_name, + where_bounds.join(",\n"), + )); + } err.emit(); + if !where_bounds.is_empty() { + return Err(ErrorReported); + } } - return Ok(bound); } @@ -1933,6 +1984,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { "Self", assoc_ident, span, + None, )? } (&ty::Param(_), Res::SelfTy(Some(param_did), None)) diff --git a/src/test/ui/associated-type/associated-type-projection-from-multiple-supertraits.rs b/src/test/ui/associated-type/associated-type-projection-from-multiple-supertraits.rs index f50587bac16..77ed784f1fa 100644 --- a/src/test/ui/associated-type/associated-type-projection-from-multiple-supertraits.rs +++ b/src/test/ui/associated-type/associated-type-projection-from-multiple-supertraits.rs @@ -20,11 +20,9 @@ fn dent(c: C, color: C::Color) { //~^ ERROR ambiguous associated type `Color` in bounds of `C` } -// FIXME: add error code to detect this case and explain that you'll want the approach in -// `dent_object_3` of using a new type param and relying on the `where` clauses. fn dent_object(c: dyn BoxCar) { //~^ ERROR ambiguous associated type - //~| ERROR the value of the associated type `Color` (from trait `Vehicle`) must be specified + //~| ERROR the value of the associated types `Color` (from trait `Vehicle`), `Color` (from } fn paint(c: C, d: C::Color) { diff --git a/src/test/ui/associated-type/associated-type-projection-from-multiple-supertraits.stderr b/src/test/ui/associated-type/associated-type-projection-from-multiple-supertraits.stderr index 9a5787db41e..aa46e5d077f 100644 --- a/src/test/ui/associated-type/associated-type-projection-from-multiple-supertraits.stderr +++ b/src/test/ui/associated-type/associated-type-projection-from-multiple-supertraits.stderr @@ -1,8 +1,10 @@ -error: equality constraints are not yet supported in where clauses (see #20041) - --> $DIR/associated-type-projection-from-multiple-supertraits.rs:34:46 +error: equality constraints are not yet supported in where clauses + --> $DIR/associated-type-projection-from-multiple-supertraits.rs:32:46 | LL | fn dent_object_2(c: dyn BoxCar) where ::Color = COLOR { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not supported + | + = note: for more information, see #20041 error[E0221]: ambiguous associated type `Color` in bounds of `C` --> $DIR/associated-type-projection-from-multiple-supertraits.rs:19:32 @@ -25,8 +27,8 @@ help: use fully qualified syntax to disambiguate LL | fn dent(c: C, color: ::Color) { | ^^^^^^^^^^^^^^^^^^^^^ -error[E0221]: ambiguous associated type `Color` in bounds of `BoxCar` - --> $DIR/associated-type-projection-from-multiple-supertraits.rs:25:30 +error[E0222]: ambiguous associated type `Color` in bounds of `BoxCar` + --> $DIR/associated-type-projection-from-multiple-supertraits.rs:23:30 | LL | type Color; | ----------- ambiguous `Color` from `Vehicle` @@ -37,26 +39,28 @@ LL | type Color; LL | fn dent_object(c: dyn BoxCar) { | ^^^^^^^^^^^^^^^^^^^ ambiguous associated type `Color` | -help: use fully qualified syntax to disambiguate - | -LL | fn dent_object(c: dyn ::Color) { - | ^^^^^^^^^^^^^^^^^^^^^^ -help: use fully qualified syntax to disambiguate - | -LL | fn dent_object(c: dyn ::Color) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider introducing a new type parameter `T` and adding `where` constraints: + where + T: BoxCar, + T: Box::Color = COLOR, + T: Vehicle::Color = COLOR -error[E0191]: the value of the associated type `Color` (from trait `Vehicle`) must be specified - --> $DIR/associated-type-projection-from-multiple-supertraits.rs:25:30 +error[E0191]: the value of the associated types `Color` (from trait `Vehicle`), `Color` (from trait `Box`) must be specified + --> $DIR/associated-type-projection-from-multiple-supertraits.rs:23:30 | LL | type Color; | ----------- `Color` defined here ... +LL | type Color; + | ----------- `Color` defined here +... LL | fn dent_object(c: dyn BoxCar) { - | ^^^^^^^^^^^^^^^^^^^ help: specify the associated type: `BoxCar` + | ^^^^^^^^^^^^^^^^^^^ associated types `Color`, `Color` must be specified + | + = help: consider introducing a new type parameter, adding `where` constraints using the fully-qualified path to the associated type error[E0221]: ambiguous associated type `Color` in bounds of `C` - --> $DIR/associated-type-projection-from-multiple-supertraits.rs:30:29 + --> $DIR/associated-type-projection-from-multiple-supertraits.rs:28:29 | LL | type Color; | ----------- ambiguous `Color` from `Vehicle` @@ -77,7 +81,7 @@ LL | fn paint(c: C, d: ::Color) { | ^^^^^^^^^^^^^^^^^^^^^ error[E0191]: the value of the associated types `Color` (from trait `Vehicle`), `Color` (from trait `Box`) must be specified - --> $DIR/associated-type-projection-from-multiple-supertraits.rs:34:32 + --> $DIR/associated-type-projection-from-multiple-supertraits.rs:32:32 | LL | type Color; | ----------- `Color` defined here @@ -86,9 +90,11 @@ LL | type Color; | ----------- `Color` defined here ... LL | fn dent_object_2(c: dyn BoxCar) where ::Color = COLOR { - | ^^^^^^ help: specify the associated types: `BoxCar` + | ^^^^^^ associated types `Color`, `Color` must be specified + | + = help: consider introducing a new type parameter, adding `where` constraints using the fully-qualified path to the associated type error: aborting due to 6 previous errors -Some errors have detailed explanations: E0191, E0221. +Some errors have detailed explanations: E0191, E0221, E0222. For more information about an error, try `rustc --explain E0191`. diff --git a/src/test/ui/where-clauses/where-equality-constraints.stderr b/src/test/ui/where-clauses/where-equality-constraints.stderr index c0241fe708f..54d6ad6a136 100644 --- a/src/test/ui/where-clauses/where-equality-constraints.stderr +++ b/src/test/ui/where-clauses/where-equality-constraints.stderr @@ -2,7 +2,7 @@ error: equality constraints are not yet supported in `where` clauses --> $DIR/where-equality-constraints.rs:1:14 | LL | fn f() where u8 = u16 {} - | ^^^^^^^^ + | ^^^^^^^^ not supported | = note: for more information, see https://github.com/rust-lang/rust/issues/20041 @@ -10,7 +10,7 @@ error: equality constraints are not yet supported in `where` clauses --> $DIR/where-equality-constraints.rs:3:14 | LL | fn g() where for<'a> &'static (u8,) == u16, {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not supported | = note: for more information, see https://github.com/rust-lang/rust/issues/20041