Skip to content

Commit 66ea471

Browse files
committed
Handle more specific case E0222
1 parent 8e74f63 commit 66ea471

File tree

7 files changed

+167
-60
lines changed

7 files changed

+167
-60
lines changed

src/librustc_error_codes/error_codes.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ E0211: include_str!("./error_codes/E0211.md"),
116116
E0214: include_str!("./error_codes/E0214.md"),
117117
E0220: include_str!("./error_codes/E0220.md"),
118118
E0221: include_str!("./error_codes/E0221.md"),
119+
E0222: include_str!("./error_codes/E0222.md"),
119120
E0223: include_str!("./error_codes/E0223.md"),
120121
E0225: include_str!("./error_codes/E0225.md"),
121122
E0229: include_str!("./error_codes/E0229.md"),
@@ -457,8 +458,6 @@ E0745: include_str!("./error_codes/E0745.md"),
457458
// E0217, // ambiguous associated type, defined in multiple supertraits
458459
// E0218, // no associated type defined
459460
// E0219, // associated type defined in higher-ranked supertrait
460-
// E0222, // Error code E0045 (variadic function must have C or cdecl calling
461-
// convention) duplicate
462461
E0224, // at least one non-builtin train is required for an object type
463462
E0226, // only a single explicit lifetime bound is permitted
464463
E0227, // ambiguous lifetime bound, explicit lifetime bound required
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
An attempt was made to constrain an associated type.
2+
For example:
3+
4+
```compile_fail,E0222
5+
pub trait Vehicle {
6+
type Color;
7+
}
8+
9+
pub trait Box {
10+
type Color;
11+
}
12+
13+
pub trait BoxCar : Box + Vehicle {}
14+
15+
fn dent_object<COLOR>(c: dyn BoxCar<Color=COLOR>) {} // Invalid constraint
16+
```
17+
18+
In this example, `BoxCar` has two super-traits: `Vehicle` and `Box`. Both of
19+
these traits define an associated type `Color`. `BoxCar` inherits two types
20+
with that name from both super-traits. Because of this, we need to use the
21+
fully qualified path syntax to refer to the appropriate `Color` associated
22+
type, either `<BoxCar as Vehicle>::Color` or `<BoxCar as Box>::Color`, but this
23+
syntax is not allowed to be used in a function signature.
24+
25+
In order to encode this kind of constraint, a `where` clause and a new type
26+
parameter are needed:
27+
28+
```
29+
pub trait Vehicle {
30+
type Color;
31+
}
32+
33+
pub trait Box {
34+
type Color;
35+
}
36+
37+
pub trait BoxCar : Box + Vehicle {}
38+
39+
// Introduce a new `CAR` type parameter
40+
fn foo<CAR, COLOR>(
41+
c: CAR,
42+
) where
43+
// Bind the type parameter `CAR` to the trait `BoxCar`
44+
CAR: BoxCar,
45+
// Further restrict `<BoxCar as Vehicle>::Color` to be the same as the
46+
// type parameter `COLOR`
47+
CAR: Vehicle<Color = COLOR>,
48+
// We can also simultaneously restrict the other trait's associated type
49+
CAR: Box<Color = COLOR>
50+
{}
51+
```

src/librustc_passes/ast_validation.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
804804
predicate.span,
805805
"equality constraints are not yet supported in `where` clauses",
806806
)
807+
.span_label(predicate.span, "not supported")
807808
.note(
808809
"for more information, see https://github.com/rust-lang/rust/issues/20041",
809810
)

src/librustc_typeck/astconv.rs

Lines changed: 85 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1267,7 +1267,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
12671267
let candidate =
12681268
if self.trait_defines_associated_type_named(trait_ref.def_id(), binding.item_name) {
12691269
// Simple case: X is defined in the current trait.
1270-
Ok(trait_ref)
1270+
trait_ref
12711271
} else {
12721272
// Otherwise, we have to walk through the supertraits to find
12731273
// those that do.
@@ -1276,8 +1276,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
12761276
&trait_ref.print_only_trait_path().to_string(),
12771277
binding.item_name,
12781278
path_span,
1279-
)
1280-
}?;
1279+
match binding.kind {
1280+
ConvertedBindingKind::Equality(ty) => Some(ty.to_string()),
1281+
_ => None,
1282+
},
1283+
)?
1284+
};
12811285

12821286
let (assoc_ident, def_scope) =
12831287
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 {
16261630
}
16271631
let mut suggestions_len = suggestions.len();
16281632
if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(sugg_span) {
1629-
if potential_assoc_types.is_empty() && trait_bounds.len() == 1 &&
1633+
let assoc_types: Vec<String> = associated_types
1634+
.iter()
1635+
.map(|item_def_id| {
1636+
let assoc_item = tcx.associated_item(*item_def_id);
1637+
format!("{} = Type", assoc_item.ident)
1638+
})
1639+
.collect();
1640+
let dedup = assoc_types.clone().drain(..).collect::<FxHashSet<_>>();
1641+
1642+
if dedup.len() != assoc_types.len() && trait_bounds.len() == 1 {
1643+
// If there are duplicates associated type names and a single trait bound do not
1644+
// use structured suggestion, it means that there are multiple super-traits with
1645+
// the same associated type name.
1646+
err.help(
1647+
"consider introducing a new type parameter, adding `where` constraints \
1648+
using the fully-qualified path to the associated type",
1649+
);
1650+
} else if dedup.len() == assoc_types.len() &&
1651+
potential_assoc_types.is_empty() &&
1652+
trait_bounds.len() == 1 &&
16301653
// Do not attempt to suggest when we don't know which path segment needs the
16311654
// type parameter set.
16321655
trait_bounds[0].trait_ref.path.segments.len() == 1
16331656
{
1634-
debug!("path segments {:?}", trait_bounds[0].trait_ref.path.segments);
16351657
applicability = Applicability::HasPlaceholders;
1636-
let assoc_types: Vec<String> = associated_types
1637-
.iter()
1638-
.map(|item_def_id| {
1639-
let assoc_item = tcx.associated_item(*item_def_id);
1640-
format!("{} = Type", assoc_item.ident)
1641-
})
1642-
.collect();
16431658
let sugg = assoc_types.join(", ");
16441659
if snippet.ends_with('>') {
16451660
// 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 {
16661681
.collect::<Vec<_>>()
16671682
.join(", ");
16681683
err.span_label(
1669-
span,
1684+
sugg_span,
16701685
format!(
16711686
"associated type{} {} must be specified",
16721687
pluralize!(associated_types.len()),
@@ -1743,15 +1758,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
17431758
&param_name.as_str(),
17441759
assoc_name,
17451760
span,
1761+
None,
17461762
)
17471763
}
17481764

1765+
// Checks that `bounds` contains exactly one element and reports appropriate
1766+
// errors otherwise.
17491767
fn one_bound_for_assoc_type<I>(
17501768
&self,
17511769
all_candidates: impl Fn() -> I,
17521770
ty_param_name: &str,
17531771
assoc_name: ast::Ident,
17541772
span: Span,
1773+
is_equality: Option<String>,
17551774
) -> Result<ty::PolyTraitRef<'tcx>, ErrorReported>
17561775
where
17571776
I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
@@ -1778,16 +1797,29 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
17781797
debug!("one_bound_for_assoc_type: bound2 = {:?}", bound2);
17791798

17801799
let bounds = iter::once(bound).chain(iter::once(bound2)).chain(matching_candidates);
1781-
let mut err = struct_span_err!(
1782-
self.tcx().sess,
1783-
span,
1784-
E0221,
1785-
"ambiguous associated type `{}` in bounds of `{}`",
1786-
assoc_name,
1787-
ty_param_name
1788-
);
1800+
let mut err = if is_equality.is_some() {
1801+
// More specific Error Index entry.
1802+
struct_span_err!(
1803+
self.tcx().sess,
1804+
span,
1805+
E0222,
1806+
"ambiguous associated type `{}` in bounds of `{}`",
1807+
assoc_name,
1808+
ty_param_name
1809+
)
1810+
} else {
1811+
struct_span_err!(
1812+
self.tcx().sess,
1813+
span,
1814+
E0221,
1815+
"ambiguous associated type `{}` in bounds of `{}`",
1816+
assoc_name,
1817+
ty_param_name
1818+
)
1819+
};
17891820
err.span_label(span, format!("ambiguous associated type `{}`", assoc_name));
17901821

1822+
let mut where_bounds = vec![];
17911823
for bound in bounds {
17921824
let bound_span = self
17931825
.tcx()
@@ -1807,17 +1839,26 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
18071839
bound.print_only_trait_path(),
18081840
),
18091841
);
1810-
err.span_suggestion(
1811-
span,
1812-
"use fully qualified syntax to disambiguate",
1813-
format!(
1814-
"<{} as {}>::{}",
1815-
ty_param_name,
1816-
bound.print_only_trait_path(),
1817-
assoc_name,
1818-
),
1819-
Applicability::MaybeIncorrect,
1820-
);
1842+
if let Some(constraint) = &is_equality {
1843+
where_bounds.push(format!(
1844+
" T: {trait}::{assoc} = {constraint}",
1845+
trait=bound.print_only_trait_path(),
1846+
assoc=assoc_name,
1847+
constraint=constraint,
1848+
));
1849+
} else {
1850+
err.span_suggestion(
1851+
span,
1852+
"use fully qualified syntax to disambiguate",
1853+
format!(
1854+
"<{} as {}>::{}",
1855+
ty_param_name,
1856+
bound.print_only_trait_path(),
1857+
assoc_name,
1858+
),
1859+
Applicability::MaybeIncorrect,
1860+
);
1861+
}
18211862
} else {
18221863
err.note(&format!(
18231864
"associated type `{}` could derive from `{}`",
@@ -1826,9 +1867,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
18261867
));
18271868
}
18281869
}
1870+
if !where_bounds.is_empty() {
1871+
err.help(&format!(
1872+
"consider introducing a new type parameter `T` and adding `where` constraints:\
1873+
\n where\n T: {},\n{}",
1874+
ty_param_name,
1875+
where_bounds.join(",\n"),
1876+
));
1877+
}
18291878
err.emit();
1879+
if !where_bounds.is_empty() {
1880+
return Err(ErrorReported);
1881+
}
18301882
}
1831-
18321883
return Ok(bound);
18331884
}
18341885

