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_index::bit_set::GrowableBitSet;
5use rustc_type_ir::inherent::*;
6use rustc_type_ir::{self as ty, Interner, TypingMode, fold_regions};
7
8use crate::delegate::SolverDelegate;
9use crate::solve::{Certainty, EvalCtxt, Goal, NoSolution, QueryResult, inspect};
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                self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
35            }
36            TypingMode::Analysis { defining_opaque_types } => {
37                let Some(def_id) = opaque_ty
38                    .def_id
39                    .as_local()
40                    .filter(|&def_id| defining_opaque_types.contains(&def_id))
41                else {
42                    self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
43                    return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
44                };
45
46                // FIXME: This may have issues when the args contain aliases...
47                match uses_unique_placeholders_ignoring_regions(self.cx(), opaque_ty.args) {
48                    Err(NotUniqueParam::NotParam(param)) if param.is_non_region_infer() => {
49                        return self.evaluate_added_goals_and_make_canonical_response(
50                            Certainty::AMBIGUOUS,
51                        );
52                    }
53                    Err(_) => {
54                        return Err(NoSolution);
55                    }
56                    Ok(()) => {}
57                }
58                // Prefer opaques registered already.
59                let opaque_type_key = ty::OpaqueTypeKey { def_id, args: opaque_ty.args };
60                // FIXME: This also unifies the previous hidden type with the expected.
61                //
62                // If that fails, we insert `expected` as a new hidden type instead of
63                // eagerly emitting an error.
64                let existing = self.probe_existing_opaque_ty(opaque_type_key);
65                if let Some((candidate_key, candidate_ty)) = existing {
66                    return self
67                        .probe(|result| inspect::ProbeKind::OpaqueTypeStorageLookup {
68                            result: *result,
69                        })
70                        .enter(|ecx| {
71                            for (a, b) in std::iter::zip(
72                                candidate_key.args.iter(),
73                                opaque_type_key.args.iter(),
74                            ) {
75                                ecx.eq(goal.param_env, a, b)?;
76                            }
77                            ecx.eq(goal.param_env, candidate_ty, expected)?;
78                            ecx.add_item_bounds_for_hidden_type(
79                                def_id.into(),
80                                candidate_key.args,
81                                goal.param_env,
82                                candidate_ty,
83                            );
84                            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
85                        });
86                }
87
88                // Otherwise, define a new opaque type
89                // FIXME: should we use `inject_hidden_type_unchecked` here?
90                self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?;
91                self.add_item_bounds_for_hidden_type(
92                    def_id.into(),
93                    opaque_ty.args,
94                    goal.param_env,
95                    expected,
96                );
97                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
98            }
99            TypingMode::PostBorrowckAnalysis { defined_opaque_types } => {
100                let Some(def_id) = opaque_ty
101                    .def_id
102                    .as_local()
103                    .filter(|&def_id| defined_opaque_types.contains(&def_id))
104                else {
105                    self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
106                    return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
107                };
108
109                let actual = cx.type_of(def_id.into()).instantiate(cx, opaque_ty.args);
110                // FIXME: Actually use a proper binder here instead of relying on `ReErased`.
111                //
112                // This is also probably unsound or sth :shrug:
113                let actual = fold_regions(cx, actual, |re, _dbi| match re.kind() {
114                    ty::ReErased => self.next_region_var(),
115                    _ => re,
116                });
117                self.eq(goal.param_env, expected, actual)?;
118                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
119            }
120            TypingMode::PostAnalysis => {
121                // FIXME: Add an assertion that opaque type storage is empty.
122                let actual = cx.type_of(opaque_ty.def_id).instantiate(cx, opaque_ty.args);
123                self.eq(goal.param_env, expected, actual)?;
124                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
125            }
126        }
127    }
128}
129
130/// Checks whether each generic argument is simply a unique generic placeholder.
131///
132/// FIXME: Interner argument is needed to constrain the `I` parameter.
133fn uses_unique_placeholders_ignoring_regions<I: Interner>(
134    _cx: I,
135    args: I::GenericArgs,
136) -> Result<(), NotUniqueParam<I>> {
137    let mut seen = GrowableBitSet::default();
138    for arg in args.iter() {
139        match arg.kind() {
140            // Ignore regions, since we can't resolve those in a canonicalized
141            // query in the trait solver.
142            ty::GenericArgKind::Lifetime(_) => {}
143            ty::GenericArgKind::Type(t) => match t.kind() {
144                ty::Placeholder(p) => {
145                    if !seen.insert(p.var()) {
146                        return Err(NotUniqueParam::DuplicateParam(t.into()));
147                    }
148                }
149                _ => return Err(NotUniqueParam::NotParam(t.into())),
150            },
151            ty::GenericArgKind::Const(c) => match c.kind() {
152                ty::ConstKind::Placeholder(p) => {
153                    if !seen.insert(p.var()) {
154                        return Err(NotUniqueParam::DuplicateParam(c.into()));
155                    }
156                }
157                _ => return Err(NotUniqueParam::NotParam(c.into())),
158            },
159        }
160    }
161
162    Ok(())
163}
164
165// FIXME: This should check for dupes and non-params first, then infer vars.
166enum NotUniqueParam<I: Interner> {
167    DuplicateParam(I::GenericArg),
168    NotParam(I::GenericArg),
169}