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                        _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
97                    }
98                }
99
100                self.add_item_bounds_for_hidden_type(
101                    def_id.into(),
102                    normalized_args,
103                    goal.param_env,
104                    expected,
105                );
106                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
107            }
108            TypingMode::PostBorrowckAnalysis { defined_opaque_types } => {
109                let Some(def_id) = opaque_ty
110                    .def_id
111                    .as_local()
112                    .filter(|&def_id| defined_opaque_types.contains(&def_id))
113                else {
114                    self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
115                    return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
116                };
117
118                let actual = cx.type_of(def_id.into()).instantiate(cx, opaque_ty.args);
119                // FIXME: Actually use a proper binder here instead of relying on `ReErased`.
120                //
121                // This is also probably unsound or sth :shrug:
122                let actual = fold_regions(cx, actual, |re, _dbi| match re.kind() {
123                    ty::ReErased => self.next_region_var(),
124                    _ => re,
125                });
126                self.eq(goal.param_env, expected, actual)?;
127                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
128            }
129            TypingMode::PostAnalysis => {
130                // FIXME: Add an assertion that opaque type storage is empty.
131                let actual = cx.type_of(opaque_ty.def_id).instantiate(cx, opaque_ty.args);
132                self.eq(goal.param_env, expected, actual)?;
133                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
134            }
135        }
136    }
137}