From f58af9ba28f1bb49f880da43227c65cc10673be2 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 9 Jan 2024 11:12:30 +0000 Subject: Add a simpler and more targetted code path for impl trait in assoc items --- compiler/rustc_hir_analysis/src/collect/type_of.rs | 6 +- .../src/collect/type_of/opaque.rs | 71 ++++++++++++++++++++-- compiler/rustc_middle/src/query/mod.rs | 9 +++ compiler/rustc_ty_utils/src/opaque_types.rs | 10 ++- 4 files changed, 88 insertions(+), 8 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index bfa9dc42422..3674a760cbf 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -530,9 +530,13 @@ pub(super) fn type_of_opaque( Ok(ty::EarlyBinder::bind(match tcx.hir_node_by_def_id(def_id) { Node::Item(item) => match item.kind { ItemKind::OpaqueTy(OpaqueTy { - origin: hir::OpaqueTyOrigin::TyAlias { .. }, + origin: hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false }, .. }) => opaque::find_opaque_ty_constraints_for_tait(tcx, def_id), + ItemKind::OpaqueTy(OpaqueTy { + origin: hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: true }, + .. + }) => opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type(tcx, def_id), // Opaque types desugared from `impl Trait`. ItemKind::OpaqueTy(&OpaqueTy { origin: diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs index 85093bc12b3..51b2401531c 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -23,6 +23,60 @@ pub fn test_opaque_hidden_types(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> res } +/// Checks "defining uses" of opaque `impl Trait` in associated types. +/// These can only be defined by associated items of the same trait. +#[instrument(skip(tcx), level = "debug")] +pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type( + tcx: TyCtxt<'_>, + def_id: LocalDefId, +) -> Ty<'_> { + let mut parent_def_id = def_id; + while tcx.def_kind(parent_def_id) == def::DefKind::OpaqueTy { + // Account for `type Alias = impl Trait;` (#116031) + parent_def_id = tcx.local_parent(parent_def_id); + } + let impl_def_id = tcx.local_parent(parent_def_id); + match tcx.def_kind(impl_def_id) { + DefKind::Impl { .. } => {} + other => bug!("invalid impl trait in assoc type parent: {other:?}"), + } + + let mut locator = TaitConstraintLocator { def_id, tcx, found: None, typeck_types: vec![] }; + + for &assoc_id in tcx.associated_item_def_ids(impl_def_id) { + let assoc = tcx.associated_item(assoc_id); + match assoc.kind { + ty::AssocKind::Const | ty::AssocKind::Fn => { + locator.check(assoc_id.expect_local(), true) + } + // Associated types don't have bodies, so they can't constrain hidden types + ty::AssocKind::Type => {} + } + } + + if let Some(hidden) = locator.found { + // Only check against typeck if we didn't already error + if !hidden.ty.references_error() { + for concrete_type in locator.typeck_types { + if concrete_type.ty != tcx.erase_regions(hidden.ty) + && !(concrete_type, hidden).references_error() + { + hidden.report_mismatch(&concrete_type, def_id, tcx).emit(); + } + } + } + + hidden.ty + } else { + let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType { + span: tcx.def_span(def_id), + name: tcx.item_name(parent_def_id.to_def_id()), + what: "impl", + }); + Ty::new_error(tcx, reported) + } +} + /// Checks "defining uses" of opaque `impl Trait` types to ensure that they meet the restrictions /// laid for "higher-order pattern unification". /// This ensures that inference is tractable. @@ -130,7 +184,7 @@ struct TaitConstraintLocator<'tcx> { impl TaitConstraintLocator<'_> { #[instrument(skip(self), level = "debug")] - fn check(&mut self, item_def_id: LocalDefId) { + fn check(&mut self, item_def_id: LocalDefId, impl_trait_in_assoc_type: bool) { // Don't try to check items that cannot possibly constrain the type. if !self.tcx.has_typeck_results(item_def_id) { debug!("no constraint: no typeck results"); @@ -182,7 +236,12 @@ impl TaitConstraintLocator<'_> { continue; } constrained = true; - if !self.tcx.opaque_types_defined_by(item_def_id).contains(&self.def_id) { + let opaque_types_defined_by = if impl_trait_in_assoc_type { + self.tcx.impl_trait_in_assoc_types_defined_by(item_def_id) + } else { + self.tcx.opaque_types_defined_by(item_def_id) + }; + if !opaque_types_defined_by.contains(&self.def_id) { self.tcx.dcx().emit_err(TaitForwardCompat { span: hidden_type.span, item_span: self @@ -240,7 +299,7 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> { } fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { if let hir::ExprKind::Closure(closure) = ex.kind { - self.check(closure.def_id); + self.check(closure.def_id, false); } intravisit::walk_expr(self, ex); } @@ -248,7 +307,7 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> { trace!(?it.owner_id); // The opaque type itself or its children are not within its reveal scope. if it.owner_id.def_id != self.def_id { - self.check(it.owner_id.def_id); + self.check(it.owner_id.def_id, false); intravisit::walk_item(self, it); } } @@ -256,13 +315,13 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> { trace!(?it.owner_id); // The opaque type itself or its children are not within its reveal scope. if it.owner_id.def_id != self.def_id { - self.check(it.owner_id.def_id); + self.check(it.owner_id.def_id, false); intravisit::walk_impl_item(self, it); } } fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) { trace!(?it.owner_id); - self.check(it.owner_id.def_id); + self.check(it.owner_id.def_id, false); intravisit::walk_trait_item(self, it); } fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index a9dc7f5d11a..23daefd5a65 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -343,6 +343,15 @@ rustc_queries! { } } + query impl_trait_in_assoc_types_defined_by( + key: LocalDefId + ) -> &'tcx ty::List { + desc { + |tcx| "computing the opaque types defined by `{}`", + tcx.def_path_str(key.to_def_id()) + } + } + /// Returns the list of bounds that can be used for /// `SelectionCandidate::ProjectionCandidate(_)` and /// `ProjectionTyCandidate::TraitDef`. diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index 0bb833c66fa..eae4c57fc99 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -272,6 +272,13 @@ impl<'tcx> TypeVisitor> for OpaqueTypeCollector<'tcx> { } } +fn impl_trait_in_assoc_types_defined_by<'tcx>( + tcx: TyCtxt<'tcx>, + item: LocalDefId, +) -> &'tcx ty::List { + opaque_types_defined_by(tcx, item) +} + fn opaque_types_defined_by<'tcx>( tcx: TyCtxt<'tcx>, item: LocalDefId, @@ -321,5 +328,6 @@ fn opaque_types_defined_by<'tcx>( } pub(super) fn provide(providers: &mut Providers) { - *providers = Providers { opaque_types_defined_by, ..*providers }; + *providers = + Providers { opaque_types_defined_by, impl_trait_in_assoc_types_defined_by, ..*providers }; } -- cgit 1.4.1-3-g733a5 From ac332bd9163990491d225e2a24ff92af89030dac Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 9 Jan 2024 11:16:32 +0000 Subject: Pull opaque type check into a separate method --- compiler/rustc_ty_utils/src/opaque_types.rs | 125 ++++++++++++++-------------- 1 file changed, 64 insertions(+), 61 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index eae4c57fc99..1d9c0405b7d 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -118,6 +118,69 @@ impl<'tcx> OpaqueTypeCollector<'tcx> { } TaitInBodyFinder { collector: self }.visit_expr(body); } + + fn visit_opaque_ty(&mut self, alias_ty: &ty::AliasTy<'tcx>) { + if !self.seen.insert(alias_ty.def_id.expect_local()) { + return; + } + + // TAITs outside their defining scopes are ignored. + let origin = self.tcx.opaque_type_origin(alias_ty.def_id.expect_local()); + trace!(?origin); + match origin { + rustc_hir::OpaqueTyOrigin::FnReturn(_) | rustc_hir::OpaqueTyOrigin::AsyncFn(_) => {} + rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty } => { + if !in_assoc_ty { + if !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) { + return; + } + } + } + } + + self.opaques.push(alias_ty.def_id.expect_local()); + + let parent_count = self.tcx.generics_of(alias_ty.def_id).parent_count; + // Only check that the parent generics of the TAIT/RPIT are unique. + // the args owned by the opaque are going to always be duplicate + // lifetime params for RPITs, and empty for TAITs. + match self + .tcx + .uses_unique_generic_params(&alias_ty.args[..parent_count], CheckRegions::FromFunction) + { + Ok(()) => { + // FIXME: implement higher kinded lifetime bounds on nested opaque types. They are not + // supported at all, so this is sound to do, but once we want to support them, you'll + // start seeing the error below. + + // Collect opaque types nested within the associated type bounds of this opaque type. + // We use identity args here, because we already know that the opaque type uses + // only generic parameters, and thus substituting would not give us more information. + for (pred, span) in self + .tcx + .explicit_item_bounds(alias_ty.def_id) + .instantiate_identity_iter_copied() + { + trace!(?pred); + self.visit_spanned(span, pred); + } + } + Err(NotUniqueParam::NotParam(arg)) => { + self.tcx.dcx().emit_err(NotParam { + arg, + span: self.span(), + opaque_span: self.tcx.def_span(alias_ty.def_id), + }); + } + Err(NotUniqueParam::DuplicateParam(arg)) => { + self.tcx.dcx().emit_err(DuplicateArg { + arg, + span: self.span(), + opaque_span: self.tcx.def_span(alias_ty.def_id), + }); + } + } + } } impl<'tcx> super::sig_types::SpannedTypeVisitor<'tcx> for OpaqueTypeCollector<'tcx> { @@ -134,67 +197,7 @@ impl<'tcx> TypeVisitor> for OpaqueTypeCollector<'tcx> { t.super_visit_with(self)?; match t.kind() { ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => { - if !self.seen.insert(alias_ty.def_id.expect_local()) { - return ControlFlow::Continue(()); - } - - // TAITs outside their defining scopes are ignored. - let origin = self.tcx.opaque_type_origin(alias_ty.def_id.expect_local()); - trace!(?origin); - match origin { - rustc_hir::OpaqueTyOrigin::FnReturn(_) - | rustc_hir::OpaqueTyOrigin::AsyncFn(_) => {} - rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty } => { - if !in_assoc_ty { - if !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) { - return ControlFlow::Continue(()); - } - } - } - } - - self.opaques.push(alias_ty.def_id.expect_local()); - - let parent_count = self.tcx.generics_of(alias_ty.def_id).parent_count; - // Only check that the parent generics of the TAIT/RPIT are unique. - // the args owned by the opaque are going to always be duplicate - // lifetime params for RPITs, and empty for TAITs. - match self.tcx.uses_unique_generic_params( - &alias_ty.args[..parent_count], - CheckRegions::FromFunction, - ) { - Ok(()) => { - // FIXME: implement higher kinded lifetime bounds on nested opaque types. They are not - // supported at all, so this is sound to do, but once we want to support them, you'll - // start seeing the error below. - - // Collect opaque types nested within the associated type bounds of this opaque type. - // We use identity args here, because we already know that the opaque type uses - // only generic parameters, and thus substituting would not give us more information. - for (pred, span) in self - .tcx - .explicit_item_bounds(alias_ty.def_id) - .instantiate_identity_iter_copied() - { - trace!(?pred); - self.visit_spanned(span, pred); - } - } - Err(NotUniqueParam::NotParam(arg)) => { - self.tcx.dcx().emit_err(NotParam { - arg, - span: self.span(), - opaque_span: self.tcx.def_span(alias_ty.def_id), - }); - } - Err(NotUniqueParam::DuplicateParam(arg)) => { - self.tcx.dcx().emit_err(DuplicateArg { - arg, - span: self.span(), - opaque_span: self.tcx.def_span(alias_ty.def_id), - }); - } - } + self.visit_opaque_ty(alias_ty); } ty::Alias(ty::Weak, alias_ty) if alias_ty.def_id.is_local() => { self.tcx -- cgit 1.4.1-3-g733a5 From f75361fec7aecfcede9cf6f0b97f2b1e7756e47b Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 9 Jan 2024 11:28:05 +0000 Subject: Limit impl trait in assoc type defining scope --- compiler/rustc_ty_utils/src/opaque_types.rs | 80 +++++++++++++++++++++- .../hidden_behind_struct_field2.rs | 8 +-- .../hidden_behind_struct_field2.stderr | 15 ++++ 3 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.stderr (limited to 'compiler') diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index 1d9c0405b7d..ef67317a601 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -275,11 +275,89 @@ impl<'tcx> TypeVisitor> 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>) -> ControlFlow { + let old = self.0.span; + self.0.span = Some(span); + value.visit_with(self); + self.0.span = old; + + ControlFlow::Continue(()) + } +} + +impl<'tcx> TypeVisitor> 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 substitutions 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_delayed_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 { - opaque_types_defined_by(tcx, item) + let mut collector = ImplTraitInAssocTypeCollector(OpaqueTypeCollector::new(tcx, item)); + super::sig_types::walk_types(tcx, item, &mut collector); + tcx.mk_local_def_ids(&collector.0.opaques) } fn opaque_types_defined_by<'tcx>( diff --git a/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.rs b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.rs index e440dce5e51..4c881dd1330 100644 --- a/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.rs +++ b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.rs @@ -1,9 +1,8 @@ -//! This test shows that we can even follow projections -//! into associated types of the same impl if they are -//! indirectly mentioned in a struct field. +//! This test shows that we do not treat opaque types +//! as defined by a method if the opaque type is +//! only indirectly mentioned in a struct field. #![feature(impl_trait_in_assoc_type)] -// check-pass struct Bar; @@ -16,6 +15,7 @@ impl Trait for Bar { type Assoc = impl std::fmt::Debug; fn foo() -> Foo { Foo { field: () } + //~^ ERROR: item constrains opaque type that is not in its signature } } diff --git a/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.stderr b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.stderr new file mode 100644 index 00000000000..5c53dfa3a75 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.stderr @@ -0,0 +1,15 @@ +error: item constrains opaque type that is not in its signature + --> $DIR/hidden_behind_struct_field2.rs:17:22 + | +LL | Foo { field: () } + | ^^ + | + = 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_field2.rs:16:8 + | +LL | fn foo() -> Foo { + | ^^^ + +error: aborting due to 1 previous error + -- cgit 1.4.1-3-g733a5 From 1829aa631fe3df4db1217922ec420e7c57501d50 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 16 Jan 2024 09:21:54 +0000 Subject: Use an enum instead of a bool --- .../src/collect/type_of/opaque.rs | 27 ++++++++++++++-------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs index 51b2401531c..79cb384c5bd 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -47,7 +47,7 @@ pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type( let assoc = tcx.associated_item(assoc_id); match assoc.kind { ty::AssocKind::Const | ty::AssocKind::Fn => { - locator.check(assoc_id.expect_local(), true) + locator.check(assoc_id.expect_local(), ImplTraitSource::AssocTy) } // Associated types don't have bodies, so they can't constrain hidden types ty::AssocKind::Type => {} @@ -182,9 +182,15 @@ struct TaitConstraintLocator<'tcx> { typeck_types: Vec>, } +#[derive(Debug)] +enum ImplTraitSource { + AssocTy, + TyAlias, +} + impl TaitConstraintLocator<'_> { #[instrument(skip(self), level = "debug")] - fn check(&mut self, item_def_id: LocalDefId, impl_trait_in_assoc_type: bool) { + fn check(&mut self, item_def_id: LocalDefId, source: ImplTraitSource) { // Don't try to check items that cannot possibly constrain the type. if !self.tcx.has_typeck_results(item_def_id) { debug!("no constraint: no typeck results"); @@ -236,10 +242,11 @@ impl TaitConstraintLocator<'_> { continue; } constrained = true; - let opaque_types_defined_by = if impl_trait_in_assoc_type { - self.tcx.impl_trait_in_assoc_types_defined_by(item_def_id) - } else { - self.tcx.opaque_types_defined_by(item_def_id) + let opaque_types_defined_by = match source { + ImplTraitSource::AssocTy => { + self.tcx.impl_trait_in_assoc_types_defined_by(item_def_id) + } + ImplTraitSource::TyAlias => self.tcx.opaque_types_defined_by(item_def_id), }; if !opaque_types_defined_by.contains(&self.def_id) { self.tcx.dcx().emit_err(TaitForwardCompat { @@ -299,7 +306,7 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> { } fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { if let hir::ExprKind::Closure(closure) = ex.kind { - self.check(closure.def_id, false); + self.check(closure.def_id, ImplTraitSource::TyAlias); } intravisit::walk_expr(self, ex); } @@ -307,7 +314,7 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> { trace!(?it.owner_id); // The opaque type itself or its children are not within its reveal scope. if it.owner_id.def_id != self.def_id { - self.check(it.owner_id.def_id, false); + self.check(it.owner_id.def_id, ImplTraitSource::TyAlias); intravisit::walk_item(self, it); } } @@ -315,13 +322,13 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> { trace!(?it.owner_id); // The opaque type itself or its children are not within its reveal scope. if it.owner_id.def_id != self.def_id { - self.check(it.owner_id.def_id, false); + self.check(it.owner_id.def_id, ImplTraitSource::TyAlias); intravisit::walk_impl_item(self, it); } } fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) { trace!(?it.owner_id); - self.check(it.owner_id.def_id, false); + self.check(it.owner_id.def_id, ImplTraitSource::TyAlias); intravisit::walk_trait_item(self, it); } fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) { -- cgit 1.4.1-3-g733a5