1
Fork 0

Handle more specific case E0222

This commit is contained in:
Esteban Küber 2019-12-12 21:15:19 -08:00
parent 8e74f63054
commit 66ea471f9d
7 changed files with 167 additions and 60 deletions

View file

@ -116,6 +116,7 @@ E0211: include_str!("./error_codes/E0211.md"),
E0214: include_str!("./error_codes/E0214.md"), E0214: include_str!("./error_codes/E0214.md"),
E0220: include_str!("./error_codes/E0220.md"), E0220: include_str!("./error_codes/E0220.md"),
E0221: include_str!("./error_codes/E0221.md"), E0221: include_str!("./error_codes/E0221.md"),
E0222: include_str!("./error_codes/E0222.md"),
E0223: include_str!("./error_codes/E0223.md"), E0223: include_str!("./error_codes/E0223.md"),
E0225: include_str!("./error_codes/E0225.md"), E0225: include_str!("./error_codes/E0225.md"),
E0229: include_str!("./error_codes/E0229.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 // E0217, // ambiguous associated type, defined in multiple supertraits
// E0218, // no associated type defined // E0218, // no associated type defined
// E0219, // associated type defined in higher-ranked supertrait // 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 E0224, // at least one non-builtin train is required for an object type
E0226, // only a single explicit lifetime bound is permitted E0226, // only a single explicit lifetime bound is permitted
E0227, // ambiguous lifetime bound, explicit lifetime bound required E0227, // ambiguous lifetime bound, explicit lifetime bound required

View file

@ -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<COLOR>(c: dyn BoxCar<Color=COLOR>) {} // 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 `<BoxCar as Vehicle>::Color` or `<BoxCar as Box>::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<CAR, COLOR>(
c: CAR,
) where
// Bind the type parameter `CAR` to the trait `BoxCar`
CAR: BoxCar,
// Further restrict `<BoxCar as Vehicle>::Color` to be the same as the
// type parameter `COLOR`
CAR: Vehicle<Color = COLOR>,
// We can also simultaneously restrict the other trait's associated type
CAR: Box<Color = COLOR>
{}
```

View file

@ -804,6 +804,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
predicate.span, predicate.span,
"equality constraints are not yet supported in `where` clauses", "equality constraints are not yet supported in `where` clauses",
) )
.span_label(predicate.span, "not supported")
.note( .note(
"for more information, see https://github.com/rust-lang/rust/issues/20041", "for more information, see https://github.com/rust-lang/rust/issues/20041",
) )

View file

@ -1267,7 +1267,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let candidate = let candidate =
if self.trait_defines_associated_type_named(trait_ref.def_id(), binding.item_name) { if self.trait_defines_associated_type_named(trait_ref.def_id(), binding.item_name) {
// Simple case: X is defined in the current trait. // Simple case: X is defined in the current trait.
Ok(trait_ref) trait_ref
} else { } else {
// Otherwise, we have to walk through the supertraits to find // Otherwise, we have to walk through the supertraits to find
// those that do. // those that do.
@ -1276,8 +1276,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
&trait_ref.print_only_trait_path().to_string(), &trait_ref.print_only_trait_path().to_string(),
binding.item_name, binding.item_name,
path_span, path_span,
) match binding.kind {
}?; ConvertedBindingKind::Equality(ty) => Some(ty.to_string()),
_ => None,
},
)?
};
let (assoc_ident, def_scope) = let (assoc_ident, def_scope) =
tcx.adjust_ident_and_get_scope(binding.item_name, candidate.def_id(), hir_ref_id); 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(); let mut suggestions_len = suggestions.len();
if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(sugg_span) { 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<String> = 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::<FxHashSet<_>>();
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 // Do not attempt to suggest when we don't know which path segment needs the
// type parameter set. // type parameter set.
trait_bounds[0].trait_ref.path.segments.len() == 1 trait_bounds[0].trait_ref.path.segments.len() == 1
{ {
debug!("path segments {:?}", trait_bounds[0].trait_ref.path.segments);
applicability = Applicability::HasPlaceholders; applicability = Applicability::HasPlaceholders;
let assoc_types: Vec<String> = 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(", "); let sugg = assoc_types.join(", ");
if snippet.ends_with('>') { if snippet.ends_with('>') {
// The user wrote `Trait<'a>` or similar and we don't have a type we can // 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::<Vec<_>>() .collect::<Vec<_>>()
.join(", "); .join(", ");
err.span_label( err.span_label(
span, sugg_span,
format!( format!(
"associated type{} {} must be specified", "associated type{} {} must be specified",
pluralize!(associated_types.len()), pluralize!(associated_types.len()),
@ -1743,15 +1758,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
&param_name.as_str(), &param_name.as_str(),
assoc_name, assoc_name,
span, span,
None,
) )
} }
// Checks that `bounds` contains exactly one element and reports appropriate
// errors otherwise.
fn one_bound_for_assoc_type<I>( fn one_bound_for_assoc_type<I>(
&self, &self,
all_candidates: impl Fn() -> I, all_candidates: impl Fn() -> I,
ty_param_name: &str, ty_param_name: &str,
assoc_name: ast::Ident, assoc_name: ast::Ident,
span: Span, span: Span,
is_equality: Option<String>,
) -> Result<ty::PolyTraitRef<'tcx>, ErrorReported> ) -> Result<ty::PolyTraitRef<'tcx>, ErrorReported>
where where
I: Iterator<Item = ty::PolyTraitRef<'tcx>>, I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
@ -1778,16 +1797,29 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
debug!("one_bound_for_assoc_type: bound2 = {:?}", bound2); debug!("one_bound_for_assoc_type: bound2 = {:?}", bound2);
let bounds = iter::once(bound).chain(iter::once(bound2)).chain(matching_candidates); let bounds = iter::once(bound).chain(iter::once(bound2)).chain(matching_candidates);
let mut err = struct_span_err!( let mut err = if is_equality.is_some() {
self.tcx().sess, // More specific Error Index entry.
span, struct_span_err!(
E0221, self.tcx().sess,
"ambiguous associated type `{}` in bounds of `{}`", span,
assoc_name, E0222,
ty_param_name "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)); err.span_label(span, format!("ambiguous associated type `{}`", assoc_name));
let mut where_bounds = vec![];
for bound in bounds { for bound in bounds {
let bound_span = self let bound_span = self
.tcx() .tcx()
@ -1807,17 +1839,26 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
bound.print_only_trait_path(), bound.print_only_trait_path(),
), ),
); );
err.span_suggestion( if let Some(constraint) = &is_equality {
span, where_bounds.push(format!(
"use fully qualified syntax to disambiguate", " T: {trait}::{assoc} = {constraint}",
format!( trait=bound.print_only_trait_path(),
"<{} as {}>::{}", assoc=assoc_name,
ty_param_name, constraint=constraint,
bound.print_only_trait_path(), ));
assoc_name, } else {
), err.span_suggestion(
Applicability::MaybeIncorrect, span,
); "use fully qualified syntax to disambiguate",
format!(
"<{} as {}>::{}",
ty_param_name,
bound.print_only_trait_path(),
assoc_name,
),
Applicability::MaybeIncorrect,
);
}
} else { } else {
err.note(&format!( err.note(&format!(
"associated type `{}` could derive from `{}`", "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(); err.emit();
if !where_bounds.is_empty() {
return Err(ErrorReported);
}
} }
return Ok(bound); return Ok(bound);
} }
@ -1933,6 +1984,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
"Self", "Self",
assoc_ident, assoc_ident,
span, span,
None,
)? )?
} }
(&ty::Param(_), Res::SelfTy(Some(param_did), None)) (&ty::Param(_), Res::SelfTy(Some(param_did), None))

