mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 06:44:35 +00:00
Correctly handle normalization in implied bounds
Special-case Bevy dependents to not error
This commit is contained in:
parent
6ae4cfbbb0
commit
d96003dd2a
@ -368,7 +368,7 @@ fn check_opaque_meets_bounds<'tcx>(
|
|||||||
// Can have different predicates to their defining use
|
// Can have different predicates to their defining use
|
||||||
hir::OpaqueTyOrigin::TyAlias { .. } => {
|
hir::OpaqueTyOrigin::TyAlias { .. } => {
|
||||||
let wf_tys = ocx.assumed_wf_types_and_report_errors(param_env, def_id)?;
|
let wf_tys = ocx.assumed_wf_types_and_report_errors(param_env, def_id)?;
|
||||||
let implied_bounds = infcx.implied_bounds_tys(param_env, def_id, wf_tys);
|
let implied_bounds = infcx.implied_bounds_tys_compat(param_env, def_id, &wf_tys);
|
||||||
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
|
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
|
||||||
ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?;
|
ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?;
|
||||||
}
|
}
|
||||||
|
@ -378,7 +378,7 @@ fn compare_method_predicate_entailment<'tcx>(
|
|||||||
// lifetime parameters.
|
// lifetime parameters.
|
||||||
let outlives_env = OutlivesEnvironment::with_bounds(
|
let outlives_env = OutlivesEnvironment::with_bounds(
|
||||||
param_env,
|
param_env,
|
||||||
infcx.implied_bounds_tys(param_env, impl_m_def_id, wf_tys),
|
infcx.implied_bounds_tys_compat(param_env, impl_m_def_id, &wf_tys),
|
||||||
);
|
);
|
||||||
let errors = infcx.resolve_regions(&outlives_env);
|
let errors = infcx.resolve_regions(&outlives_env);
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
@ -702,7 +702,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
|
|||||||
// lifetime parameters.
|
// lifetime parameters.
|
||||||
let outlives_env = OutlivesEnvironment::with_bounds(
|
let outlives_env = OutlivesEnvironment::with_bounds(
|
||||||
param_env,
|
param_env,
|
||||||
infcx.implied_bounds_tys(param_env, impl_m_def_id, wf_tys),
|
infcx.implied_bounds_tys_compat(param_env, impl_m_def_id, &wf_tys),
|
||||||
);
|
);
|
||||||
ocx.resolve_regions_and_report_errors(impl_m_def_id, &outlives_env)?;
|
ocx.resolve_regions_and_report_errors(impl_m_def_id, &outlives_env)?;
|
||||||
|
|
||||||
@ -2070,7 +2070,8 @@ pub(super) fn check_type_bounds<'tcx>(
|
|||||||
|
|
||||||
// Finally, resolve all regions. This catches wily misuses of
|
// Finally, resolve all regions. This catches wily misuses of
|
||||||
// lifetime parameters.
|
// lifetime parameters.
|
||||||
let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_def_id, assumed_wf_types);
|
let implied_bounds =
|
||||||
|
infcx.implied_bounds_tys_compat(param_env, impl_ty_def_id, &assumed_wf_types);
|
||||||
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
|
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
|
||||||
ocx.resolve_regions_and_report_errors(impl_ty_def_id, &outlives_env)
|
ocx.resolve_regions_and_report_errors(impl_ty_def_id, &outlives_env)
|
||||||
}
|
}
|
||||||
|
@ -158,7 +158,7 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
|
|||||||
}
|
}
|
||||||
let outlives_env = OutlivesEnvironment::with_bounds(
|
let outlives_env = OutlivesEnvironment::with_bounds(
|
||||||
param_env,
|
param_env,
|
||||||
infcx.implied_bounds_tys(param_env, impl_m.def_id.expect_local(), implied_wf_types),
|
infcx.implied_bounds_tys_compat(param_env, impl_m.def_id.expect_local(), &implied_wf_types),
|
||||||
);
|
);
|
||||||
let errors = infcx.resolve_regions(&outlives_env);
|
let errors = infcx.resolve_regions(&outlives_env);
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
|
@ -15,6 +15,7 @@ use rustc_infer::infer::outlives::obligations::TypeOutlives;
|
|||||||
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
|
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
|
||||||
use rustc_middle::mir::ConstraintCategory;
|
use rustc_middle::mir::ConstraintCategory;
|
||||||
use rustc_middle::query::Providers;
|
use rustc_middle::query::Providers;
|
||||||
|
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||||
use rustc_middle::ty::trait_def::TraitSpecializationKind;
|
use rustc_middle::ty::trait_def::TraitSpecializationKind;
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
|
self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
|
||||||
@ -112,8 +113,6 @@ where
|
|||||||
|
|
||||||
let assumed_wf_types = wfcx.ocx.assumed_wf_types_and_report_errors(param_env, body_def_id)?;
|
let assumed_wf_types = wfcx.ocx.assumed_wf_types_and_report_errors(param_env, body_def_id)?;
|
||||||
|
|
||||||
let implied_bounds = infcx.implied_bounds_tys(param_env, body_def_id, assumed_wf_types);
|
|
||||||
|
|
||||||
let errors = wfcx.select_all_or_error();
|
let errors = wfcx.select_all_or_error();
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
let err = infcx.err_ctxt().report_fulfillment_errors(errors);
|
let err = infcx.err_ctxt().report_fulfillment_errors(errors);
|
||||||
@ -128,10 +127,58 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let infcx_compat = infcx.fork();
|
||||||
|
|
||||||
|
debug!(?assumed_wf_types);
|
||||||
|
let implied_bounds = infcx.implied_bounds_tys(param_env, body_def_id, &assumed_wf_types);
|
||||||
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
|
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
|
||||||
|
|
||||||
wfcx.ocx.resolve_regions_and_report_errors(body_def_id, &outlives_env)?;
|
let errors = infcx.resolve_regions(&outlives_env);
|
||||||
infcx.tainted_by_errors().error_reported()
|
if errors.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_bevy = 'is_bevy: {
|
||||||
|
// We don't want to emit this for dependents of Bevy, for now.
|
||||||
|
// See #119956
|
||||||
|
let is_bevy_paramset = |def: ty::AdtDef<'_>| {
|
||||||
|
let adt_did = with_no_trimmed_paths!(infcx.tcx.def_path_str(def.0.did));
|
||||||
|
adt_did.contains("ParamSet")
|
||||||
|
};
|
||||||
|
for ty in assumed_wf_types.iter() {
|
||||||
|
match ty.kind() {
|
||||||
|
ty::Adt(def, _) => {
|
||||||
|
if is_bevy_paramset(*def) {
|
||||||
|
break 'is_bevy true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ty::Ref(_, ty, _) => match ty.kind() {
|
||||||
|
ty::Adt(def, _) => {
|
||||||
|
if is_bevy_paramset(*def) {
|
||||||
|
break 'is_bevy true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_bevy {
|
||||||
|
let implied_bounds =
|
||||||
|
infcx_compat.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types);
|
||||||
|
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
|
||||||
|
let errors_compat = infcx_compat.resolve_regions(&outlives_env);
|
||||||
|
if errors_compat.is_empty() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(infcx_compat.err_ctxt().report_region_errors(body_def_id, &errors_compat))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(infcx.err_ctxt().report_region_errors(body_def_id, &errors))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) -> Result<(), ErrorGuaranteed> {
|
fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) -> Result<(), ErrorGuaranteed> {
|
||||||
@ -723,7 +770,7 @@ fn resolve_regions_with_wf_tys<'tcx>(
|
|||||||
let infcx = tcx.infer_ctxt().build();
|
let infcx = tcx.infer_ctxt().build();
|
||||||
let outlives_environment = OutlivesEnvironment::with_bounds(
|
let outlives_environment = OutlivesEnvironment::with_bounds(
|
||||||
param_env,
|
param_env,
|
||||||
infcx.implied_bounds_tys(param_env, id, wf_tys.clone()),
|
infcx.implied_bounds_tys_compat(param_env, id, wf_tys),
|
||||||
);
|
);
|
||||||
let region_bound_pairs = outlives_environment.region_bound_pairs();
|
let region_bound_pairs = outlives_environment.region_bound_pairs();
|
||||||
|
|
||||||
|
@ -202,7 +202,8 @@ fn get_impl_args(
|
|||||||
return Err(guar);
|
return Err(guar);
|
||||||
}
|
}
|
||||||
|
|
||||||
let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_def_id, assumed_wf_types);
|
let implied_bounds =
|
||||||
|
infcx.implied_bounds_tys_compat(param_env, impl1_def_id, &assumed_wf_types);
|
||||||
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
|
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
|
||||||
let _ = ocx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env);
|
let _ = ocx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env);
|
||||||
let Ok(impl2_args) = infcx.fully_resolve(impl2_args) else {
|
let Ok(impl2_args) = infcx.fully_resolve(impl2_args) else {
|
||||||
|
@ -74,6 +74,10 @@ impl<T> EraseType for Result<&'_ T, traits::query::NoSolution> {
|
|||||||
type Result = [u8; size_of::<Result<&'static (), traits::query::NoSolution>>()];
|
type Result = [u8; size_of::<Result<&'static (), traits::query::NoSolution>>()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> EraseType for Result<&'_ [T], traits::query::NoSolution> {
|
||||||
|
type Result = [u8; size_of::<Result<&'static [()], traits::query::NoSolution>>()];
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> EraseType for Result<&'_ T, rustc_errors::ErrorGuaranteed> {
|
impl<T> EraseType for Result<&'_ T, rustc_errors::ErrorGuaranteed> {
|
||||||
type Result = [u8; size_of::<Result<&'static (), rustc_errors::ErrorGuaranteed>>()];
|
type Result = [u8; size_of::<Result<&'static (), rustc_errors::ErrorGuaranteed>>()];
|
||||||
}
|
}
|
||||||
|
@ -1949,7 +1949,7 @@ rustc_queries! {
|
|||||||
desc { "normalizing `{}`", goal.value }
|
desc { "normalizing `{}`", goal.value }
|
||||||
}
|
}
|
||||||
|
|
||||||
query implied_outlives_bounds(
|
query implied_outlives_bounds_compat(
|
||||||
goal: CanonicalTyGoal<'tcx>
|
goal: CanonicalTyGoal<'tcx>
|
||||||
) -> Result<
|
) -> Result<
|
||||||
&'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec<OutlivesBound<'tcx>>>>,
|
&'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec<OutlivesBound<'tcx>>>>,
|
||||||
@ -1958,6 +1958,15 @@ rustc_queries! {
|
|||||||
desc { "computing implied outlives bounds for `{}`", goal.value.value }
|
desc { "computing implied outlives bounds for `{}`", goal.value.value }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
query implied_outlives_bounds(
|
||||||
|
goal: CanonicalTyGoal<'tcx>
|
||||||
|
) -> Result<
|
||||||
|
&'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec<OutlivesBound<'tcx>>>>,
|
||||||
|
NoSolution,
|
||||||
|
> {
|
||||||
|
desc { "computing implied outlives bounds v2 for `{}`", goal.value.value }
|
||||||
|
}
|
||||||
|
|
||||||
/// Do not call this query directly:
|
/// Do not call this query directly:
|
||||||
/// invoke `DropckOutlives::new(dropped_ty)).fully_perform(typeck.infcx)` instead.
|
/// invoke `DropckOutlives::new(dropped_ty)).fully_perform(typeck.infcx)` instead.
|
||||||
query dropck_outlives(
|
query dropck_outlives(
|
||||||
|
@ -191,7 +191,7 @@ pub struct NormalizationResult<'tcx> {
|
|||||||
/// case they are called implied bounds). They are fed to the
|
/// case they are called implied bounds). They are fed to the
|
||||||
/// `OutlivesEnv` which in turn is supplied to the region checker and
|
/// `OutlivesEnv` which in turn is supplied to the region checker and
|
||||||
/// other parts of the inference system.
|
/// other parts of the inference system.
|
||||||
#[derive(Clone, Debug, TypeFoldable, TypeVisitable, HashStable)]
|
#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, HashStable)]
|
||||||
pub enum OutlivesBound<'tcx> {
|
pub enum OutlivesBound<'tcx> {
|
||||||
RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>),
|
RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>),
|
||||||
RegionSubParam(ty::Region<'tcx>, ty::ParamTy),
|
RegionSubParam(ty::Region<'tcx>, ty::ParamTy),
|
||||||
|
@ -191,10 +191,10 @@ pub fn all_fields_implement_trait<'tcx>(
|
|||||||
// Check regions assuming the self type of the impl is WF
|
// Check regions assuming the self type of the impl is WF
|
||||||
let outlives_env = OutlivesEnvironment::with_bounds(
|
let outlives_env = OutlivesEnvironment::with_bounds(
|
||||||
param_env,
|
param_env,
|
||||||
infcx.implied_bounds_tys(
|
infcx.implied_bounds_tys_compat(
|
||||||
param_env,
|
param_env,
|
||||||
parent_cause.body_id,
|
parent_cause.body_id,
|
||||||
FxIndexSet::from_iter([self_type]),
|
&FxIndexSet::from_iter([self_type]),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
let errors = infcx.resolve_regions(&outlives_env);
|
let errors = infcx.resolve_regions(&outlives_env);
|
||||||
|
@ -9,24 +9,24 @@ use rustc_span::def_id::LocalDefId;
|
|||||||
|
|
||||||
pub use rustc_middle::traits::query::OutlivesBound;
|
pub use rustc_middle::traits::query::OutlivesBound;
|
||||||
|
|
||||||
|
pub type BoundsCompat<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a;
|
||||||
pub type Bounds<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a;
|
pub type Bounds<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a;
|
||||||
pub trait InferCtxtExt<'a, 'tcx> {
|
pub trait InferCtxtExt<'a, 'tcx> {
|
||||||
fn implied_outlives_bounds(
|
fn implied_bounds_tys_compat(
|
||||||
&self,
|
&'a self,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
body_id: LocalDefId,
|
body_id: LocalDefId,
|
||||||
ty: Ty<'tcx>,
|
tys: &'a FxIndexSet<Ty<'tcx>>,
|
||||||
) -> Vec<OutlivesBound<'tcx>>;
|
) -> BoundsCompat<'a, 'tcx>;
|
||||||
|
|
||||||
fn implied_bounds_tys(
|
fn implied_bounds_tys(
|
||||||
&'a self,
|
&'a self,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
body_id: LocalDefId,
|
body_id: LocalDefId,
|
||||||
tys: FxIndexSet<Ty<'tcx>>,
|
tys: &'a FxIndexSet<Ty<'tcx>>,
|
||||||
) -> Bounds<'a, 'tcx>;
|
) -> Bounds<'a, 'tcx>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
|
|
||||||
/// Implied bounds are region relationships that we deduce
|
/// Implied bounds are region relationships that we deduce
|
||||||
/// automatically. The idea is that (e.g.) a caller must check that a
|
/// automatically. The idea is that (e.g.) a caller must check that a
|
||||||
/// function's argument types are well-formed immediately before
|
/// function's argument types are well-formed immediately before
|
||||||
@ -46,15 +46,16 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
|
|||||||
/// Note that this may cause outlives obligations to be injected
|
/// Note that this may cause outlives obligations to be injected
|
||||||
/// into the inference context with this body-id.
|
/// into the inference context with this body-id.
|
||||||
/// - `ty`, the type that we are supposed to assume is WF.
|
/// - `ty`, the type that we are supposed to assume is WF.
|
||||||
#[instrument(level = "debug", skip(self, param_env, body_id), ret)]
|
#[instrument(level = "debug", skip(infcx, param_env, body_id), ret)]
|
||||||
fn implied_outlives_bounds(
|
fn implied_outlives_bounds<'a, 'tcx>(
|
||||||
&self,
|
infcx: &'a InferCtxt<'tcx>,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
body_id: LocalDefId,
|
body_id: LocalDefId,
|
||||||
ty: Ty<'tcx>,
|
ty: Ty<'tcx>,
|
||||||
|
compat: bool,
|
||||||
) -> Vec<OutlivesBound<'tcx>> {
|
) -> Vec<OutlivesBound<'tcx>> {
|
||||||
let ty = self.resolve_vars_if_possible(ty);
|
let ty = infcx.resolve_vars_if_possible(ty);
|
||||||
let ty = OpportunisticRegionResolver::new(self).fold_ty(ty);
|
let ty = OpportunisticRegionResolver::new(infcx).fold_ty(ty);
|
||||||
|
|
||||||
// We do not expect existential variables in implied bounds.
|
// We do not expect existential variables in implied bounds.
|
||||||
// We may however encounter unconstrained lifetime variables
|
// We may however encounter unconstrained lifetime variables
|
||||||
@ -65,13 +66,18 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
|
|||||||
assert!(!ty.has_non_region_infer());
|
assert!(!ty.has_non_region_infer());
|
||||||
|
|
||||||
let mut canonical_var_values = OriginalQueryValues::default();
|
let mut canonical_var_values = OriginalQueryValues::default();
|
||||||
let canonical_ty = self.canonicalize_query(param_env.and(ty), &mut canonical_var_values);
|
let canonical_ty = infcx.canonicalize_query(param_env.and(ty), &mut canonical_var_values);
|
||||||
let Ok(canonical_result) = self.tcx.implied_outlives_bounds(canonical_ty) else {
|
let implied_bounds_result = if compat {
|
||||||
|
infcx.tcx.implied_outlives_bounds_compat(canonical_ty)
|
||||||
|
} else {
|
||||||
|
infcx.tcx.implied_outlives_bounds(canonical_ty)
|
||||||
|
};
|
||||||
|
let Ok(canonical_result) = implied_bounds_result else {
|
||||||
return vec![];
|
return vec![];
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut constraints = QueryRegionConstraints::default();
|
let mut constraints = QueryRegionConstraints::default();
|
||||||
let Ok(InferOk { value: mut bounds, obligations }) = self
|
let Ok(InferOk { value: mut bounds, obligations }) = infcx
|
||||||
.instantiate_nll_query_response_and_region_obligations(
|
.instantiate_nll_query_response_and_region_obligations(
|
||||||
&ObligationCause::dummy(),
|
&ObligationCause::dummy(),
|
||||||
param_env,
|
param_env,
|
||||||
@ -89,7 +95,7 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
|
|||||||
bounds.retain(|bound| !bound.has_placeholders());
|
bounds.retain(|bound| !bound.has_placeholders());
|
||||||
|
|
||||||
if !constraints.is_empty() {
|
if !constraints.is_empty() {
|
||||||
let span = self.tcx.def_span(body_id);
|
let span = infcx.tcx.def_span(body_id);
|
||||||
|
|
||||||
debug!(?constraints);
|
debug!(?constraints);
|
||||||
if !constraints.member_constraints.is_empty() {
|
if !constraints.member_constraints.is_empty() {
|
||||||
@ -98,10 +104,10 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
|
|||||||
|
|
||||||
// Instantiation may have produced new inference variables and constraints on those
|
// Instantiation may have produced new inference variables and constraints on those
|
||||||
// variables. Process these constraints.
|
// variables. Process these constraints.
|
||||||
let ocx = ObligationCtxt::new(self);
|
let ocx = ObligationCtxt::new(infcx);
|
||||||
let cause = ObligationCause::misc(span, body_id);
|
let cause = ObligationCause::misc(span, body_id);
|
||||||
for &constraint in &constraints.outlives {
|
for &constraint in &constraints.outlives {
|
||||||
ocx.register_obligation(self.query_outlives_constraint_to_obligation(
|
ocx.register_obligation(infcx.query_outlives_constraint_to_obligation(
|
||||||
constraint,
|
constraint,
|
||||||
cause.clone(),
|
cause.clone(),
|
||||||
param_env,
|
param_env,
|
||||||
@ -110,7 +116,7 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
|
|||||||
|
|
||||||
let errors = ocx.select_all_or_error();
|
let errors = ocx.select_all_or_error();
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
self.dcx().span_delayed_bug(
|
infcx.dcx().span_delayed_bug(
|
||||||
span,
|
span,
|
||||||
"implied_outlives_bounds failed to solve obligations from instantiation",
|
"implied_outlives_bounds failed to solve obligations from instantiation",
|
||||||
);
|
);
|
||||||
@ -120,12 +126,22 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
|
|||||||
bounds
|
bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
|
||||||
|
fn implied_bounds_tys_compat(
|
||||||
|
&'a self,
|
||||||
|
param_env: ParamEnv<'tcx>,
|
||||||
|
body_id: LocalDefId,
|
||||||
|
tys: &'a FxIndexSet<Ty<'tcx>>,
|
||||||
|
) -> BoundsCompat<'a, 'tcx> {
|
||||||
|
tys.iter().flat_map(move |ty| implied_outlives_bounds(self, param_env, body_id, *ty, true))
|
||||||
|
}
|
||||||
|
|
||||||
fn implied_bounds_tys(
|
fn implied_bounds_tys(
|
||||||
&'a self,
|
&'a self,
|
||||||
param_env: ParamEnv<'tcx>,
|
param_env: ParamEnv<'tcx>,
|
||||||
body_id: LocalDefId,
|
body_id: LocalDefId,
|
||||||
tys: FxIndexSet<Ty<'tcx>>,
|
tys: &'a FxIndexSet<Ty<'tcx>>,
|
||||||
) -> Bounds<'a, 'tcx> {
|
) -> Bounds<'a, 'tcx> {
|
||||||
tys.into_iter().flat_map(move |ty| self.implied_outlives_bounds(param_env, body_id, ty))
|
tys.iter().flat_map(move |ty| implied_outlives_bounds(self, param_env, body_id, *ty, false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,11 @@ use crate::traits::ObligationCtxt;
|
|||||||
|
|
||||||
use rustc_infer::infer::canonical::Canonical;
|
use rustc_infer::infer::canonical::Canonical;
|
||||||
use rustc_infer::infer::outlives::components::{push_outlives_components, Component};
|
use rustc_infer::infer::outlives::components::{push_outlives_components, Component};
|
||||||
|
use rustc_infer::infer::resolve::OpportunisticRegionResolver;
|
||||||
use rustc_infer::traits::query::OutlivesBound;
|
use rustc_infer::traits::query::OutlivesBound;
|
||||||
use rustc_middle::infer::canonical::CanonicalQueryResponse;
|
use rustc_middle::infer::canonical::CanonicalQueryResponse;
|
||||||
use rustc_middle::traits::ObligationCause;
|
use rustc_middle::traits::ObligationCause;
|
||||||
use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt};
|
use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFolder, TypeVisitableExt};
|
||||||
use rustc_span::def_id::CRATE_DEF_ID;
|
use rustc_span::def_id::CRATE_DEF_ID;
|
||||||
use rustc_span::DUMMY_SP;
|
use rustc_span::DUMMY_SP;
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
@ -47,14 +48,14 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> {
|
|||||||
param_env.and(ty)
|
param_env.and(ty)
|
||||||
});
|
});
|
||||||
|
|
||||||
tcx.implied_outlives_bounds(canonicalized)
|
tcx.implied_outlives_bounds_compat(canonicalized)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn perform_locally_with_next_solver(
|
fn perform_locally_with_next_solver(
|
||||||
ocx: &ObligationCtxt<'_, 'tcx>,
|
ocx: &ObligationCtxt<'_, 'tcx>,
|
||||||
key: ParamEnvAnd<'tcx, Self>,
|
key: ParamEnvAnd<'tcx, Self>,
|
||||||
) -> Result<Self::QueryResponse, NoSolution> {
|
) -> Result<Self::QueryResponse, NoSolution> {
|
||||||
compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty)
|
compute_implied_outlives_bounds_compat_inner(ocx, key.param_env, key.value.ty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,6 +63,85 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
|
|||||||
ocx: &ObligationCtxt<'_, 'tcx>,
|
ocx: &ObligationCtxt<'_, 'tcx>,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
ty: Ty<'tcx>,
|
ty: Ty<'tcx>,
|
||||||
|
) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> {
|
||||||
|
let normalize_op = |ty| {
|
||||||
|
let ty = ocx.normalize(&ObligationCause::dummy(), param_env, ty);
|
||||||
|
if !ocx.select_all_or_error().is_empty() {
|
||||||
|
return Err(NoSolution);
|
||||||
|
}
|
||||||
|
let ty = ocx.infcx.resolve_vars_if_possible(ty);
|
||||||
|
let ty = OpportunisticRegionResolver::new(&ocx.infcx).fold_ty(ty);
|
||||||
|
Ok(ty)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sometimes when we ask what it takes for T: WF, we get back that
|
||||||
|
// U: WF is required; in that case, we push U onto this stack and
|
||||||
|
// process it next. Because the resulting predicates aren't always
|
||||||
|
// guaranteed to be a subset of the original type, so we need to store the
|
||||||
|
// WF args we've computed in a set.
|
||||||
|
let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default();
|
||||||
|
let mut wf_args = vec![ty.into(), normalize_op(ty)?.into()];
|
||||||
|
|
||||||
|
let mut outlives_bounds: Vec<OutlivesBound<'tcx>> = vec![];
|
||||||
|
|
||||||
|
while let Some(arg) = wf_args.pop() {
|
||||||
|
if !checked_wf_args.insert(arg) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// From the full set of obligations, just filter down to the region relationships.
|
||||||
|
for obligation in
|
||||||
|
wf::unnormalized_obligations(ocx.infcx, param_env, arg).into_iter().flatten()
|
||||||
|
{
|
||||||
|
assert!(!obligation.has_escaping_bound_vars());
|
||||||
|
let Some(pred) = obligation.predicate.kind().no_bound_vars() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
match pred {
|
||||||
|
// FIXME(const_generics): Make sure that `<'a, 'b, const N: &'a &'b u32>` is sound
|
||||||
|
// if we ever support that
|
||||||
|
ty::PredicateKind::Clause(ty::ClauseKind::Trait(..))
|
||||||
|
| ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
|
||||||
|
| ty::PredicateKind::Subtype(..)
|
||||||
|
| ty::PredicateKind::Coerce(..)
|
||||||
|
| ty::PredicateKind::Clause(ty::ClauseKind::Projection(..))
|
||||||
|
| ty::PredicateKind::ObjectSafe(..)
|
||||||
|
| ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
|
||||||
|
| ty::PredicateKind::ConstEquate(..)
|
||||||
|
| ty::PredicateKind::Ambiguous
|
||||||
|
| ty::PredicateKind::NormalizesTo(..)
|
||||||
|
| ty::PredicateKind::AliasRelate(..) => {}
|
||||||
|
|
||||||
|
// We need to search through *all* WellFormed predicates
|
||||||
|
ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
|
||||||
|
wf_args.push(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to register region relationships
|
||||||
|
ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(
|
||||||
|
ty::OutlivesPredicate(r_a, r_b),
|
||||||
|
)) => outlives_bounds.push(OutlivesBound::RegionSubRegion(r_b, r_a)),
|
||||||
|
|
||||||
|
ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(
|
||||||
|
ty_a,
|
||||||
|
r_b,
|
||||||
|
))) => {
|
||||||
|
let ty_a = normalize_op(ty_a)?;
|
||||||
|
let mut components = smallvec![];
|
||||||
|
push_outlives_components(ocx.infcx.tcx, ty_a, &mut components);
|
||||||
|
outlives_bounds.extend(implied_bounds_from_components(r_b, components))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(outlives_bounds)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compute_implied_outlives_bounds_compat_inner<'tcx>(
|
||||||
|
ocx: &ObligationCtxt<'_, 'tcx>,
|
||||||
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
|
ty: Ty<'tcx>,
|
||||||
) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> {
|
) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> {
|
||||||
let tcx = ocx.infcx.tcx;
|
let tcx = ocx.infcx.tcx;
|
||||||
|
|
||||||
|
@ -8,13 +8,29 @@ use rustc_infer::traits::query::OutlivesBound;
|
|||||||
use rustc_middle::query::Providers;
|
use rustc_middle::query::Providers;
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::TyCtxt;
|
||||||
use rustc_trait_selection::infer::InferCtxtBuilderExt;
|
use rustc_trait_selection::infer::InferCtxtBuilderExt;
|
||||||
use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::compute_implied_outlives_bounds_inner;
|
use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::{
|
||||||
|
compute_implied_outlives_bounds_compat_inner, compute_implied_outlives_bounds_inner,
|
||||||
|
};
|
||||||
use rustc_trait_selection::traits::query::{CanonicalTyGoal, NoSolution};
|
use rustc_trait_selection::traits::query::{CanonicalTyGoal, NoSolution};
|
||||||
|
|
||||||
pub(crate) fn provide(p: &mut Providers) {
|
pub(crate) fn provide(p: &mut Providers) {
|
||||||
|
*p = Providers { implied_outlives_bounds_compat, ..*p };
|
||||||
*p = Providers { implied_outlives_bounds, ..*p };
|
*p = Providers { implied_outlives_bounds, ..*p };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn implied_outlives_bounds_compat<'tcx>(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
goal: CanonicalTyGoal<'tcx>,
|
||||||
|
) -> Result<
|
||||||
|
&'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec<OutlivesBound<'tcx>>>>,
|
||||||
|
NoSolution,
|
||||||
|
> {
|
||||||
|
tcx.infer_ctxt().enter_canonical_trait_query(&goal, |ocx, key| {
|
||||||
|
let (param_env, ty) = key.into_parts();
|
||||||
|
compute_implied_outlives_bounds_compat_inner(ocx, param_env, ty)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn implied_outlives_bounds<'tcx>(
|
fn implied_outlives_bounds<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
goal: CanonicalTyGoal<'tcx>,
|
goal: CanonicalTyGoal<'tcx>,
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
// Related to Bevy regression #118553
|
|
||||||
|
|
||||||
pub trait WorldQuery {}
|
|
||||||
impl WorldQuery for &u8 {}
|
|
||||||
|
|
||||||
pub struct Query<Q: WorldQuery>(Q);
|
|
||||||
|
|
||||||
pub trait SystemParam {
|
|
||||||
type State;
|
|
||||||
}
|
|
||||||
impl<Q: WorldQuery + 'static> SystemParam for Query<Q> {
|
|
||||||
type State = ();
|
|
||||||
// `Q: 'static` is required because we need the TypeId of Q ...
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ParamSet<T: SystemParam>(T)
|
|
||||||
where
|
|
||||||
T::State: Sized;
|
|
@ -1,11 +1,26 @@
|
|||||||
// aux-crate:bevy_ecs=bevy_ecs.rs
|
|
||||||
// check-pass
|
// check-pass
|
||||||
// Related to Bevy regression #118553
|
|
||||||
|
|
||||||
extern crate bevy_ecs;
|
// We currently special case bevy from erroring on incorrect implied bounds
|
||||||
|
// from normalization (issue #109628).
|
||||||
|
// Otherwise, we would expect this to hit that error.
|
||||||
|
|
||||||
use bevy_ecs::*;
|
pub trait WorldQuery {}
|
||||||
|
impl WorldQuery for &u8 {}
|
||||||
|
|
||||||
|
pub struct Query<Q: WorldQuery>(Q);
|
||||||
|
|
||||||
|
pub trait SystemParam {
|
||||||
|
type State;
|
||||||
|
}
|
||||||
|
impl<Q: WorldQuery + 'static> SystemParam for Query<Q> {
|
||||||
|
type State = ();
|
||||||
|
// `Q: 'static` is required because we need the TypeId of Q ...
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ParamSet<T: SystemParam>(T) where T::State: Sized;
|
||||||
|
|
||||||
fn handler<'a>(_: ParamSet<Query<&'a u8>>) {}
|
fn handler<'a>(_: ParamSet<Query<&'a u8>>) {}
|
||||||
|
|
||||||
|
fn ref_handler<'a>(_: &ParamSet<Query<&'a u8>>) {}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
// check-pass
|
|
||||||
// known-bug: #109628
|
|
||||||
|
|
||||||
trait Trait {
|
trait Trait {
|
||||||
type Assoc;
|
type Assoc;
|
||||||
}
|
}
|
||||||
@ -14,11 +11,13 @@ where
|
|||||||
T::Assoc: Clone; // any predicate using `T::Assoc` works here
|
T::Assoc: Clone; // any predicate using `T::Assoc` works here
|
||||||
|
|
||||||
fn func1(foo: Foo<(&str,)>) {
|
fn func1(foo: Foo<(&str,)>) {
|
||||||
|
//~^ ERROR `&str` does not fulfill the required lifetime
|
||||||
let _: &'static str = foo.0.0;
|
let _: &'static str = foo.0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
trait TestTrait {}
|
trait TestTrait {}
|
||||||
|
|
||||||
impl<X> TestTrait for [Foo<(X,)>; 1] {}
|
impl<X> TestTrait for [Foo<(X,)>; 1] {}
|
||||||
|
//~^ ERROR `X` may not live long enough
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
26
tests/ui/implied-bounds/from-trait-impl.stderr
Normal file
26
tests/ui/implied-bounds/from-trait-impl.stderr
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
error[E0477]: the type `&str` does not fulfill the required lifetime
|
||||||
|
--> $DIR/from-trait-impl.rs:13:15
|
||||||
|
|
|
||||||
|
LL | fn func1(foo: Foo<(&str,)>) {
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: type must satisfy the static lifetime
|
||||||
|
|
||||||
|
error[E0310]: the parameter type `X` may not live long enough
|
||||||
|
--> $DIR/from-trait-impl.rs:20:23
|
||||||
|
|
|
||||||
|
LL | impl<X> TestTrait for [Foo<(X,)>; 1] {}
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
| |
|
||||||
|
| the parameter type `X` must be valid for the static lifetime...
|
||||||
|
| ...so that the type `X` will meet its required lifetime bounds
|
||||||
|
|
|
||||||
|
help: consider adding an explicit lifetime bound
|
||||||
|
|
|
||||||
|
LL | impl<X: 'static> TestTrait for [Foo<(X,)>; 1] {}
|
||||||
|
| +++++++++
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
Some errors have detailed explanations: E0310, E0477.
|
||||||
|
For more information about an error, try `rustc --explain E0310`.
|
@ -1,5 +1,5 @@
|
|||||||
// check-pass
|
// check-pass
|
||||||
// Related to Bevy regression #118553
|
// Found in a crater run on #118553
|
||||||
|
|
||||||
pub trait QueryBase {
|
pub trait QueryBase {
|
||||||
type Db;
|
type Db;
|
||||||
@ -17,8 +17,7 @@ pub struct QueryTable<'me, Q, DB> {
|
|||||||
_marker: Option<&'me ()>,
|
_marker: Option<&'me ()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'me, Q> QueryTable<'me, Q, <Q as QueryBase>::Db>
|
impl<'me, Q> QueryTable<'me, Q, <Q as QueryBase>::Db> // projection is important
|
||||||
// projection is important
|
|
||||||
// ^^^ removing 'me (and in QueryTable) gives a different error
|
// ^^^ removing 'me (and in QueryTable) gives a different error
|
||||||
where
|
where
|
||||||
Q: for<'f> AsyncQueryFunction<'f>,
|
Q: for<'f> AsyncQueryFunction<'f>,
|
||||||
|
@ -1,33 +1,10 @@
|
|||||||
error[E0759]: `fn` parameter has lifetime `'x` but it needs to satisfy a `'static` lifetime requirement
|
error: lifetime may not live long enough
|
||||||
--> $DIR/normalization-nested.rs:35:28
|
--> $DIR/normalization-nested.rs:38:5
|
||||||
|
|
|
||||||
LL | pub fn test_wfcheck<'x>(_: Map<Vec<&'x ()>>) {}
|
|
||||||
| ^^^^^^^^^^^^^^^^
|
|
||||||
| |
|
|
||||||
| this data with lifetime `'x`...
|
|
||||||
| ...is used and required to live as long as `'static` here
|
|
||||||
|
|
|
||||||
note: `'static` lifetime requirement introduced by this bound
|
|
||||||
--> $DIR/normalization-nested.rs:33:14
|
|
||||||
|
|
|
||||||
LL | I::Item: 'static;
|
|
||||||
| ^^^^^^^
|
|
||||||
|
|
||||||
error[E0759]: `fn` parameter has lifetime `'x` but it needs to satisfy a `'static` lifetime requirement
|
|
||||||
--> $DIR/normalization-nested.rs:37:29
|
|
||||||
|
|
|
|
||||||
LL | pub fn test_borrowck<'x>(_: Map<Vec<&'x ()>>, s: &'x str) -> &'static str {
|
LL | pub fn test_borrowck<'x>(_: Map<Vec<&'x ()>>, s: &'x str) -> &'static str {
|
||||||
| ^^^^^^^^^^^^^^^^
|
| -- lifetime `'x` defined here
|
||||||
| |
|
LL | s
|
||||||
| this data with lifetime `'x`...
|
| ^ returning this value requires that `'x` must outlive `'static`
|
||||||
| ...is used and required to live as long as `'static` here
|
|
||||||
|
|
|
||||||
note: `'static` lifetime requirement introduced by this bound
|
|
||||||
--> $DIR/normalization-nested.rs:33:14
|
|
||||||
|
|
|
||||||
LL | I::Item: 'static;
|
|
||||||
| ^^^^^^^
|
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0759`.
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
// check-pass
|
// Found in a crater run on #118553
|
||||||
// Related to crater regressions on #118553
|
|
||||||
|
|
||||||
pub trait Debug {}
|
pub trait Debug {}
|
||||||
|
|
||||||
@ -30,6 +29,9 @@ impl<P: Service, S: Service<Input = P::Output>> ServiceChainBuilder<P, S> {
|
|||||||
pub fn next<NS: Service<Input = S::Output>>(
|
pub fn next<NS: Service<Input = S::Output>>(
|
||||||
self,
|
self,
|
||||||
) -> ServiceChainBuilder<ServiceChain<P, S>, NS> {
|
) -> ServiceChainBuilder<ServiceChain<P, S>, NS> {
|
||||||
|
//~^ the associated type
|
||||||
|
//~| the associated type
|
||||||
|
//~| the associated type
|
||||||
panic!();
|
panic!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
31
tests/ui/implied-bounds/sod_service_chain.stderr
Normal file
31
tests/ui/implied-bounds/sod_service_chain.stderr
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
error[E0310]: the associated type `<P as Service>::Error` may not live long enough
|
||||||
|
--> $DIR/sod_service_chain.rs:31:10
|
||||||
|
|
|
||||||
|
LL | ) -> ServiceChainBuilder<ServiceChain<P, S>, NS> {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
| |
|
||||||
|
| the associated type `<P as Service>::Error` must be valid for the static lifetime...
|
||||||
|
| ...so that the type `<P as Service>::Error` will meet its required lifetime bounds
|
||||||
|
|
|
||||||
|
help: consider adding an explicit lifetime bound
|
||||||
|
|
|
||||||
|
LL | ) -> ServiceChainBuilder<ServiceChain<P, S>, NS> where <P as Service>::Error: 'static {
|
||||||
|
| ++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
error[E0310]: the associated type `<S as Service>::Error` may not live long enough
|
||||||
|
--> $DIR/sod_service_chain.rs:31:10
|
||||||
|
|
|
||||||
|
LL | ) -> ServiceChainBuilder<ServiceChain<P, S>, NS> {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
| |
|
||||||
|
| the associated type `<S as Service>::Error` must be valid for the static lifetime...
|
||||||
|
| ...so that the type `<S as Service>::Error` will meet its required lifetime bounds
|
||||||
|
|
|
||||||
|
help: consider adding an explicit lifetime bound
|
||||||
|
|
|
||||||
|
LL | ) -> ServiceChainBuilder<ServiceChain<P, S>, NS> where <S as Service>::Error: 'static {
|
||||||
|
| ++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0310`.
|
Loading…
Reference in New Issue
Block a user