Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 67278cd

Browse files
authoredDec 14, 2024
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: https://github.com/rust-lang/rust/blob/1fc691e6ddc24506b5234d586a5c084eb767f1ad/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: #133392 (comment) 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.
2 parents ecfa09a + 43e2fd5 commit 67278cd

10 files changed

+184
-163
lines changed
 

‎compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs

+28-24
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
1+
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
22
use rustc_errors::codes::*;
33
use rustc_errors::struct_span_code_err;
44
use rustc_hir as hir;
55
use rustc_hir::def::{DefKind, Res};
6-
use rustc_hir::def_id::DefId;
76
use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS;
87
use rustc_middle::span_bug;
98
use rustc_middle::ty::fold::BottomUpFolder;
109
use rustc_middle::ty::{
1110
self, DynKind, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable,
1211
TypeVisitableExt, Upcast,
1312
};
14-
use rustc_span::{ErrorGuaranteed, Span};
13+
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
1514
use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility;
1615
use rustc_trait_selection::traits::{self, hir_ty_lowering_dyn_compatibility_violations};
1716
use rustc_type_ir::elaborate::ClauseWithSupertraitSpan;
@@ -128,8 +127,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
128127
}
129128
}
130129

131-
let mut associated_types: FxIndexMap<Span, FxIndexSet<DefId>> = FxIndexMap::default();
130+
let mut needed_associated_types = FxIndexSet::default();
132131

