Rollup merge of #133392 - compiler-errors:object-sup, r=lcnr
Fix ICE when multiple supertrait substitutions need assoc but only one is provided
Dyn traits must have all of their associated types constrained either by:
1. writing them in the dyn trait itself as an associated type bound, like `dyn Iterator<Item = u32>`,
2. A supertrait bound, like `trait ConstrainedIterator: Iterator<Item = u32> {}`, then you may write `dyn ConstrainedIterator` which doesn't need to mention `Item`.
However, the object type lowering code did not consider the fact that there may be multiple supertraits with different substitutions, so it just used the associated type's *def id* as a key for keeping track of which associated types are missing:
1fc691e6dd/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs (L131)
This means that we can have missing associated types when there are mutliple supertraits with different substitutions and only one of them is constrained, like:
```rust
trait Sup<T> {
type Assoc: Default;
}
impl<T: Default> Sup<T> for () {
type Assoc = T;
}
impl<T: Default, U: Default> Dyn<T, U> for () {}
trait Dyn<A, B>: Sup<A, Assoc = A> + Sup<B> {}
```
The above example allows you to name `<dyn Dyn<i32, u32> as Sup<u32>>::Assoc` even though it is not possible to project since it's neither constrained by a manually written projection bound or a supertrait bound. This successfully type-checks, but leads to a codegen ICE since we are not able to project the associated type.
This PR fixes the validation for checking that a dyn trait mentions all of its associated type bounds. This is theoretically a breaking change, since you could technically use that `dyn Dyn<A, B>` type mentionedin the example above without actually *projecting* to the bad associated type, but I don't expect it to ever be relevant to a user since it's almost certainly a bug. This is corroborated with the crater results[^crater], which show no failures[^unknown].
Crater: https://github.com/rust-lang/rust/pull/133392#issuecomment-2508769703
Fixes #133388
[^crater]: I cratered this originally with #133397, which is a PR that is stacked on top, then re-ran crater with just the failures from that PR.
[^unknown]: If you look at the crater results, it shows all of the passes as "unknown". I believe this is a crater bug, since looking at the results manually shows them as passes.
This commit is contained in:
commit
67278cd9f4
10 changed files with 184 additions and 163 deletions
|
@ -1,9 +1,8 @@
|
||||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
|
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
|
||||||
use rustc_errors::codes::*;
|
use rustc_errors::codes::*;
|
||||||
use rustc_errors::struct_span_code_err;
|
use rustc_errors::struct_span_code_err;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::{DefKind, Res};
|
use rustc_hir::def::{DefKind, Res};
|
||||||
use rustc_hir::def_id::DefId;
|
|
||||||
use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS;
|
use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS;
|
||||||
use rustc_middle::span_bug;
|
use rustc_middle::span_bug;
|
||||||
use rustc_middle::ty::fold::BottomUpFolder;
|
use rustc_middle::ty::fold::BottomUpFolder;
|
||||||
|
@ -11,7 +10,7 @@ use rustc_middle::ty::{
|
||||||
self, DynKind, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable,
|
self, DynKind, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable,
|
||||||
TypeVisitableExt, Upcast,
|
TypeVisitableExt, Upcast,
|
||||||
};
|
};
|
||||||
use rustc_span::{ErrorGuaranteed, Span};
|
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
|
||||||
use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility;
|
use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility;
|
||||||
use rustc_trait_selection::traits::{self, hir_ty_lowering_dyn_compatibility_violations};
|
use rustc_trait_selection::traits::{self, hir_ty_lowering_dyn_compatibility_violations};
|
||||||
use rustc_type_ir::elaborate::ClauseWithSupertraitSpan;
|
use rustc_type_ir::elaborate::ClauseWithSupertraitSpan;
|
||||||
|
@ -128,8 +127,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut associated_types: FxIndexMap<Span, FxIndexSet<DefId>> = FxIndexMap::default();
|
let mut needed_associated_types = FxIndexSet::default();
|
||||||
|
|
||||||
|
let principal_span = regular_traits.first().map_or(DUMMY_SP, |info| info.bottom().1);
|
||||||
let regular_traits_refs_spans = trait_bounds
|
let regular_traits_refs_spans = trait_bounds
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id()));
|
.filter(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id()));
|
||||||
|
@ -145,13 +145,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||||
let bound_predicate = pred.kind();
|
let bound_predicate = pred.kind();
|
||||||
match bound_predicate.skip_binder() {
|
match bound_predicate.skip_binder() {
|
||||||
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
|
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
|
||||||
let pred = bound_predicate.rebind(pred);
|
// FIXME(negative_bounds): Handle this correctly...
|
||||||
associated_types.entry(original_span).or_default().extend(
|
let trait_ref =
|
||||||
tcx.associated_items(pred.def_id())
|
tcx.anonymize_bound_vars(bound_predicate.rebind(pred.trait_ref));
|
||||||
|
needed_associated_types.extend(
|
||||||
|
tcx.associated_items(trait_ref.def_id())
|
||||||
.in_definition_order()
|
.in_definition_order()
|
||||||
.filter(|item| item.kind == ty::AssocKind::Type)
|
.filter(|item| item.kind == ty::AssocKind::Type)
|
||||||
.filter(|item| !item.is_impl_trait_in_trait())
|
.filter(|item| !item.is_impl_trait_in_trait())
|
||||||
.map(|item| item.def_id),
|
// If the associated type has a `where Self: Sized` bound,
|
||||||
|
// we do not need to constrain the associated type.
|
||||||
|
.filter(|item| !tcx.generics_require_sized_self(item.def_id))
|
||||||
|
.map(|item| (item.def_id, trait_ref)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred)) => {
|
ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred)) => {
|
||||||
|
@ -201,26 +206,25 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||||
// So every `Projection` clause is an `Assoc = Foo` bound. `associated_types` contains all associated
|
// So every `Projection` clause is an `Assoc = Foo` bound. `associated_types` contains all associated
|
||||||
// types's `DefId`, so the following loop removes all the `DefIds` of the associated types that have a
|
// types's `DefId`, so the following loop removes all the `DefIds` of the associated types that have a
|
||||||
// corresponding `Projection` clause
|
// corresponding `Projection` clause
|
||||||
for def_ids in associated_types.values_mut() {
|
for (projection_bound, span) in &projection_bounds {
|
||||||
for (projection_bound, span) in &projection_bounds {
|
let def_id = projection_bound.item_def_id();
|
||||||
let def_id = projection_bound.item_def_id();
|
let trait_ref = tcx.anonymize_bound_vars(
|
||||||
def_ids.swap_remove(&def_id);
|
projection_bound.map_bound(|p| p.projection_term.trait_ref(tcx)),
|
||||||
if tcx.generics_require_sized_self(def_id) {
|
);
|
||||||
tcx.emit_node_span_lint(
|
needed_associated_types.swap_remove(&(def_id, trait_ref));
|
||||||
UNUSED_ASSOCIATED_TYPE_BOUNDS,
|
if tcx.generics_require_sized_self(def_id) {
|
||||||
hir_id,
|
tcx.emit_node_span_lint(
|
||||||
*span,
|
UNUSED_ASSOCIATED_TYPE_BOUNDS,
|
||||||
crate::errors::UnusedAssociatedTypeBounds { span: *span },
|
hir_id,
|
||||||
);
|
*span,
|
||||||
}
|
crate::errors::UnusedAssociatedTypeBounds { span: *span },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// If the associated type has a `where Self: Sized` bound, we do not need to constrain the associated
|
|
||||||
// type in the `dyn Trait`.
|
|
||||||
def_ids.retain(|def_id| !tcx.generics_require_sized_self(def_id));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(guar) = self.check_for_required_assoc_tys(
|
if let Err(guar) = self.check_for_required_assoc_tys(
|
||||||
associated_types,
|
principal_span,
|
||||||
|
needed_associated_types,
|
||||||
potential_assoc_types,
|
potential_assoc_types,
|
||||||
hir_trait_bounds,
|
hir_trait_bounds,
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -721,51 +721,42 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||||
/// emit a generic note suggesting using a `where` clause to constraint instead.
|
/// emit a generic note suggesting using a `where` clause to constraint instead.
|
||||||
pub(crate) fn check_for_required_assoc_tys(
|
pub(crate) fn check_for_required_assoc_tys(
|
||||||
&self,
|
&self,
|
||||||
associated_types: FxIndexMap<Span, FxIndexSet<DefId>>,
|
principal_span: Span,
|
||||||
|
missing_assoc_types: FxIndexSet<(DefId, ty::PolyTraitRef<'tcx>)>,
|
||||||
potential_assoc_types: Vec<usize>,
|
potential_assoc_types: Vec<usize>,
|
||||||
trait_bounds: &[hir::PolyTraitRef<'_>],
|
trait_bounds: &[hir::PolyTraitRef<'_>],
|
||||||
) -> Result<(), ErrorGuaranteed> {
|
) -> Result<(), ErrorGuaranteed> {
|
||||||
if associated_types.values().all(|v| v.is_empty()) {
|
if missing_assoc_types.is_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let tcx = self.tcx();
|
let tcx = self.tcx();
|
||||||
// FIXME: Marked `mut` so that we can replace the spans further below with a more
|
// FIXME: This logic needs some more care w.r.t handling of conflicts
|
||||||
// appropriate one, but this should be handled earlier in the span assignment.
|
let missing_assoc_types: Vec<_> = missing_assoc_types
|
||||||
let associated_types: FxIndexMap<Span, Vec<_>> = associated_types
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(span, def_ids)| {
|
.map(|(def_id, trait_ref)| (tcx.associated_item(def_id), trait_ref))
|
||||||
(span, def_ids.into_iter().map(|did| tcx.associated_item(did)).collect())
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
let mut names: FxIndexMap<String, Vec<Symbol>> = Default::default();
|
let mut names: FxIndexMap<_, Vec<Symbol>> = Default::default();
|
||||||
let mut names_len = 0;
|
let mut names_len = 0;
|
||||||
|
|
||||||
// Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and
|
// Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and
|
||||||
// `issue-22560.rs`.
|
// `issue-22560.rs`.
|
||||||
let mut trait_bound_spans: Vec<Span> = vec![];
|
|
||||||
let mut dyn_compatibility_violations = Ok(());
|
let mut dyn_compatibility_violations = Ok(());
|
||||||
for (span, items) in &associated_types {
|
for (assoc_item, trait_ref) in &missing_assoc_types {
|
||||||
if !items.is_empty() {
|
names.entry(trait_ref).or_default().push(assoc_item.name);
|
||||||
trait_bound_spans.push(*span);
|
names_len += 1;
|
||||||
}
|
|
||||||
for assoc_item in items {
|
|
||||||
let trait_def_id = assoc_item.container_id(tcx);
|
|
||||||
names.entry(tcx.def_path_str(trait_def_id)).or_default().push(assoc_item.name);
|
|
||||||
names_len += 1;
|
|
||||||
|
|
||||||
let violations =
|
let violations =
|
||||||
dyn_compatibility_violations_for_assoc_item(tcx, trait_def_id, *assoc_item);
|
dyn_compatibility_violations_for_assoc_item(tcx, trait_ref.def_id(), *assoc_item);
|
||||||
if !violations.is_empty() {
|
if !violations.is_empty() {
|
||||||
dyn_compatibility_violations = Err(report_dyn_incompatibility(
|
dyn_compatibility_violations = Err(report_dyn_incompatibility(
|
||||||
tcx,
|
tcx,
|
||||||
*span,
|
principal_span,
|
||||||
None,
|
None,
|
||||||
trait_def_id,
|
trait_ref.def_id(),
|
||||||
&violations,
|
&violations,
|
||||||
)
|
)
|
||||||
.emit());
|
.emit());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -813,6 +804,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(trait_, mut assocs)| {
|
.map(|(trait_, mut assocs)| {
|
||||||
assocs.sort();
|
assocs.sort();
|
||||||
|
let trait_ = trait_.print_trait_sugared();
|
||||||
format!("{} in `{trait_}`", match &assocs[..] {
|
format!("{} in `{trait_}`", match &assocs[..] {
|
||||||
[] => String::new(),
|
[] => String::new(),
|
||||||
[only] => format!("`{only}`"),
|
[only] => format!("`{only}`"),
|
||||||
|
@ -826,10 +818,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||||
names.sort();
|
names.sort();
|
||||||
let names = names.join(", ");
|
let names = names.join(", ");
|
||||||
|
|
||||||
trait_bound_spans.sort();
|
|
||||||
let mut err = struct_span_code_err!(
|
let mut err = struct_span_code_err!(
|
||||||
self.dcx(),
|
self.dcx(),
|
||||||
trait_bound_spans,
|
principal_span,
|
||||||
E0191,
|
E0191,
|
||||||
"the value of the associated type{} {} must be specified",
|
"the value of the associated type{} {} must be specified",
|
||||||
pluralize!(names_len),
|
pluralize!(names_len),
|
||||||
|
@ -839,81 +830,83 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||||
let mut types_count = 0;
|
let mut types_count = 0;
|
||||||
let mut where_constraints = vec![];
|
let mut where_constraints = vec![];
|
||||||
let mut already_has_generics_args_suggestion = false;
|
let mut already_has_generics_args_suggestion = false;
|
||||||
for (span, assoc_items) in &associated_types {
|
|
||||||
let mut names: UnordMap<_, usize> = Default::default();
|
|
||||||
for item in assoc_items {
|
|
||||||
types_count += 1;
|
|
||||||
*names.entry(item.name).or_insert(0) += 1;
|
|
||||||
}
|
|
||||||
let mut dupes = false;
|
|
||||||
let mut shadows = false;
|
|
||||||
for item in assoc_items {
|
|
||||||
let prefix = if names[&item.name] > 1 {
|
|
||||||
let trait_def_id = item.container_id(tcx);
|
|
||||||
dupes = true;
|
|
||||||
format!("{}::", tcx.def_path_str(trait_def_id))
|
|
||||||
} else if bound_names.get(&item.name).is_some_and(|x| x != &item) {
|
|
||||||
let trait_def_id = item.container_id(tcx);
|
|
||||||
shadows = true;
|
|
||||||
format!("{}::", tcx.def_path_str(trait_def_id))
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut is_shadowed = false;
|
let mut names: UnordMap<_, usize> = Default::default();
|
||||||
|
for (item, _) in &missing_assoc_types {
|
||||||
|
types_count += 1;
|
||||||
|
*names.entry(item.name).or_insert(0) += 1;
|
||||||
|
}
|
||||||
|
let mut dupes = false;
|
||||||
|
let mut shadows = false;
|
||||||
|
for (item, trait_ref) in &missing_assoc_types {
|
||||||
|
let prefix = if names[&item.name] > 1 {
|
||||||
|
let trait_def_id = trait_ref.def_id();
|
||||||
|
dupes = true;
|
||||||
|
format!("{}::", tcx.def_path_str(trait_def_id))
|
||||||
|
} else if bound_names.get(&item.name).is_some_and(|x| *x != item) {
|
||||||
|
let trait_def_id = trait_ref.def_id();
|
||||||
|
shadows = true;
|
||||||
|
format!("{}::", tcx.def_path_str(trait_def_id))
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(assoc_item) = bound_names.get(&item.name)
|
let mut is_shadowed = false;
|
||||||
&& assoc_item != &item
|
|
||||||
{
|
|
||||||
is_shadowed = true;
|
|
||||||
|
|
||||||
let rename_message =
|
if let Some(assoc_item) = bound_names.get(&item.name)
|
||||||
if assoc_item.def_id.is_local() { ", consider renaming it" } else { "" };
|
&& *assoc_item != item
|
||||||
err.span_label(
|
|
||||||
tcx.def_span(assoc_item.def_id),
|
|
||||||
format!("`{}{}` shadowed here{}", prefix, item.name, rename_message),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let rename_message = if is_shadowed { ", consider renaming it" } else { "" };
|
|
||||||
|
|
||||||
if let Some(sp) = tcx.hir().span_if_local(item.def_id) {
|
|
||||||
err.span_label(
|
|
||||||
sp,
|
|
||||||
format!("`{}{}` defined here{}", prefix, item.name, rename_message),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if potential_assoc_types.len() == assoc_items.len() {
|
|
||||||
// When the amount of missing associated types equals the number of
|
|
||||||
// extra type arguments present. A suggesting to replace the generic args with
|
|
||||||
// associated types is already emitted.
|
|
||||||
already_has_generics_args_suggestion = true;
|
|
||||||
} else if let (Ok(snippet), false, false) =
|
|
||||||
(tcx.sess.source_map().span_to_snippet(*span), dupes, shadows)
|
|
||||||
{
|
{
|
||||||
let types: Vec<_> =
|
is_shadowed = true;
|
||||||
assoc_items.iter().map(|item| format!("{} = Type", item.name)).collect();
|
|
||||||
let code = if snippet.ends_with('>') {
|
let rename_message =
|
||||||
// The user wrote `Trait<'a>` or similar and we don't have a type we can
|
if assoc_item.def_id.is_local() { ", consider renaming it" } else { "" };
|
||||||
// suggest, but at least we can clue them to the correct syntax
|
err.span_label(
|
||||||
// `Trait<'a, Item = Type>` while accounting for the `<'a>` in the
|
tcx.def_span(assoc_item.def_id),
|
||||||
// suggestion.
|
format!("`{}{}` shadowed here{}", prefix, item.name, rename_message),
|
||||||
format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", "))
|
);
|
||||||
} else if in_expr_or_pat {
|
}
|
||||||
// The user wrote `Iterator`, so we don't have a type we can suggest, but at
|
|
||||||
// least we can clue them to the correct syntax `Iterator::<Item = Type>`.
|
let rename_message = if is_shadowed { ", consider renaming it" } else { "" };
|
||||||
format!("{}::<{}>", snippet, types.join(", "))
|
|
||||||
} else {
|
if let Some(sp) = tcx.hir().span_if_local(item.def_id) {
|
||||||
// The user wrote `Iterator`, so we don't have a type we can suggest, but at
|
err.span_label(
|
||||||
// least we can clue them to the correct syntax `Iterator<Item = Type>`.
|
sp,
|
||||||
format!("{}<{}>", snippet, types.join(", "))
|
format!("`{}{}` defined here{}", prefix, item.name, rename_message),
|
||||||
};
|
);
|
||||||
suggestions.push((*span, code));
|
|
||||||
} else if dupes {
|
|
||||||
where_constraints.push(*span);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if potential_assoc_types.len() == missing_assoc_types.len() {
|
||||||
|
// When the amount of missing associated types equals the number of
|
||||||
|
// extra type arguments present. A suggesting to replace the generic args with
|
||||||
|
// associated types is already emitted.
|
||||||
|
already_has_generics_args_suggestion = true;
|
||||||
|
} else if let (Ok(snippet), false, false) =
|
||||||
|
(tcx.sess.source_map().span_to_snippet(principal_span), dupes, shadows)
|
||||||
|
{
|
||||||
|
let types: Vec<_> = missing_assoc_types
|
||||||
|
.iter()
|
||||||
|
.map(|(item, _)| format!("{} = Type", item.name))
|
||||||
|
.collect();
|
||||||
|
let code = if snippet.ends_with('>') {
|
||||||
|
// The user wrote `Trait<'a>` or similar and we don't have a type we can
|
||||||
|
// suggest, but at least we can clue them to the correct syntax
|
||||||
|
// `Trait<'a, Item = Type>` while accounting for the `<'a>` in the
|
||||||
|
// suggestion.
|
||||||
|
format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", "))
|
||||||
|
} else if in_expr_or_pat {
|
||||||
|
// The user wrote `Iterator`, so we don't have a type we can suggest, but at
|
||||||
|
// least we can clue them to the correct syntax `Iterator::<Item = Type>`.
|
||||||
|
format!("{}::<{}>", snippet, types.join(", "))
|
||||||
|
} else {
|
||||||
|
// The user wrote `Iterator`, so we don't have a type we can suggest, but at
|
||||||
|
// least we can clue them to the correct syntax `Iterator<Item = Type>`.
|
||||||
|
format!("{}<{}>", snippet, types.join(", "))
|
||||||
|
};
|
||||||
|
suggestions.push((principal_span, code));
|
||||||
|
} else if dupes {
|
||||||
|
where_constraints.push(principal_span);
|
||||||
|
}
|
||||||
|
|
||||||
let where_msg = "consider introducing a new type parameter, adding `where` constraints \
|
let where_msg = "consider introducing a new type parameter, adding `where` constraints \
|
||||||
using the fully-qualified path to the associated types";
|
using the fully-qualified path to the associated types";
|
||||||
if !where_constraints.is_empty() && suggestions.is_empty() {
|
if !where_constraints.is_empty() && suggestions.is_empty() {
|
||||||
|
@ -924,32 +917,29 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||||
}
|
}
|
||||||
if suggestions.len() != 1 || already_has_generics_args_suggestion {
|
if suggestions.len() != 1 || already_has_generics_args_suggestion {
|
||||||
// We don't need this label if there's an inline suggestion, show otherwise.
|
// We don't need this label if there's an inline suggestion, show otherwise.
|
||||||
for (span, assoc_items) in &associated_types {
|
let mut names: FxIndexMap<_, usize> = FxIndexMap::default();
|
||||||
let mut names: FxIndexMap<_, usize> = FxIndexMap::default();
|
for (item, _) in &missing_assoc_types {
|
||||||
for item in assoc_items {
|
types_count += 1;
|
||||||
types_count += 1;
|
*names.entry(item.name).or_insert(0) += 1;
|
||||||
*names.entry(item.name).or_insert(0) += 1;
|
}
|
||||||
}
|
let mut label = vec![];
|
||||||
let mut label = vec![];
|
for (item, trait_ref) in &missing_assoc_types {
|
||||||
for item in assoc_items {
|
let postfix = if names[&item.name] > 1 {
|
||||||
let postfix = if names[&item.name] > 1 {
|
format!(" (from trait `{}`)", trait_ref.print_trait_sugared())
|
||||||
let trait_def_id = item.container_id(tcx);
|
} else {
|
||||||
format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id))
|
String::new()
|
||||||
} else {
|
};
|
||||||
String::new()
|
label.push(format!("`{}`{}", item.name, postfix));
|
||||||
};
|
}
|
||||||
label.push(format!("`{}`{}", item.name, postfix));
|
if !label.is_empty() {
|
||||||
}
|
err.span_label(
|
||||||
if !label.is_empty() {
|
principal_span,
|
||||||
err.span_label(
|
format!(
|
||||||
*span,
|
"associated type{} {} must be specified",
|
||||||
format!(
|
pluralize!(label.len()),
|
||||||
"associated type{} {} must be specified",
|
label.join(", "),
|
||||||
pluralize!(label.len()),
|
),
|
||||||
label.join(", "),
|
);
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
suggestions.sort_by_key(|&(span, _)| span);
|
suggestions.sort_by_key(|&(span, _)| span);
|
||||||
|
|
|
@ -42,11 +42,11 @@ LL | type Bat<Rhs> = dyn Add<Rhs> + Sub<Rhs> + Fine<Rhs>;
|
||||||
= help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Add<Rhs> + Sub<Rhs> + Fine<Rhs> {}`
|
= help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Add<Rhs> + Sub<Rhs> + Fine<Rhs> {}`
|
||||||
= note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>
|
= note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>
|
||||||
|
|
||||||
error[E0191]: the value of the associated types `Output` in `Div`, `Output` in `Mul` must be specified
|
error[E0191]: the value of the associated types `Output` in `Div<Rhs>`, `Output` in `Mul<Rhs>` must be specified
|
||||||
--> $DIR/missing-associated-types.rs:20:21
|
--> $DIR/missing-associated-types.rs:20:21
|
||||||
|
|
|
|
||||||
LL | type Bal<Rhs> = dyn X<Rhs>;
|
LL | type Bal<Rhs> = dyn X<Rhs>;
|
||||||
| ^^^^^^ associated types `Output` (from trait `Div`), `Output` (from trait `Mul`) must be specified
|
| ^^^^^^ associated types `Output` (from trait `Div<Rhs>`), `Output` (from trait `Mul<Rhs>`) must be specified
|
||||||
|
|
|
|
||||||
= help: consider introducing a new type parameter, adding `where` constraints using the fully-qualified path to the associated types
|
= help: consider introducing a new type parameter, adding `where` constraints using the fully-qualified path to the associated types
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ trait Foo<T> {
|
||||||
trait Cake {}
|
trait Cake {}
|
||||||
impl Cake for () {}
|
impl Cake for () {}
|
||||||
|
|
||||||
fn foo(_: &dyn Foo<()>) {} //~ ERROR: the value of the associated type `Bar` in `Foo` must be specified
|
fn foo(_: &dyn Foo<()>) {} //~ ERROR: the value of the associated type `Bar` in `Foo<()>` must be specified
|
||||||
fn bar(_: &dyn Foo<i32>) {} //~ ERROR: the value of the associated type `Bar` in `Foo` must be specified
|
fn bar(_: &dyn Foo<i32>) {} //~ ERROR: the value of the associated type `Bar` in `Foo<i32>` must be specified
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
error[E0191]: the value of the associated type `Bar` in `Foo` must be specified
|
error[E0191]: the value of the associated type `Bar` in `Foo<()>` must be specified
|
||||||
--> $DIR/assoc_type_bounds.rs:10:16
|
--> $DIR/assoc_type_bounds.rs:10:16
|
||||||
|
|
|
|
||||||
LL | type Bar
|
LL | type Bar
|
||||||
|
@ -7,7 +7,7 @@ LL | type Bar
|
||||||
LL | fn foo(_: &dyn Foo<()>) {}
|
LL | fn foo(_: &dyn Foo<()>) {}
|
||||||
| ^^^^^^^ help: specify the associated type: `Foo<(), Bar = Type>`
|
| ^^^^^^^ help: specify the associated type: `Foo<(), Bar = Type>`
|
||||||
|
|
||||||
error[E0191]: the value of the associated type `Bar` in `Foo` must be specified
|
error[E0191]: the value of the associated type `Bar` in `Foo<i32>` must be specified
|
||||||
--> $DIR/assoc_type_bounds.rs:11:16
|
--> $DIR/assoc_type_bounds.rs:11:16
|
||||||
|
|
|
|
||||||
LL | type Bar
|
LL | type Bar
|
||||||
|
|
|
@ -7,7 +7,7 @@ trait Foo<T> {
|
||||||
trait Cake {}
|
trait Cake {}
|
||||||
impl Cake for () {}
|
impl Cake for () {}
|
||||||
|
|
||||||
fn foo(_: &dyn Foo<()>) {} //~ ERROR: the value of the associated type `Bar` in `Foo` must be specified
|
fn foo(_: &dyn Foo<()>) {} //~ ERROR: the value of the associated type `Bar` in `Foo<()>` must be specified
|
||||||
fn bar(_: &dyn Foo<i32>) {} //~ ERROR: the value of the associated type `Bar` in `Foo` must be specified
|
fn bar(_: &dyn Foo<i32>) {} //~ ERROR: the value of the associated type `Bar` in `Foo<i32>` must be specified
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
error[E0191]: the value of the associated type `Bar` in `Foo` must be specified
|
error[E0191]: the value of the associated type `Bar` in `Foo<()>` must be specified
|
||||||
--> $DIR/assoc_type_bounds2.rs:10:16
|
--> $DIR/assoc_type_bounds2.rs:10:16
|
||||||
|
|
|
|
||||||
LL | type Bar
|
LL | type Bar
|
||||||
|
@ -7,7 +7,7 @@ LL | type Bar
|
||||||
LL | fn foo(_: &dyn Foo<()>) {}
|
LL | fn foo(_: &dyn Foo<()>) {}
|
||||||
| ^^^^^^^ help: specify the associated type: `Foo<(), Bar = Type>`
|
| ^^^^^^^ help: specify the associated type: `Foo<(), Bar = Type>`
|
||||||
|
|
||||||
error[E0191]: the value of the associated type `Bar` in `Foo` must be specified
|
error[E0191]: the value of the associated type `Bar` in `Foo<i32>` must be specified
|
||||||
--> $DIR/assoc_type_bounds2.rs:11:16
|
--> $DIR/assoc_type_bounds2.rs:11:16
|
||||||
|
|
|
|
||||||
LL | type Bar
|
LL | type Bar
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
trait Sup<T> {
|
||||||
|
type Assoc: Default;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Default> Sup<T> for () {
|
||||||
|
type Assoc = T;
|
||||||
|
}
|
||||||
|
impl<T: Default, U: Default> Dyn<T, U> for () {}
|
||||||
|
|
||||||
|
trait Dyn<A, B>: Sup<A, Assoc = A> + Sup<B> {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let q: <dyn Dyn<i32, u32> as Sup<u32>>::Assoc = Default::default();
|
||||||
|
//~^ ERROR the value of the associated type `Assoc` in `Sup<u32>` must be specified
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
error[E0191]: the value of the associated type `Assoc` in `Sup<u32>` must be specified
|
||||||
|
--> $DIR/require-assoc-for-all-super-substs.rs:13:17
|
||||||
|
|
|
||||||
|
LL | type Assoc: Default;
|
||||||
|
| ------------------- `Assoc` defined here
|
||||||
|
...
|
||||||
|
LL | let q: <dyn Dyn<i32, u32> as Sup<u32>>::Assoc = Default::default();
|
||||||
|
| ^^^^^^^^^^^^^ help: specify the associated type: `Dyn<i32, u32, Assoc = Type>`
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0191`.
|
|
@ -12,7 +12,7 @@ help: if this is a dyn-compatible trait, use `dyn`
|
||||||
LL | let x: u8 = <dyn BitXor>::bitor(0 as u8, 0 as u8);
|
LL | let x: u8 = <dyn BitXor>::bitor(0 as u8, 0 as u8);
|
||||||
| ++++ +
|
| ++++ +
|
||||||
|
|
||||||
error[E0191]: the value of the associated type `Output` in `BitXor` must be specified
|
error[E0191]: the value of the associated type `Output` in `BitXor<_>` must be specified
|
||||||
--> $DIR/issue-28344.rs:4:17
|
--> $DIR/issue-28344.rs:4:17
|
||||||
|
|
|
|
||||||
LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
|
LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
|
||||||
|
@ -31,7 +31,7 @@ help: if this is a dyn-compatible trait, use `dyn`
|
||||||
LL | let g = <dyn BitXor>::bitor;
|
LL | let g = <dyn BitXor>::bitor;
|
||||||
| ++++ +
|
| ++++ +
|
||||||
|
|
||||||
error[E0191]: the value of the associated type `Output` in `BitXor` must be specified
|
error[E0191]: the value of the associated type `Output` in `BitXor<_>` must be specified
|
||||||
--> $DIR/issue-28344.rs:9:13
|
--> $DIR/issue-28344.rs:9:13
|
||||||
|
|
|
|
||||||
LL | let g = BitXor::bitor;
|
LL | let g = BitXor::bitor;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue