Skip to content

Commit fef3c8f

Browse files
committed
Handle more specific case E0222
1 parent e9ef8c6 commit fef3c8f

File tree

7 files changed

+195
-81
lines changed

7 files changed

+195
-81
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"),
@@ -456,8 +457,6 @@ E0745: include_str!("./error_codes/E0745.md"),
456457
// E0217, // ambiguous associated type, defined in multiple supertraits
457458
// E0218, // no associated type defined
458459
// E0219, // associated type defined in higher-ranked supertrait
459-
// E0222, // Error code E0045 (variadic function must have C or cdecl calling
460-
// convention) duplicate
461460
E0224, // at least one non-builtin train is required for an object type
462461
E0226, // only a single explicit lifetime bound is permitted
463462
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: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -683,9 +683,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
683683

684684
for predicate in &generics.where_clause.predicates {
685685
if let WherePredicate::EqPredicate(ref predicate) = *predicate {
686-
self.err_handler()
687-
.span_err(predicate.span, "equality constraints are not yet \
688-
supported in where clauses (see #20041)");
686+
self.err_handler().struct_span_err(
687+
predicate.span,
688+
"equality constraints are not yet supported in where clauses",
689+
)
690+
.span_label(predicate.span, "not supported")
691+
.note("for more information, see #20041")
692+
.emit();
689693
}
690694
}
691695

src/librustc_typeck/astconv.rs

Lines changed: 101 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,10 +1220,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
12201220
}
12211221
}
12221222

1223-
let candidate = if self.trait_defines_associated_type_named(trait_ref.def_id(),
1224-
binding.item_name) {
1223+
let candidate = if self.trait_defines_associated_type_named(
1224+
trait_ref.def_id(),
1225+
binding.item_name,
1226+
) {
12251227
// Simple case: X is defined in the current trait.
1226-
Ok(trait_ref)
1228+
trait_ref
12271229
} else {
12281230
// Otherwise, we have to walk through the supertraits to find
12291231
// those that do.
@@ -1232,8 +1234,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
12321234
&trait_ref.print_only_trait_path().to_string(),
12331235
binding.item_name,
12341236
path_span,
1235-
)
1236-
}?;
1237+
match binding.kind {
1238+
ConvertedBindingKind::Equality(ty) => Some(ty.to_string()),
1239+
_ => None,
1240+
},
1241+
)?
1242+
};
12371243