132+
let principal_span = regular_traits.first().map_or(DUMMY_SP, |info| info.bottom().1);
133133
let regular_traits_refs_spans = trait_bounds
134134
.into_iter()
135135
.filter(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id()));
@@ -145,13 +145,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
145145
let bound_predicate = pred.kind();
146146
match bound_predicate.skip_binder() {
147147
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
148-
let pred = bound_predicate.rebind(pred);
149-
associated_types.entry(original_span).or_default().extend(
150-
tcx.associated_items(pred.def_id())
148+
// FIXME(negative_bounds): Handle this correctly...
149+
let trait_ref =
150+
tcx.anonymize_bound_vars(bound_predicate.rebind(pred.trait_ref));
151+
needed_associated_types.extend(
152+
tcx.associated_items(trait_ref.def_id())
151153
.in_definition_order()
152154
.filter(|item| item.kind == ty::AssocKind::Type)
153155
.filter(|item| !item.is_impl_trait_in_trait())
154-
.map(|item| item.def_id),
156+
// If the associated type has a `where Self: Sized` bound,
157+
// we do not need to constrain the associated type.
158+
.filter(|item| !tcx.generics_require_sized_self(item.def_id))
159+
.map(|item| (item.def_id, trait_ref)),
155160
);
156161
}
157162
ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred)) => {
@@ -201,26 +206,25 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
201206
// So every `Projection` clause is an `Assoc = Foo` bound. `associated_types` contains all associated
202207
// types's `DefId`, so the following loop removes all the `DefIds` of the associated types that have a
203208
// corresponding `Projection` clause
204-
for def_ids in associated_types.values_mut() {
205-
for (projection_bound, span) in &projection_bounds {
206-
let def_id = projection_bound.item_def_id();
207-
def_ids.swap_remove(&def_id);
208-
if tcx.generics_require_sized_self(def_id) {
209-
tcx.emit_node_span_lint(
210-
UNUSED_ASSOCIATED_TYPE_BOUNDS,
211-
hir_id,
212-
*span,
213-
crate::errors::UnusedAssociatedTypeBounds { span: *span },
214-
);
215-
}
209+
for (projection_bound, span) in &projection_bounds {
210+
let def_id = projection_bound.item_def_id();
211+
let trait_ref = tcx.anonymize_bound_vars(
212+
projection_bound.map_bound(|p| p.projection_term.trait_ref(tcx)),
213+
);
214+
needed_associated_types.swap_remove(&(def_id, trait_ref));
215+
if tcx.generics_require_sized_self(def_id) {
216+
tcx.emit_node_span_lint(
217+
UNUSED_ASSOCIATED_TYPE_BOUNDS,
218+
hir_id,
219+
*span,
220+
crate::errors::UnusedAssociatedTypeBounds { span: *span },
221+
);
216222
}
217-
// If the associated type has a `where Self: Sized` bound, we do not need to constrain the associated
218-
// type in the `dyn Trait`.
219-
def_ids.retain(|def_id| !tcx.generics_require_sized_self(def_id));
220223
}
221224

222225
if let Err(guar) = self.check_for_required_assoc_tys(
223-
associated_types,
226+
principal_span,
227+
needed_associated_types,
224228
potential_assoc_types,
225229
hir_trait_bounds,
226230
) {

‎compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs

+117-127
Original file line numberDiff line numberDiff line change
@@ -721,51 +721,42 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
721721
/// emit a generic note suggesting using a `where` clause to constraint instead.
722722
pub(crate) fn check_for_required_assoc_tys(
723723
&self,
724-
associated_types: FxIndexMap<Span, FxIndexSet<DefId>>,
724+
principal_span: Span,
725+
missing_assoc_types: FxIndexSet<(DefId, ty::PolyTraitRef<'tcx>)>,
725726
potential_assoc_types: Vec<usize>,
726727
trait_bounds: &[hir::PolyTraitRef<'_>],
727728
) -> Result<(), ErrorGuaranteed> {
728-
if associated_types.values().all(|v| v.is_empty()) {
729+
if missing_assoc_types.is_empty() {
729730
return Ok(());
730731
}
731732

732733
let tcx = self.tcx();
733-
// FIXME: Marked `mut` so that we can replace the spans further below with a more
734-
// appropriate one, but this should be handled earlier in the span assignment.
735-
let associated_types: FxIndexMap<Span, Vec<_>> = associated_types
734+
// FIXME: This logic needs some more care w.r.t handling of conflicts
735+
let missing_assoc_types: Vec<_> = missing_assoc_types
736736
.into_iter()
737-
.map(|(span, def_ids)| {
738-
(span, def_ids.into_iter().map(|did| tcx.associated_item(did)).collect())
739-
})
737+
.map(|(def_id, trait_ref)| (tcx.associated_item(def_id), trait_ref))
740738
.collect();
741-
let mut names: FxIndexMap<String, Vec<Symbol>> = Default::default();
739+
let mut names: FxIndexMap<_, Vec<Symbol>> = Default::default();
742740
let mut names_len = 0;
743741

744742
// Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and
745743
// `issue-22560.rs`.
746-
let mut trait_bound_spans: Vec<Span> = vec![];
747744
let mut dyn_compatibility_violations = Ok(());
748-
for (span, items) in &associated_types {
749-
if !items.is_empty() {
750-
trait_bound_spans.push(*span);
751-
}
752-
for assoc_item in items {
753-
let trait_def_id = assoc_item.container_id(tcx);
754-
names.entry(tcx.def_path_str(trait_def_id)).or_default().push(assoc_item.name);
755-
names_len += 1;
756-
757-
let violations =
758-
dyn_compatibility_violations_for_assoc_item(tcx, trait_def_id, *assoc_item);
759-
if !violations.is_empty() {
760-
dyn_compatibility_violations = Err(report_dyn_incompatibility(
761-
tcx,
762-
*span,
763-
None,
764-
trait_def_id,
765-
&violations,
766-
)
767-
.emit());
768-
}
745+
for (assoc_item, trait_ref) in &missing_assoc_types {
746+
names.entry(trait_ref).or_default().push(assoc_item.name);
747+
names_len += 1;
748+
749+
let violations =
750+
dyn_compatibility_violations_for_assoc_item(tcx, trait_ref.def_id(), *assoc_item);
751+
if !violations.is_empty() {
752+
dyn_compatibility_violations = Err(report_dyn_incompatibility(
753+
tcx,
754+
principal_span,
755+
None,
756+
trait_ref.def_id(),
757+
&violations,
758+
)
759+
.emit());
769760
}
770761
}
771762

@@ -813,6 +804,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
813804
.into_iter()
814805
.map(|(trait_, mut assocs)| {
815806
assocs.sort();
807+
let trait_ = trait_.print_trait_sugared();
816808
format!("{} in `{trait_}`", match &assocs[..] {
817809
[] => String::new(),
818810
[only] => format!("`{only}`"),
@@ -826,10 +818,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
826818
names.sort();
827819
let names = names.join(", ");
828820

829-
trait_bound_spans.sort();
830821
let mut err = struct_span_code_err!(
831822
self.dcx(),
832-
trait_bound_spans,
823+
principal_span,
833824
E0191,
834825
"the value of the associated type{} {} must be specified",
835826
pluralize!(names_len),
@@ -839,81 +830,83 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
839830
let mut types_count = 0;
840831
let mut where_constraints = vec![];
841832
let mut already_has_generics_args_suggestion = false;
842-
for (span, assoc_items) in &associated_types {
843-
let mut names: UnordMap<_, usize> = Default::default();
844-
for item in assoc_items {
845-
types_count += 1;
846-
*names.entry(item.name).or_insert(0) += 1;
847-
}
848-
let mut dupes = false;
849-
let mut shadows = false;
850-
for item in assoc_items {
851-
let prefix = if names[&item.name] > 1 {
852-
let trait_def_id = item.container_id(tcx);
853-
dupes = true;
854-
format!("{}::", tcx.def_path_str(trait_def_id))
855-
} else if bound_names.get(&item.name).is_some_and(|x| x != &item) {
856-
let trait_def_id = item.container_id(tcx);
857-
shadows = true;
858-
format!("{}::", tcx.def_path_str(trait_def_id))
859-
} else {
860-
String::new()
861-
};
862833

863-
let mut is_shadowed = false;
864-
865-
if let Some(assoc_item) = bound_names.get(&item.name)
866-
&& assoc_item != &item
867-
{
868-
is_shadowed = true;
834+
let mut names: UnordMap<_, usize> = Default::default();
835+
for (item, _) in &missing_assoc_types {
836+
types_count += 1;
837+
*names.entry(item.name).or_insert(0) += 1;
838+
}
839+
let mut dupes = false;
840+
let mut shadows = false;
841+
for (item, trait_ref) in &missing_assoc_types {
842+
let prefix = if names[&item.name] > 1 {
843+
let trait_def_id = trait_ref.def_id();
844+
dupes = true;
845+
format!("{}::", tcx.def_path_str(trait_def_id))
846+
} else if bound_names.get(&item.name).is_some_and(|x| *x != item) {
847+
let trait_def_id = trait_ref.def_id();
848+
shadows = true;
849+
format!("{}::", tcx.def_path_str(trait_def_id))
850+
} else {
851+
String::new()
852+
};
869853

870-
let rename_message =
871-
if assoc_item.def_id.is_local() { ", consider renaming it" } else { "" };
872-
err.span_label(
873-
tcx.def_span(assoc_item.def_id),
874-
format!("`{}{}` shadowed here{}", prefix, item.name, rename_message),
875-
);
876-
}
854+
let mut is_shadowed = false;
877855

878-
let rename_message = if is_shadowed { ", consider renaming it" } else { "" };
856+
if let Some(assoc_item) = bound_names.get(&item.name)
857+
&& *assoc_item != item
858+
{
859+
is_shadowed = true;
879860

880-
if let Some(sp) = tcx.hir().span_if_local(item.def_id) {
881-
err.span_label(
882-
sp,
883-
format!("`{}{}` defined here{}", prefix, item.name, rename_message),
884-
);
885-
}
861+
let rename_message =
862+
if assoc_item.def_id.is_local() { ", consider renaming it" } else { "" };
863+
err.span_label(
864+
tcx.def_span(assoc_item.def_id),
865+
format!("`{}{}` shadowed here{}", prefix, item.name, rename_message),
866+
);
886867
}
887-
if potential_assoc_types.len() == assoc_items.len() {
888-
// When the amount of missing associated types equals the number of
889-
// extra type arguments present. A suggesting to replace the generic args with
890-
// associated types is already emitted.
891-
already_has_generics_args_suggestion = true;
892-
} else if let (Ok(snippet), false, false) =
893-
(tcx.sess.source_map().span_to_snippet(*span), dupes, shadows)
894-
{
895-
let types: Vec<_> =
896-
assoc_items.iter().map(|item| format!("{} = Type", item.name)).collect();
897-
let code = if snippet.ends_with('>') {
898-
// The user wrote `Trait<'a>` or similar and we don't have a type we can
899-
// suggest, but at least we can clue them to the correct syntax
900-
// `Trait<'a, Item = Type>` while accounting for the `<'a>` in the
901-
// suggestion.
902-
format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", "))
903-
} else if in_expr_or_pat {
904-
// The user wrote `Iterator`, so we don't have a type we can suggest, but at
905-
// least we can clue them to the correct syntax `Iterator::<Item = Type>`.
906-
format!("{}::<{}>", snippet, types.join(", "))
907-
} else {
908-
// The user wrote `Iterator`, so we don't have a type we can suggest, but at
909-
// least we can clue them to the correct syntax `Iterator<Item = Type>`.
910-
format!("{}<{}>", snippet, types.join(", "))
911-
};
912-
suggestions.push((*span, code));
913-
} else if dupes {
914-
where_constraints.push(*span);
868+
869+
let rename_message = if is_shadowed { ", consider renaming it" } else { "" };
870+
871+
if let Some(sp) = tcx.hir().span_if_local(item.def_id) {
872+
err.span_label(
873+
sp,
874+
format!("`{}{}` defined here{}", prefix, item.name, rename_message),
875+
);
915876
}
916877
}
878+
if potential_assoc_types.len() == missing_assoc_types.len() {
879+
// When the amount of missing associated types equals the number of
880+
// extra type arguments present. A suggesting to replace the generic args with
881+
// associated types is already emitted.
882+
already_has_generics_args_suggestion = true;
883+
} else if let (Ok(snippet), false, false) =
884+
(tcx.sess.source_map().span_to_snippet(principal_span), dupes, shadows)
885+
{
886+
let types: Vec<_> = missing_assoc_types
887+
.iter()
888+
.map(|(item, _)| format!("{} = Type", item.name))
889+
.collect();
890+
let code = if snippet.ends_with('>') {
891+
// The user wrote `Trait<'a>` or similar and we don't have a type we can
892+
// suggest, but at least we can clue them to the correct syntax
893+
// `Trait<'a, Item = Type>` while accounting for the `<'a>` in the
894+
// suggestion.
895+
format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", "))
896+
} else if in_expr_or_pat {
897+
// The user wrote `Iterator`, so we don't have a type we can suggest, but at
898+
// least we can clue them to the correct syntax `Iterator::<Item = Type>`.
899+
format!("{}::<{}>", snippet, types.join(", "))
900+
} else {
901+
// The user wrote `Iterator`, so we don't have a type we can suggest, but at
902+
// least we can clue them to the correct syntax `Iterator<Item = Type>`.
903+
format!("{}<{}>", snippet, types.join(", "))
904+
};
905+
suggestions.push((principal_span, code));
906+
} else if dupes {
907+
where_constraints.push(principal_span);
908+
}
909+
917910
let where_msg = "consider introducing a new type parameter, adding `where` constraints \
918911
using the fully-qualified path to the associated types";
919912
if !where_constraints.is_empty() && suggestions.is_empty() {
@@ -924,32 +917,29 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
924917
}
925918
if suggestions.len() != 1 || already_has_generics_args_suggestion {
926919
// We don't need this label if there's an inline suggestion, show otherwise.
927-
for (span, assoc_items) in &associated_types {
928-
let mut names: FxIndexMap<_, usize> = FxIndexMap::default();
929-
for item in assoc_items {
930-
types_count += 1;
931-
*names.entry(item.name).or_insert(0) += 1;
932-
}
933-
let mut label = vec![];
934-
for item in assoc_items {
935-
let postfix = if names[&item.name] > 1 {
936-
let trait_def_id = item.container_id(tcx);
937-
format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id))
938-
} else {
939-
String::new()
940-
};
941-
label.push(format!("`{}`{}", item.name, postfix));
942-
}
943-
if !label.is_empty() {
944-
err.span_label(
945-
*span,
946-
format!(
947-
"associated type{} {} must be specified",
948-
pluralize!(label.len()),
949-
label.join(", "),
950-
),
951-
);
952-
}
920+
let mut names: FxIndexMap<_, usize> = FxIndexMap::default();
921+
for (item, _) in &missing_assoc_types {
922+
types_count += 1;
923+
*names.entry(item.name).or_insert(0) += 1;
924+
}
925+
let mut label = vec![];
926+
for (item, trait_ref) in &missing_assoc_types {
927+
let postfix = if names[&item.name] > 1 {
928+
format!(" (from trait `{}`)", trait_ref.print_trait_sugared())
929+
} else {
930+
String::new()
931+
};
932+
label.push(format!("`{}`{}", item.name, postfix));
933+
}
934+
if !label.is_empty() {
935+
err.span_label(
936+
principal_span,
937+
format!(
938+
"associated type{} {} must be specified",
939+
pluralize!(label.len()),
940+
label.join(", "),
941+
),
942+
);
953943
}
954944
}
955945
suggestions.sort_by_key(|&(span, _)| span);
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Failed to load comments.