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