Rollup merge of #63678 - Aaron1011:fix/hrtb-leak, r=nikomatsakis
Improve HRTB error span when -Zno-leak-check is used As described in #57374, NLL currently produces unhelpful higher-ranked trait bound (HRTB) errors when '-Zno-leak-check' is enabled. This PR tackles one half of this issue - making the error message point at the proper span. The error message itself is still the very generic "higher-ranked subtype error", but this can be improved in a follow-up PR. The root cause of the bad spans lies in how NLL attempts to compute the 'blamed' region, for which it will retrieve a span for. Consider the following code, which (correctly) does not compile: ```rust let my_val: u8 = 25; let a: &u8 = &my_val; let b = a; let c = b; let d: &'static u8 = c; ``` This will cause NLL to generate the following subtype constraints: d :< c c :< b b <: a Since normal Rust lifetimes are covariant, this results in the following region constraints (I'm using 'd to denote the lifetime of 'd', 'c to denote the lifetime of 'c, etc.): 'c: 'd 'b: 'c 'a: 'b From this, we can derive that 'a: 'd holds, which implies that 'a: 'static must hold. However, this is not the case, since 'a refers to 'my_val', which does not outlive the current function. When NLL attempts to infer regions for this code, it will see that the region 'a has grown 'too large' - it will be inferred to outlive 'static, despite the fact that is not declared as outliving 'static We can find the region responsible, 'd, by starting at the *end* of the 'constraint chain' we generated above. This works because for normal (non-higher-ranked) lifetimes, we generally build up a 'chain' of lifetime constraints *away* from the original variable/lifetime. That is, our original lifetime 'a is required to outlive progressively more regions. If it ends up living for too long, we can look at the 'end' of this chain to determine the 'most recent' usage that caused the lifetime to grow too large. However, this logic does not work correctly when higher-ranked trait bounds (HRTBs) come into play. This is because HRTBs have *contravariance* with respect to their bound regions. For example, this code snippet compiles: ```rust let a: for<'a> fn(&'a ()) = |_| {}; let b: fn(&'static ()) = a; ``` Here, we require that 'a' is a subtype of 'b'. Because of contravariance, we end up with the region constraint 'static: 'a, *not* 'a: 'static This means that our 'constraint chains' grow in the opposite direction of 'normal lifetime' constraint chains. As we introduce subtypes, our lifetime ends up being outlived by other lifetimes, rather than outliving other lifetimes. Therefore, starting at the end of the 'constraint chain' will cause us to 'blame' a lifetime close to the original definition of a variable, instead of close to where the bad lifetime constraint is introduced. This PR improves how we select the region to blame for 'too large' universal lifetimes, when bound lifetimes are involved. If the region we're checking is a 'placeholder' region (e.g. the region 'a' in for<'a>, or the implicit region in fn(&())), we start traversing the constraint chain from the beginning, rather than the end. There are two (maybe more) different ways we generate region constraints for NLL: requirements generated from trait queries, and requirements generated from MIR subtype constraints. While the former always use explicit placeholder regions, the latter is more tricky. In order to implement contravariance for HRTBs, TypeRelating replaces placeholder regions with existential regions. This requires us to keep track of whether or not an existential region was originally a placeholder region. When we look for a region to blame, we check if our starting region is either a placeholder region or is an existential region created from a placeholder region. If so, we start iterating from the beginning of the constraint chain, rather than the end.
This commit is contained in:
commit
3e15e51acd
13 changed files with 172 additions and 30 deletions
|
@ -418,7 +418,19 @@ pub enum NLLRegionVariableOrigin {
|
||||||
/// from a `for<'a> T` binder). Meant to represent "any region".
|
/// from a `for<'a> T` binder). Meant to represent "any region".
|
||||||
Placeholder(ty::PlaceholderRegion),
|
Placeholder(ty::PlaceholderRegion),
|
||||||
|
|
||||||
Existential,
|
Existential {
|
||||||
|
/// If this is true, then this variable was created to represent a lifetime
|
||||||
|
/// bound in a `for` binder. For example, it might have been created to
|
||||||
|
/// represent the lifetime `'a` in a type like `for<'a> fn(&'a u32)`.
|
||||||
|
/// Such variables are created when we are trying to figure out if there
|
||||||
|
/// is any valid instantiation of `'a` that could fit into some scenario.
|
||||||
|
///
|
||||||
|
/// This is used to inform error reporting: in the case that we are trying to
|
||||||
|
/// determine whether there is any valid instantiation of a `'a` variable that meets
|
||||||
|
/// some constraint C, we want to blame the "source" of that `for` type,
|
||||||
|
/// rather than blaming the source of the constraint C.
|
||||||
|
from_forall: bool
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NLLRegionVariableOrigin {
|
impl NLLRegionVariableOrigin {
|
||||||
|
@ -426,7 +438,7 @@ impl NLLRegionVariableOrigin {
|
||||||
match self {
|
match self {
|
||||||
NLLRegionVariableOrigin::FreeRegion => true,
|
NLLRegionVariableOrigin::FreeRegion => true,
|
||||||
NLLRegionVariableOrigin::Placeholder(..) => true,
|
NLLRegionVariableOrigin::Placeholder(..) => true,
|
||||||
NLLRegionVariableOrigin::Existential => false,
|
NLLRegionVariableOrigin::Existential{ .. } => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,7 @@ pub trait TypeRelatingDelegate<'tcx> {
|
||||||
/// we will invoke this method to instantiate `'a` with an
|
/// we will invoke this method to instantiate `'a` with an
|
||||||
/// inference variable (though `'b` would be instantiated first,
|
/// inference variable (though `'b` would be instantiated first,
|
||||||
/// as a placeholder).
|
/// as a placeholder).
|
||||||
fn next_existential_region_var(&mut self) -> ty::Region<'tcx>;
|
fn next_existential_region_var(&mut self, was_placeholder: bool) -> ty::Region<'tcx>;
|
||||||
|
|
||||||
/// Creates a new region variable representing a
|
/// Creates a new region variable representing a
|
||||||
/// higher-ranked region that is instantiated universally.
|
/// higher-ranked region that is instantiated universally.
|
||||||
|
@ -193,7 +193,7 @@ where
|
||||||
let placeholder = ty::PlaceholderRegion { universe, name: br };
|
let placeholder = ty::PlaceholderRegion { universe, name: br };
|
||||||
delegate.next_placeholder_region(placeholder)
|
delegate.next_placeholder_region(placeholder)
|
||||||
} else {
|
} else {
|
||||||
delegate.next_existential_region_var()
|
delegate.next_existential_region_var(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -97,9 +97,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
&self,
|
&self,
|
||||||
body: &Body<'tcx>,
|
body: &Body<'tcx>,
|
||||||
from_region: RegionVid,
|
from_region: RegionVid,
|
||||||
|
from_region_origin: NLLRegionVariableOrigin,
|
||||||
target_test: impl Fn(RegionVid) -> bool,
|
target_test: impl Fn(RegionVid) -> bool,
|
||||||
) -> (ConstraintCategory, bool, Span) {
|
) -> (ConstraintCategory, bool, Span) {
|
||||||
debug!("best_blame_constraint(from_region={:?})", from_region);
|
debug!("best_blame_constraint(from_region={:?}, from_region_origin={:?})",
|
||||||
|
from_region, from_region_origin);
|
||||||
|
|
||||||
// Find all paths
|
// Find all paths
|
||||||
let (path, target_region) =
|
let (path, target_region) =
|
||||||
|
@ -152,19 +154,85 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
// we still want to screen for an "interesting" point to
|
// we still want to screen for an "interesting" point to
|
||||||
// highlight (e.g., a call site or something).
|
// highlight (e.g., a call site or something).
|
||||||
let target_scc = self.constraint_sccs.scc(target_region);
|
let target_scc = self.constraint_sccs.scc(target_region);
|
||||||
let best_choice = (0..path.len()).rev().find(|&i| {
|
let mut range = 0..path.len();
|
||||||
let constraint = path[i];
|
|
||||||
|
// As noted above, when reporting an error, there is typically a chain of constraints
|
||||||
|
// leading from some "source" region which must outlive some "target" region.
|
||||||
|
// In most cases, we prefer to "blame" the constraints closer to the target --
|
||||||
|
// but there is one exception. When constraints arise from higher-ranked subtyping,
|
||||||
|
// we generally prefer to blame the source value,
|
||||||
|
// as the "target" in this case tends to be some type annotation that the user gave.
|
||||||
|
// Therefore, if we find that the region origin is some instantiation
|
||||||
|
// of a higher-ranked region, we start our search from the "source" point
|
||||||
|
// rather than the "target", and we also tweak a few other things.
|
||||||
|
//
|
||||||
|
// An example might be this bit of Rust code:
|
||||||
|
//
|
||||||
|
// ```rust
|
||||||
|
// let x: fn(&'static ()) = |_| {};
|
||||||
|
// let y: for<'a> fn(&'a ()) = x;
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// In MIR, this will be converted into a combination of assignments and type ascriptions.
|
||||||
|
// In particular, the 'static is imposed through a type ascription:
|
||||||
|
//
|
||||||
|
// ```rust
|
||||||
|
// x = ...;
|
||||||
|
// AscribeUserType(x, fn(&'static ())
|
||||||
|
// y = x;
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// We wind up ultimately with constraints like
|
||||||
|
//
|
||||||
|
// ```rust
|
||||||
|
// !a: 'temp1 // from the `y = x` statement
|
||||||
|
// 'temp1: 'temp2
|
||||||
|
// 'temp2: 'static // from the AscribeUserType
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// and here we prefer to blame the source (the y = x statement).
|
||||||
|
let blame_source = match from_region_origin {
|
||||||
|
NLLRegionVariableOrigin::FreeRegion
|
||||||
|
| NLLRegionVariableOrigin::Existential { from_forall: false } => {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
NLLRegionVariableOrigin::Placeholder(_)
|
||||||
|
| NLLRegionVariableOrigin::Existential { from_forall: true } => {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let find_region = |i: &usize| {
|
||||||
|
let constraint = path[*i];
|
||||||
|
|
||||||
let constraint_sup_scc = self.constraint_sccs.scc(constraint.sup);
|
let constraint_sup_scc = self.constraint_sccs.scc(constraint.sup);
|
||||||
|
|
||||||
match categorized_path[i].0 {
|
if blame_source {
|
||||||
ConstraintCategory::OpaqueType | ConstraintCategory::Boring |
|
match categorized_path[*i].0 {
|
||||||
ConstraintCategory::BoringNoLocation | ConstraintCategory::Internal => false,
|
ConstraintCategory::OpaqueType | ConstraintCategory::Boring |
|
||||||
ConstraintCategory::TypeAnnotation | ConstraintCategory::Return |
|
ConstraintCategory::BoringNoLocation | ConstraintCategory::Internal => false,
|
||||||
ConstraintCategory::Yield => true,
|
ConstraintCategory::TypeAnnotation | ConstraintCategory::Return |
|
||||||
_ => constraint_sup_scc != target_scc,
|
ConstraintCategory::Yield => true,
|
||||||
|
_ => constraint_sup_scc != target_scc,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match categorized_path[*i].0 {
|
||||||
|
ConstraintCategory::OpaqueType | ConstraintCategory::Boring |
|
||||||
|
ConstraintCategory::BoringNoLocation | ConstraintCategory::Internal => false,
|
||||||
|
_ => true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
|
let best_choice = if blame_source {
|
||||||
|
range.rev().find(find_region)
|
||||||
|
} else {
|
||||||
|
range.find(find_region)
|
||||||
|
};
|
||||||
|
|
||||||
|
debug!("best_blame_constraint: best_choice={:?} blame_source={}",
|
||||||
|
best_choice, blame_source);
|
||||||
|
|
||||||
if let Some(i) = best_choice {
|
if let Some(i) = best_choice {
|
||||||
if let Some(next) = categorized_path.get(i + 1) {
|
if let Some(next) = categorized_path.get(i + 1) {
|
||||||
if categorized_path[i].0 == ConstraintCategory::Return
|
if categorized_path[i].0 == ConstraintCategory::Return
|
||||||
|
@ -300,12 +368,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
infcx: &'a InferCtxt<'a, 'tcx>,
|
infcx: &'a InferCtxt<'a, 'tcx>,
|
||||||
mir_def_id: DefId,
|
mir_def_id: DefId,
|
||||||
fr: RegionVid,
|
fr: RegionVid,
|
||||||
|
fr_origin: NLLRegionVariableOrigin,
|
||||||
outlived_fr: RegionVid,
|
outlived_fr: RegionVid,
|
||||||
renctx: &mut RegionErrorNamingCtx,
|
renctx: &mut RegionErrorNamingCtx,
|
||||||
) -> DiagnosticBuilder<'a> {
|
) -> DiagnosticBuilder<'a> {
|
||||||
debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
|
debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
|
||||||
|
|
||||||
let (category, _, span) = self.best_blame_constraint(body, fr, |r| {
|
let (category, _, span) = self.best_blame_constraint(body, fr, fr_origin, |r| {
|
||||||
self.provides_universal_region(r, fr, outlived_fr)
|
self.provides_universal_region(r, fr, outlived_fr)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -712,6 +781,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
let (category, from_closure, span) = self.best_blame_constraint(
|
let (category, from_closure, span) = self.best_blame_constraint(
|
||||||
body,
|
body,
|
||||||
borrow_region,
|
borrow_region,
|
||||||
|
NLLRegionVariableOrigin::FreeRegion,
|
||||||
|r| self.provides_universal_region(r, borrow_region, outlived_region)
|
|r| self.provides_universal_region(r, borrow_region, outlived_region)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -771,11 +841,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
&self,
|
&self,
|
||||||
body: &Body<'tcx>,
|
body: &Body<'tcx>,
|
||||||
fr1: RegionVid,
|
fr1: RegionVid,
|
||||||
|
fr1_origin: NLLRegionVariableOrigin,
|
||||||
fr2: RegionVid,
|
fr2: RegionVid,
|
||||||
) -> (ConstraintCategory, Span) {
|
) -> (ConstraintCategory, Span) {
|
||||||
let (category, _, span) = self.best_blame_constraint(
|
let (category, _, span) = self.best_blame_constraint(
|
||||||
body,
|
body,
|
||||||
fr1,
|
fr1,
|
||||||
|
fr1_origin,
|
||||||
|r| self.provides_universal_region(r, fr1, fr2),
|
|r| self.provides_universal_region(r, fr1, fr2),
|
||||||
);
|
);
|
||||||
(category, span)
|
(category, span)
|
||||||
|
@ -828,7 +900,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
universe1.cannot_name(placeholder.universe)
|
universe1.cannot_name(placeholder.universe)
|
||||||
}
|
}
|
||||||
|
|
||||||
NLLRegionVariableOrigin::FreeRegion | NLLRegionVariableOrigin::Existential => false,
|
NLLRegionVariableOrigin::FreeRegion | NLLRegionVariableOrigin::Existential { .. } => {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -406,7 +406,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NLLRegionVariableOrigin::Existential => {
|
NLLRegionVariableOrigin::Existential { .. } => {
|
||||||
// For existential, regions, nothing to do.
|
// For existential, regions, nothing to do.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1348,7 +1348,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
self.check_bound_universal_region(infcx, body, mir_def_id, fr, placeholder);
|
self.check_bound_universal_region(infcx, body, mir_def_id, fr, placeholder);
|
||||||
}
|
}
|
||||||
|
|
||||||
NLLRegionVariableOrigin::Existential => {
|
NLLRegionVariableOrigin::Existential { .. } => {
|
||||||
// nothing to check here
|
// nothing to check here
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1461,7 +1461,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
debug!("check_universal_region: fr_minus={:?}", fr_minus);
|
debug!("check_universal_region: fr_minus={:?}", fr_minus);
|
||||||
|
|
||||||
let blame_span_category =
|
let blame_span_category =
|
||||||
self.find_outlives_blame_span(body, longer_fr, shorter_fr);
|
self.find_outlives_blame_span(body, longer_fr,
|
||||||
|
NLLRegionVariableOrigin::FreeRegion,shorter_fr);
|
||||||
|
|
||||||
// Grow `shorter_fr` until we find some non-local regions. (We
|
// Grow `shorter_fr` until we find some non-local regions. (We
|
||||||
// always will.) We'll call them `shorter_fr+` -- they're ever
|
// always will.) We'll call them `shorter_fr+` -- they're ever
|
||||||
|
@ -1494,6 +1495,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
infcx,
|
infcx,
|
||||||
mir_def_id,
|
mir_def_id,
|
||||||
longer_fr,
|
longer_fr,
|
||||||
|
NLLRegionVariableOrigin::FreeRegion,
|
||||||
shorter_fr,
|
shorter_fr,
|
||||||
region_naming,
|
region_naming,
|
||||||
);
|
);
|
||||||
|
@ -1547,7 +1549,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
|
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
|
||||||
let (_, span) = self.find_outlives_blame_span(body, longer_fr, error_region);
|
let (_, span) = self.find_outlives_blame_span(
|
||||||
|
body, longer_fr, NLLRegionVariableOrigin::Placeholder(placeholder), error_region
|
||||||
|
);
|
||||||
|
|
||||||
// Obviously, this error message is far from satisfactory.
|
// Obviously, this error message is far from satisfactory.
|
||||||
// At present, though, it only appears in unit tests --
|
// At present, though, it only appears in unit tests --
|
||||||
|
@ -1608,7 +1612,7 @@ impl<'tcx> RegionDefinition<'tcx> {
|
||||||
|
|
||||||
let origin = match rv_origin {
|
let origin = match rv_origin {
|
||||||
RegionVariableOrigin::NLL(origin) => origin,
|
RegionVariableOrigin::NLL(origin) => origin,
|
||||||
_ => NLLRegionVariableOrigin::Existential,
|
_ => NLLRegionVariableOrigin::Existential { from_forall: false },
|
||||||
};
|
};
|
||||||
|
|
||||||
Self { origin, universe, external_name: None }
|
Self { origin, universe, external_name: None }
|
||||||
|
|
|
@ -35,7 +35,7 @@ where
|
||||||
infcx
|
infcx
|
||||||
.tcx
|
.tcx
|
||||||
.fold_regions(value, &mut false, |_region, _depth| {
|
.fold_regions(value, &mut false, |_region, _depth| {
|
||||||
let origin = NLLRegionVariableOrigin::Existential;
|
let origin = NLLRegionVariableOrigin::Existential { from_forall: false };
|
||||||
infcx.next_nll_region_var(origin)
|
infcx.next_nll_region_var(origin)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,9 +66,9 @@ impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> {
|
||||||
self.infcx.create_next_universe()
|
self.infcx.create_next_universe()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_existential_region_var(&mut self) -> ty::Region<'tcx> {
|
fn next_existential_region_var(&mut self, from_forall: bool) -> ty::Region<'tcx> {
|
||||||
if let Some(_) = &mut self.borrowck_context {
|
if let Some(_) = &mut self.borrowck_context {
|
||||||
let origin = NLLRegionVariableOrigin::Existential;
|
let origin = NLLRegionVariableOrigin::Existential { from_forall };
|
||||||
self.infcx.next_nll_region_var(origin)
|
self.infcx.next_nll_region_var(origin)
|
||||||
} else {
|
} else {
|
||||||
self.infcx.tcx.lifetimes.re_erased
|
self.infcx.tcx.lifetimes.re_erased
|
||||||
|
@ -88,7 +88,9 @@ impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> {
|
||||||
|
|
||||||
fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> {
|
fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> {
|
||||||
self.infcx
|
self.infcx
|
||||||
.next_nll_region_var_in_universe(NLLRegionVariableOrigin::Existential, universe)
|
.next_nll_region_var_in_universe(NLLRegionVariableOrigin::Existential {
|
||||||
|
from_forall: false
|
||||||
|
}, universe)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>) {
|
fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>) {
|
||||||
|
|
|
@ -65,7 +65,7 @@ impl TypeRelatingDelegate<'tcx> for &mut ChalkTypeRelatingDelegate<'_, 'tcx> {
|
||||||
self.infcx.create_next_universe()
|
self.infcx.create_next_universe()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_existential_region_var(&mut self) -> ty::Region<'tcx> {
|
fn next_existential_region_var(&mut self, _was_placeholder: bool) -> ty::Region<'tcx> {
|
||||||
self.infcx.next_region_var(RegionVariableOrigin::MiscVariable(DUMMY_SP))
|
self.infcx.next_region_var(RegionVariableOrigin::MiscVariable(DUMMY_SP))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,20 @@
|
||||||
error: higher-ranked subtype error
|
error: higher-ranked subtype error
|
||||||
--> $DIR/issue-30786.rs:113:18
|
--> $DIR/issue-30786.rs:108:15
|
||||||
|
|
|
||||||
|
LL | let map = source.map(|x: &_| x);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: higher-ranked subtype error
|
||||||
|
--> $DIR/issue-30786.rs:114:18
|
||||||
|
|
|
|
||||||
LL | let filter = map.filter(|x: &_| true);
|
LL | let filter = map.filter(|x: &_| true);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: higher-ranked subtype error
|
error: higher-ranked subtype error
|
||||||
--> $DIR/issue-30786.rs:115:17
|
--> $DIR/issue-30786.rs:116:17
|
||||||
|
|
|
|
||||||
LL | let count = filter.count(); // Assert that we still have a valid stream.
|
LL | let count = filter.count(); // Assert that we still have a valid stream.
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
|
|
@ -106,7 +106,8 @@ impl<T> StreamExt for T where for<'a> &'a mut T: Stream { }
|
||||||
fn main() {
|
fn main() {
|
||||||
let source = Repeat(10);
|
let source = Repeat(10);
|
||||||
let map = source.map(|x: &_| x);
|
let map = source.map(|x: &_| x);
|
||||||
//[migrate]~^ ERROR implementation of `Stream` is not general enough
|
//[nll]~^ ERROR higher-ranked subtype error
|
||||||
|
//[migrate]~^^ ERROR implementation of `Stream` is not general enough
|
||||||
//[migrate]~| NOTE `Stream` would have to be implemented for the type `&'0 mut Map
|
//[migrate]~| NOTE `Stream` would have to be implemented for the type `&'0 mut Map
|
||||||
//[migrate]~| NOTE but `Stream` is actually implemented for the type `&'1
|
//[migrate]~| NOTE but `Stream` is actually implemented for the type `&'1
|
||||||
//[migrate]~| NOTE implementation of `Stream` is not general enough
|
//[migrate]~| NOTE implementation of `Stream` is not general enough
|
||||||
|
@ -114,4 +115,5 @@ fn main() {
|
||||||
//[nll]~^ ERROR higher-ranked subtype error
|
//[nll]~^ ERROR higher-ranked subtype error
|
||||||
let count = filter.count(); // Assert that we still have a valid stream.
|
let count = filter.count(); // Assert that we still have a valid stream.
|
||||||
//[nll]~^ ERROR higher-ranked subtype error
|
//[nll]~^ ERROR higher-ranked subtype error
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
10
src/test/ui/nll/relate_tys/fn-subtype.rs
Normal file
10
src/test/ui/nll/relate_tys/fn-subtype.rs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// Test that NLL produces correct spans for higher-ranked subtyping errors.
|
||||||
|
//
|
||||||
|
// compile-flags:-Zno-leak-check
|
||||||
|
|
||||||
|
#![feature(nll)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x: fn(&'static ()) = |_| {};
|
||||||
|
let y: for<'a> fn(&'a ()) = x; //~ ERROR higher-ranked subtype error
|
||||||
|
}
|
8
src/test/ui/nll/relate_tys/fn-subtype.stderr
Normal file
8
src/test/ui/nll/relate_tys/fn-subtype.stderr
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
error: higher-ranked subtype error
|
||||||
|
--> $DIR/fn-subtype.rs:9:33
|
||||||
|
|
|
||||||
|
LL | let y: for<'a> fn(&'a ()) = x;
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
16
src/test/ui/nll/relate_tys/trait-hrtb.rs
Normal file
16
src/test/ui/nll/relate_tys/trait-hrtb.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// Test that NLL generates proper error spans for trait HRTB errors
|
||||||
|
//
|
||||||
|
// compile-flags:-Zno-leak-check
|
||||||
|
|
||||||
|
#![feature(nll)]
|
||||||
|
|
||||||
|
trait Foo<'a> {}
|
||||||
|
|
||||||
|
fn make_foo<'a>() -> Box<dyn Foo<'a>> {
|
||||||
|
panic!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x: Box<dyn Foo<'static>> = make_foo();
|
||||||
|
let y: Box<dyn for<'a> Foo<'a>> = x; //~ ERROR higher-ranked subtype error
|
||||||
|
}
|
8
src/test/ui/nll/relate_tys/trait-hrtb.stderr
Normal file
8
src/test/ui/nll/relate_tys/trait-hrtb.stderr
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
error: higher-ranked subtype error
|
||||||
|
--> $DIR/trait-hrtb.rs:15:39
|
||||||
|
|
|
||||||
|
LL | let y: Box<dyn for<'a> Foo<'a>> = x;
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue