Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use the correct logic for nested impl trait in assoc types #121838

Merged
merged 2 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
28 changes: 28 additions & 0 deletions tests/ui/type-alias-impl-trait/hidden_behind_struct_field3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//! This test demonstrates a bug where we accidentally
//! detected opaque types in struct fields, but only if nested
//! in projections of another opaque type.

#![feature(impl_trait_in_assoc_type)]

struct Bar;

trait Trait: Sized {
type Assoc2;
type Assoc;
fn foo() -> Self::Assoc;
}

impl Trait for Bar {
type Assoc2 = impl std::fmt::Debug;
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
}
}

struct Foo {
field: <Bar as Trait>::Assoc2,
}

fn main() {}
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

Loading