rustc_middle/ty/
opaque_types.rs

1use rustc_data_structures::fx::FxHashMap;
2use rustc_span::Span;
3use rustc_span::def_id::DefId;
4use tracing::{debug, instrument, trace};
5
6use crate::error::ConstNotUsedTraitAlias;
7use crate::ty::{
8    self, GenericArg, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
9};
10
11pub type OpaqueTypeKey<'tcx> = rustc_type_ir::OpaqueTypeKey<TyCtxt<'tcx>>;
12
13/// Converts generic params of a TypeFoldable from one
14/// item's generics to another. Usually from a function's generics
15/// list to the opaque type's own generics.
16pub(super) struct ReverseMapper<'tcx> {
17    tcx: TyCtxt<'tcx>,
18    map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>,
19    /// see call sites to fold_kind_no_missing_regions_error
20    /// for an explanation of this field.
21    do_not_error: bool,
22
23    /// We do not want to emit any errors in typeck because
24    /// the spans in typeck are subpar at the moment.
25    /// Borrowck will do the same work again (this time with
26    /// lifetime information) and thus report better errors.
27    ignore_errors: bool,
28
29    /// Span of function being checked.
30    span: Span,
31}
32
33impl<'tcx> ReverseMapper<'tcx> {
34    pub(super) fn new(
35        tcx: TyCtxt<'tcx>,
36        map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>,
37        span: Span,
38        ignore_errors: bool,
39    ) -> Self {
40        Self { tcx, map, do_not_error: false, ignore_errors, span }
41    }
42
43    fn fold_kind_no_missing_regions_error(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> {
44        assert!(!self.do_not_error);
45        self.do_not_error = true;
46        let kind = kind.fold_with(self);
47        self.do_not_error = false;
48        kind
49    }
50
51    fn fold_kind_normally(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> {
52        assert!(!self.do_not_error);
53        kind.fold_with(self)
54    }
55
56    fn fold_closure_args(
57        &mut self,
58        def_id: DefId,
59        args: ty::GenericArgsRef<'tcx>,
60    ) -> ty::GenericArgsRef<'tcx> {
61        // I am a horrible monster and I pray for death. When
62        // we encounter a closure here, it is always a closure
63        // from within the function that we are currently
64        // type-checking -- one that is now being encapsulated
65        // in an opaque type. Ideally, we would
66        // go through the types/lifetimes that it references
67        // and treat them just like we would any other type,
68        // which means we would error out if we find any
69        // reference to a type/region that is not in the
70        // "reverse map".
71        //
72        // **However,** in the case of closures, there is a
73        // somewhat subtle (read: hacky) consideration. The
74        // problem is that our closure types currently include
75        // all the lifetime parameters declared on the
76        // enclosing function, even if they are unused by the
77        // closure itself. We can't readily filter them out,
78        // so here we replace those values with `'empty`. This
79        // can't really make a difference to the rest of the
80        // compiler; those regions are ignored for the
81        // outlives relation, and hence don't affect trait
82        // selection or auto traits, and they are erased
83        // during codegen.
84
85        let generics = self.tcx.generics_of(def_id);
86        self.tcx.mk_args_from_iter(args.iter().enumerate().map(|(index, kind)| {
87            if index < generics.parent_count {
88                // Accommodate missing regions in the parent kinds...
89                self.fold_kind_no_missing_regions_error(kind)
90            } else {
91                // ...but not elsewhere.
92                self.fold_kind_normally(kind)
93            }
94        }))
95    }
96}
97
98impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> {
99    fn cx(&self) -> TyCtxt<'tcx> {
100        self.tcx
101    }
102
103    #[instrument(skip(self), level = "debug")]
104    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
105        match *r {
106            // Ignore bound regions and `'static` regions that appear in the
107            // type, we only need to remap regions that reference lifetimes
108            // from the function declaration.
109            //
110            // E.g. We ignore `'r` in a type like `for<'r> fn(&'r u32)`.
111            ty::ReBound(..) | ty::ReStatic => return r,
112
113            // If regions have been erased (by writeback), don't try to unerase
114            // them.
115            ty::ReErased => return r,
116
117            ty::ReError(_) => return r,
118
119            // The regions that we expect from borrow checking.
120            ty::ReEarlyParam(_) | ty::ReLateParam(_) => {}
121
122            ty::RePlaceholder(_) | ty::ReVar(_) => {
123                // All of the regions in the type should either have been
124                // erased by writeback, or mapped back to named regions by
125                // borrow checking.
126                bug!("unexpected region kind in opaque type: {:?}", r);
127            }
128        }
129
130        match self.map.get(&r.into()).map(|k| k.unpack()) {
131            Some(GenericArgKind::Lifetime(r1)) => r1,
132            Some(u) => panic!("region mapped to unexpected kind: {u:?}"),
133            None if self.do_not_error => self.tcx.lifetimes.re_static,
134            None => {
135                let e = self
136                    .tcx
137                    .dcx()
138                    .struct_span_err(self.span, "non-defining opaque type use in defining scope")
139                    .with_span_label(
140                        self.span,
141                        format!(
142                            "lifetime `{r}` is part of concrete type but not used in \
143                             parameter list of the `impl Trait` type alias"
144                        ),
145                    )
146                    .emit();
147
148                ty::Region::new_error(self.cx(), e)
149            }
150        }
151    }
152
153    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
154        match *ty.kind() {
155            ty::Closure(def_id, args) => {
156                let args = self.fold_closure_args(def_id, args);
157                Ty::new_closure(self.tcx, def_id, args)
158            }
159
160            ty::Coroutine(def_id, args) => {
161                let args = self.fold_closure_args(def_id, args);
162                Ty::new_coroutine(self.tcx, def_id, args)
163            }
164
165            ty::CoroutineWitness(def_id, args) => {
166                let args = self.fold_closure_args(def_id, args);
167                Ty::new_coroutine_witness(self.tcx, def_id, args)
168            }
169
170            ty::Param(param) => {
171                // Look it up in the generic parameters list.
172                match self.map.get(&ty.into()).map(|k| k.unpack()) {
173                    // Found it in the generic parameters list; replace with the parameter from the
174                    // opaque type.
175                    Some(GenericArgKind::Type(t1)) => t1,
176                    Some(u) => panic!("type mapped to unexpected kind: {u:?}"),
177                    None => {
178                        debug!(?param, ?self.map);
179                        if !self.ignore_errors {
180                            self.tcx
181                                .dcx()
182                                .struct_span_err(
183                                    self.span,
184                                    format!(
185                                        "type parameter `{ty}` is part of concrete type but not \
186                                          used in parameter list for the `impl Trait` type alias"
187                                    ),
188                                )
189                                .emit();
190                        }
191
192                        Ty::new_misc_error(self.tcx)
193                    }
194                }
195            }
196
197            _ => ty.super_fold_with(self),
198        }
199    }
200
201    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
202        trace!("checking const {:?}", ct);
203        // Find a const parameter
204        match ct.kind() {
205            ty::ConstKind::Param(..) => {
206                // Look it up in the generic parameters list.
207                match self.map.get(&ct.into()).map(|k| k.unpack()) {
208                    // Found it in the generic parameters list, replace with the parameter from the
209                    // opaque type.
210                    Some(GenericArgKind::Const(c1)) => c1,
211                    Some(u) => panic!("const mapped to unexpected kind: {u:?}"),
212                    None => {
213                        let guar = self
214                            .tcx
215                            .dcx()
216                            .create_err(ConstNotUsedTraitAlias {
217                                ct: ct.to_string(),
218                                span: self.span,
219                            })
220                            .emit_unless(self.ignore_errors);
221
222                        ty::Const::new_error(self.tcx, guar)
223                    }
224                }
225            }
226
227            _ => ct,
228        }
229    }
230}