Skip to main content

hir_ty/infer/
opaques.rs

1//! Defining opaque types via inference.
2
3use rustc_type_ir::{TypeVisitableExt, fold_regions};
4use tracing::{debug, instrument};
5
6use crate::{
7    Span,
8    infer::InferenceContext,
9    next_solver::{
10        EarlyBinder, OpaqueTypeKey, SolverDefId, TypingMode,
11        infer::{opaque_types::OpaqueHiddenType, traits::ObligationCause},
12    },
13};
14
15impl<'db> InferenceContext<'db> {
16    /// This takes all the opaque type uses during HIR typeck. It first computes
17    /// the concrete hidden type by iterating over all defining uses.
18    ///
19    /// A use during HIR typeck is defining if all non-lifetime arguments are
20    /// unique generic parameters and the hidden type does not reference any
21    /// inference variables.
22    ///
23    /// It then uses these defining uses to guide inference for all other uses.
24    #[instrument(level = "debug", skip(self))]
25    pub(super) fn handle_opaque_type_uses(&mut self) {
26        // We clone the opaques instead of stealing them here as they are still used for
27        // normalization in the next generation trait solver.
28        let opaque_types: Vec<_> = self.table.infer_ctxt.clone_opaque_types();
29
30        self.compute_definition_site_hidden_types(opaque_types);
31    }
32}
33
34#[expect(unused, reason = "rustc has this")]
35#[derive(Copy, Clone, Debug)]
36enum UsageKind<'db> {
37    None,
38    NonDefiningUse(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>),
39    UnconstrainedHiddenType(OpaqueHiddenType<'db>),
40    HasDefiningUse(OpaqueHiddenType<'db>),
41}
42
43impl<'db> UsageKind<'db> {
44    fn merge(&mut self, other: UsageKind<'db>) {
45        match (&*self, &other) {
46            (UsageKind::HasDefiningUse(_), _) | (_, UsageKind::None) => unreachable!(),
47            (UsageKind::None, _) => *self = other,
48            // When mergining non-defining uses, prefer earlier ones. This means
49            // the error happens as early as possible.
50            (
51                UsageKind::NonDefiningUse(..) | UsageKind::UnconstrainedHiddenType(..),
52                UsageKind::NonDefiningUse(..),
53            ) => {}
54            // When merging unconstrained hidden types, we prefer later ones. This is
55            // used as in most cases, the defining use is the final return statement
56            // of our function, and other uses with defining arguments are likely not
57            // intended to be defining.
58            (
59                UsageKind::NonDefiningUse(..) | UsageKind::UnconstrainedHiddenType(..),
60                UsageKind::UnconstrainedHiddenType(..) | UsageKind::HasDefiningUse(_),
61            ) => *self = other,
62        }
63    }
64}
65
66impl<'db> InferenceContext<'db> {
67    fn compute_definition_site_hidden_types(
68        &mut self,
69        mut opaque_types: Vec<(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)>,
70    ) {
71        for entry in opaque_types.iter_mut() {
72            *entry = self.resolve_vars_if_possible(*entry);
73        }
74        debug!(?opaque_types);
75
76        let interner = self.interner();
77        let TypingMode::Analysis { defining_opaque_types_and_generators } =
78            self.table.infer_ctxt.typing_mode_raw()
79        else {
80            unreachable!();
81        };
82
83        for def_id in defining_opaque_types_and_generators {
84            let def_id = match def_id {
85                SolverDefId::InternedOpaqueTyId(it) => it,
86                _ => continue,
87            };
88
89            // We do actually need to check this the second pass (we can't just
90            // store this), because we can go from `UnconstrainedHiddenType` to
91            // `HasDefiningUse` (because of fallback)
92            let mut usage_kind = UsageKind::None;
93            for &(opaque_type_key, hidden_type) in &opaque_types {
94                if opaque_type_key.def_id != def_id.into() {
95                    continue;
96                }
97
98                usage_kind.merge(self.consider_opaque_type_use(opaque_type_key, hidden_type));
99
100                if let UsageKind::HasDefiningUse(..) = usage_kind {
101                    break;
102                }
103            }
104
105            if let UsageKind::HasDefiningUse(ty) = usage_kind {
106                for &(opaque_type_key, hidden_type) in &opaque_types {
107                    if opaque_type_key.def_id != def_id.into() {
108                        continue;
109                    }
110
111                    let expected = EarlyBinder::bind(ty.ty)
112                        .instantiate(interner, opaque_type_key.args)
113                        .skip_norm_wip();
114                    _ = self.demand_eqtype_fixme_no_diag(expected, hidden_type.ty);
115                }
116
117                self.result.type_of_opaque.insert(def_id, ty.ty.store());
118
119                continue;
120            }
121
122            self.result.type_of_opaque.insert(def_id, self.types.types.error.store());
123        }
124    }
125
126    #[tracing::instrument(skip(self), ret)]
127    fn consider_opaque_type_use(
128        &self,
129        opaque_type_key: OpaqueTypeKey<'db>,
130        hidden_type: OpaqueHiddenType<'db>,
131    ) -> UsageKind<'db> {
132        // We ignore uses of the opaque if they have any inference variables
133        // as this can frequently happen with recursive calls.
134        //
135        // See `tests/ui/traits/next-solver/opaques/universal-args-non-defining.rs`.
136        if hidden_type.ty.has_non_region_infer() {
137            return UsageKind::UnconstrainedHiddenType(hidden_type);
138        }
139
140        // FIXME: This should not use a dummy span.
141        let cause = ObligationCause::new(Span::Dummy);
142        let at = self.table.infer_ctxt.at(&cause, self.table.param_env);
143        let hidden_type = match at.deeply_normalize(hidden_type) {
144            Ok(hidden_type) => hidden_type,
145            Err(_errors) => OpaqueHiddenType { ty: self.types.types.error },
146        };
147        let hidden_type =
148            fold_regions(self.interner(), hidden_type, |_, _| self.types.regions.erased);
149        UsageKind::HasDefiningUse(hidden_type)
150    }
151}