Skip to main content

rustc_next_trait_solver/solve/project_goals/
opaque_types.rs

1//! Computes a 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, QueryResultOrRerunNonErased, RerunReason};
6use rustc_type_ir::{self as ty, Interner, MayBeErased, TypingMode, fold_regions};
7
8use crate::delegate::SolverDelegate;
9use crate::solve::{Certainty, EvalCtxt, Goal};
10
11impl<D, I> EvalCtxt<'_, D>
12where
13    D: SolverDelegate<Interner = I>,
14    I: Interner,
15{
16    {}
#[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL &&
                ::tracing::Level::INFO <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("normalize_opaque_type",
                                    "rustc_next_trait_solver::solve::project_goals::opaque_types",
                                    ::tracing::Level::INFO,
                                    ::tracing_core::__macro_support::Option::Some("/rustc-dev/215a8af4bb4c106cccf6d6535f84eaae91818265/compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs"),
                                    ::tracing_core::__macro_support::Option::Some(16u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_next_trait_solver::solve::project_goals::opaque_types"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("goal")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("goal");
                                                        NAME.as_str()
                                                    }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::INFO <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::INFO <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&goal)
                                                            as &dyn ::tracing::field::Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: QueryResultOrRerunNonErased<I> =
                loop {};
            return __tracing_attr_fake_return;
        }
        {
            let cx = self.cx();
            let opaque_ty = goal.predicate.projection_term;
            let expected =
                goal.predicate.term.as_type().expect("no such thing as an opaque const");
            let def_id = opaque_ty.expect_opaque_ty_def_id();
            match self.typing_mode() {
                TypingMode::Coherence => {
                    self.add_item_bounds_for_hidden_type(def_id, opaque_ty.args,
                            goal.param_env, expected)?;
                    self.add_goal(GoalSource::Misc,
                            goal.with(cx, ty::PredicateKind::Ambiguous))?;
                    self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes).map_err(Into::into)
                }
                TypingMode::Typeck {
                    defining_opaque_types_and_generators: defining_opaque_types
                    } | TypingMode::PostTypeckUntilBorrowck {
                    defining_opaque_types } => {
                    let Some(def_id) =
                        def_id.as_local().filter(|&def_id|
                                defining_opaque_types.contains(&def_id.into())) else {
                            self.eq(goal.param_env,
                                    opaque_ty.to_term(cx, ty::IsRigid::Yes),
                                    goal.predicate.term)?;
                            return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes).map_err(Into::into);
                        };
                    let normalized_args =
                        cx.mk_args_from_iter(opaque_ty.args.iter().map(|arg|
                                        match arg.kind() {
                                            ty::GenericArgKind::Lifetime(lt) => Ok(lt.into()),
                                            ty::GenericArgKind::Type(ty) => {
                                                self.structurally_normalize_ty(goal.param_env,
                                                        ty).map(Into::into)
                                            }
                                            ty::GenericArgKind::Const(ct) => {
                                                self.structurally_normalize_const(goal.param_env,
                                                        ct).map(Into::into)
                                            }
                                        }))?;
                    let opaque_type_key =
                        ty::OpaqueTypeKey { def_id, args: normalized_args };
                    if let Some(prev) =
                            self.register_hidden_type_in_storage(opaque_type_key,
                                expected) {
                        self.eq(goal.param_env, expected, prev)?;
                    } else {
                        match self.typing_mode().assert_not_erased() {
                            TypingMode::Typeck { .. } => {}
                            TypingMode::PostTypeckUntilBorrowck { .. } => {
                                let actual =
                                    cx.type_of_opaque_hir_typeck(def_id).instantiate(cx,
                                        opaque_ty.args);
                                let actual =
                                    actual.map(|v|
                                            {
                                                fold_regions(cx, v,
                                                    |re, _dbi|
                                                        match re.kind() {
                                                            ty::ReErased => self.next_region_var(),
                                                            _ => re,
                                                        })
                                            });
                                let actual =
                                    self.normalize(GoalSource::Misc, goal.param_env, actual)?;
                                self.eq(goal.param_env, expected, actual)?;
                            }
                            TypingMode::Coherence | TypingMode::PostBorrowck { .. } |
                                TypingMode::PostAnalysis | TypingMode::Reflection |
                                TypingMode::Codegen =>
                                ::core::panicking::panic("internal error: entered unreachable code"),
                        }
                    }
                    self.add_item_bounds_for_hidden_type(def_id.into(),
                            normalized_args, goal.param_env, expected)?;
                    self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes).map_err(Into::into)
                }
                TypingMode::PostBorrowck { defined_opaque_types } => {
                    let Some(def_id) =
                        def_id.as_local().filter(|&def_id|
                                defined_opaque_types.contains(&def_id.into())) else {
                            self.eq(goal.param_env,
                                    opaque_ty.to_term(cx, ty::IsRigid::Yes),
                                    goal.predicate.term)?;
                            return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes).map_err(Into::into);
                        };
                    let actual =
                        cx.type_of(def_id.into()).instantiate(cx, opaque_ty.args);
                    let actual =
                        actual.map(|v|
                                {
                                    fold_regions(cx, v,
                                        |re, _dbi|
                                            match re.kind() {
                                                ty::ReErased => self.next_region_var(),
                                                _ => re,
                                            })
                                });
                    let actual =
                        self.normalize(GoalSource::Misc, goal.param_env, actual)?;
                    self.eq(goal.param_env, expected, actual)?;
                    self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes).map_err(Into::into)
                }
                TypingMode::Reflection | TypingMode::PostAnalysis |
                    TypingMode::Codegen => {
                    let actual =
                        cx.type_of(def_id.into()).instantiate(cx, opaque_ty.args);
                    let actual =
                        self.normalize(GoalSource::Misc, goal.param_env, actual)?;
                    self.eq(goal.param_env, expected, actual)?;
                    self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes).map_err(Into::into)
                }
                TypingMode::ErasedNotCoherence(MayBeErased) => {
                    if def_id.as_local().is_some() {
                        match self.opaque_accesses.rerun_always(RerunReason::NormalizeOpaqueType)?
                            {}
                    } else {
                        self.opaque_accesses.rerun_if_in_post_analysis(RerunReason::NormalizeOpaqueTypeRemoteCrate)?;
                    }
                    self.eq(goal.param_env,
                            opaque_ty.to_term(cx, ty::IsRigid::Yes),
                            goal.predicate.term)?;
                    self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes).map_err(Into::into)
                }
            }
        }
    }
}#[tracing::instrument(skip(self))]
17    pub(super) fn normalize_opaque_type(
18        &mut self,
19        goal: Goal<I, ty::ProjectionClause<I>>,
20    ) -> QueryResultOrRerunNonErased<I> {
21        let cx = self.cx();
22        let opaque_ty = goal.predicate.projection_term;
23        let expected = goal.predicate.term.as_type().expect("no such thing as an opaque const");
24        let def_id = opaque_ty.expect_opaque_ty_def_id();
25
26        match self.typing_mode() {
27            TypingMode::Coherence => {
28                // An impossible opaque type bound is the only way this goal will fail
29                // e.g. assigning `impl Copy := NotCopy`
30                self.add_item_bounds_for_hidden_type(
31                    def_id,
32                    opaque_ty.args,
33                    goal.param_env,
34                    expected,
35                )?;
36                // Trying to normalize an opaque type during coherence is always ambiguous.
37                // We add a nested ambiguous goal here instead of using `Certainty::AMBIGUOUS`.
38                // This allows us to return the nested goals to the parent `AliasRelate` goal.
39                // This can then allow nested goals to fail after we've constrained the `term`.
40                self.add_goal(GoalSource::Misc, goal.with(cx, ty::PredicateKind::Ambiguous))?;
41                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
42                    .map_err(Into::into)
43            }
44            TypingMode::Typeck { defining_opaque_types_and_generators: defining_opaque_types }
45            | TypingMode::PostTypeckUntilBorrowck { defining_opaque_types } => {
46                let Some(def_id) = def_id
47                    .as_local()
48                    .filter(|&def_id| defining_opaque_types.contains(&def_id.into()))
49                else {
50                    // If we're not in the defining scope, treat the alias as rigid.
51                    self.eq(
52                        goal.param_env,
53                        opaque_ty.to_term(cx, ty::IsRigid::Yes),
54                        goal.predicate.term,
55                    )?;
56                    return self
57                        .evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
58                        .map_err(Into::into);
59                };
60
61                // We structurally normalize the args so that we're able to detect defining uses
62                // later on.
63                //
64                // This reduces the amount of duplicate definitions in the `opaque_type_storage` and
65                // strengthens inference. This causes us to subtly depend on the normalization behavior
66                // when inferring the hidden type of opaques.
67                //
68                // E.g. it's observable that we don't normalize nested aliases with bound vars in
69                // `structurally_normalize` and because we use structural lookup, we also don't
70                // reuse an entry for `Tait<for<'a> fn(&'a ())>` for `Tait<for<'b> fn(&'b ())>`.
71                let normalized_args =
72                    cx.mk_args_from_iter(opaque_ty.args.iter().map(|arg| match arg.kind() {
73                        ty::GenericArgKind::Lifetime(lt) => Ok(lt.into()),
74                        ty::GenericArgKind::Type(ty) => {
75                            self.structurally_normalize_ty(goal.param_env, ty).map(Into::into)
76                        }
77                        ty::GenericArgKind::Const(ct) => {
78                            self.structurally_normalize_const(goal.param_env, ct).map(Into::into)
79                        }
80                    }))?;
81
82                let opaque_type_key = ty::OpaqueTypeKey { def_id, args: normalized_args };
83                if let Some(prev) = self.register_hidden_type_in_storage(opaque_type_key, expected)
84                {
85                    self.eq(goal.param_env, expected, prev)?;
86                } else {
87                    // During HIR typeck, opaque types start out as unconstrained
88                    // inference variables. In borrowck we instead use the type
89                    // computed in HIR typeck as the initial value.
90                    match self.typing_mode().assert_not_erased() {
91                        TypingMode::Typeck { .. } => {}
92                        TypingMode::PostTypeckUntilBorrowck { .. } => {
93                            let actual = cx
94                                .type_of_opaque_hir_typeck(def_id)
95                                .instantiate(cx, opaque_ty.args);
96                            let actual = actual.map(|v| {
97                                fold_regions(cx, v, |re, _dbi| match re.kind() {
98                                    ty::ReErased => self.next_region_var(),
99                                    _ => re,
100                                })
101                            });
102                            let actual =
103                                self.normalize(GoalSource::Misc, goal.param_env, actual)?;
104                            self.eq(goal.param_env, expected, actual)?;
105                        }
106                        TypingMode::Coherence
107                        | TypingMode::PostBorrowck { .. }
108                        | TypingMode::PostAnalysis
109                        | TypingMode::Reflection
110                        | TypingMode::Codegen => unreachable!(),
111                    }
112                }
113
114                self.add_item_bounds_for_hidden_type(
115                    def_id.into(),
116                    normalized_args,
117                    goal.param_env,
118                    expected,
119                )?;
120                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
121                    .map_err(Into::into)
122            }
123            TypingMode::PostBorrowck { defined_opaque_types } => {
124                let Some(def_id) = def_id
125                    .as_local()
126                    .filter(|&def_id| defined_opaque_types.contains(&def_id.into()))
127                else {
128                    self.eq(
129                        goal.param_env,
130                        opaque_ty.to_term(cx, ty::IsRigid::Yes),
131                        goal.predicate.term,
132                    )?;
133                    return self
134                        .evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
135                        .map_err(Into::into);
136                };
137
138                let actual = cx.type_of(def_id.into()).instantiate(cx, opaque_ty.args);
139                // FIXME: Actually use a proper binder here instead of relying on `ReErased`.
140                //
141                // This is also probably unsound or sth :shrug:
142                let actual = actual.map(|v| {
143                    fold_regions(cx, v, |re, _dbi| match re.kind() {
144                        ty::ReErased => self.next_region_var(),
145                        _ => re,
146                    })
147                });
148                let actual = self.normalize(GoalSource::Misc, goal.param_env, actual)?;
149                self.eq(goal.param_env, expected, actual)?;
150                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
151                    .map_err(Into::into)
152            }
153            // FIXME(try_as_dyn): probably want to treat opaques opaquely and rigid
154            TypingMode::Reflection | TypingMode::PostAnalysis | TypingMode::Codegen => {
155                // FIXME: Add an assertion that opaque type storage is empty.
156                let actual = cx.type_of(def_id.into()).instantiate(cx, opaque_ty.args);
157                let actual = self.normalize(GoalSource::Misc, goal.param_env, actual)?;
158                self.eq(goal.param_env, expected, actual)?;
159                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
160                    .map_err(Into::into)
161            }
162            TypingMode::ErasedNotCoherence(MayBeErased) => {
163                // If we have a local defid, in other typing modes we check whether
164                // this is the defining scope, and otherwise treat it as rigid.
165                // However, in `ErasedNotcoherence` we *always* treat it as rigid.
166                // This is the same as other modes if def_id is None, but wrong if we do have a DefId.
167                // Thus we should rerun if the opaque is in the defining scope. And we should do
168                // so immediately, otherwise we might continue evaluating with rigid opaques that
169                // should be revealed and trigger query cycle when leaking it for auto trait.
170                // See `tests/ui/traits/next-solver/normalize/always-rerun-normalizing-opaques.rs`.
171                //
172                // FIXME: verify that we can get away with not rerunning immediately when
173                // normalizing foreign opaques. Currently we don't do so because that would greatly
174                // worsen the compilation time of `wg-grammar`.
175                if def_id.as_local().is_some() {
176                    match self.opaque_accesses.rerun_always(RerunReason::NormalizeOpaqueType)? {}
177                } else {
178                    self.opaque_accesses
179                        .rerun_if_in_post_analysis(RerunReason::NormalizeOpaqueTypeRemoteCrate)?;
180                }
181
182                // Always treat the opaque type as rigid.
183                self.eq(
184                    goal.param_env,
185                    opaque_ty.to_term(cx, ty::IsRigid::Yes),
186                    goal.predicate.term,
187                )?;
188                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
189                    .map_err(Into::into)
190            }
191        }
192    }
193}