Skip to main content

rustc_next_trait_solver/solve/
effect_goals.rs

1//! Dealing with host effect goals, i.e. enforcing the constness in
2//! `T: const Trait` or `T: [const] Trait`.
3
4use rustc_type_ir::fast_reject::DeepRejectCtxt;
5use rustc_type_ir::inherent::*;
6use rustc_type_ir::lang_items::SolverTraitLangItem;
7use rustc_type_ir::solve::inspect::ProbeKind;
8use rustc_type_ir::solve::{AliasBoundKind, SizedTraitKind};
9use rustc_type_ir::{self as ty, Interner, TypingMode, Unnormalized, elaborate};
10use tracing::instrument;
11
12use super::assembly::{Candidate, structural_traits};
13use crate::delegate::SolverDelegate;
14use crate::solve::{
15    BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, NoSolution,
16    QueryResult, assembly,
17};
18
19impl<D, I> assembly::GoalKind<D> for ty::HostEffectPredicate<I>
20where
21    D: SolverDelegate<Interner = I>,
22    I: Interner,
23{
24    fn self_ty(self) -> I::Ty {
25        self.self_ty()
26    }
27
28    fn trait_ref(self, _: I) -> ty::TraitRef<I> {
29        self.trait_ref
30    }
31
32    fn with_replaced_self_ty(self, cx: I, self_ty: I::Ty) -> Self {
33        self.with_replaced_self_ty(cx, self_ty)
34    }
35
36    fn trait_def_id(self, _: I) -> I::TraitId {
37        self.def_id()
38    }
39
40    fn fast_reject_assumption(
41        ecx: &mut EvalCtxt<'_, D>,
42        goal: Goal<I, Self>,
43        assumption: I::Clause,
44    ) -> Result<(), NoSolution> {
45        if let Some(host_clause) = assumption.as_host_effect_clause()
46            && host_clause.def_id() == goal.predicate.def_id()
47            && host_clause.constness().satisfies(goal.predicate.constness)
48            && DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
49                goal.predicate.trait_ref.args,
50                host_clause.skip_binder().trait_ref.args,
51            )
52        {
53            Ok(())
54        } else {
55            Err(NoSolution)
56        }
57    }
58
59    fn match_assumption(
60        ecx: &mut EvalCtxt<'_, D>,
61        goal: Goal<I, Self>,
62        assumption: I::Clause,
63        then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
64    ) -> QueryResult<I> {
65        let host_clause = assumption.as_host_effect_clause().unwrap();
66
67        let assumption_trait_pred = ecx.instantiate_binder_with_infer(host_clause);
68        ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?;
69
70        then(ecx)
71    }
72
73    /// Register additional assumptions for aliases corresponding to `[const]` item bounds.
74    ///
75    /// Unlike item bounds, they are not simply implied by the well-formedness of the alias.
76    /// Instead, they only hold if the const conditions on the alias also hold. This is why
77    /// we also register the const conditions of the alias after matching the goal against
78    /// the assumption.
79    fn consider_additional_alias_assumptions(
80        ecx: &mut EvalCtxt<'_, D>,
81        goal: Goal<I, Self>,
82        alias_ty: ty::AliasTy<I>,
83    ) -> Vec<Candidate<I>> {
84        let cx = ecx.cx();
85        let mut candidates = ::alloc::vec::Vec::new()vec![];
86
87        if !ecx.cx().alias_has_const_conditions(alias_ty.kind.def_id()) {
88            return ::alloc::vec::Vec::new()vec![];
89        }
90
91        for clause in elaborate::elaborate(
92            cx,
93            cx.explicit_implied_const_bounds(alias_ty.kind.def_id())
94                .iter_instantiated(cx, alias_ty.args)
95                .map(|trait_ref| {
96                    trait_ref.to_host_effect_clause(cx, goal.predicate.constness).skip_norm_wip()
97                }),
98        ) {
99            candidates.extend(Self::probe_and_match_goal_against_assumption(
100                ecx,
101                CandidateSource::AliasBound(AliasBoundKind::SelfBounds),
102                goal,
103                clause,
104                |ecx| {
105                    // Const conditions must hold for the implied const bound to hold.
106                    ecx.add_goals(
107                        GoalSource::AliasBoundConstCondition,
108                        cx.const_conditions(alias_ty.kind.def_id())
109                            .iter_instantiated(cx, alias_ty.args)
110                            .map(|trait_ref| {
111                                goal.with(
112                                    cx,
113                                    trait_ref
114                                        .to_host_effect_clause(cx, goal.predicate.constness)
115                                        .skip_norm_wip(),
116                                )
117                            }),
118                    );
119                    ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
120                },
121            ));
122        }
123
124        candidates
125    }
126
127    fn consider_impl_candidate(
128        ecx: &mut EvalCtxt<'_, D>,
129        goal: Goal<I, Self>,
130        impl_def_id: I::ImplId,
131        then: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResult<I>,
132    ) -> Result<Candidate<I>, NoSolution> {
133        let cx = ecx.cx();
134
135        let impl_trait_ref = cx.impl_trait_ref(impl_def_id);
136        if !DeepRejectCtxt::relate_rigid_infer(ecx.cx())
137            .args_may_unify(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args)
138        {
139            return Err(NoSolution);
140        }
141
142        let impl_polarity = cx.impl_polarity(impl_def_id);
143        let certainty = match impl_polarity {
144            ty::ImplPolarity::Negative => return Err(NoSolution),
145            ty::ImplPolarity::Reservation => match ecx.typing_mode() {
146                TypingMode::Coherence => Certainty::AMBIGUOUS,
147                TypingMode::Analysis { .. }
148                | TypingMode::Borrowck { .. }
149                | TypingMode::PostBorrowckAnalysis { .. }
150                | TypingMode::PostAnalysis => return Err(NoSolution),
151            },
152            ty::ImplPolarity::Positive => Certainty::Yes,
153        };
154
155        if !cx.impl_is_const(impl_def_id) {
156            return Err(NoSolution);
157        }
158
159        ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
160            let impl_args = ecx.fresh_args_for_item(impl_def_id.into());
161            ecx.record_impl_args(impl_args);
162            let impl_trait_ref = impl_trait_ref.instantiate(cx, impl_args).skip_norm_wip();
163
164            ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?;
165            let where_clause_bounds = cx
166                .predicates_of(impl_def_id.into())
167                .iter_instantiated(cx, impl_args)
168                .map(Unnormalized::skip_norm_wip)
169                .map(|pred| goal.with(cx, pred));
170            ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds);
171
172            // For this impl to be `const`, we need to check its `[const]` bounds too.
173            let const_conditions = cx
174                .const_conditions(impl_def_id.into())
175                .iter_instantiated(cx, impl_args)
176                .map(|bound_trait_ref| {
177                    goal.with(
178                        cx,
179                        bound_trait_ref
180                            .to_host_effect_clause(cx, goal.predicate.constness)
181                            .skip_norm_wip(),
182                    )
183                });
184            ecx.add_goals(GoalSource::ImplWhereBound, const_conditions);
185
186            then(ecx, certainty)
187        })
188    }
189
190    fn consider_error_guaranteed_candidate(
191        ecx: &mut EvalCtxt<'_, D>,
192        _guar: I::ErrorGuaranteed,
193    ) -> Result<Candidate<I>, NoSolution> {
194        ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
195            .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
196    }
197
198    fn consider_auto_trait_candidate(
199        ecx: &mut EvalCtxt<'_, D>,
200        _goal: Goal<I, Self>,
201    ) -> Result<Candidate<I>, NoSolution> {
202        ecx.cx().delay_bug("auto traits are never const");
203        Err(NoSolution)
204    }
205
206    fn consider_trait_alias_candidate(
207        ecx: &mut EvalCtxt<'_, D>,
208        goal: Goal<I, Self>,
209    ) -> Result<Candidate<I>, NoSolution> {
210        let cx = ecx.cx();
211
212        ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
213            let where_clause_bounds = cx
214                .predicates_of(goal.predicate.def_id().into())
215                .iter_instantiated(cx, goal.predicate.trait_ref.args)
216                .map(Unnormalized::skip_norm_wip)
217                .map(|p| goal.with(cx, p));
218
219            let const_conditions = cx
220                .const_conditions(goal.predicate.def_id().into())
221                .iter_instantiated(cx, goal.predicate.trait_ref.args)
222                .map(|bound_trait_ref| {
223                    goal.with(
224                        cx,
225                        bound_trait_ref
226                            .to_host_effect_clause(cx, goal.predicate.constness)
227                            .skip_norm_wip(),
228                    )
229                });
230            // While you could think of trait aliases to have a single builtin impl
231            // which uses its implied trait bounds as where-clauses, using
232            // `GoalSource::ImplWhereClause` here would be incorrect, as we also
233            // impl them, which means we're "stepping out of the impl constructor"
234            // again. To handle this, we treat these cycles as ambiguous for now.
235            ecx.add_goals(GoalSource::Misc, where_clause_bounds);
236            ecx.add_goals(GoalSource::Misc, const_conditions);
237            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
238        })
239    }
240
241    fn consider_builtin_sizedness_candidates(
242        _ecx: &mut EvalCtxt<'_, D>,
243        _goal: Goal<I, Self>,
244        _sizedness: SizedTraitKind,
245    ) -> Result<Candidate<I>, NoSolution> {
246        {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("Sized/MetaSized is never const")));
}unreachable!("Sized/MetaSized is never const")
247    }
248
249    fn consider_builtin_copy_clone_candidate(
250        ecx: &mut EvalCtxt<'_, D>,
251        goal: Goal<I, Self>,
252    ) -> Result<Candidate<I>, NoSolution> {
253        let cx = ecx.cx();
254
255        let self_ty = goal.predicate.self_ty();
256        let constituent_tys =
257            structural_traits::instantiate_constituent_tys_for_copy_clone_trait(ecx, self_ty)?;
258
259        ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
260            ecx.enter_forall(constituent_tys, |ecx, tys| {
261                ecx.add_goals(
262                    GoalSource::ImplWhereBound,
263                    tys.into_iter().map(|ty| {
264                        goal.with(
265                            cx,
266                            ty::ClauseKind::HostEffect(
267                                goal.predicate.with_replaced_self_ty(cx, ty),
268                            ),
269                        )
270                    }),
271                );
272            });
273
274            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
275        })
276    }
277
278    fn consider_builtin_fn_ptr_trait_candidate(
279        _ecx: &mut EvalCtxt<'_, D>,
280        _goal: Goal<I, Self>,
281    ) -> Result<Candidate<I>, NoSolution> {
282        {
    ::core::panicking::panic_fmt(format_args!("not yet implemented: {0}",
            format_args!("Fn* are not yet const")));
}todo!("Fn* are not yet const")
283    }
284
285    x;#[instrument(level = "trace", skip_all, ret)]
286    fn consider_builtin_fn_trait_candidates(
287        ecx: &mut EvalCtxt<'_, D>,
288        goal: Goal<I, Self>,
289        _kind: rustc_type_ir::ClosureKind,
290    ) -> Result<Candidate<I>, NoSolution> {
291        let cx = ecx.cx();
292
293        let self_ty = goal.predicate.self_ty();
294        let (inputs_and_output, def_id, args) =
295            structural_traits::extract_fn_def_from_const_callable(cx, self_ty)?;
296        let (inputs, output) = ecx.instantiate_binder_with_infer(inputs_and_output);
297
298        // A built-in `Fn` impl only holds if the output is sized.
299        // (FIXME: technically we only need to check this if the type is a fn ptr...)
300        let output_is_sized_pred =
301            ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]);
302        let requirements = cx
303            .const_conditions(def_id)
304            .iter_instantiated(cx, args)
305            .map(|trait_ref| {
306                (
307                    GoalSource::ImplWhereBound,
308                    goal.with(
309                        cx,
310                        trait_ref
311                            .to_host_effect_clause(cx, goal.predicate.constness)
312                            .skip_norm_wip(),
313                    ),
314                )
315            })
316            .chain([(GoalSource::ImplWhereBound, goal.with(cx, output_is_sized_pred))]);
317
318        let pred = ty::Binder::dummy(ty::TraitRef::new(
319            cx,
320            goal.predicate.def_id(),
321            [goal.predicate.self_ty(), inputs],
322        ))
323        .to_host_effect_clause(cx, goal.predicate.constness);
324
325        Self::probe_and_consider_implied_clause(
326            ecx,
327            CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
328            goal,
329            pred,
330            requirements,
331        )
332    }
333
334    fn consider_builtin_async_fn_trait_candidates(
335        _ecx: &mut EvalCtxt<'_, D>,
336        _goal: Goal<I, Self>,
337        _kind: rustc_type_ir::ClosureKind,
338    ) -> Result<Candidate<I>, NoSolution> {
339        {
    ::core::panicking::panic_fmt(format_args!("not yet implemented: {0}",
            format_args!("AsyncFn* are not yet const")));
}todo!("AsyncFn* are not yet const")
340    }
341
342    fn consider_builtin_async_fn_kind_helper_candidate(
343        _ecx: &mut EvalCtxt<'_, D>,
344        _goal: Goal<I, Self>,
345    ) -> Result<Candidate<I>, NoSolution> {
346        {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("AsyncFnKindHelper is not const")));
}unreachable!("AsyncFnKindHelper is not const")
347    }
348
349    fn consider_builtin_tuple_candidate(
350        _ecx: &mut EvalCtxt<'_, D>,
351        _goal: Goal<I, Self>,
352    ) -> Result<Candidate<I>, NoSolution> {
353        {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("Tuple trait is not const")));
}unreachable!("Tuple trait is not const")
354    }
355
356    fn consider_builtin_pointee_candidate(
357        _ecx: &mut EvalCtxt<'_, D>,
358        _goal: Goal<I, Self>,
359    ) -> Result<Candidate<I>, NoSolution> {
360        {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("Pointee is not const")));
}unreachable!("Pointee is not const")
361    }
362
363    fn consider_builtin_future_candidate(
364        _ecx: &mut EvalCtxt<'_, D>,
365        _goal: Goal<I, Self>,
366    ) -> Result<Candidate<I>, NoSolution> {
367        {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("Future is not const")));
}unreachable!("Future is not const")
368    }
369
370    fn consider_builtin_iterator_candidate(
371        _ecx: &mut EvalCtxt<'_, D>,
372        _goal: Goal<I, Self>,
373    ) -> Result<Candidate<I>, NoSolution> {
374        {
    ::core::panicking::panic_fmt(format_args!("not yet implemented: {0}",
            format_args!("Iterator is not yet const")));
}todo!("Iterator is not yet const")
375    }
376
377    fn consider_builtin_fused_iterator_candidate(
378        _ecx: &mut EvalCtxt<'_, D>,
379        _goal: Goal<I, Self>,
380    ) -> Result<Candidate<I>, NoSolution> {
381        {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("FusedIterator is not const")));
}unreachable!("FusedIterator is not const")
382    }
383
384    fn consider_builtin_async_iterator_candidate(
385        _ecx: &mut EvalCtxt<'_, D>,
386        _goal: Goal<I, Self>,
387    ) -> Result<Candidate<I>, NoSolution> {
388        {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("AsyncIterator is not const")));
}unreachable!("AsyncIterator is not const")
389    }
390
391    fn consider_builtin_coroutine_candidate(
392        _ecx: &mut EvalCtxt<'_, D>,
393        _goal: Goal<I, Self>,
394    ) -> Result<Candidate<I>, NoSolution> {
395        {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("Coroutine is not const")));
}unreachable!("Coroutine is not const")
396    }
397
398    fn consider_builtin_discriminant_kind_candidate(
399        _ecx: &mut EvalCtxt<'_, D>,
400        _goal: Goal<I, Self>,
401    ) -> Result<Candidate<I>, NoSolution> {
402        {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("DiscriminantKind is not const")));
}unreachable!("DiscriminantKind is not const")
403    }
404
405    fn consider_builtin_destruct_candidate(
406        ecx: &mut EvalCtxt<'_, D>,
407        goal: Goal<I, Self>,
408    ) -> Result<Candidate<I>, NoSolution> {
409        let cx = ecx.cx();
410
411        let self_ty = goal.predicate.self_ty();
412        let const_conditions = structural_traits::const_conditions_for_destruct(cx, self_ty)?;
413
414        ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
415            ecx.add_goals(
416                GoalSource::AliasBoundConstCondition,
417                const_conditions.into_iter().map(|trait_ref| {
418                    goal.with(
419                        cx,
420                        ty::Binder::dummy(trait_ref)
421                            .to_host_effect_clause(cx, goal.predicate.constness),
422                    )
423                }),
424            );
425            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
426        })
427    }
428
429    fn consider_builtin_transmute_candidate(
430        _ecx: &mut EvalCtxt<'_, D>,
431        _goal: Goal<I, Self>,
432    ) -> Result<Candidate<I>, NoSolution> {
433        {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("TransmuteFrom is not const")));
}unreachable!("TransmuteFrom is not const")
434    }
435
436    fn consider_builtin_bikeshed_guaranteed_no_drop_candidate(
437        _ecx: &mut EvalCtxt<'_, D>,
438        _goal: Goal<I, Self>,
439    ) -> Result<Candidate<I>, NoSolution> {
440        {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("BikeshedGuaranteedNoDrop is not const")));
};unreachable!("BikeshedGuaranteedNoDrop is not const");
441    }
442
443    fn consider_structural_builtin_unsize_candidates(
444        _ecx: &mut EvalCtxt<'_, D>,
445        _goal: Goal<I, Self>,
446    ) -> Vec<Candidate<I>> {
447        {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("Unsize is not const")));
}unreachable!("Unsize is not const")
448    }
449
450    fn consider_builtin_field_candidate(
451        _ecx: &mut EvalCtxt<'_, D>,
452        _goal: Goal<<D as SolverDelegate>::Interner, Self>,
453    ) -> Result<Candidate<<D as SolverDelegate>::Interner>, NoSolution> {
454        {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("Field is not const")));
}unreachable!("Field is not const")
455    }
456}
457
458impl<D, I> EvalCtxt<'_, D>
459where
460    D: SolverDelegate<Interner = I>,
461    I: Interner,
462{
463    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::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("compute_host_effect_goal",
                                    "rustc_next_trait_solver::solve::effect_goals",
                                    ::tracing::Level::TRACE,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_next_trait_solver/src/solve/effect_goals.rs"),
                                    ::tracing_core::__macro_support::Option::Some(463u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_next_trait_solver::solve::effect_goals"),
                                    ::tracing_core::field::FieldSet::new(&["goal"],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::TRACE <=
                                    ::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};
                                let mut iter = meta.fields().iter();
                                meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&goal)
                                                            as &dyn 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: QueryResult<I> = loop {};
            return __tracing_attr_fake_return;
        }
        {
            let (_, proven_via) =
                self.probe(|_|
                                ProbeKind::ShadowedEnvProbing).enter(|ecx|
                            {
                                let trait_goal: Goal<I, ty::TraitPredicate<I>> =
                                    goal.with(ecx.cx(), goal.predicate.trait_ref);
                                ecx.compute_trait_goal(trait_goal)
                            })?;
            self.assemble_and_merge_candidates(proven_via, goal, |_ecx| None,
                |_ecx| Err(NoSolution))
        }
    }
}#[instrument(level = "trace", skip(self))]
464    pub(super) fn compute_host_effect_goal(
465        &mut self,
466        goal: Goal<I, ty::HostEffectPredicate<I>>,
467    ) -> QueryResult<I> {
468        let (_, proven_via) = self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| {
469            let trait_goal: Goal<I, ty::TraitPredicate<I>> =
470                goal.with(ecx.cx(), goal.predicate.trait_ref);
471            ecx.compute_trait_goal(trait_goal)
472        })?;
473        self.assemble_and_merge_candidates(proven_via, goal, |_ecx| None, |_ecx| Err(NoSolution))
474    }
475}