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