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, DefiningScopeKind, Ty, TyCtxt, TypeVisitableExt};
7use tracing::{debug, instrument, trace};
8
9use crate::errors::{TaitForwardCompat2, UnconstrainedOpaqueType};
10
11/// Checks "defining uses" of opaque `impl Trait` in associated types.
12/// These can only be defined by associated items of the same trait.
13#[instrument(skip(tcx), level = "debug")]
14pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
15    tcx: TyCtxt<'_>,
16    def_id: LocalDefId,
17    opaque_types_from: DefiningScopeKind,
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, opaque_types_from };
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 { .. } => {
36                locator.check(assoc_id.expect_local())
37            }
38            // Associated types don't have bodies, so they can't constrain hidden types
39            ty::AssocKind::Type { .. } => {}
40        }
41    }
42
43    if let Some(hidden) = locator.found {
44        hidden.ty
45    } else {
46        let guar = tcx.dcx().emit_err(UnconstrainedOpaqueType {
47            span: tcx.def_span(def_id),
48            name: tcx.item_ident(parent_def_id.to_def_id()),
49            what: "impl",
50        });
51        Ty::new_error(tcx, guar)
52    }
53}
54
55/// Checks "defining uses" of opaque `impl Trait` types to ensure that they meet the restrictions
56/// laid for "higher-order pattern unification".
57/// This ensures that inference is tractable.
58/// In particular, definitions of opaque types can only use other generics as arguments,
59/// and they cannot repeat an argument. Example:
60///
61/// ```ignore (illustrative)
62/// type Foo<A, B> = impl Bar<A, B>;
63///
64/// // Okay -- `Foo` is applied to two distinct, generic types.
65/// fn a<T, U>() -> Foo<T, U> { .. }
66///
67/// // Not okay -- `Foo` is applied to `T` twice.
68/// fn b<T>() -> Foo<T, T> { .. }
69///
70/// // Not okay -- `Foo` is applied to a non-generic type.
71/// fn b<T>() -> Foo<T, u32> { .. }
72/// ```
73#[instrument(skip(tcx), level = "debug")]
74pub(super) fn find_opaque_ty_constraints_for_tait(
75    tcx: TyCtxt<'_>,
76    def_id: LocalDefId,
77    opaque_types_from: DefiningScopeKind,
78) -> Ty<'_> {
79    let mut locator = TaitConstraintLocator { def_id, tcx, found: None, opaque_types_from };
80
81    tcx.hir_walk_toplevel_module(&mut locator);
82
83    if let Some(hidden) = locator.found {
84        hidden.ty
85    } else {
86        let mut parent_def_id = def_id;
87        while tcx.def_kind(parent_def_id) == def::DefKind::OpaqueTy {
88            // Account for `type Alias = impl Trait<Foo = impl Trait>;` (#116031)
89            parent_def_id = tcx.local_parent(parent_def_id);
90        }
91        let guar = tcx.dcx().emit_err(UnconstrainedOpaqueType {
92            span: tcx.def_span(def_id),
93            name: tcx.item_ident(parent_def_id.to_def_id()),
94            what: "crate",
95        });
96        Ty::new_error(tcx, guar)
97    }
98}
99
100struct TaitConstraintLocator<'tcx> {
101    tcx: TyCtxt<'tcx>,
102
103    /// def_id of the opaque type whose defining uses are being checked
104    def_id: LocalDefId,
105
106    /// as we walk the defining uses, we are checking that all of them
107    /// define the same hidden type. This variable is set to `Some`
108    /// with the first type that we find, and then later types are
109    /// checked against it (we also carry the span of that first
110    /// type).
111    found: Option<ty::OpaqueHiddenType<'tcx>>,
112
113    opaque_types_from: DefiningScopeKind,
114}
115
116impl<'tcx> TaitConstraintLocator<'tcx> {
117    fn insert_found(&mut self, hidden_ty: ty::OpaqueHiddenType<'tcx>) {
118        if let Some(prev) = &mut self.found {
119            if hidden_ty.ty != prev.ty {
120                let (Ok(guar) | Err(guar)) =
121                    prev.build_mismatch_error(&hidden_ty, self.tcx).map(|d| d.emit());
122                prev.ty = Ty::new_error(self.tcx, guar);
123            }
124        } else {
125            self.found = Some(hidden_ty);
126        }
127    }
128
129    fn non_defining_use_in_defining_scope(&mut self, item_def_id: LocalDefId) {
130        let guar = self.tcx.dcx().emit_err(TaitForwardCompat2 {
131            span: self
132                .tcx
133                .def_ident_span(item_def_id)
134                .unwrap_or_else(|| self.tcx.def_span(item_def_id)),
135            opaque_type_span: self.tcx.def_span(self.def_id),
136            opaque_type: self.tcx.def_path_str(self.def_id),
137        });
138        self.insert_found(ty::OpaqueHiddenType::new_error(self.tcx, guar));
139    }
140
141    #[instrument(skip(self), level = "debug")]
142    fn check(&mut self, item_def_id: LocalDefId) {
143        // Don't try to check items that cannot possibly constrain the type.
144        let tcx = self.tcx;
145        if !tcx.has_typeck_results(item_def_id) {
146            debug!("no constraint: no typeck results");
147            return;
148        }
149
150        let opaque_types_defined_by = tcx.opaque_types_defined_by(item_def_id);
151        // Don't try to check items that cannot possibly constrain the type.
152        if !opaque_types_defined_by.contains(&self.def_id) {
153            debug!("no constraint: no opaque types defined");
154            return;
155        }
156
157        // Function items with `_` in their return type already emit an error, skip any
158        // "non-defining use" errors for them.
159        // Note that we use `Node::fn_sig` instead of `Node::fn_decl` here, because the former
160        // excludes closures, which are allowed to have `_` in their return type.
161        let hir_node = tcx.hir_node_by_def_id(item_def_id);
162        debug_assert!(
163            !matches!(hir_node, Node::ForeignItem(..)),
164            "foreign items cannot constrain opaque types",
165        );
166        if let Some(hir_sig) = hir_node.fn_sig()
167            && hir_sig.decl.output.is_suggestable_infer_ty().is_some()
168        {
169            let guar = self.tcx.dcx().span_delayed_bug(
170                hir_sig.decl.output.span(),
171                "inferring return types and opaque types do not mix well",
172            );
173            self.found = Some(ty::OpaqueHiddenType::new_error(tcx, guar));
174            return;
175        }
176
177        match self.opaque_types_from {
178            DefiningScopeKind::HirTypeck => {
179                let tables = tcx.typeck(item_def_id);
180                if let Some(guar) = tables.tainted_by_errors {
181                    self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar));
182                } else if let Some(&hidden_type) = tables.concrete_opaque_types.get(&self.def_id) {
183                    self.insert_found(hidden_type);
184                } else {
185                    self.non_defining_use_in_defining_scope(item_def_id);
186                }
187            }
188            DefiningScopeKind::MirBorrowck => match tcx.mir_borrowck(item_def_id) {
189                Err(guar) => self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar)),
190                Ok(concrete_opaque_types) => {
191                    if let Some(&hidden_type) = concrete_opaque_types.0.get(&self.def_id) {
192                        debug!(?hidden_type, "found constraint");
193                        self.insert_found(hidden_type);
194                    } else if let Err(guar) = tcx
195                        .type_of_opaque_hir_typeck(self.def_id)
196                        .instantiate_identity()
197                        .error_reported()
198                    {
199                        self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar));
200                    } else {
201                        self.non_defining_use_in_defining_scope(item_def_id);
202                    }
203                }
204            },
205        }
206    }
207}
208
209impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
210    type NestedFilter = nested_filter::All;
211
212    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
213        self.tcx
214    }
215    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
216        intravisit::walk_expr(self, ex);
217    }
218    fn visit_item(&mut self, it: &'tcx Item<'tcx>) {
219        trace!(?it.owner_id);
220        self.check(it.owner_id.def_id);
221        intravisit::walk_item(self, it);
222    }
223    fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) {
224        trace!(?it.owner_id);
225        self.check(it.owner_id.def_id);
226        intravisit::walk_impl_item(self, it);
227    }
228    fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) {
229        trace!(?it.owner_id);
230        self.check(it.owner_id.def_id);
231        intravisit::walk_trait_item(self, it);
232    }
233    fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
234        trace!(?it.owner_id);
235        assert_ne!(it.owner_id.def_id, self.def_id);
236        // No need to call `check`, as we do not run borrowck on foreign items.
237        intravisit::walk_foreign_item(self, it);
238    }
239}
240
241pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>(
242    tcx: TyCtxt<'tcx>,
243    def_id: LocalDefId,
244    owner_def_id: LocalDefId,
245    opaque_types_from: DefiningScopeKind,
246) -> Ty<'tcx> {
247    match opaque_types_from {
248        DefiningScopeKind::HirTypeck => {
249            let tables = tcx.typeck(owner_def_id);
250            if let Some(guar) = tables.tainted_by_errors {
251                Ty::new_error(tcx, guar)
252            } else if let Some(hidden_ty) = tables.concrete_opaque_types.get(&def_id) {
253                hidden_ty.ty
254            } else {
255                // FIXME(-Znext-solver): This should not be necessary and we should
256                // instead rely on inference variable fallback inside of typeck itself.
257
258                // We failed to resolve the opaque type or it
259                // resolves to itself. We interpret this as the
260                // no values of the hidden type ever being constructed,
261                // so we can just make the hidden type be `!`.
262                // For backwards compatibility reasons, we fall back to
263                // `()` until we the diverging default is changed.
264                Ty::new_diverging_default(tcx)
265            }
266        }
267        DefiningScopeKind::MirBorrowck => match tcx.mir_borrowck(owner_def_id) {
268            Ok(concrete_opaque_types) => {
269                if let Some(hidden_ty) = concrete_opaque_types.0.get(&def_id) {
270                    hidden_ty.ty
271                } else {
272                    let hir_ty = tcx.type_of_opaque_hir_typeck(def_id).instantiate_identity();
273                    if let Err(guar) = hir_ty.error_reported() {
274                        Ty::new_error(tcx, guar)
275                    } else {
276                        hir_ty
277                    }
278                }
279            }
280            Err(guar) => Ty::new_error(tcx, guar),
281        },
282    }
283}