@@ -1933,6 +1984,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
19331984
"Self",
19341985
assoc_ident,
19351986
span,
1987+
None,
19361988
)?
19371989
}
19381990
(&ty::Param(_), Res::SelfTy(Some(param_did), None))

src/test/ui/associated-type/associated-type-projection-from-multiple-supertraits.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,9 @@ fn dent<C:BoxCar>(c: C, color: C::Color) {
2020
//~^ ERROR ambiguous associated type `Color` in bounds of `C`
2121
}
2222

23-
// FIXME: add error code to detect this case and explain that you'll want the approach in
24-
// `dent_object_3` of using a new type param and relying on the `where` clauses.
2523
fn dent_object<COLOR>(c: dyn BoxCar<Color=COLOR>) {
2624
//~^ ERROR ambiguous associated type
27-
//~| ERROR the value of the associated type `Color` (from trait `Vehicle`) must be specified
25+
//~| ERROR the value of the associated types `Color` (from trait `Vehicle`), `Color` (from
2826
}
2927

3028
fn paint<C:BoxCar>(c: C, d: C::Color) {

src/test/ui/associated-type/associated-type-projection-from-multiple-supertraits.stderr

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
error: equality constraints are not yet supported in where clauses (see #20041)
2-
--> $DIR/associated-type-projection-from-multiple-supertraits.rs:34:46
1+
error: equality constraints are not yet supported in where clauses
2+
--> $DIR/associated-type-projection-from-multiple-supertraits.rs:32:46
33
|
44
LL | fn dent_object_2<COLOR>(c: dyn BoxCar) where <dyn BoxCar as Vehicle>::Color = COLOR {
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not supported
6+
|
7+
= note: for more information, see #20041
68

79
error[E0221]: ambiguous associated type `Color` in bounds of `C`
810
--> $DIR/associated-type-projection-from-multiple-supertraits.rs:19:32
@@ -25,8 +27,8 @@ help: use fully qualified syntax to disambiguate
2527
LL | fn dent<C:BoxCar>(c: C, color: <C as Vehicle>::Color) {
2628
| ^^^^^^^^^^^^^^^^^^^^^
2729

28-
error[E0221]: ambiguous associated type `Color` in bounds of `BoxCar`
29-
--> $DIR/associated-type-projection-from-multiple-supertraits.rs:25:30
30+
error[E0222]: ambiguous associated type `Color` in bounds of `BoxCar`
31+
--> $DIR/associated-type-projection-from-multiple-supertraits.rs:23:30
3032
|
3133
LL | type Color;
3234
| ----------- ambiguous `Color` from `Vehicle`
@@ -37,26 +39,28 @@ LL | type Color;
3739
LL | fn dent_object<COLOR>(c: dyn BoxCar<Color=COLOR>) {
3840
| ^^^^^^^^^^^^^^^^^^^ ambiguous associated type `Color`
3941
|
40-
help: use fully qualified syntax to disambiguate
41-
|
42-
LL | fn dent_object<COLOR>(c: dyn <BoxCar as Box>::Color) {
43-
| ^^^^^^^^^^^^^^^^^^^^^^
44-
help: use fully qualified syntax to disambiguate
45-
|
46-
LL | fn dent_object<COLOR>(c: dyn <BoxCar as Vehicle>::Color) {
47-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
42+
= help: consider introducing a new type parameter `T` and adding `where` constraints:
43+
where
44+
T: BoxCar,
45+
T: Box::Color = COLOR,
46+
T: Vehicle::Color = COLOR
4847

49-
error[E0191]: the value of the associated type `Color` (from trait `Vehicle`) must be specified
50-
--> $DIR/associated-type-projection-from-multiple-supertraits.rs:25:30
48+
error[E0191]: the value of the associated types `Color` (from trait `Vehicle`), `Color` (from trait `Box`) must be specified
49+
--> $DIR/associated-type-projection-from-multiple-supertraits.rs:23:30
5150
|
5251
LL | type Color;
5352
| ----------- `Color` defined here
5453
...
54+
LL | type Color;
55+
| ----------- `Color` defined here
56+
...
5557
LL | fn dent_object<COLOR>(c: dyn BoxCar<Color=COLOR>) {
56-
| ^^^^^^^^^^^^^^^^^^^ help: specify the associated type: `BoxCar<Color=COLOR, Color = Type>`
58+
| ^^^^^^^^^^^^^^^^^^^ associated types `Color`, `Color` must be specified
59+
|
60+
= help: consider introducing a new type parameter, adding `where` constraints using the fully-qualified path to the associated type
5761

5862
error[E0221]: ambiguous associated type `Color` in bounds of `C`
59-
--> $DIR/associated-type-projection-from-multiple-supertraits.rs:30:29
63+
--> $DIR/associated-type-projection-from-multiple-supertraits.rs:28:29
6064
|
6165
LL | type Color;
6266
| ----------- ambiguous `Color` from `Vehicle`
@@ -77,7 +81,7 @@ LL | fn paint<C:BoxCar>(c: C, d: <C as Vehicle>::Color) {
7781
| ^^^^^^^^^^^^^^^^^^^^^
7882

7983
error[E0191]: the value of the associated types `Color` (from trait `Vehicle`), `Color` (from trait `Box`) must be specified
80-
--> $DIR/associated-type-projection-from-multiple-supertraits.rs:34:32
84+
--> $DIR/associated-type-projection-from-multiple-supertraits.rs:32:32
8185
|
8286
LL | type Color;
8387
| ----------- `Color` defined here
@@ -86,9 +90,11 @@ LL | type Color;
8690
| ----------- `Color` defined here
8791
...
8892
LL | fn dent_object_2<COLOR>(c: dyn BoxCar) where <dyn BoxCar as Vehicle>::Color = COLOR {
89-
| ^^^^^^ help: specify the associated types: `BoxCar<Color = Type, Color = Type>`
93+
| ^^^^^^ associated types `Color`, `Color` must be specified
94+
|
95+
= help: consider introducing a new type parameter, adding `where` constraints using the fully-qualified path to the associated type
9096

9197
error: aborting due to 6 previous errors
9298

93-
Some errors have detailed explanations: E0191, E0221.
99+
Some errors have detailed explanations: E0191, E0221, E0222.
94100
For more information about an error, try `rustc --explain E0191`.

src/test/ui/where-clauses/where-equality-constraints.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ error: equality constraints are not yet supported in `where` clauses
22
--> $DIR/where-equality-constraints.rs:1:14
33
|
44
LL | fn f() where u8 = u16 {}
5-
| ^^^^^^^^
5+
| ^^^^^^^^ not supported
66
|
77
= note: for more information, see https://github.com/rust-lang/rust/issues/20041
88

99
error: equality constraints are not yet supported in `where` clauses
1010
--> $DIR/where-equality-constraints.rs:3:14
1111
|
1212
LL | fn g() where for<'a> &'static (u8,) == u16, {}
13-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not supported
1414
|
1515
= note: for more information, see https://github.com/rust-lang/rust/issues/20041
1616

0 commit comments

Comments
 (0)