12381244
let (assoc_ident, def_scope) =
12391245
tcx.adjust_ident_and_get_scope(binding.item_name, candidate.def_id(), hir_ref_id);
@@ -1564,19 +1570,28 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
15641570
}
15651571
let mut suggestions_len = suggestions.len();
15661572
if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(sugg_span) {
1567-
if potential_assoc_types.is_empty() && trait_bounds.len() == 1 &&
1573+
let assoc_types: Vec<String> = associated_types.iter()
1574+
.map(|item_def_id| {
1575+
let assoc_item = tcx.associated_item(*item_def_id);
1576+
format!("{} = Type", assoc_item.ident)
1577+
})
1578+
.collect();
1579+
let dedup = assoc_types.clone().drain(..).collect::<FxHashSet<_>>();
1580+
1581+
if dedup.len() != assoc_types.len() && trait_bounds.len() == 1 {
1582+
// If there are duplicates associated type names and a single trait bound do not
1583+
// use structured suggestion, it means that there are multiple super-traits with
1584+
// the same associated type name.
1585+
err.help("consider introducing a new type parameter, adding `where` constraints \
1586+
using the fully-qualified path to the associated type");
1587+
} else if dedup.len() == assoc_types.len() &&
1588+
potential_assoc_types.is_empty() &&
1589+
trait_bounds.len() == 1 &&
15681590
// Do not attempt to suggest when we don't know which path segment needs the
15691591
// type parameter set.
15701592
trait_bounds[0].trait_ref.path.segments.len() == 1
15711593
{
1572-
debug!("path segments {:?}", trait_bounds[0].trait_ref.path.segments);
15731594
applicability = Applicability::HasPlaceholders;
1574-
let assoc_types: Vec<String> = associated_types.iter()
1575-
.map(|item_def_id| {
1576-
let assoc_item = tcx.associated_item(*item_def_id);
1577-
format!("{} = Type", assoc_item.ident)
1578-
})
1579-
.collect();
15801595
let sugg = assoc_types.join(", ");
15811596
if snippet.ends_with('>') {
15821597
// The user wrote `Trait<'a>` or similar and we don't have a type we can
@@ -1602,7 +1617,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
16021617
.map(|t| format!("`{}`", tcx.associated_item(*t).ident))
16031618
.collect::<Vec<_>>()
16041619
.join(", ");
1605-
err.span_label(span, format!(
1620+
err.span_label(sugg_span, format!(
16061621
"associated type{} {} must be specified",
16071622
pluralize!(associated_types.len()),
16081623
names,
@@ -1635,10 +1650,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
16351650
);
16361651
} else {
16371652
err.span_suggestion(
1638-
span,
1639-
"use fully-qualified syntax",
1640-
format!("<{} as {}>::{}", type_str, trait_str, name),
1641-
Applicability::HasPlaceholders
1653+
span,
1654+
"use fully-qualified syntax",
1655+
format!("<{} as {}>::{}", type_str, trait_str, name),
1656+
Applicability::HasPlaceholders
16421657
);
16431658
}
16441659
err.emit();
@@ -1648,12 +1663,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
16481663
// given by `assoc_name`. `ty_param_def_id` is the `DefId` of the type parameter
16491664
// This function will fail if there are no suitable bounds or there is
16501665
// any ambiguity.
1651-
fn find_bound_for_assoc_item(&self,
1652-
ty_param_def_id: DefId,
1653-
assoc_name: ast::Ident,
1654-
span: Span)
1655-
-> Result<ty::PolyTraitRef<'tcx>, ErrorReported>
1656-
{
1666+
fn find_bound_for_assoc_item(
1667+
&self,
1668+
ty_param_def_id: DefId,
1669+
assoc_name: ast::Ident,
1670+
span: Span,
1671+
) -> Result<ty::PolyTraitRef<'tcx>, ErrorReported> {
16571672
let tcx = self.tcx();
16581673

16591674
debug!(
@@ -1675,16 +1690,21 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
16751690
&param_name.as_str(),
16761691
assoc_name,
16771692
span,
1693+
None,
16781694
)
16791695
}
16801696

1681-
fn one_bound_for_assoc_type<I>(&self,
1682-
all_candidates: impl Fn() -> I,
1683-
ty_param_name: &str,
1684-
assoc_name: ast::Ident,
1685-
span: Span)
1686-
-> Result<ty::PolyTraitRef<'tcx>, ErrorReported>
1687-
where I: Iterator<Item = ty::PolyTraitRef<'tcx>>
1697+
// Checks that `bounds` contains exactly one element and reports appropriate
1698+
// errors otherwise.
1699+
fn one_bound_for_assoc_type<I>(
1700+
&self,
1701+
all_candidates: impl Fn() -> I,
1702+
ty_param_name: &str,
1703+
assoc_name: ast::Ident,
1704+
span: Span,
1705+
is_equality: Option<String>,
1706+
) -> Result<ty::PolyTraitRef<'tcx>, ErrorReported>
1707+
where I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
16881708
{
16891709
let mut matching_candidates = all_candidates().filter(|r| {
16901710
self.trait_defines_associated_type_named(r.def_id(), assoc_name)
@@ -1709,13 +1729,25 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
17091729
debug!("one_bound_for_assoc_type: bound2 = {:?}", bound2);
17101730

17111731
let bounds = iter::once(bound).chain(iter::once(bound2)).chain(matching_candidates);
1712-
let mut err = struct_span_err!(
1713-
self.tcx().sess, span, E0221,
1714-
"ambiguous associated type `{}` in bounds of `{}`",
1715-
assoc_name,
1716-
ty_param_name);
1732+
let mut err = if is_equality.is_some() {
1733+
// More specific Error Index entry.
1734+
struct_span_err!(
1735+
self.tcx().sess, span, E0222,
1736+
"ambiguous associated type `{}` in bounds of `{}`",
1737+
assoc_name,
1738+
ty_param_name
1739+
)
1740+
} else {
1741+
struct_span_err!(
1742+
self.tcx().sess, span, E0221,
1743+
"ambiguous associated type `{}` in bounds of `{}`",
1744+
assoc_name,
1745+
ty_param_name
1746+
)
1747+
};
17171748
err.span_label(span, format!("ambiguous associated type `{}`", assoc_name));
17181749

1750+
let mut where_bounds = vec![];
17191751
for bound in bounds {
17201752
let bound_span = self.tcx().associated_items(bound.def_id()).find(|item| {
17211753
item.kind == ty::AssocKind::Type &&
@@ -1729,17 +1761,26 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
17291761
assoc_name,
17301762
bound.print_only_trait_path(),
17311763
));
1732-
err.span_suggestion(
1733-
span,
1734-
"use fully qualified syntax to disambiguate",
1735-
format!(
1736-
"<{} as {}>::{}",
1737-
ty_param_name,
1738-
bound.print_only_trait_path(),
1739-
assoc_name,
1740-
),
1741-
Applicability::MaybeIncorrect,
1742-
);
1764+
if let Some(constraint) = &is_equality {
1765+
where_bounds.push(format!(
1766+
" T: {trait}::{assoc} = {constraint}",
1767+
trait=bound.print_only_trait_path(),
1768+
assoc=assoc_name,
1769+
constraint=constraint,
1770+
));
1771+
} else {
1772+
err.span_suggestion(
1773+
span,
1774+
"use fully qualified syntax to disambiguate",
1775+
format!(
1776+
"<{} as {}>::{}",
1777+
ty_param_name,
1778+
bound.print_only_trait_path(),
1779+
assoc_name,
1780+
),
1781+
Applicability::MaybeIncorrect,
1782+
);
1783+
}
17431784
} else {
17441785
err.note(&format!(
17451786
"associated type `{}` could derive from `{}`",
@@ -1748,9 +1789,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
17481789
));
17491790
}
17501791
}
1792+
if !where_bounds.is_empty() {
1793+
err.help(&format!(
1794+
"consider introducing a new type parameter `T` and adding `where` constraints:\
1795+
\n where\n T: {},\n{}",
1796+
ty_param_name,
1797+
where_bounds.join(",\n"),
1798+
));
1799+
}
17511800
err.emit();
1801+
if !where_bounds.is_empty() {
1802+
return Err(ErrorReported);
1803+
}
17521804
}
1753-
17541805
return Ok(bound);
17551806
}
17561807

@@ -1856,7 +1907,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
18561907
|| traits::supertraits(tcx, ty::Binder::bind(trait_ref)),
18571908
"Self",
18581909
assoc_ident,
1859-
span
1910+
span,
1911+
None,
18601912
)?
18611913
}
18621914
(&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) {

0 commit comments

Comments
 (0)