about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_error_codes/error_codes.rs3
-rw-r--r--src/librustc_error_codes/error_codes/E0222.md51
-rw-r--r--src/librustc_passes/ast_validation.rs1
-rw-r--r--src/librustc_typeck/astconv.rs118
-rw-r--r--src/test/ui/associated-type/associated-type-projection-from-multiple-supertraits.rs4
-rw-r--r--src/test/ui/associated-type/associated-type-projection-from-multiple-supertraits.stderr46
-rw-r--r--src/test/ui/where-clauses/where-equality-constraints.stderr4
7 files changed, 167 insertions, 60 deletions
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<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>
+{}
+```
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<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
                 // 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<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(", ");
                 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::<Vec<_>>()
                 .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 {
             &param_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<I>(
         &self,
         all_candidates: impl Fn() -> I,
         ty_param_name: &str,
         assoc_name: ast::Ident,
         span: Span,
+        is_equality: Option<String>,
     ) -> Result<ty::PolyTraitRef<'tcx>, ErrorReported>
     where
         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);
 
             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:BoxCar>(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<COLOR>(c: dyn BoxCar<Color=COLOR>) {
     //~^ 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) {
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<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`
   --> $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) {
    |                                ^^^^^^^^^^^^^^^^^^^^^
 
-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<COLOR>(c: dyn BoxCar<Color=COLOR>) {
    |                              ^^^^^^^^^^^^^^^^^^^ ambiguous associated type `Color`
    |
-help: use fully qualified syntax to disambiguate
-   |
-LL | fn dent_object<COLOR>(c: dyn <BoxCar as Box>::Color) {
-   |                              ^^^^^^^^^^^^^^^^^^^^^^
-help: use fully qualified syntax to disambiguate
-   |
-LL | fn dent_object<COLOR>(c: dyn <BoxCar as Vehicle>::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<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`
-  --> $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: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
-  --> $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<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
 
-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