| use crate::error::ConstNotUsedTraitAlias; |
| use crate::ty::fold::{TypeFolder, TypeSuperFoldable}; |
| use crate::ty::{self, Ty, TyCtxt, TypeFoldable}; |
| use crate::ty::{GenericArg, GenericArgKind}; |
| use rustc_data_structures::fx::FxHashMap; |
| use rustc_span::def_id::DefId; |
| use rustc_span::Span; |
| |
| /// Converts generic params of a TypeFoldable from one |
| /// item's generics to another. Usually from a function's generics |
| /// list to the opaque type's own generics. |
| pub(super) struct ReverseMapper<'tcx> { |
| tcx: TyCtxt<'tcx>, |
| map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>, |
| /// see call sites to fold_kind_no_missing_regions_error |
| /// for an explanation of this field. |
| do_not_error: bool, |
| |
| /// We do not want to emit any errors in typeck because |
| /// the spans in typeck are subpar at the moment. |
| /// Borrowck will do the same work again (this time with |
| /// lifetime information) and thus report better errors. |
| ignore_errors: bool, |
| |
| /// Span of function being checked. |
| span: Span, |
| } |
| |
| impl<'tcx> ReverseMapper<'tcx> { |
| pub(super) fn new( |
| tcx: TyCtxt<'tcx>, |
| map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>, |
| span: Span, |
| ignore_errors: bool, |
| ) -> Self { |
| Self { tcx, map, do_not_error: false, ignore_errors, span } |
| } |
| |
| fn fold_kind_no_missing_regions_error(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> { |
| assert!(!self.do_not_error); |
| self.do_not_error = true; |
| let kind = kind.fold_with(self); |
| self.do_not_error = false; |
| kind |
| } |
| |
| fn fold_kind_normally(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> { |
| assert!(!self.do_not_error); |
| kind.fold_with(self) |
| } |
| |
| fn fold_closure_args( |
| &mut self, |
| def_id: DefId, |
| args: ty::GenericArgsRef<'tcx>, |
| ) -> ty::GenericArgsRef<'tcx> { |
| // I am a horrible monster and I pray for death. When |
| // we encounter a closure here, it is always a closure |
| // from within the function that we are currently |
| // type-checking -- one that is now being encapsulated |
| // in an opaque type. Ideally, we would |
| // go through the types/lifetimes that it references |
| // and treat them just like we would any other type, |
| // which means we would error out if we find any |
| // reference to a type/region that is not in the |
| // "reverse map". |
| // |
| // **However,** in the case of closures, there is a |
| // somewhat subtle (read: hacky) consideration. The |
| // problem is that our closure types currently include |
| // all the lifetime parameters declared on the |
| // enclosing function, even if they are unused by the |
| // closure itself. We can't readily filter them out, |
| // so here we replace those values with `'empty`. This |
| // can't really make a difference to the rest of the |
| // compiler; those regions are ignored for the |
| // outlives relation, and hence don't affect trait |
| // selection or auto traits, and they are erased |
| // during codegen. |
| |
| let generics = self.tcx.generics_of(def_id); |
| self.tcx.mk_args_from_iter(args.iter().enumerate().map(|(index, kind)| { |
| if index < generics.parent_count { |
| // Accommodate missing regions in the parent kinds... |
| self.fold_kind_no_missing_regions_error(kind) |
| } else { |
| // ...but not elsewhere. |
| self.fold_kind_normally(kind) |
| } |
| })) |
| } |
| } |
| |
| impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> { |
| fn interner(&self) -> TyCtxt<'tcx> { |
| self.tcx |
| } |
| |
| #[instrument(skip(self), level = "debug")] |
| fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { |
| match *r { |
| // Ignore bound regions and `'static` regions that appear in the |
| // type, we only need to remap regions that reference lifetimes |
| // from the function declaration. |
| // This would ignore `'r` in a type like `for<'r> fn(&'r u32)`. |
| ty::ReLateBound(..) | ty::ReStatic => return r, |
| |
| // If regions have been erased (by writeback), don't try to unerase |
| // them. |
| ty::ReErased => return r, |
| |
| ty::ReError(_) => return r, |
| |
| // The regions that we expect from borrow checking. |
| ty::ReEarlyBound(_) | ty::ReFree(_) => {} |
| |
| ty::RePlaceholder(_) | ty::ReVar(_) => { |
| // All of the regions in the type should either have been |
| // erased by writeback, or mapped back to named regions by |
| // borrow checking. |
| bug!("unexpected region kind in opaque type: {:?}", r); |
| } |
| } |
| |
| match self.map.get(&r.into()).map(|k| k.unpack()) { |
| Some(GenericArgKind::Lifetime(r1)) => r1, |
| Some(u) => panic!("region mapped to unexpected kind: {u:?}"), |
| None if self.do_not_error => self.tcx.lifetimes.re_static, |
| None => { |
| let e = self |
| .tcx |
| .sess |
| .struct_span_err(self.span, "non-defining opaque type use in defining scope") |
| .span_label( |
| self.span, |
| format!( |
| "lifetime `{r}` is part of concrete type but not used in \ |
| parameter list of the `impl Trait` type alias" |
| ), |
| ) |
| .emit(); |
| |
| ty::Region::new_error(self.interner(), e) |
| } |
| } |
| } |
| |
| fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { |
| match *ty.kind() { |
| ty::Closure(def_id, args) => { |
| let args = self.fold_closure_args(def_id, args); |
| Ty::new_closure(self.tcx, def_id, args) |
| } |
| |
| ty::Coroutine(def_id, args, movability) => { |
| let args = self.fold_closure_args(def_id, args); |
| Ty::new_coroutine(self.tcx, def_id, args, movability) |
| } |
| |
| ty::CoroutineWitness(def_id, args) => { |
| let args = self.fold_closure_args(def_id, args); |
| Ty::new_coroutine_witness(self.tcx, def_id, args) |
| } |
| |
| ty::Param(param) => { |
| // Look it up in the substitution list. |
| match self.map.get(&ty.into()).map(|k| k.unpack()) { |
| // Found it in the substitution list; replace with the parameter from the |
| // opaque type. |
| Some(GenericArgKind::Type(t1)) => t1, |
| Some(u) => panic!("type mapped to unexpected kind: {u:?}"), |
| None => { |
| debug!(?param, ?self.map); |
| if !self.ignore_errors { |
| self.tcx |
| .sess |
| .struct_span_err( |
| self.span, |
| format!( |
| "type parameter `{ty}` is part of concrete type but not \ |
| used in parameter list for the `impl Trait` type alias" |
| ), |
| ) |
| .emit(); |
| } |
| |
| Ty::new_misc_error(self.tcx) |
| } |
| } |
| } |
| |
| _ => ty.super_fold_with(self), |
| } |
| } |
| |
| fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { |
| trace!("checking const {:?}", ct); |
| // Find a const parameter |
| match ct.kind() { |
| ty::ConstKind::Param(..) => { |
| // Look it up in the substitution list. |
| match self.map.get(&ct.into()).map(|k| k.unpack()) { |
| // Found it in the substitution list, replace with the parameter from the |
| // opaque type. |
| Some(GenericArgKind::Const(c1)) => c1, |
| Some(u) => panic!("const mapped to unexpected kind: {u:?}"), |
| None => { |
| let guar = self |
| .tcx |
| .sess |
| .create_err(ConstNotUsedTraitAlias { |
| ct: ct.to_string(), |
| span: self.span, |
| }) |
| .emit_unless(self.ignore_errors); |
| |
| ty::Const::new_error(self.tcx, guar, ct.ty()) |
| } |
| } |
| } |
| |
| _ => ct, |
| } |
| } |
| } |