View file

@ -20,11 +20,9 @@ fn dent<C:BoxCar>(c: C, color: C::Color) {
//~^ ERROR ambiguous associated type `Color` in bounds of `C` //~^ 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<COLOR>(c: dyn BoxCar<Color=COLOR>) { fn dent_object<COLOR>(c: dyn BoxCar<Color=COLOR>) {
//~^ ERROR ambiguous associated type //~^ 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:BoxCar>(c: C, d: C::Color) { fn paint<C:BoxCar>(c: C, d: C::Color) {

View file

@ -1,8 +1,10 @@
error: equality constraints are not yet supported in where clauses (see #20041) error: equality constraints are not yet supported in where clauses
--> $DIR/associated-type-projection-from-multiple-supertraits.rs:34:46 --> $DIR/associated-type-projection-from-multiple-supertraits.rs:32:46
| |
LL | fn dent_object_2<COLOR>(c: dyn BoxCar) where <dyn BoxCar as Vehicle>::Color = COLOR { LL | fn dent_object_2<COLOR>(c: dyn BoxCar) where <dyn BoxCar as Vehicle>::Color = COLOR {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not supported
|
= note: for more information, see #20041
error[E0221]: ambiguous associated type `Color` in bounds of `C` error[E0221]: ambiguous associated type `Color` in bounds of `C`
--> $DIR/associated-type-projection-from-multiple-supertraits.rs:19:32 --> $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:BoxCar>(c: C, color: <C as Vehicle>::Color) { LL | fn dent<C:BoxCar>(c: C, color: <C as Vehicle>::Color) {
| ^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^
error[E0221]: ambiguous associated type `Color` in bounds of `BoxCar` error[E0222]: ambiguous associated type `Color` in bounds of `BoxCar`
--> $DIR/associated-type-projection-from-multiple-supertraits.rs:25:30 --> $DIR/associated-type-projection-from-multiple-supertraits.rs:23:30
| |
LL | type Color; LL | type Color;
| ----------- ambiguous `Color` from `Vehicle` | ----------- ambiguous `Color` from `Vehicle`
@ -37,26 +39,28 @@ LL | type Color;
LL | fn dent_object<COLOR>(c: dyn BoxCar<Color=COLOR>) { LL | fn dent_object<COLOR>(c: dyn BoxCar<Color=COLOR>) {
| ^^^^^^^^^^^^^^^^^^^ ambiguous associated type `Color` | ^^^^^^^^^^^^^^^^^^^ ambiguous associated type `Color`
| |
help: use fully qualified syntax to disambiguate = help: consider introducing a new type parameter `T` and adding `where` constraints:
| where
LL | fn dent_object<COLOR>(c: dyn <BoxCar as Box>::Color) { T: BoxCar,
| ^^^^^^^^^^^^^^^^^^^^^^ T: Box::Color = COLOR,
help: use fully qualified syntax to disambiguate T: Vehicle::Color = COLOR
|
LL | fn dent_object<COLOR>(c: dyn <BoxCar as Vehicle>::Color) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0191]: the value of the associated type `Color` (from trait `Vehicle`) must be specified 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:25:30 --> $DIR/associated-type-projection-from-multiple-supertraits.rs:23:30
| |
LL | type Color; LL | type Color;
| ----------- `Color` defined here | ----------- `Color` defined here
... ...
LL | type Color;
| ----------- `Color` defined here
...
LL | fn dent_object<COLOR>(c: dyn BoxCar<Color=COLOR>) { LL | fn dent_object<COLOR>(c: dyn BoxCar<Color=COLOR>) {
| ^^^^^^^^^^^^^^^^^^^ help: specify the associated type: `BoxCar<Color=COLOR, Color = Type>` | ^^^^^^^^^^^^^^^^^^^ 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` 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; LL | type Color;
| ----------- ambiguous `Color` from `Vehicle` | ----------- ambiguous `Color` from `Vehicle`
@ -77,7 +81,7 @@ LL | fn paint<C:BoxCar>(c: C, d: <C as Vehicle>::Color) {
| ^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^
error[E0191]: the value of the associated types `Color` (from trait `Vehicle`), `Color` (from trait `Box`) must be specified 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; LL | type Color;
| ----------- `Color` defined here | ----------- `Color` defined here
@ -86,9 +90,11 @@ LL | type Color;
| ----------- `Color` defined here | ----------- `Color` defined here
... ...
LL | fn dent_object_2<COLOR>(c: dyn BoxCar) where <dyn BoxCar as Vehicle>::Color = COLOR { LL | fn dent_object_2<COLOR>(c: dyn BoxCar) where <dyn BoxCar as Vehicle>::Color = COLOR {
| ^^^^^^ help: specify the associated types: `BoxCar<Color = Type, Color = Type>` | ^^^^^^ 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 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`. For more information about an error, try `rustc --explain E0191`.

View file

@ -2,7 +2,7 @@ error: equality constraints are not yet supported in `where` clauses
--> $DIR/where-equality-constraints.rs:1:14 --> $DIR/where-equality-constraints.rs:1:14
| |
LL | fn f() where u8 = u16 {} LL | fn f() where u8 = u16 {}
| ^^^^^^^^ | ^^^^^^^^ not supported
| |
= note: for more information, see https://github.com/rust-lang/rust/issues/20041 = 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 --> $DIR/where-equality-constraints.rs:3:14
| |
LL | fn g() where for<'a> &'static (u8,) == u16, {} LL | fn g() where for<'a> &'static (u8,) == u16, {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not supported
| |
= note: for more information, see https://github.com/rust-lang/rust/issues/20041 = note: for more information, see https://github.com/rust-lang/rust/issues/20041