rustc_hir_analysis/collect/type_of/
opaque.rs

1use rustc_hir::def::DefKind;
2use rustc_hir::def_id::LocalDefId;
3use rustc_hir::{self as hir, Expr, ImplItem, Item, Node, TraitItem, def, intravisit};
4use rustc_middle::bug;
5use rustc_middle::hir::nested_filter;
6use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
7use rustc_span::DUMMY_SP;
8use tracing::{debug, instrument, trace};
9
10use crate::errors::{TaitForwardCompat2, UnconstrainedOpaqueType};
11
12/// Checks "defining uses" of opaque `impl Trait` in associated types.
13/// These can only be defined by associated items of the same trait.
14#[instrument(skip(tcx), level = "debug")]
15pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
16    tcx: TyCtxt<'_>,
17    def_id: LocalDefId,
18) -> Ty<'_> {
19    let mut parent_def_id = def_id;
20    while tcx.def_kind(parent_def_id) == def::DefKind::OpaqueTy {
21        // Account for `type Alias = impl Trait<Foo = impl Trait>;` (#116031)
22        parent_def_id = tcx.local_parent(parent_def_id);
23    }
24    let impl_def_id = tcx.local_parent(parent_def_id);
25    match tcx.def_kind(impl_def_id) {
26        DefKind::Impl { .. } => {}
27        other => bug!("invalid impl trait in assoc type parent: {other:?}"),
28    }
29
30    let mut locator = TaitConstraintLocator { def_id, tcx, found: None, typeck_types: vec![] };
31
32    for &assoc_id in tcx.associated_item_def_ids(impl_def_id) {
33        let assoc = tcx.associated_item(assoc_id);
34        match assoc.kind {
35            ty::AssocKind::Const | ty::AssocKind::Fn => locator.check(assoc_id.expect_local()),
36            // Associated types don't have bodies, so they can't constrain hidden types
37            ty::AssocKind::Type => {}
38        }
39    }
40
41    if let Some(hidden) = locator.found {
42        // Only check against typeck if we didn't already error
43        if !hidden.ty.references_error() {
44            for concrete_type in locator.typeck_types {
45                if concrete_type.ty != tcx.erase_regions(hidden.ty) {
46                    if let Ok(d) = hidden.build_mismatch_error(&concrete_type, tcx) {
47                        d.emit();
48                    }
49                }
50            }
51        }
52
53        hidden.ty
54    } else {
55        let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType {
56            span: tcx.def_span(def_id),
57            name: tcx.item_ident(parent_def_id.to_def_id()),
58            what: "impl",
59        });
60        Ty::new_error(tcx, reported)
61    }
62}
63
64/// Checks "defining uses" of opaque `impl Trait` types to ensure that they meet the restrictions
65/// laid for "higher-order pattern unification".
66/// This ensures that inference is tractable.
67/// In particular, definitions of opaque types can only use other generics as arguments,
68/// and they cannot repeat an argument. Example:
69///
70/// ```ignore (illustrative)
71/// type Foo<A, B> = impl Bar<A, B>;
72///
73/// // Okay -- `Foo` is applied to two distinct, generic types.
74/// fn a<T, U>() -> Foo<T, U> { .. }
75///
76/// // Not okay -- `Foo` is applied to `T` twice.
77/// fn b<T>() -> Foo<T, T> { .. }
78///
79/// // Not okay -- `Foo` is applied to a non-generic type.
80/// fn b<T>() -> Foo<T, u32> { .. }
81/// ```
82#[instrument(skip(tcx), level = "debug")]
83pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> {
84    let mut locator = TaitConstraintLocator { def_id, tcx, found: None, typeck_types: vec![] };
85
86    tcx.hir_walk_toplevel_module(&mut locator);
87
88    if let Some(hidden) = locator.found {
89        // Only check against typeck if we didn't already error
90        if !hidden.ty.references_error() {
91            for concrete_type in locator.typeck_types {
92                if concrete_type.ty != tcx.erase_regions(hidden.ty) {
93                    if let Ok(d) = hidden.build_mismatch_error(&concrete_type, tcx) {
94                        d.emit();
95                    }
96                }
97            }
98        }
99
100        hidden.ty
101    } else {
102        let mut parent_def_id = def_id;
103        while tcx.def_kind(parent_def_id) == def::DefKind::OpaqueTy {
104            // Account for `type Alias = impl Trait<Foo = impl Trait>;` (#116031)
105            parent_def_id = tcx.local_parent(parent_def_id);
106        }
107        let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType {
108            span: tcx.def_span(def_id),
109            name: tcx.item_ident(parent_def_id.to_def_id()),
110            what: "crate",
111        });
112        Ty::new_error(tcx, reported)
113    }
114}
115
116struct TaitConstraintLocator<'tcx> {
117    tcx: TyCtxt<'tcx>,
118
119    /// def_id of the opaque type whose defining uses are being checked
120    def_id: LocalDefId,
121
122    /// as we walk the defining uses, we are checking that all of them
123    /// define the same hidden type. This variable is set to `Some`
124    /// with the first type that we find, and then later types are
125    /// checked against it (we also carry the span of that first
126    /// type).
127    found: Option<ty::OpaqueHiddenType<'tcx>>,
128
129    /// In the presence of dead code, typeck may figure out a hidden type
130    /// while borrowck will not. We collect these cases here and check at
131    /// the end that we actually found a type that matches (modulo regions).
132    typeck_types: Vec<ty::OpaqueHiddenType<'tcx>>,
133}
134
135impl TaitConstraintLocator<'_> {
136    #[instrument(skip(self), level = "debug")]
137    fn check(&mut self, item_def_id: LocalDefId) {
138        // Don't try to check items that cannot possibly constrain the type.
139        if !self.tcx.has_typeck_results(item_def_id) {
140            debug!("no constraint: no typeck results");
141            return;
142        }
143
144        let opaque_types_defined_by = self.tcx.opaque_types_defined_by(item_def_id);
145        // Don't try to check items that cannot possibly constrain the type.
146        if !opaque_types_defined_by.contains(&self.def_id) {
147            debug!("no constraint: no opaque types defined");
148            return;
149        }
150
151        // Function items with `_` in their return type already emit an error, skip any
152        // "non-defining use" errors for them.
153        // Note that we use `Node::fn_sig` instead of `Node::fn_decl` here, because the former
154        // excludes closures, which are allowed to have `_` in their return type.
155        let hir_node = self.tcx.hir_node_by_def_id(item_def_id);
156        debug_assert!(
157            !matches!(hir_node, Node::ForeignItem(..)),
158            "foreign items cannot constrain opaque types",
159        );
160        if let Some(hir_sig) = hir_node.fn_sig()
161            && hir_sig.decl.output.is_suggestable_infer_ty().is_some()
162        {
163            let guar = self.tcx.dcx().span_delayed_bug(
164                hir_sig.decl.output.span(),
165                "inferring return types and opaque types do not mix well",
166            );
167            self.found =
168                Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: Ty::new_error(self.tcx, guar) });
169            return;
170        }
171
172        // Calling `mir_borrowck` can lead to cycle errors through
173        // const-checking, avoid calling it if we don't have to.
174        // ```rust
175        // type Foo = impl Fn() -> usize; // when computing type for this
176        // const fn bar() -> Foo {
177        //     || 0usize
178        // }
179        // const BAZR: Foo = bar(); // we would mir-borrowck this, causing cycles
180        // // because we again need to reveal `Foo` so we can check whether the
181        // // constant does not contain interior mutability.
182        // ```
183        let tables = self.tcx.typeck(item_def_id);
184        if let Some(guar) = tables.tainted_by_errors {
185            self.found =
186                Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: Ty::new_error(self.tcx, guar) });
187            return;
188        }
189
190        let mut constrained = false;
191        for (&opaque_type_key, &hidden_type) in &tables.concrete_opaque_types {
192            if opaque_type_key.def_id != self.def_id {
193                continue;
194            }
195            constrained = true;
196
197            let concrete_type =
198                self.tcx.erase_regions(hidden_type.remap_generic_params_to_declaration_params(
199                    opaque_type_key,
200                    self.tcx,
201                    true,
202                ));
203            if self.typeck_types.iter().all(|prev| prev.ty != concrete_type.ty) {
204                self.typeck_types.push(concrete_type);
205            }
206        }
207
208        if !constrained {
209            debug!("no constraints in typeck results");
210            if opaque_types_defined_by.contains(&self.def_id) {
211                let guar = self.tcx.dcx().emit_err(TaitForwardCompat2 {
212                    span: self
213                        .tcx
214                        .def_ident_span(item_def_id)
215                        .unwrap_or_else(|| self.tcx.def_span(item_def_id)),
216                    opaque_type_span: self.tcx.def_span(self.def_id),
217                    opaque_type: self.tcx.def_path_str(self.def_id),
218                });
219                // Avoid "opaque type not constrained" errors on the opaque itself.
220                self.found = Some(ty::OpaqueHiddenType {
221                    span: DUMMY_SP,
222                    ty: Ty::new_error(self.tcx, guar),
223                });
224            }
225            return;
226        };
227
228        // Use borrowck to get the type with unerased regions.
229        let borrowck_results = &self.tcx.mir_borrowck(item_def_id);
230
231        // If the body was tainted, then assume the opaque may have been constrained and just set it to error.
232        if let Some(guar) = borrowck_results.tainted_by_errors {
233            self.found =
234                Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: Ty::new_error(self.tcx, guar) });
235            return;
236        }
237
238        debug!(?borrowck_results.concrete_opaque_types);
239        if let Some(&concrete_type) = borrowck_results.concrete_opaque_types.get(&self.def_id) {
240            debug!(?concrete_type, "found constraint");
241            if let Some(prev) = &mut self.found {
242                if concrete_type.ty != prev.ty {
243                    let (Ok(guar) | Err(guar)) =
244                        prev.build_mismatch_error(&concrete_type, self.tcx).map(|d| d.emit());
245                    prev.ty = Ty::new_error(self.tcx, guar);
246                }
247            } else {
248                self.found = Some(concrete_type);
249            }
250        }
251    }
252}
253
254impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
255    type NestedFilter = nested_filter::All;
256
257    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
258        self.tcx
259    }
260    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
261        intravisit::walk_expr(self, ex);
262    }
263    fn visit_item(&mut self, it: &'tcx Item<'tcx>) {
264        trace!(?it.owner_id);
265        self.check(it.owner_id.def_id);
266        intravisit::walk_item(self, it);
267    }
268    fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) {
269        trace!(?it.owner_id);
270        self.check(it.owner_id.def_id);
271        intravisit::walk_impl_item(self, it);
272    }
273    fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) {
274        trace!(?it.owner_id);
275        self.check(it.owner_id.def_id);
276        intravisit::walk_trait_item(self, it);
277    }
278    fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
279        trace!(?it.owner_id);
280        assert_ne!(it.owner_id.def_id, self.def_id);
281        // No need to call `check`, as we do not run borrowck on foreign items.
282        intravisit::walk_foreign_item(self, it);
283    }
284}
285
286pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>(
287    tcx: TyCtxt<'tcx>,
288    def_id: LocalDefId,
289    owner_def_id: LocalDefId,
290) -> Ty<'tcx> {
291    let tables = tcx.typeck(owner_def_id);
292
293    // Check that all of the opaques we inferred during HIR are compatible.
294    // FIXME: We explicitly don't check that the types inferred during HIR
295    // typeck are compatible with the one that we infer during borrowck,
296    // because that one actually sometimes has consts evaluated eagerly so
297    // using strict type equality will fail.
298    let mut hir_opaque_ty: Option<ty::OpaqueHiddenType<'tcx>> = None;
299    if tables.tainted_by_errors.is_none() {
300        for (&opaque_type_key, &hidden_type) in &tables.concrete_opaque_types {
301            if opaque_type_key.def_id != def_id {
302                continue;
303            }
304            let concrete_type = tcx.erase_regions(
305                hidden_type.remap_generic_params_to_declaration_params(opaque_type_key, tcx, true),
306            );
307            if let Some(prev) = &mut hir_opaque_ty {
308                if concrete_type.ty != prev.ty {
309                    if let Ok(d) = prev.build_mismatch_error(&concrete_type, tcx) {
310                        d.emit();
311                    }
312                }
313            } else {
314                hir_opaque_ty = Some(concrete_type);
315            }
316        }
317    }
318
319    let mir_opaque_ty = tcx.mir_borrowck(owner_def_id).concrete_opaque_types.get(&def_id).copied();
320    if let Some(mir_opaque_ty) = mir_opaque_ty {
321        if mir_opaque_ty.references_error() {
322            return mir_opaque_ty.ty;
323        }
324
325        debug!(?owner_def_id);
326        let mut locator = RpitConstraintChecker { def_id, tcx, found: mir_opaque_ty };
327
328        match tcx.hir_node_by_def_id(owner_def_id) {
329            Node::Item(it) => intravisit::walk_item(&mut locator, it),
330            Node::ImplItem(it) => intravisit::walk_impl_item(&mut locator, it),
331            Node::TraitItem(it) => intravisit::walk_trait_item(&mut locator, it),
332            other => bug!("{:?} is not a valid scope for an opaque type item", other),
333        }
334
335        mir_opaque_ty.ty
336    } else if let Some(guar) = tables.tainted_by_errors {
337        // Some error in the owner fn prevented us from populating
338        // the `concrete_opaque_types` table.
339        Ty::new_error(tcx, guar)
340    } else {
341        // Fall back to the RPIT we inferred during HIR typeck
342        if let Some(hir_opaque_ty) = hir_opaque_ty {
343            hir_opaque_ty.ty
344        } else {
345            // We failed to resolve the opaque type or it
346            // resolves to itself. We interpret this as the
347            // no values of the hidden type ever being constructed,
348            // so we can just make the hidden type be `!`.
349            // For backwards compatibility reasons, we fall back to
350            // `()` until we the diverging default is changed.
351            Ty::new_diverging_default(tcx)
352        }
353    }
354}
355
356struct RpitConstraintChecker<'tcx> {
357    tcx: TyCtxt<'tcx>,
358
359    /// def_id of the opaque type whose defining uses are being checked
360    def_id: LocalDefId,
361
362    found: ty::OpaqueHiddenType<'tcx>,
363}
364
365impl RpitConstraintChecker<'_> {
366    #[instrument(skip(self), level = "debug")]
367    fn check(&self, def_id: LocalDefId) {
368        // Use borrowck to get the type with unerased regions.
369        let concrete_opaque_types = &self.tcx.mir_borrowck(def_id).concrete_opaque_types;
370        debug!(?concrete_opaque_types);
371        if let Some(&concrete_type) = concrete_opaque_types.get(&self.def_id) {
372            debug!(?concrete_type, "found constraint");
373            if concrete_type.ty != self.found.ty {
374                if let Ok(d) = self.found.build_mismatch_error(&concrete_type, self.tcx) {
375                    d.emit();
376                }
377            }
378        }
379    }
380}
381
382impl<'tcx> intravisit::Visitor<'tcx> for RpitConstraintChecker<'tcx> {
383    type NestedFilter = nested_filter::OnlyBodies;
384
385    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
386        self.tcx
387    }
388    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
389        intravisit::walk_expr(self, ex);
390    }
391    fn visit_item(&mut self, it: &'tcx Item<'tcx>) {
392        trace!(?it.owner_id);
393        // The opaque type itself or its children are not within its reveal scope.
394        if it.owner_id.def_id != self.def_id {
395            self.check(it.owner_id.def_id);
396            intravisit::walk_item(self, it);
397        }
398    }
399    fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) {
400        trace!(?it.owner_id);
401        // The opaque type itself or its children are not within its reveal scope.
402        if it.owner_id.def_id != self.def_id {
403            self.check(it.owner_id.def_id);
404            intravisit::walk_impl_item(self, it);
405        }
406    }
407    fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) {
408        trace!(?it.owner_id);
409        self.check(it.owner_id.def_id);
410        intravisit::walk_trait_item(self, it);
411    }
412}