Skip to main content

rustc_next_trait_solver/solve/normalizes_to/
opaque_types.rs

1//! Computes a normalizes-to (projection) goal for opaque types. This goal
2//! behaves differently depending on the current `TypingMode`.
3
4use rustc_type_ir::inherent::*;
5use rustc_type_ir::solve::GoalSource;
6use rustc_type_ir::{self as ty, Interner, TypingMode, fold_regions};
7
8use crate::delegate::SolverDelegate;
9use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult};
10
11impl<D, I> EvalCtxt<'_, D>
12where
13    D: SolverDelegate<Interner = I>,
14    I: Interner,
15{
16    pub(super) fn normalize_opaque_type(
17        &mut self,
18        goal: Goal<I, ty::NormalizesTo<I>>,
19    ) -> QueryResult<I> {
20        let cx = self.cx();
21        let opaque_ty = goal.predicate.alias;
22        let expected = goal.predicate.term.as_type().expect("no such thing as an opaque const");
23
24        match self.typing_mode() {
25            TypingMode::Coherence => {
26                // An impossible opaque type bound is the only way this goal will fail
27                // e.g. assigning `impl Copy := NotCopy`
28                self.add_item_bounds_for_hidden_type(
29                    opaque_ty.def_id,
30                    opaque_ty.args,
31                    goal.param_env,
32                    expected,
33                );
34                // Trying to normalize an opaque type during coherence is always ambiguous.
35                // We add a nested ambiguous goal here instead of using `Certainty::AMBIGUOUS`.
36                // This allows us to return the nested goals to the parent `AliasRelate` goal.
37                // This can then allow nested goals to fail after we've constrained the `term`.
38                self.add_goal(GoalSource::Misc, goal.with(cx, ty::PredicateKind::Ambiguous));
39                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
40            }
41            TypingMode::Analysis {
42                defining_opaque_types_and_generators: defining_opaque_types,
43            }
44            | TypingMode::Borrowck { defining_opaque_types } => {
45                let Some(def_id) = opaque_ty
46                    .def_id
47                    .as_local()
48                    .filter(|&def_id| defining_opaque_types.contains(&def_id))
49                else {
50                    // If we're not in the defining scope, treat the alias as rigid.
51                    self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
52                    return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
53                };
54
55                // We structurally normalize the args so that we're able to detect defining uses
56                // later on.
57                //
58                // This reduces the amount of duplicate definitions in the `opaque_type_storage` and
59                // strengthens inference. This causes us to subtly depend on the normalization behavior
60                // when inferring the hidden type of opaques.
61                //
62                // E.g. it's observable that we don't normalize nested aliases with bound vars in
63                // `structurally_normalize` and because we use structural lookup, we also don't
64                // reuse an entry for `Tait<for<'a> fn(&'a ())>` for `Tait<for<'b> fn(&'b ())>`.
65                let normalized_args =
66                    cx.mk_args_from_iter(opaque_ty.args.iter().map(|arg| match arg.kind() {
67                        ty::GenericArgKind::Lifetime(lt) => Ok(lt.into()),
68                        ty::GenericArgKind::Type(ty) => {
69                            self.structurally_normalize_ty(goal.param_env, ty).map(Into::into)
70                        }
71                        ty::GenericArgKind::Const(ct) => {
72                            self.structurally_normalize_const(goal.param_env, ct).map(Into::into)
73                        }
74                    }))?;
75
76                let opaque_type_key = ty::OpaqueTypeKey { def_id, args: normalized_args };
77                if let Some(prev) = self.register_hidden_type_in_storage(opaque_type_key, expected)
78                {
79                    self.eq(goal.param_env, expected, prev)?;
80                } else {
81                    // During HIR typeck, opaque types start out as unconstrained
82                    // inference variables. In borrowck we instead use the type
83                    // computed in HIR typeck as the initial value.
84                    match self.typing_mode() {
85                        TypingMode::Analysis { .. } => {}
86                        TypingMode::Borrowck { .. } => {
87                            let actual = cx
88                                .type_of_opaque_hir_typeck(def_id)
89                                .instantiate(cx, opaque_ty.args);
90                            let actual = fold_regions(cx, actual, |re, _dbi| match re.kind() {
91                                ty::ReErased => self.next_region_var(),
92                                _ => re,
93                            });
94                            self.eq(goal.param_env, expected, actual)?;
95                        }
96                        TypingMode::Coherence
97                        | TypingMode::PostBorrowckAnalysis { .. }
98                        | TypingMode::PostAnalysis => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
99                    }
100                }
101
102                self.add_item_bounds_for_hidden_type(
103                    def_id.into(),
104                    normalized_args,
105                    goal.param_env,
106                    expected,
107                );
108                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
109            }
110            TypingMode::PostBorrowckAnalysis { defined_opaque_types } => {
111                let Some(def_id) = opaque_ty
112                    .def_id
113                    .as_local()
114                    .filter(|&def_id| defined_opaque_types.contains(&def_id))
115                else {
116                    self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
117                    return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
118                };
119
120                let actual = cx.type_of(def_id.into()).instantiate(cx, opaque_ty.args);
121                // FIXME: Actually use a proper binder here instead of relying on `ReErased`.
122                //
123                // This is also probably unsound or sth :shrug:
124                let actual = fold_regions(cx, actual, |re, _dbi| match re.kind() {
125                    ty::ReErased => self.next_region_var(),
126                    _ => re,
127                });
128                self.eq(goal.param_env, expected, actual)?;
129                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
130            }
131            TypingMode::PostAnalysis => {
132                // FIXME: Add an assertion that opaque type storage is empty.
133                let actual = cx.type_of(opaque_ty.def_id).instantiate(cx, opaque_ty.args);
134                self.eq(goal.param_env, expected, actual)?;
135                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
136            }
137        }
138    }
139}