Auto merge of #99743 - compiler-errors:fulfillment-context-cleanups, r=jackh726
Some `FulfillmentContext`-related cleanups Use `ObligationCtxt` in some places, remove some `FulfillmentContext`s in others... r? types
This commit is contained in:
commit
bd04658eb6
12 changed files with 126 additions and 162 deletions
|
@ -5,12 +5,11 @@
|
||||||
use rustc_errors::ErrorGuaranteed;
|
use rustc_errors::ErrorGuaranteed;
|
||||||
use rustc_hir::LangItem;
|
use rustc_hir::LangItem;
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
use rustc_infer::traits::TraitEngine;
|
|
||||||
use rustc_middle::mir::*;
|
use rustc_middle::mir::*;
|
||||||
use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty};
|
use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty};
|
||||||
use rustc_span::DUMMY_SP;
|
use rustc_span::DUMMY_SP;
|
||||||
use rustc_trait_selection::traits::{
|
use rustc_trait_selection::traits::{
|
||||||
self, ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngineExt,
|
self, ImplSource, Obligation, ObligationCause, SelectionContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::ConstCx;
|
use super::ConstCx;
|
||||||
|
@ -189,15 +188,8 @@ impl Qualif for NeedsNonConstDrop {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we successfully found one, then select all of the predicates
|
|
||||||
// implied by our const drop impl.
|
|
||||||
let mut fcx = <dyn TraitEngine<'tcx>>::new(cx.tcx);
|
|
||||||
for nested in impl_src.nested_obligations() {
|
|
||||||
fcx.register_predicate_obligation(&infcx, nested);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we had any errors, then it's bad
|
// If we had any errors, then it's bad
|
||||||
!fcx.select_all_or_error(&infcx).is_empty()
|
!traits::fully_solve_obligations(&infcx, impl_src.nested_obligations()).is_empty()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -205,10 +205,8 @@ impl<'tcx> AutoTraitFinder<'tcx> {
|
||||||
// At this point, we already have all of the bounds we need. FulfillmentContext is used
|
// At this point, we already have all of the bounds we need. FulfillmentContext is used
|
||||||
// to store all of the necessary region/lifetime bounds in the InferContext, as well as
|
// to store all of the necessary region/lifetime bounds in the InferContext, as well as
|
||||||
// an additional sanity check.
|
// an additional sanity check.
|
||||||
let mut fulfill = <dyn TraitEngine<'tcx>>::new(tcx);
|
let errors =
|
||||||
fulfill.register_bound(&infcx, full_env, ty, trait_did, ObligationCause::dummy());
|
super::fully_solve_bound(&infcx, ObligationCause::dummy(), full_env, ty, trait_did);
|
||||||
let errors = fulfill.select_all_or_error(&infcx);
|
|
||||||
|
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
panic!("Unable to fulfill trait {:?} for '{:?}': {:?}", trait_did, ty, errors);
|
panic!("Unable to fulfill trait {:?} for '{:?}': {:?}", trait_did, ty, errors);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,14 +10,14 @@ use crate::traits::select::IntercrateAmbiguityCause;
|
||||||
use crate::traits::util::impl_subject_and_oblig;
|
use crate::traits::util::impl_subject_and_oblig;
|
||||||
use crate::traits::SkipLeakCheck;
|
use crate::traits::SkipLeakCheck;
|
||||||
use crate::traits::{
|
use crate::traits::{
|
||||||
self, FulfillmentContext, Normalized, Obligation, ObligationCause, PredicateObligation,
|
self, Normalized, Obligation, ObligationCause, PredicateObligation, PredicateObligations,
|
||||||
PredicateObligations, SelectionContext, TraitEngineExt,
|
SelectionContext,
|
||||||
};
|
};
|
||||||
use rustc_data_structures::fx::FxIndexSet;
|
use rustc_data_structures::fx::FxIndexSet;
|
||||||
use rustc_errors::Diagnostic;
|
use rustc_errors::Diagnostic;
|
||||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
|
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
|
||||||
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
|
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
|
||||||
use rustc_infer::traits::{util, TraitEngine};
|
use rustc_infer::traits::util;
|
||||||
use rustc_middle::traits::specialization_graph::OverlapMode;
|
use rustc_middle::traits::specialization_graph::OverlapMode;
|
||||||
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
|
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
|
||||||
use rustc_middle::ty::subst::Subst;
|
use rustc_middle::ty::subst::Subst;
|
||||||
|
@ -302,7 +302,6 @@ fn negative_impl<'cx, 'tcx>(
|
||||||
let impl_env = tcx.param_env(impl1_def_id);
|
let impl_env = tcx.param_env(impl1_def_id);
|
||||||
let subject1 = match traits::fully_normalize(
|
let subject1 = match traits::fully_normalize(
|
||||||
&infcx,
|
&infcx,
|
||||||
FulfillmentContext::new(),
|
|
||||||
ObligationCause::dummy(),
|
ObligationCause::dummy(),
|
||||||
impl_env,
|
impl_env,
|
||||||
tcx.impl_subject(impl1_def_id),
|
tcx.impl_subject(impl1_def_id),
|
||||||
|
@ -385,16 +384,11 @@ fn resolve_negative_obligation<'cx, 'tcx>(
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut fulfillment_cx = <dyn TraitEngine<'tcx>>::new(infcx.tcx);
|
let errors = super::fully_solve_obligation(infcx, o);
|
||||||
fulfillment_cx.register_predicate_obligation(infcx, o);
|
|
||||||
|
|
||||||
let errors = fulfillment_cx.select_all_or_error(infcx);
|
|
||||||
|
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME -- also add "assumed to be well formed" types into the `outlives_env`
|
|
||||||
let outlives_env = OutlivesEnvironment::new(param_env);
|
let outlives_env = OutlivesEnvironment::new(param_env);
|
||||||
infcx.process_registered_region_obligations(outlives_env.region_bound_pairs(), param_env);
|
infcx.process_registered_region_obligations(outlives_env.region_bound_pairs(), param_env);
|
||||||
|
|
||||||
|
|
|
@ -63,8 +63,7 @@ pub fn can_type_implement_copy<'tcx>(
|
||||||
} else {
|
} else {
|
||||||
ObligationCause::dummy_with_span(span)
|
ObligationCause::dummy_with_span(span)
|
||||||
};
|
};
|
||||||
let ctx = traits::FulfillmentContext::new();
|
match traits::fully_normalize(&infcx, cause, param_env, ty) {
|
||||||
match traits::fully_normalize(&infcx, ctx, cause, param_env, ty) {
|
|
||||||
Ok(ty) => {
|
Ok(ty) => {
|
||||||
if !infcx.type_is_copy_modulo_regions(param_env, ty, span) {
|
if !infcx.type_is_copy_modulo_regions(param_env, ty, span) {
|
||||||
infringing.push((field, ty));
|
infringing.push((field, ty));
|
||||||
|
|
|
@ -30,6 +30,7 @@ use rustc_errors::ErrorGuaranteed;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::lang_items::LangItem;
|
use rustc_hir::lang_items::LangItem;
|
||||||
|
use rustc_infer::traits::TraitEngineExt as _;
|
||||||
use rustc_middle::ty::fold::TypeFoldable;
|
use rustc_middle::ty::fold::TypeFoldable;
|
||||||
use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
|
use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
|
||||||
use rustc_middle::ty::visit::TypeVisitable;
|
use rustc_middle::ty::visit::TypeVisitable;
|
||||||
|
@ -161,22 +162,20 @@ pub fn type_known_to_meet_bound_modulo_regions<'a, 'tcx>(
|
||||||
// this function's result remains infallible, we must confirm
|
// this function's result remains infallible, we must confirm
|
||||||
// that guess. While imperfect, I believe this is sound.
|
// that guess. While imperfect, I believe this is sound.
|
||||||
|
|
||||||
// The handling of regions in this area of the code is terrible,
|
|
||||||
// see issue #29149. We should be able to improve on this with
|
|
||||||
// NLL.
|
|
||||||
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(infcx.tcx);
|
|
||||||
|
|
||||||
// We can use a dummy node-id here because we won't pay any mind
|
// We can use a dummy node-id here because we won't pay any mind
|
||||||
// to region obligations that arise (there shouldn't really be any
|
// to region obligations that arise (there shouldn't really be any
|
||||||
// anyhow).
|
// anyhow).
|
||||||
let cause = ObligationCause::misc(span, hir::CRATE_HIR_ID);
|
let cause = ObligationCause::misc(span, hir::CRATE_HIR_ID);
|
||||||
|
|
||||||
fulfill_cx.register_bound(infcx, param_env, ty, def_id, cause);
|
// The handling of regions in this area of the code is terrible,
|
||||||
|
// see issue #29149. We should be able to improve on this with
|
||||||
|
// NLL.
|
||||||
|
let errors = fully_solve_bound(infcx, cause, param_env, ty, def_id);
|
||||||
|
|
||||||
// Note: we only assume something is `Copy` if we can
|
// Note: we only assume something is `Copy` if we can
|
||||||
// *definitively* show that it implements `Copy`. Otherwise,
|
// *definitively* show that it implements `Copy`. Otherwise,
|
||||||
// assume it is move; linear is always ok.
|
// assume it is move; linear is always ok.
|
||||||
match fulfill_cx.select_all_or_error(infcx).as_slice() {
|
match &errors[..] {
|
||||||
[] => {
|
[] => {
|
||||||
debug!(
|
debug!(
|
||||||
"type_known_to_meet_bound_modulo_regions: ty={:?} bound={} success",
|
"type_known_to_meet_bound_modulo_regions: ty={:?} bound={} success",
|
||||||
|
@ -222,15 +221,13 @@ fn do_normalize_predicates<'tcx>(
|
||||||
// them here too, and we will remove this function when
|
// them here too, and we will remove this function when
|
||||||
// we move over to lazy normalization *anyway*.
|
// we move over to lazy normalization *anyway*.
|
||||||
tcx.infer_ctxt().ignoring_regions().enter(|infcx| {
|
tcx.infer_ctxt().ignoring_regions().enter(|infcx| {
|
||||||
let fulfill_cx = FulfillmentContext::new();
|
let predicates = match fully_normalize(&infcx, cause, elaborated_env, predicates) {
|
||||||
let predicates =
|
Ok(predicates) => predicates,
|
||||||
match fully_normalize(&infcx, fulfill_cx, cause, elaborated_env, predicates) {
|
Err(errors) => {
|
||||||
Ok(predicates) => predicates,
|
let reported = infcx.report_fulfillment_errors(&errors, None, false);
|
||||||
Err(errors) => {
|
return Err(reported);
|
||||||
let reported = infcx.report_fulfillment_errors(&errors, None, false);
|
}
|
||||||
return Err(reported);
|
};
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
debug!("do_normalize_predictes: normalized predicates = {:?}", predicates);
|
debug!("do_normalize_predictes: normalized predicates = {:?}", predicates);
|
||||||
|
|
||||||
|
@ -381,9 +378,9 @@ pub fn normalize_param_env_or_error<'tcx>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Normalize a type and process all resulting obligations, returning any errors
|
||||||
pub fn fully_normalize<'a, 'tcx, T>(
|
pub fn fully_normalize<'a, 'tcx, T>(
|
||||||
infcx: &InferCtxt<'a, 'tcx>,
|
infcx: &InferCtxt<'a, 'tcx>,
|
||||||
mut fulfill_cx: FulfillmentContext<'tcx>,
|
|
||||||
cause: ObligationCause<'tcx>,
|
cause: ObligationCause<'tcx>,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
value: T,
|
value: T,
|
||||||
|
@ -399,8 +396,10 @@ where
|
||||||
"fully_normalize: normalized_value={:?} obligations={:?}",
|
"fully_normalize: normalized_value={:?} obligations={:?}",
|
||||||
normalized_value, obligations
|
normalized_value, obligations
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let mut fulfill_cx = FulfillmentContext::new();
|
||||||
for obligation in obligations {
|
for obligation in obligations {
|
||||||
fulfill_cx.register_predicate_obligation(selcx.infcx(), obligation);
|
fulfill_cx.register_predicate_obligation(infcx, obligation);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("fully_normalize: select_all_or_error start");
|
debug!("fully_normalize: select_all_or_error start");
|
||||||
|
@ -414,6 +413,43 @@ where
|
||||||
Ok(resolved_value)
|
Ok(resolved_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Process an obligation (and any nested obligations that come from it) to
|
||||||
|
/// completion, returning any errors
|
||||||
|
pub fn fully_solve_obligation<'a, 'tcx>(
|
||||||
|
infcx: &InferCtxt<'a, 'tcx>,
|
||||||
|
obligation: PredicateObligation<'tcx>,
|
||||||
|
) -> Vec<FulfillmentError<'tcx>> {
|
||||||
|
let mut engine = <dyn TraitEngine<'tcx>>::new(infcx.tcx);
|
||||||
|
engine.register_predicate_obligation(infcx, obligation);
|
||||||
|
engine.select_all_or_error(infcx)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process a set of obligations (and any nested obligations that come from them)
|
||||||
|
/// to completion
|
||||||
|
pub fn fully_solve_obligations<'a, 'tcx>(
|
||||||
|
infcx: &InferCtxt<'a, 'tcx>,
|
||||||
|
obligations: impl IntoIterator<Item = PredicateObligation<'tcx>>,
|
||||||
|
) -> Vec<FulfillmentError<'tcx>> {
|
||||||
|
let mut engine = <dyn TraitEngine<'tcx>>::new(infcx.tcx);
|
||||||
|
engine.register_predicate_obligations(infcx, obligations);
|
||||||
|
engine.select_all_or_error(infcx)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process a bound (and any nested obligations that come from it) to completion.
|
||||||
|
/// This is a convenience function for traits that have no generic arguments, such
|
||||||
|
/// as auto traits, and builtin traits like Copy or Sized.
|
||||||
|
pub fn fully_solve_bound<'a, 'tcx>(
|
||||||
|
infcx: &InferCtxt<'a, 'tcx>,
|
||||||
|
cause: ObligationCause<'tcx>,
|
||||||
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
|
ty: Ty<'tcx>,
|
||||||
|
bound: DefId,
|
||||||
|
) -> Vec<FulfillmentError<'tcx>> {
|
||||||
|
let mut engine = <dyn TraitEngine<'tcx>>::new(infcx.tcx);
|
||||||
|
engine.register_bound(infcx, param_env, ty, bound, cause);
|
||||||
|
engine.select_all_or_error(infcx)
|
||||||
|
}
|
||||||
|
|
||||||
/// Normalizes the predicates and checks whether they hold in an empty environment. If this
|
/// Normalizes the predicates and checks whether they hold in an empty environment. If this
|
||||||
/// returns true, then either normalize encountered an error or one of the predicates did not
|
/// returns true, then either normalize encountered an error or one of the predicates did not
|
||||||
/// hold. Used when creating vtables to check for unsatisfiable methods.
|
/// hold. Used when creating vtables to check for unsatisfiable methods.
|
||||||
|
@ -428,20 +464,13 @@ pub fn impossible_predicates<'tcx>(
|
||||||
infcx.set_tainted_by_errors();
|
infcx.set_tainted_by_errors();
|
||||||
|
|
||||||
let param_env = ty::ParamEnv::reveal_all();
|
let param_env = ty::ParamEnv::reveal_all();
|
||||||
let mut selcx = SelectionContext::new(&infcx);
|
let ocx = ObligationCtxt::new(&infcx);
|
||||||
let mut fulfill_cx = FulfillmentContext::new();
|
let predicates = ocx.normalize(ObligationCause::dummy(), param_env, predicates);
|
||||||
let cause = ObligationCause::dummy();
|
|
||||||
let Normalized { value: predicates, obligations } =
|
|
||||||
normalize(&mut selcx, param_env, cause.clone(), predicates);
|
|
||||||
for obligation in obligations {
|
|
||||||
fulfill_cx.register_predicate_obligation(&infcx, obligation);
|
|
||||||
}
|
|
||||||
for predicate in predicates {
|
for predicate in predicates {
|
||||||
let obligation = Obligation::new(cause.clone(), param_env, predicate);
|
let obligation = Obligation::new(ObligationCause::dummy(), param_env, predicate);
|
||||||
fulfill_cx.register_predicate_obligation(&infcx, obligation);
|
ocx.register_obligation(obligation);
|
||||||
}
|
}
|
||||||
|
let errors = ocx.select_all_or_error();
|
||||||
let errors = fulfill_cx.select_all_or_error(&infcx);
|
|
||||||
|
|
||||||
// Clean up after ourselves
|
// Clean up after ourselves
|
||||||
let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
|
let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
use crate::infer::canonical::query_response;
|
use crate::infer::canonical::query_response;
|
||||||
use crate::infer::{InferCtxt, InferOk};
|
use crate::infer::{InferCtxt, InferOk};
|
||||||
use crate::traits::engine::TraitEngineExt as _;
|
use crate::traits;
|
||||||
use crate::traits::query::type_op::TypeOpOutput;
|
use crate::traits::query::type_op::TypeOpOutput;
|
||||||
use crate::traits::query::Fallible;
|
use crate::traits::query::Fallible;
|
||||||
use crate::traits::TraitEngine;
|
|
||||||
use rustc_infer::infer::region_constraints::RegionConstraintData;
|
use rustc_infer::infer::region_constraints::RegionConstraintData;
|
||||||
use rustc_infer::traits::TraitEngineExt as _;
|
|
||||||
use rustc_span::source_map::DUMMY_SP;
|
use rustc_span::source_map::DUMMY_SP;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -62,8 +60,6 @@ pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>(
|
||||||
infcx: &InferCtxt<'_, 'tcx>,
|
infcx: &InferCtxt<'_, 'tcx>,
|
||||||
op: impl FnOnce() -> Fallible<InferOk<'tcx, R>>,
|
op: impl FnOnce() -> Fallible<InferOk<'tcx, R>>,
|
||||||
) -> Fallible<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>)> {
|
) -> Fallible<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>)> {
|
||||||
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
|
|
||||||
|
|
||||||
// During NLL, we expect that nobody will register region
|
// During NLL, we expect that nobody will register region
|
||||||
// obligations **except** as part of a custom type op (and, at the
|
// obligations **except** as part of a custom type op (and, at the
|
||||||
// end of each custom type op, we scrape out the region
|
// end of each custom type op, we scrape out the region
|
||||||
|
@ -77,8 +73,7 @@ pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>(
|
||||||
);
|
);
|
||||||
|
|
||||||
let InferOk { value, obligations } = infcx.commit_if_ok(|_| op())?;
|
let InferOk { value, obligations } = infcx.commit_if_ok(|_| op())?;
|
||||||
fulfill_cx.register_predicate_obligations(infcx, obligations);
|
let errors = traits::fully_solve_obligations(infcx, obligations);
|
||||||
let errors = fulfill_cx.select_all_or_error(infcx);
|
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
infcx.tcx.sess.diagnostic().delay_span_bug(
|
infcx.tcx.sess.diagnostic().delay_span_bug(
|
||||||
DUMMY_SP,
|
DUMMY_SP,
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
use hir::LangItem;
|
use hir::LangItem;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_infer::traits::TraitEngine;
|
use rustc_infer::traits::ObligationCause;
|
||||||
use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
|
use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
|
||||||
use rustc_lint_defs::builtin::DEREF_INTO_DYN_SUPERTRAIT;
|
use rustc_lint_defs::builtin::DEREF_INTO_DYN_SUPERTRAIT;
|
||||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||||
|
@ -706,8 +706,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
fn need_migrate_deref_output_trait_object(
|
fn need_migrate_deref_output_trait_object(
|
||||||
&mut self,
|
&mut self,
|
||||||
ty: Ty<'tcx>,
|
ty: Ty<'tcx>,
|
||||||
cause: &traits::ObligationCause<'tcx>,
|
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
|
cause: &ObligationCause<'tcx>,
|
||||||
) -> Option<(Ty<'tcx>, DefId)> {
|
) -> Option<(Ty<'tcx>, DefId)> {
|
||||||
let tcx = self.tcx();
|
let tcx = self.tcx();
|
||||||
if tcx.features().trait_upcasting {
|
if tcx.features().trait_upcasting {
|
||||||
|
@ -729,24 +729,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut fulfillcx = traits::FulfillmentContext::new_in_snapshot();
|
let ty = traits::normalize_projection_type(
|
||||||
let normalized_ty = fulfillcx.normalize_projection_type(
|
self,
|
||||||
&self.infcx,
|
|
||||||
param_env,
|
param_env,
|
||||||
ty::ProjectionTy {
|
ty::ProjectionTy {
|
||||||
item_def_id: tcx.lang_items().deref_target()?,
|
item_def_id: tcx.lang_items().deref_target()?,
|
||||||
substs: trait_ref.substs,
|
substs: trait_ref.substs,
|
||||||
},
|
},
|
||||||
cause.clone(),
|
cause.clone(),
|
||||||
);
|
0,
|
||||||
|
// We're *intentionally* throwing these away,
|
||||||
|
// since we don't actually use them.
|
||||||
|
&mut vec![],
|
||||||
|
)
|
||||||
|
.ty()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let ty::Dynamic(data, ..) = normalized_ty.kind() else {
|
if let ty::Dynamic(data, ..) = ty.kind() {
|
||||||
return None;
|
Some((ty, data.principal_def_id()?))
|
||||||
};
|
} else {
|
||||||
|
None
|
||||||
let def_id = data.principal_def_id()?;
|
}
|
||||||
|
|
||||||
return Some((normalized_ty, def_id));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Searches for unsizing that might apply to `obligation`.
|
/// Searches for unsizing that might apply to `obligation`.
|
||||||
|
@ -809,8 +812,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
if let Some((deref_output_ty, deref_output_trait_did)) = self
|
if let Some((deref_output_ty, deref_output_trait_did)) = self
|
||||||
.need_migrate_deref_output_trait_object(
|
.need_migrate_deref_output_trait_object(
|
||||||
source,
|
source,
|
||||||
&obligation.cause,
|
|
||||||
obligation.param_env,
|
obligation.param_env,
|
||||||
|
&obligation.cause,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if deref_output_trait_did == target_trait_did {
|
if deref_output_trait_did == target_trait_did {
|
||||||
|
|
|
@ -14,9 +14,7 @@ use specialization_graph::GraphExt;
|
||||||
|
|
||||||
use crate::infer::{InferCtxt, InferOk, TyCtxtInferExt};
|
use crate::infer::{InferCtxt, InferOk, TyCtxtInferExt};
|
||||||
use crate::traits::select::IntercrateAmbiguityCause;
|
use crate::traits::select::IntercrateAmbiguityCause;
|
||||||
use crate::traits::{
|
use crate::traits::{self, coherence, FutureCompatOverlapErrorKind, ObligationCause};
|
||||||
self, coherence, FutureCompatOverlapErrorKind, ObligationCause, TraitEngine, TraitEngineExt,
|
|
||||||
};
|
|
||||||
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
|
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
|
||||||
use rustc_errors::{struct_span_err, EmissionGuarantee, LintDiagnosticBuilder};
|
use rustc_errors::{struct_span_err, EmissionGuarantee, LintDiagnosticBuilder};
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
|
@ -26,8 +24,8 @@ use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK;
|
||||||
use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS;
|
use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS;
|
||||||
use rustc_span::{Span, DUMMY_SP};
|
use rustc_span::{Span, DUMMY_SP};
|
||||||
|
|
||||||
|
use super::util;
|
||||||
use super::SelectionContext;
|
use super::SelectionContext;
|
||||||
use super::{util, FulfillmentContext};
|
|
||||||
|
|
||||||
/// Information pertinent to an overlapping impl error.
|
/// Information pertinent to an overlapping impl error.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -153,7 +151,6 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId,
|
||||||
tcx.infer_ctxt().enter(|infcx| {
|
tcx.infer_ctxt().enter(|infcx| {
|
||||||
let impl1_trait_ref = match traits::fully_normalize(
|
let impl1_trait_ref = match traits::fully_normalize(
|
||||||
&infcx,
|
&infcx,
|
||||||
FulfillmentContext::new(),
|
|
||||||
ObligationCause::dummy(),
|
ObligationCause::dummy(),
|
||||||
penv,
|
penv,
|
||||||
impl1_trait_ref,
|
impl1_trait_ref,
|
||||||
|
@ -211,11 +208,8 @@ fn fulfill_implication<'a, 'tcx>(
|
||||||
// (which are packed up in penv)
|
// (which are packed up in penv)
|
||||||
|
|
||||||
infcx.save_and_restore_in_snapshot_flag(|infcx| {
|
infcx.save_and_restore_in_snapshot_flag(|infcx| {
|
||||||
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(infcx.tcx);
|
let errors = traits::fully_solve_obligations(&infcx, obligations.chain(more_obligations));
|
||||||
for oblig in obligations.chain(more_obligations) {
|
match &errors[..] {
|
||||||
fulfill_cx.register_predicate_obligation(&infcx, oblig);
|
|
||||||
}
|
|
||||||
match fulfill_cx.select_all_or_error(infcx).as_slice() {
|
|
||||||
[] => {
|
[] => {
|
||||||
debug!(
|
debug!(
|
||||||
"fulfill_implication: an impl for {:?} specializes {:?}",
|
"fulfill_implication: an impl for {:?} specializes {:?}",
|
||||||
|
|
|
@ -15,7 +15,7 @@ use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeV
|
||||||
use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
|
use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
|
||||||
use rustc_trait_selection::traits::misc::{can_type_implement_copy, CopyImplementationError};
|
use rustc_trait_selection::traits::misc::{can_type_implement_copy, CopyImplementationError};
|
||||||
use rustc_trait_selection::traits::predicate_for_trait_def;
|
use rustc_trait_selection::traits::predicate_for_trait_def;
|
||||||
use rustc_trait_selection::traits::{self, ObligationCause, TraitEngine, TraitEngineExt};
|
use rustc_trait_selection::traits::{self, ObligationCause};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) {
|
pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) {
|
||||||
|
@ -109,15 +109,13 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
|
||||||
// it is not immediately clear why Copy is not implemented for a field, since
|
// it is not immediately clear why Copy is not implemented for a field, since
|
||||||
// all we point at is the field itself.
|
// all we point at is the field itself.
|
||||||
tcx.infer_ctxt().ignoring_regions().enter(|infcx| {
|
tcx.infer_ctxt().ignoring_regions().enter(|infcx| {
|
||||||
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(tcx);
|
for error in traits::fully_solve_bound(
|
||||||
fulfill_cx.register_bound(
|
|
||||||
&infcx,
|
&infcx,
|
||||||
|
traits::ObligationCause::dummy_with_span(field_ty_span),
|
||||||
param_env,
|
param_env,
|
||||||
ty,
|
ty,
|
||||||
tcx.lang_items().copy_trait().unwrap(),
|
tcx.lang_items().copy_trait().unwrap(),
|
||||||
traits::ObligationCause::dummy_with_span(field_ty_span),
|
) {
|
||||||
);
|
|
||||||
for error in fulfill_cx.select_all_or_error(&infcx) {
|
|
||||||
let error_predicate = error.obligation.predicate;
|
let error_predicate = error.obligation.predicate;
|
||||||
// Only note if it's not the root obligation, otherwise it's trivial and
|
// Only note if it's not the root obligation, otherwise it's trivial and
|
||||||
// should be self-explanatory (i.e. a field literally doesn't implement Copy).
|
// should be self-explanatory (i.e. a field literally doesn't implement Copy).
|
||||||
|
@ -315,24 +313,20 @@ fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did:
|
||||||
))
|
))
|
||||||
.emit();
|
.emit();
|
||||||
} else {
|
} else {
|
||||||
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
|
let errors = traits::fully_solve_obligations(
|
||||||
|
&infcx,
|
||||||
for field in coerced_fields {
|
coerced_fields.into_iter().map(|field| {
|
||||||
let predicate = predicate_for_trait_def(
|
predicate_for_trait_def(
|
||||||
tcx,
|
tcx,
|
||||||
param_env,
|
param_env,
|
||||||
cause.clone(),
|
cause.clone(),
|
||||||
dispatch_from_dyn_trait,
|
dispatch_from_dyn_trait,
|
||||||
0,
|
0,
|
||||||
field.ty(tcx, substs_a),
|
field.ty(tcx, substs_a),
|
||||||
&[field.ty(tcx, substs_b).into()],
|
&[field.ty(tcx, substs_b).into()],
|
||||||
);
|
)
|
||||||
|
}),
|
||||||
fulfill_cx.register_predicate_obligation(&infcx, predicate);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
// Check that all transitive obligations are satisfied.
|
|
||||||
let errors = fulfill_cx.select_all_or_error(&infcx);
|
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
infcx.report_fulfillment_errors(&errors, None, false);
|
infcx.report_fulfillment_errors(&errors, None, false);
|
||||||
}
|
}
|
||||||
|
@ -573,8 +567,6 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
|
|
||||||
|
|
||||||
// Register an obligation for `A: Trait<B>`.
|
// Register an obligation for `A: Trait<B>`.
|
||||||
let cause = traits::ObligationCause::misc(span, impl_hir_id);
|
let cause = traits::ObligationCause::misc(span, impl_hir_id);
|
||||||
let predicate = predicate_for_trait_def(
|
let predicate = predicate_for_trait_def(
|
||||||
|
@ -586,10 +578,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn
|
||||||
source,
|
source,
|
||||||
&[target.into()],
|
&[target.into()],
|
||||||
);
|
);
|
||||||
fulfill_cx.register_predicate_obligation(&infcx, predicate);
|
let errors = traits::fully_solve_obligation(&infcx, predicate);
|
||||||
|
|
||||||
// Check that all transitive obligations are satisfied.
|
|
||||||
let errors = fulfill_cx.select_all_or_error(&infcx);
|
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
infcx.report_fulfillment_errors(&errors, None, false);
|
infcx.report_fulfillment_errors(&errors, None, false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,10 @@ use rustc_hir as hir;
|
||||||
use rustc_hir::intravisit::{self, Visitor};
|
use rustc_hir::intravisit::{self, Visitor};
|
||||||
use rustc_hir::{ForeignItem, ForeignItemKind, HirId};
|
use rustc_hir::{ForeignItem, ForeignItemKind, HirId};
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
use rustc_infer::traits::TraitEngine;
|
|
||||||
use rustc_infer::traits::{ObligationCause, WellFormedLoc};
|
use rustc_infer::traits::{ObligationCause, WellFormedLoc};
|
||||||
use rustc_middle::ty::query::Providers;
|
use rustc_middle::ty::query::Providers;
|
||||||
use rustc_middle::ty::{self, Region, ToPredicate, TyCtxt, TypeFoldable, TypeFolder};
|
use rustc_middle::ty::{self, Region, ToPredicate, TyCtxt, TypeFoldable, TypeFolder};
|
||||||
use rustc_trait_selection::traits::{self, TraitEngineExt};
|
use rustc_trait_selection::traits;
|
||||||
|
|
||||||
pub fn provide(providers: &mut Providers) {
|
pub fn provide(providers: &mut Providers) {
|
||||||
*providers = Providers { diagnostic_hir_wf_check, ..*providers };
|
*providers = Providers { diagnostic_hir_wf_check, ..*providers };
|
||||||
|
@ -66,7 +65,6 @@ fn diagnostic_hir_wf_check<'tcx>(
|
||||||
impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> {
|
impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> {
|
||||||
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
|
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
|
||||||
self.tcx.infer_ctxt().enter(|infcx| {
|
self.tcx.infer_ctxt().enter(|infcx| {
|
||||||
let mut fulfill = <dyn TraitEngine<'tcx>>::new(self.tcx);
|
|
||||||
let tcx_ty =
|
let tcx_ty =
|
||||||
self.icx.to_ty(ty).fold_with(&mut EraseAllBoundRegions { tcx: self.tcx });
|
self.icx.to_ty(ty).fold_with(&mut EraseAllBoundRegions { tcx: self.tcx });
|
||||||
let cause = traits::ObligationCause::new(
|
let cause = traits::ObligationCause::new(
|
||||||
|
@ -74,7 +72,7 @@ fn diagnostic_hir_wf_check<'tcx>(
|
||||||
self.hir_id,
|
self.hir_id,
|
||||||
traits::ObligationCauseCode::WellFormed(None),
|
traits::ObligationCauseCode::WellFormed(None),
|
||||||
);
|
);
|
||||||
fulfill.register_predicate_obligation(
|
let errors = traits::fully_solve_obligation(
|
||||||
&infcx,
|
&infcx,
|
||||||
traits::Obligation::new(
|
traits::Obligation::new(
|
||||||
cause,
|
cause,
|
||||||
|
@ -83,8 +81,6 @@ fn diagnostic_hir_wf_check<'tcx>(
|
||||||
.to_predicate(self.tcx),
|
.to_predicate(self.tcx),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
let errors = fulfill.select_all_or_error(&infcx);
|
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
debug!("Wf-check got errors for {:?}: {:?}", ty, errors);
|
debug!("Wf-check got errors for {:?}: {:?}", ty, errors);
|
||||||
for error in errors {
|
for error in errors {
|
||||||
|
|
|
@ -104,7 +104,6 @@ use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::{Node, CRATE_HIR_ID};
|
use rustc_hir::{Node, CRATE_HIR_ID};
|
||||||
use rustc_infer::infer::{InferOk, TyCtxtInferExt};
|
use rustc_infer::infer::{InferOk, TyCtxtInferExt};
|
||||||
use rustc_infer::traits::TraitEngineExt as _;
|
|
||||||
use rustc_middle::middle;
|
use rustc_middle::middle;
|
||||||
use rustc_middle::ty::query::Providers;
|
use rustc_middle::ty::query::Providers;
|
||||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||||
|
@ -112,11 +111,8 @@ use rustc_middle::util;
|
||||||
use rustc_session::config::EntryFnType;
|
use rustc_session::config::EntryFnType;
|
||||||
use rustc_span::{symbol::sym, Span, DUMMY_SP};
|
use rustc_span::{symbol::sym, Span, DUMMY_SP};
|
||||||
use rustc_target::spec::abi::Abi;
|
use rustc_target::spec::abi::Abi;
|
||||||
use rustc_trait_selection::infer::InferCtxtExt;
|
|
||||||
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
|
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
|
||||||
use rustc_trait_selection::traits::{
|
use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
|
||||||
self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt as _,
|
|
||||||
};
|
|
||||||
|
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
|
@ -148,18 +144,15 @@ fn require_same_types<'tcx>(
|
||||||
) -> bool {
|
) -> bool {
|
||||||
tcx.infer_ctxt().enter(|ref infcx| {
|
tcx.infer_ctxt().enter(|ref infcx| {
|
||||||
let param_env = ty::ParamEnv::empty();
|
let param_env = ty::ParamEnv::empty();
|
||||||
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
|
let errors = match infcx.at(cause, param_env).eq(expected, actual) {
|
||||||
match infcx.at(cause, param_env).eq(expected, actual) {
|
Ok(InferOk { obligations, .. }) => traits::fully_solve_obligations(infcx, obligations),
|
||||||
Ok(InferOk { obligations, .. }) => {
|
|
||||||
fulfill_cx.register_predicate_obligations(infcx, obligations);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
infcx.report_mismatched_types(cause, expected, actual, err).emit();
|
infcx.report_mismatched_types(cause, expected, actual, err).emit();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
match fulfill_cx.select_all_or_error(infcx).as_slice() {
|
match &errors[..] {
|
||||||
[] => true,
|
[] => true,
|
||||||
errors => {
|
errors => {
|
||||||
infcx.report_fulfillment_errors(errors, None, false);
|
infcx.report_fulfillment_errors(errors, None, false);
|
||||||
|
@ -303,7 +296,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let expected_return_type;
|
let expected_return_type;
|
||||||
if let Some(term_id) = tcx.lang_items().termination() {
|
if let Some(term_did) = tcx.lang_items().termination() {
|
||||||
let return_ty = main_fnsig.output();
|
let return_ty = main_fnsig.output();
|
||||||
let return_ty_span = main_fn_return_type_span(tcx, main_def_id).unwrap_or(main_span);
|
let return_ty_span = main_fn_return_type_span(tcx, main_def_id).unwrap_or(main_span);
|
||||||
if !return_ty.bound_vars().is_empty() {
|
if !return_ty.bound_vars().is_empty() {
|
||||||
|
@ -314,33 +307,17 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
|
||||||
}
|
}
|
||||||
let return_ty = return_ty.skip_binder();
|
let return_ty = return_ty.skip_binder();
|
||||||
tcx.infer_ctxt().enter(|infcx| {
|
tcx.infer_ctxt().enter(|infcx| {
|
||||||
|
// Main should have no WC, so empty param env is OK here.
|
||||||
|
let param_env = ty::ParamEnv::empty();
|
||||||
let cause = traits::ObligationCause::new(
|
let cause = traits::ObligationCause::new(
|
||||||
return_ty_span,
|
return_ty_span,
|
||||||
main_diagnostics_hir_id,
|
main_diagnostics_hir_id,
|
||||||
ObligationCauseCode::MainFunctionType,
|
ObligationCauseCode::MainFunctionType,
|
||||||
);
|
);
|
||||||
let mut fulfillment_cx = traits::FulfillmentContext::new();
|
let ocx = traits::ObligationCtxt::new(&infcx);
|
||||||
// normalize any potential projections in the return type, then add
|
let norm_return_ty = ocx.normalize(cause.clone(), param_env, return_ty);
|
||||||
// any possible obligations to the fulfillment context.
|
ocx.register_bound(cause, param_env, norm_return_ty, term_did);
|
||||||
// HACK(ThePuzzlemaker) this feels symptomatic of a problem within
|
let errors = ocx.select_all_or_error();
|
||||||
// checking trait fulfillment, not this here. I'm not sure why it
|
|
||||||
// works in the example in `fn test()` given in #88609? This also
|
|
||||||
// probably isn't the best way to do this.
|
|
||||||
let InferOk { value: norm_return_ty, obligations } = infcx
|
|
||||||
.partially_normalize_associated_types_in(
|
|
||||||
cause.clone(),
|
|
||||||
ty::ParamEnv::empty(),
|
|
||||||
return_ty,
|
|
||||||
);
|
|
||||||
fulfillment_cx.register_predicate_obligations(&infcx, obligations);
|
|
||||||
fulfillment_cx.register_bound(
|
|
||||||
&infcx,
|
|
||||||
ty::ParamEnv::empty(),
|
|
||||||
norm_return_ty,
|
|
||||||
term_id,
|
|
||||||
cause,
|
|
||||||
);
|
|
||||||
let errors = fulfillment_cx.select_all_or_error(&infcx);
|
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
infcx.report_fulfillment_errors(&errors, None, false);
|
infcx.report_fulfillment_errors(&errors, None, false);
|
||||||
error = true;
|
error = true;
|
||||||
|
|
|
@ -9,7 +9,7 @@ use rustc_middle::ty::{EarlyBinder, Opaque, PredicateKind::Trait};
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
use rustc_span::{sym, Span};
|
use rustc_span::{sym, Span};
|
||||||
use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt;
|
use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt;
|
||||||
use rustc_trait_selection::traits::{self, FulfillmentError, TraitEngine};
|
use rustc_trait_selection::traits::{self, FulfillmentError};
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
|
@ -80,9 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
|
||||||
let span = decl.output.span();
|
let span = decl.output.span();
|
||||||
let send_errors = cx.tcx.infer_ctxt().enter(|infcx| {
|
let send_errors = cx.tcx.infer_ctxt().enter(|infcx| {
|
||||||
let cause = traits::ObligationCause::misc(span, hir_id);
|
let cause = traits::ObligationCause::misc(span, hir_id);
|
||||||
let mut fulfillment_cx = traits::FulfillmentContext::new();
|
traits::fully_solve_bound(&infcx, cause, cx.param_env, ret_ty, send_trait)
|
||||||
fulfillment_cx.register_bound(&infcx, cx.param_env, ret_ty, send_trait, cause);
|
|
||||||
fulfillment_cx.select_all_or_error(&infcx)
|
|
||||||
});
|
});
|
||||||
if !send_errors.is_empty() {
|
if !send_errors.is_empty() {
|
||||||
span_lint_and_then(
|
span_lint_and_then(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue