Skip to content

Commit

Permalink
Merge the impl trait in assoc type collector into the opaque type col…
Browse files Browse the repository at this point in the history
…lector and use a runtime switch instead
  • Loading branch information
oli-obk committed Mar 4, 2024
1 parent f5f11e1 commit 8364a06
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 82 deletions.
99 changes: 18 additions & 81 deletions compiler/rustc_ty_utils/src/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,20 @@ struct OpaqueTypeCollector<'tcx> {
seen: FxHashSet<LocalDefId>,

span: Option<Span>,

mode: CollectionMode,
}

enum CollectionMode {
/// For impl trait in assoc types we only permit collecting them from
/// associated types of the same impl block.
ImplTraitInAssocTypes,
TypeAliasImplTraitTransition,
}

impl<'tcx> OpaqueTypeCollector<'tcx> {
fn new(tcx: TyCtxt<'tcx>, item: LocalDefId) -> Self {
Self { tcx, opaques: Vec::new(), item, seen: Default::default(), span: None }
fn new(tcx: TyCtxt<'tcx>, item: LocalDefId, mode: CollectionMode) -> Self {
Self { tcx, opaques: Vec::new(), item, seen: Default::default(), span: None, mode }
}

fn span(&self) -> Span {
Expand Down Expand Up @@ -251,6 +260,9 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
}
}
ty::Adt(def, _) if def.did().is_local() => {
if let CollectionMode::ImplTraitInAssocTypes = self.mode {
return ControlFlow::Continue(());
}
if !self.seen.insert(def.did().expect_local()) {
return ControlFlow::Continue(());
}
Expand All @@ -275,89 +287,13 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
}
}

struct ImplTraitInAssocTypeCollector<'tcx>(OpaqueTypeCollector<'tcx>);

impl<'tcx> super::sig_types::SpannedTypeVisitor<'tcx> for ImplTraitInAssocTypeCollector<'tcx> {
#[instrument(skip(self), ret, level = "trace")]
fn visit(&mut self, span: Span, value: impl TypeVisitable<TyCtxt<'tcx>>) -> ControlFlow<!> {
let old = self.0.span;
self.0.span = Some(span);
value.visit_with(self);
self.0.span = old;

ControlFlow::Continue(())
}
}

impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInAssocTypeCollector<'tcx> {
#[instrument(skip(self), ret, level = "trace")]
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<!> {
t.super_visit_with(self)?;
match t.kind() {
ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => {
self.0.visit_opaque_ty(alias_ty);
}
ty::Alias(ty::Projection, alias_ty) => {
// This avoids having to do normalization of `Self::AssocTy` by only
// supporting the case of a method defining opaque types from assoc types
// in the same impl block.
let parent_trait_ref = self
.0
.parent_trait_ref()
.expect("impl trait in assoc type collector used on non-assoc item");
// If the trait ref of the associated item and the impl differs,
// then we can't use the impl's identity args below, so
// just skip.
if alias_ty.trait_ref(self.0.tcx) == parent_trait_ref {
let parent = self.0.parent().expect("we should have a parent here");

for &assoc in self.0.tcx.associated_items(parent).in_definition_order() {
trace!(?assoc);
if assoc.trait_item_def_id != Some(alias_ty.def_id) {
continue;
}

// If the type is further specializable, then the type_of
// is not actually correct below.
if !assoc.defaultness(self.0.tcx).is_final() {
continue;
}

let impl_args = alias_ty.args.rebase_onto(
self.0.tcx,
parent_trait_ref.def_id,
ty::GenericArgs::identity_for_item(self.0.tcx, parent),
);

if check_args_compatible(self.0.tcx, assoc, impl_args) {
return self
.0
.tcx
.type_of(assoc.def_id)
.instantiate(self.0.tcx, impl_args)
.visit_with(self);
} else {
self.0.tcx.dcx().span_bug(
self.0.tcx.def_span(assoc.def_id),
"item had incorrect args",
);
}
}
}
}
_ => trace!(kind=?t.kind()),
}
ControlFlow::Continue(())
}
}

fn impl_trait_in_assoc_types_defined_by<'tcx>(
tcx: TyCtxt<'tcx>,
item: LocalDefId,
) -> &'tcx ty::List<LocalDefId> {
let mut collector = ImplTraitInAssocTypeCollector(OpaqueTypeCollector::new(tcx, item));
let mut collector = OpaqueTypeCollector::new(tcx, item, CollectionMode::ImplTraitInAssocTypes);
super::sig_types::walk_types(tcx, item, &mut collector);
tcx.mk_local_def_ids(&collector.0.opaques)
tcx.mk_local_def_ids(&collector.opaques)
}

fn opaque_types_defined_by<'tcx>(
Expand All @@ -366,7 +302,8 @@ fn opaque_types_defined_by<'tcx>(
) -> &'tcx ty::List<LocalDefId> {
let kind = tcx.def_kind(item);
trace!(?kind);
let mut collector = OpaqueTypeCollector::new(tcx, item);
let mut collector =
OpaqueTypeCollector::new(tcx, item, CollectionMode::TypeAliasImplTraitTransition);
super::sig_types::walk_types(tcx, item, &mut collector);
match kind {
DefKind::AssocFn
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! This test demonstrates a bug where we accidentally
//! detected opaque types in struct fields, but only if nested
//! in projections of another opaque type.
//@ check-pass
#![feature(impl_trait_in_assoc_type)]

Expand All @@ -18,6 +17,7 @@ impl Trait for Bar {
type Assoc = impl Iterator<Item = Foo>;
fn foo() -> Self::Assoc {
vec![Foo { field: () }].into_iter()
//~^ ERROR item constrains opaque type that is not in its signature
}
}

Expand Down
15 changes: 15 additions & 0 deletions tests/ui/type-alias-impl-trait/hidden_behind_struct_field3.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error: item constrains opaque type that is not in its signature
--> $DIR/hidden_behind_struct_field3.rs:19:27
|
LL | vec![Foo { field: () }].into_iter()
| ^^
|
= note: this item must mention the opaque type in its signature in order to be able to register hidden types
note: this item must mention the opaque type in its signature in order to be able to register hidden types
--> $DIR/hidden_behind_struct_field3.rs:18:8
|
LL | fn foo() -> Self::Assoc {
| ^^^

error: aborting due to 1 previous error

0 comments on commit 8364a06

Please sign in to comment.