rewrite leak check to be based on universes
In the new leak check, instead of getting a list of placeholders to track, we look for any placeholder that is part of a universe which was created during the snapshot. We are looking for the following error patterns: * P1: P2, where P1 != P2 * P1: R, where R is in some universe that cannot name P1 This new leak check is more precise than before, in that it accepts this patterns: * R: P1, even if R cannot name P1, because R = 'static is a valid sol'n * R: P1, R: P2, as above Note that this leak check, when running during subtyping, is less efficient than before in some sense because it is going to check and re-check all the universes created since the snapshot. We're going to move when the leak check runs to try and correct that.
This commit is contained in:
parent
4199b3ae26
commit
f2cf994483
29 changed files with 572 additions and 320 deletions
|
|
@ -33,7 +33,7 @@ impl<'a, 'tcx> CombineFields<'a, 'tcx> {
|
|||
self.infcx.commit_if_ok(|snapshot| {
|
||||
// First, we instantiate each bound region in the supertype with a
|
||||
// fresh placeholder region.
|
||||
let (b_prime, placeholder_map) = self.infcx.replace_bound_vars_with_placeholders(b);
|
||||
let (b_prime, _) = self.infcx.replace_bound_vars_with_placeholders(b);
|
||||
|
||||
// Next, we instantiate each bound region in the subtype
|
||||
// with a fresh region variable. These region variables --
|
||||
|
|
@ -48,7 +48,7 @@ impl<'a, 'tcx> CombineFields<'a, 'tcx> {
|
|||
// Compare types now that bound regions have been replaced.
|
||||
let result = self.sub(a_is_expected).relate(&a_prime, &b_prime)?;
|
||||
|
||||
self.infcx.leak_check(!a_is_expected, &placeholder_map, snapshot)?;
|
||||
self.infcx.leak_check(!a_is_expected, snapshot)?;
|
||||
|
||||
debug!("higher_ranked_sub: OK result={:?}", result);
|
||||
|
||||
|
|
@ -119,7 +119,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||
pub fn leak_check(
|
||||
&self,
|
||||
overly_polymorphic: bool,
|
||||
placeholder_map: &PlaceholderMap<'tcx>,
|
||||
snapshot: &CombinedSnapshot<'_, 'tcx>,
|
||||
) -> RelateResult<'tcx, ()> {
|
||||
// If the user gave `-Zno-leak-check`, or we have been
|
||||
|
|
@ -135,7 +134,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||
self.inner.borrow_mut().unwrap_region_constraints().leak_check(
|
||||
self.tcx,
|
||||
overly_polymorphic,
|
||||
placeholder_map,
|
||||
self.universe(),
|
||||
snapshot,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -992,12 +992,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||
}
|
||||
|
||||
Some(self.commit_if_ok(|snapshot| {
|
||||
let (ty::SubtypePredicate { a_is_expected, a, b }, placeholder_map) =
|
||||
let (ty::SubtypePredicate { a_is_expected, a, b }, _) =
|
||||
self.replace_bound_vars_with_placeholders(&predicate);
|
||||
|
||||
let ok = self.at(cause, param_env).sub_exp(a_is_expected, a, b)?;
|
||||
|
||||
self.leak_check(false, &placeholder_map, snapshot)?;
|
||||
self.leak_check(false, snapshot)?;
|
||||
|
||||
Ok(ok.unit())
|
||||
}))
|
||||
|
|
@ -1009,13 +1009,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||
predicate: ty::PolyRegionOutlivesPredicate<'tcx>,
|
||||
) -> UnitResult<'tcx> {
|
||||
self.commit_if_ok(|snapshot| {
|
||||
let (ty::OutlivesPredicate(r_a, r_b), placeholder_map) =
|
||||
let (ty::OutlivesPredicate(r_a, r_b), _) =
|
||||
self.replace_bound_vars_with_placeholders(&predicate);
|
||||
let origin = SubregionOrigin::from_obligation_cause(cause, || {
|
||||
RelateRegionParamBound(cause.span)
|
||||
});
|
||||
self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b`
|
||||
self.leak_check(false, &placeholder_map, snapshot)?;
|
||||
self.leak_check(false, snapshot)?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,158 +1,445 @@
|
|||
use super::*;
|
||||
use crate::infer::{CombinedSnapshot, PlaceholderMap};
|
||||
use rustc_data_structures::undo_log::UndoLogs;
|
||||
use crate::infer::CombinedSnapshot;
|
||||
use rustc_data_structures::{
|
||||
graph::{scc::Sccs, vec_graph::VecGraph},
|
||||
undo_log::UndoLogs,
|
||||
};
|
||||
use rustc_index::vec::Idx;
|
||||
use rustc_middle::ty::error::TypeError;
|
||||
use rustc_middle::ty::relate::RelateResult;
|
||||
|
||||
impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
|
||||
/// Searches region constraints created since `snapshot` that
|
||||
/// affect one of the placeholders in `placeholder_map`, returning
|
||||
/// an error if any of the placeholders are related to another
|
||||
/// placeholder or would have to escape into some parent universe
|
||||
/// that cannot name them.
|
||||
/// Searches new universes created during `snapshot`, looking for
|
||||
/// placeholders that may "leak" out from the universes they are contained
|
||||
/// in. If any leaking placeholders are found, then an `Err` is returned
|
||||
/// (typically leading to the snapshot being reversed).
|
||||
///
|
||||
/// This is a temporary backwards compatibility measure to try and
|
||||
/// retain the older (arguably incorrect) behavior of the
|
||||
/// compiler.
|
||||
/// The leak check *used* to be the only way we had to handle higher-ranked
|
||||
/// obligations. Now that we have integrated universes into the region
|
||||
/// solvers, this is no longer the case, but we retain the leak check for
|
||||
/// backwards compatibility purposes. In particular, it lets us make "early"
|
||||
/// decisions about whether a region error will be reported that are used in
|
||||
/// coherence and elsewhere -- see #56105 and #59490 for more details. The
|
||||
/// eventual fate of the leak checker is not yet settled.
|
||||
///
|
||||
/// NB. Although `_snapshot` isn't used, it's passed in to prove
|
||||
/// that we are in a snapshot, which guarantees that we can just
|
||||
/// search the "undo log" for edges. This is mostly an efficiency
|
||||
/// thing -- we could search *all* region constraints, but that'd be
|
||||
/// a bigger set and the data structures are not setup for that. If
|
||||
/// we wind up keeping some form of this check long term, it would
|
||||
/// probably be better to remove the snapshot parameter and to
|
||||
/// refactor the constraint set.
|
||||
/// The leak checker works by searching for the following error patterns:
|
||||
///
|
||||
/// * P1: P2, where P1 != P2
|
||||
/// * P1: R, where R is in some universe that cannot name P1
|
||||
///
|
||||
/// The idea here is that each of these patterns represents something that
|
||||
/// the region solver would eventually report as an error, so we can detect
|
||||
/// the error early. There is a fly in the ointment, though, in that this is
|
||||
/// not entirely true. In particular, in the future, we may extend the
|
||||
/// environment with implied bounds or other info about how placeholders
|
||||
/// relate to regions in outer universes. In that case, `P1: R` for example
|
||||
/// might become solveable.
|
||||
///
|
||||
/// # Summary of the implementation
|
||||
///
|
||||
/// The leak checks as follows. First, we construct a graph where `R2: R1`
|
||||
/// implies `R2 -> R1`, and we compute the SCCs.
|
||||
///
|
||||
/// For each SCC S, we compute:
|
||||
///
|
||||
/// * what placeholder P it must be equal to, if any
|
||||
/// * if there are multiple placeholders that must be equal, report an error because `P1: P2`
|
||||
/// * the minimum universe of its constituents
|
||||
///
|
||||
/// Then we walk the SCCs in dependency order and compute
|
||||
///
|
||||
/// * what placeholder they must outlive transitively
|
||||
/// * if they must also be equal to a placeholder, report an error because `P1: P2`
|
||||
/// * minimum universe U of all SCCs they must outlive
|
||||
/// * if they must also be equal to a placeholder P, and U cannot name P, report an error, as that
|
||||
/// indicates `P: R` and `R` is in an incompatible universe
|
||||
///
|
||||
/// # Historical note
|
||||
///
|
||||
/// Older variants of the leak check used to report errors for these
|
||||
/// patterns, but we no longer do:
|
||||
///
|
||||
/// * R: P1, even if R cannot name P1, because R = 'static is a valid sol'n
|
||||
/// * R: P1, R: P2, as above
|
||||
pub fn leak_check(
|
||||
&mut self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
overly_polymorphic: bool,
|
||||
placeholder_map: &PlaceholderMap<'tcx>,
|
||||
_snapshot: &CombinedSnapshot<'_, 'tcx>,
|
||||
max_universe: ty::UniverseIndex,
|
||||
snapshot: &CombinedSnapshot<'_, 'tcx>,
|
||||
) -> RelateResult<'tcx, ()> {
|
||||
debug!("leak_check(placeholders={:?})", placeholder_map);
|
||||
debug!(
|
||||
"leak_check(max_universe={:?}, snapshot.universe={:?}, overly_polymorphic={:?})",
|
||||
max_universe, snapshot.universe, overly_polymorphic
|
||||
);
|
||||
|
||||
assert!(UndoLogs::<super::UndoLog<'_>>::in_snapshot(&self.undo_log));
|
||||
|
||||
// Go through each placeholder that we created.
|
||||
for &placeholder_region in placeholder_map.values() {
|
||||
// Find the universe this placeholder inhabits.
|
||||
let placeholder = match placeholder_region {
|
||||
ty::RePlaceholder(p) => p,
|
||||
_ => bug!("leak_check: expected placeholder found {:?}", placeholder_region,),
|
||||
};
|
||||
let universe_at_start_of_snapshot = snapshot.universe;
|
||||
if universe_at_start_of_snapshot == max_universe {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Find all regions that are related to this placeholder
|
||||
// in some way. This means any region that either outlives
|
||||
// or is outlived by a placeholder.
|
||||
let mut taint_set = TaintSet::new(TaintDirections::both(), placeholder_region);
|
||||
taint_set.fixed_point(
|
||||
tcx,
|
||||
self.undo_log.region_constraints(),
|
||||
&self.storage.data.verifys,
|
||||
let mini_graph =
|
||||
&MiniGraph::new(tcx, self.undo_log.region_constraints(), &self.storage.data.verifys);
|
||||
|
||||
let mut leak_check = LeakCheck::new(
|
||||
tcx,
|
||||
universe_at_start_of_snapshot,
|
||||
max_universe,
|
||||
overly_polymorphic,
|
||||
mini_graph,
|
||||
self,
|
||||
);
|
||||
leak_check.assign_placeholder_values()?;
|
||||
leak_check.propagate_scc_value()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct LeakCheck<'me, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
universe_at_start_of_snapshot: ty::UniverseIndex,
|
||||
overly_polymorphic: bool,
|
||||
mini_graph: &'me MiniGraph<'tcx>,
|
||||
rcc: &'me RegionConstraintCollector<'me, 'tcx>,
|
||||
|
||||
// Initially, for each SCC S, stores a placeholder `P` such that `S = P`
|
||||
// must hold.
|
||||
//
|
||||
// Later, during the [`LeakCheck::propagate_scc_value`] function, this array
|
||||
// is repurposed to store some placeholder `P` such that the weaker
|
||||
// condition `S: P` must hold. (This is true if `S: S1` transitively and `S1
|
||||
// = P`.)
|
||||
scc_placeholders: IndexVec<LeakCheckScc, Option<ty::PlaceholderRegion>>,
|
||||
|
||||
// For each SCC S, track the minimum universe that flows into it. Note that
|
||||
// this is both the minimum of the universes for every region that is a
|
||||
// member of the SCC, but also if you have `R1: R2`, then the universe of
|
||||
// `R2` must be less than the universe of `R1` (i.e., `R1` flows `R2`). To
|
||||
// see that, imagine that you have `P1: R` -- in that case, `R` must be
|
||||
// either the placeholder `P1` or the empty region in that same universe.
|
||||
//
|
||||
// To detect errors, we look for an SCC S where the values in
|
||||
// `scc_values[S]` (if any) cannot be stored into `scc_universes[S]`.
|
||||
scc_universes: IndexVec<LeakCheckScc, SccUniverse<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
|
||||
fn new(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
universe_at_start_of_snapshot: ty::UniverseIndex,
|
||||
max_universe: ty::UniverseIndex,
|
||||
overly_polymorphic: bool,
|
||||
mini_graph: &'me MiniGraph<'tcx>,
|
||||
rcc: &'me RegionConstraintCollector<'me, 'tcx>,
|
||||
) -> Self {
|
||||
let dummy_scc_universe = SccUniverse { universe: max_universe, region: None };
|
||||
Self {
|
||||
tcx,
|
||||
universe_at_start_of_snapshot,
|
||||
overly_polymorphic,
|
||||
mini_graph,
|
||||
rcc,
|
||||
scc_placeholders: IndexVec::from_elem_n(None, mini_graph.sccs.num_sccs()),
|
||||
scc_universes: IndexVec::from_elem_n(dummy_scc_universe, mini_graph.sccs.num_sccs()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute what placeholders (if any) each SCC must be equal to.
|
||||
/// Also compute the minimum universe of all the regions in each SCC.
|
||||
fn assign_placeholder_values(&mut self) -> RelateResult<'tcx, ()> {
|
||||
// First walk: find each placeholder that is from a newly created universe.
|
||||
for (region, leak_check_node) in &self.mini_graph.nodes {
|
||||
let scc = self.mini_graph.sccs.scc(*leak_check_node);
|
||||
|
||||
// Set the universe of each SCC to be the minimum of its constituent universes
|
||||
let universe = self.rcc.universe(region);
|
||||
debug!(
|
||||
"assign_placeholder_values: scc={:?} universe={:?} region={:?}",
|
||||
scc, universe, region
|
||||
);
|
||||
let tainted_regions = taint_set.into_set();
|
||||
self.scc_universes[scc].take_min(universe, region);
|
||||
|
||||
// Report an error if two placeholders in the same universe
|
||||
// are related to one another, or if a placeholder is related
|
||||
// to something from a parent universe.
|
||||
for &tainted_region in &tainted_regions {
|
||||
if let ty::RePlaceholder(_) = tainted_region {
|
||||
// Two placeholders cannot be related:
|
||||
if tainted_region == placeholder_region {
|
||||
continue;
|
||||
}
|
||||
} else if self.universe(tainted_region).can_name(placeholder.universe) {
|
||||
continue;
|
||||
// Detect those SCCs that directly contain a placeholder
|
||||
if let ty::RePlaceholder(placeholder) = region {
|
||||
if self.universe_at_start_of_snapshot.cannot_name(placeholder.universe) {
|
||||
self.assign_scc_value(scc, *placeholder)?;
|
||||
}
|
||||
|
||||
return Err(if overly_polymorphic {
|
||||
debug!("overly polymorphic!");
|
||||
TypeError::RegionsOverlyPolymorphic(placeholder.name, tainted_region)
|
||||
} else {
|
||||
debug!("not as polymorphic!");
|
||||
TypeError::RegionsInsufficientlyPolymorphic(placeholder.name, tainted_region)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TaintSet<'tcx> {
|
||||
directions: TaintDirections,
|
||||
regions: FxHashSet<ty::Region<'tcx>>,
|
||||
}
|
||||
// assign_scc_value(S, P): Update `scc_values` to account for the fact that `P: S` must hold.
|
||||
// This may create an error.
|
||||
fn assign_scc_value(
|
||||
&mut self,
|
||||
scc: LeakCheckScc,
|
||||
placeholder: ty::PlaceholderRegion,
|
||||
) -> RelateResult<'tcx, ()> {
|
||||
match self.scc_placeholders[scc] {
|
||||
Some(p) => {
|
||||
assert_ne!(p, placeholder);
|
||||
return Err(self.placeholder_error(p, placeholder));
|
||||
}
|
||||
None => {
|
||||
self.scc_placeholders[scc] = Some(placeholder);
|
||||
}
|
||||
};
|
||||
|
||||
impl<'tcx> TaintSet<'tcx> {
|
||||
fn new(directions: TaintDirections, initial_region: ty::Region<'tcx>) -> Self {
|
||||
let mut regions = FxHashSet::default();
|
||||
regions.insert(initial_region);
|
||||
TaintSet { directions, regions }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fixed_point<'a>(
|
||||
&mut self,
|
||||
/// For each SCC S, iterate over each successor S1 where `S: S1`:
|
||||
///
|
||||
/// * Compute
|
||||
/// Iterate over each SCC `S` and ensure that, for each `S1` where `S1: S`,
|
||||
/// `universe(S) <= universe(S1)`. This executes after
|
||||
/// `assign_placeholder_values`, so `universe(S)` is already the minimum
|
||||
/// universe of any of its direct constituents.
|
||||
fn propagate_scc_value(&mut self) -> RelateResult<'tcx, ()> {
|
||||
// Loop invariants:
|
||||
//
|
||||
// On start of the loop iteration for `scc1`:
|
||||
//
|
||||
// * `scc_universes[scc1]` contains the minimum universe of the
|
||||
// constituents of `scc1`
|
||||
// * `scc_placeholder[scc1]` stores the placeholder that `scc1` must
|
||||
// be equal to (if any)
|
||||
//
|
||||
// For each succssor `scc2` where `scc1: scc2`:
|
||||
//
|
||||
// * `scc_placeholder[scc2]` stores some placeholder `P` where
|
||||
// `scc2: P` (if any)
|
||||
// * `scc_universes[scc2]` contains the minimum universe of the
|
||||
// constituents of `scc2` and any of its successors
|
||||
for scc1 in self.mini_graph.sccs.all_sccs() {
|
||||
debug!(
|
||||
"propagate_scc_value: scc={:?} with universe {:?}",
|
||||
scc1, self.scc_universes[scc1]
|
||||
);
|
||||
|
||||
// Walk over each `scc2` such that `scc1: scc2` and compute:
|
||||
//
|
||||
// * `scc1_universe`: the minimum universe of `scc2` and the constituents of `scc1`
|
||||
// * `succ_bound`: placeholder `P` that the successors must outlive, if any (if there are multiple,
|
||||
// we pick one arbitrarily)
|
||||
let mut scc1_universe = self.scc_universes[scc1];
|
||||
let mut succ_bound = None;
|
||||
for &scc2 in self.mini_graph.sccs.successors(scc1) {
|
||||
let SccUniverse { universe: scc2_universe, region: scc2_region } =
|
||||
self.scc_universes[scc2];
|
||||
|
||||
scc1_universe.take_min(scc2_universe, scc2_region.unwrap());
|
||||
|
||||
if let Some(b) = self.scc_placeholders[scc2] {
|
||||
succ_bound = Some(b);
|
||||
}
|
||||
}
|
||||
|
||||
// Update minimum universe of scc1.
|
||||
self.scc_universes[scc1] = scc1_universe;
|
||||
|
||||
// At this point, `scc_placholder[scc1]` stores the placeholder that
|
||||
// `scc1` must be equal to, if any.
|
||||
if let Some(scc1_placeholder) = self.scc_placeholders[scc1] {
|
||||
debug!(
|
||||
"propagate_scc_value: scc1={:?} placeholder={:?} scc1_universe={:?}",
|
||||
scc1, scc1_placeholder, scc1_universe
|
||||
);
|
||||
|
||||
// Check if `P1: R` for some `R` in a universe that cannot name
|
||||
// P1. That's an error.
|
||||
if scc1_universe.universe.cannot_name(scc1_placeholder.universe) {
|
||||
return Err(self.error(scc1_placeholder, scc1_universe.region.unwrap()));
|
||||
}
|
||||
|
||||
// Check if we have some placeholder where `S: P2`
|
||||
// (transitively). In that case, since `S = P1`, that implies
|
||||
// `P1: P2`, which is an error condition.
|
||||
if let Some(scc2_placeholder) = succ_bound {
|
||||
assert_ne!(scc1_placeholder, scc2_placeholder);
|
||||
return Err(self.placeholder_error(scc1_placeholder, scc2_placeholder));
|
||||
}
|
||||
} else {
|
||||
// Otherwise, we can reach a placeholder if some successor can.
|
||||
self.scc_placeholders[scc1] = succ_bound;
|
||||
}
|
||||
|
||||
// At this point, `scc_placeholder[scc1]` stores some placeholder that `scc1` must outlive (if any).
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn placeholder_error(
|
||||
&self,
|
||||
placeholder1: ty::PlaceholderRegion,
|
||||
placeholder2: ty::PlaceholderRegion,
|
||||
) -> TypeError<'tcx> {
|
||||
self.error(placeholder1, self.tcx.mk_region(ty::RePlaceholder(placeholder2)))
|
||||
}
|
||||
|
||||
fn error(
|
||||
&self,
|
||||
placeholder: ty::PlaceholderRegion,
|
||||
other_region: ty::Region<'tcx>,
|
||||
) -> TypeError<'tcx> {
|
||||
if self.overly_polymorphic {
|
||||
return TypeError::RegionsOverlyPolymorphic(placeholder.name, other_region);
|
||||
} else {
|
||||
return TypeError::RegionsInsufficientlyPolymorphic(placeholder.name, other_region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// States we need to distinguish:
|
||||
//
|
||||
// * must be equal to a placeholder (i.e., a placeholder is in the SCC)
|
||||
// * it could conflict with some other regions in the SCC in different universes
|
||||
// * or a different placeholder
|
||||
// * `P1: S` and `S` must be equal to a placeholder
|
||||
// * `P1: S` and `S` is in an incompatible universe
|
||||
//
|
||||
// So if we
|
||||
//
|
||||
// (a) compute which placeholder (if any) each SCC must be equal to
|
||||
// (b) compute its minimum universe
|
||||
// (c) compute *some* placeholder where `S: P1` (any one will do)
|
||||
//
|
||||
// then we get an error if:
|
||||
//
|
||||
// - it must be equal to a placeholder `P1` and minimum universe cannot name `P1`
|
||||
// - `S: P1` and minimum universe cannot name `P1`
|
||||
// - `S: P1` and we must be equal to `P2`
|
||||
//
|
||||
// So we want to track:
|
||||
//
|
||||
// * Equal placeholder (if any)
|
||||
// * Some bounding placeholder (if any)
|
||||
// * Minimum universe
|
||||
//
|
||||
// * We compute equal placeholder + minimum universe of constituents in first pass
|
||||
// * Then we walk in order and compute from our dependencies `S1` where `S: S1` (`S -> S1`)
|
||||
// * bounding placeholder (if any)
|
||||
// * minimum universe
|
||||
// * And if we must be equal to a placeholder then we check it against
|
||||
// * minimum universe
|
||||
// * no bounding placeholder
|
||||
|
||||
/// Tracks the "minimum universe" for each SCC, along with some region that
|
||||
/// caused it to change.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct SccUniverse<'tcx> {
|
||||
/// For some SCC S, the minimum universe of:
|
||||
///
|
||||
/// * each region R in S
|
||||
/// * each SCC S1 such that S: S1
|
||||
universe: ty::UniverseIndex,
|
||||
|
||||
/// Some region that caused `universe` to be what it is.
|
||||
region: Option<ty::Region<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> SccUniverse<'tcx> {
|
||||
/// If `universe` is less than our current universe, then update
|
||||
/// `self.universe` and `self.region`.
|
||||
fn take_min(&mut self, universe: ty::UniverseIndex, region: ty::Region<'tcx>) {
|
||||
if universe < self.universe || self.region.is_none() {
|
||||
self.universe = universe;
|
||||
self.region = Some(region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
struct LeakCheckNode {
|
||||
DEBUG_FORMAT = "LeakCheckNode({})"
|
||||
}
|
||||
}
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
struct LeakCheckScc {
|
||||
DEBUG_FORMAT = "LeakCheckScc({})"
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the graph of constraints. For each `R1: R2` constraint we create
|
||||
/// an edge `R1 -> R2` in the graph.
|
||||
struct MiniGraph<'tcx> {
|
||||
/// Map from a region to the index of the node in the graph.
|
||||
nodes: FxHashMap<ty::Region<'tcx>, LeakCheckNode>,
|
||||
|
||||
/// Map from node index to SCC, and stores the successors of each SCC. All
|
||||
/// the regions in the same SCC are equal to one another, and if `S1 -> S2`,
|
||||
/// then `S1: S2`.
|
||||
sccs: Sccs<LeakCheckNode, LeakCheckScc>,
|
||||
}
|
||||
|
||||
impl<'tcx> MiniGraph<'tcx> {
|
||||
fn new<'a>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
undo_log: impl IntoIterator<Item = &'a UndoLog<'tcx>> + Clone,
|
||||
undo_log: impl Iterator<Item = &'a UndoLog<'tcx>>,
|
||||
verifys: &[Verify<'tcx>],
|
||||
) -> Self
|
||||
where
|
||||
'tcx: 'a,
|
||||
{
|
||||
let mut nodes = FxHashMap::default();
|
||||
let mut edges = Vec::new();
|
||||
|
||||
// Note that if `R2: R1`, we get a callback `r1, r2`, so `target` is first parameter.
|
||||
Self::iterate_undo_log(tcx, undo_log, verifys, |target, source| {
|
||||
let source_node = Self::add_node(&mut nodes, source);
|
||||
let target_node = Self::add_node(&mut nodes, target);
|
||||
edges.push((source_node, target_node));
|
||||
});
|
||||
let graph = VecGraph::new(nodes.len(), edges);
|
||||
let sccs = Sccs::new(&graph);
|
||||
Self { nodes, sccs }
|
||||
}
|
||||
|
||||
/// Invokes `each_edge(R1, R2)` for each edge where `R2: R1`
|
||||
fn iterate_undo_log<'a>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
undo_log: impl Iterator<Item = &'a UndoLog<'tcx>>,
|
||||
verifys: &[Verify<'tcx>],
|
||||
mut each_edge: impl FnMut(ty::Region<'tcx>, ty::Region<'tcx>),
|
||||
) where
|
||||
'tcx: 'a,
|
||||
{
|
||||
let mut prev_len = 0;
|
||||
while prev_len < self.len() {
|
||||
debug!("tainted: prev_len = {:?} new_len = {:?}", prev_len, self.len());
|
||||
|
||||
prev_len = self.len();
|
||||
|
||||
for undo_entry in undo_log.clone() {
|
||||
match undo_entry {
|
||||
&AddConstraint(Constraint::VarSubVar(a, b)) => {
|
||||
self.add_edge(tcx.mk_region(ReVar(a)), tcx.mk_region(ReVar(b)));
|
||||
}
|
||||
&AddConstraint(Constraint::RegSubVar(a, b)) => {
|
||||
self.add_edge(a, tcx.mk_region(ReVar(b)));
|
||||
}
|
||||
&AddConstraint(Constraint::VarSubReg(a, b)) => {
|
||||
self.add_edge(tcx.mk_region(ReVar(a)), b);
|
||||
}
|
||||
&AddConstraint(Constraint::RegSubReg(a, b)) => {
|
||||
self.add_edge(a, b);
|
||||
}
|
||||
&AddGiven(a, b) => {
|
||||
self.add_edge(a, tcx.mk_region(ReVar(b)));
|
||||
}
|
||||
&AddVerify(i) => span_bug!(
|
||||
verifys[i].origin.span(),
|
||||
"we never add verifications while doing higher-ranked things",
|
||||
),
|
||||
&AddCombination(..) | &AddVar(..) => {}
|
||||
for undo_entry in undo_log {
|
||||
match undo_entry {
|
||||
&AddConstraint(Constraint::VarSubVar(a, b)) => {
|
||||
each_edge(tcx.mk_region(ReVar(a)), tcx.mk_region(ReVar(b)));
|
||||
}
|
||||
&AddConstraint(Constraint::RegSubVar(a, b)) => {
|
||||
each_edge(a, tcx.mk_region(ReVar(b)));
|
||||
}
|
||||
&AddConstraint(Constraint::VarSubReg(a, b)) => {
|
||||
each_edge(tcx.mk_region(ReVar(a)), b);
|
||||
}
|
||||
&AddConstraint(Constraint::RegSubReg(a, b)) => {
|
||||
each_edge(a, b);
|
||||
}
|
||||
&AddGiven(a, b) => {
|
||||
each_edge(a, tcx.mk_region(ReVar(b)));
|
||||
}
|
||||
&AddVerify(i) => span_bug!(
|
||||
verifys[i].origin.span(),
|
||||
"we never add verifications while doing higher-ranked things",
|
||||
),
|
||||
&AddCombination(..) | &AddVar(..) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn into_set(self) -> FxHashSet<ty::Region<'tcx>> {
|
||||
self.regions
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.regions.len()
|
||||
}
|
||||
|
||||
fn add_edge(&mut self, source: ty::Region<'tcx>, target: ty::Region<'tcx>) {
|
||||
if self.directions.incoming {
|
||||
if self.regions.contains(&target) {
|
||||
self.regions.insert(source);
|
||||
}
|
||||
}
|
||||
|
||||
if self.directions.outgoing {
|
||||
if self.regions.contains(&source) {
|
||||
self.regions.insert(target);
|
||||
}
|
||||
}
|
||||
fn add_node(
|
||||
nodes: &mut FxHashMap<ty::Region<'tcx>, LeakCheckNode>,
|
||||
r: ty::Region<'tcx>,
|
||||
) -> LeakCheckNode {
|
||||
let l = nodes.len();
|
||||
*nodes.entry(r).or_insert(LeakCheckNode::new(l))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@
|
|||
#![feature(bool_to_option)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(box_syntax)]
|
||||
#![feature(const_fn)]
|
||||
#![feature(const_if_match)]
|
||||
#![feature(const_panic)]
|
||||
#![feature(extend_one)]
|
||||
#![feature(never_type)]
|
||||
#![feature(or_patterns)]
|
||||
|
|
|
|||
|
|
@ -150,14 +150,12 @@ pub fn poly_project_and_unify_type<'cx, 'tcx>(
|
|||
|
||||
let infcx = selcx.infcx();
|
||||
infcx.commit_if_ok(|snapshot| {
|
||||
let (placeholder_predicate, placeholder_map) =
|
||||
let (placeholder_predicate, _) =
|
||||
infcx.replace_bound_vars_with_placeholders(&obligation.predicate);
|
||||
|
||||
let placeholder_obligation = obligation.with(placeholder_predicate);
|
||||
let result = project_and_unify_type(selcx, &placeholder_obligation)?;
|
||||
infcx
|
||||
.leak_check(false, &placeholder_map, snapshot)
|
||||
.map_err(|err| MismatchedProjectionTypes { err })?;
|
||||
infcx.leak_check(false, snapshot).map_err(|err| MismatchedProjectionTypes { err })?;
|
||||
Ok(result)
|
||||
})
|
||||
}
|
||||
|
|
@ -300,7 +298,11 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
|
|||
fn fold<T: TypeFoldable<'tcx>>(&mut self, value: &T) -> T {
|
||||
let value = self.selcx.infcx().resolve_vars_if_possible(value);
|
||||
|
||||
if !value.has_projections() { value } else { value.fold_with(self) }
|
||||
if !value.has_projections() {
|
||||
value
|
||||
} else {
|
||||
value.fold_with(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ use super::{Normalized, ProjectionCacheKey};
|
|||
use super::{ObligationCause, PredicateObligation, TraitObligation};
|
||||
use super::{Overflow, SelectionError, Unimplemented};
|
||||
|
||||
use crate::infer::{CombinedSnapshot, InferCtxt, InferOk, PlaceholderMap, TypeFreshener};
|
||||
use crate::infer::{CombinedSnapshot, InferCtxt, InferOk, TypeFreshener};
|
||||
use crate::traits::error_reporting::InferCtxtExt;
|
||||
use crate::traits::project::ProjectionCacheKeyExt;
|
||||
use rustc_ast::attr;
|
||||
|
|
@ -1265,7 +1265,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
snapshot: &CombinedSnapshot<'_, 'tcx>,
|
||||
) -> bool {
|
||||
let poly_trait_predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate);
|
||||
let (placeholder_trait_predicate, placeholder_map) =
|
||||
let (placeholder_trait_predicate, _) =
|
||||
self.infcx().replace_bound_vars_with_placeholders(&poly_trait_predicate);
|
||||
debug!(
|
||||
"match_projection_obligation_against_definition_bounds: \
|
||||
|
|
@ -1297,7 +1297,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
obligation,
|
||||
bound,
|
||||
placeholder_trait_predicate.trait_ref,
|
||||
&placeholder_map,
|
||||
snapshot,
|
||||
)
|
||||
}) {
|
||||
|
|
@ -1320,7 +1319,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
obligation,
|
||||
bound,
|
||||
placeholder_trait_predicate.trait_ref,
|
||||
&placeholder_map,
|
||||
snapshot,
|
||||
);
|
||||
|
||||
|
|
@ -1335,7 +1333,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
obligation: &TraitObligation<'tcx>,
|
||||
trait_bound: ty::PolyTraitRef<'tcx>,
|
||||
placeholder_trait_ref: ty::TraitRef<'tcx>,
|
||||
placeholder_map: &PlaceholderMap<'tcx>,
|
||||
snapshot: &CombinedSnapshot<'_, 'tcx>,
|
||||
) -> bool {
|
||||
debug_assert!(!placeholder_trait_ref.has_escaping_bound_vars());
|
||||
|
|
@ -1343,7 +1340,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
.at(&obligation.cause, obligation.param_env)
|
||||
.sup(ty::Binder::dummy(placeholder_trait_ref), trait_bound)
|
||||
.is_ok()
|
||||
&& self.infcx.leak_check(false, placeholder_map, snapshot).is_ok()
|
||||
&& self.infcx.leak_check(false, snapshot).is_ok()
|
||||
}
|
||||
|
||||
fn evaluate_where_clause<'o>(
|
||||
|
|
@ -1837,7 +1834,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
return Err(());
|
||||
}
|
||||
|
||||
let (placeholder_obligation, placeholder_map) =
|
||||
let (placeholder_obligation, _) =
|
||||
self.infcx().replace_bound_vars_with_placeholders(&obligation.predicate);
|
||||
let placeholder_obligation_trait_ref = placeholder_obligation.trait_ref;
|
||||
|
||||
|
|
@ -1869,7 +1866,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
.map_err(|e| debug!("match_impl: failed eq_trait_refs due to `{}`", e))?;
|
||||
nested_obligations.extend(obligations);
|
||||
|
||||
if let Err(e) = self.infcx.leak_check(false, &placeholder_map, snapshot) {
|
||||
if let Err(e) = self.infcx.leak_check(false, snapshot) {
|
||||
debug!("match_impl: failed leak check due to `{}`", e);
|
||||
return Err(());
|
||||
}
|
||||
|
|
@ -2405,7 +2402,11 @@ impl<'o, 'tcx> TraitObligationStackList<'o, 'tcx> {
|
|||
}
|
||||
|
||||
fn depth(&self) -> usize {
|
||||
if let Some(head) = self.head { head.depth } else { 0 }
|
||||
if let Some(head) = self.head {
|
||||
head.depth
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ LL | where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'x isize>
|
|||
| ------------- required by this bound in `tuple_one`
|
||||
...
|
||||
LL | tuple_one::<Tuple>();
|
||||
| ^^^^^^^^^^^^^^^^^^ expected bound lifetime parameter 'x, found concrete lifetime
|
||||
| ^^^^^^^^^^^^^^^^^^ expected bound lifetime parameter 'y, found concrete lifetime
|
||||
|
||||
error[E0277]: the trait bound `for<'x, 'y> Tuple: TheTrait<(&'x isize, &'y isize)>` is not satisfied
|
||||
--> $DIR/associated-types-eq-hr.rs:97:17
|
||||
|
|
@ -74,7 +74,7 @@ LL | where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'y isize>
|
|||
| ------------- required by this bound in `tuple_two`
|
||||
...
|
||||
LL | tuple_two::<Tuple>();
|
||||
| ^^^^^^^^^^^^^^^^^^ expected bound lifetime parameter 'x, found concrete lifetime
|
||||
| ^^^^^^^^^^^^^^^^^^ expected bound lifetime parameter 'y, found concrete lifetime
|
||||
|
||||
error[E0277]: the trait bound `for<'x, 'y> Tuple: TheTrait<(&'x isize, &'y isize)>` is not satisfied
|
||||
--> $DIR/associated-types-eq-hr.rs:107:18
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/hr-subtype.rs:39:26
|
||||
--> $DIR/hr-subtype.rs:45:26
|
||||
|
|
||||
LL | gimme::<$t1>(None::<$t2>);
|
||||
| ^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter 'a
|
||||
...
|
||||
LL | / check! { bound_a_b_ret_a_vs_bound_a_ret_a: (for<'a,'b> fn(&'a u32, &'b u32) -> &'a u32,
|
||||
LL | | for<'a> fn(&'a u32, &'a u32) -> &'a u32) }
|
||||
| |_________________________________________________________________________________________- in this macro invocation
|
||||
LL | | for<'a> fn(&'a u32, &'a u32) -> &'a u32) }
|
||||
| |_____________________________________________- in this macro invocation
|
||||
|
|
||||
= note: expected enum `std::option::Option<for<'a, 'b> fn(&'a u32, &'b u32) -> &'a u32>`
|
||||
found enum `std::option::Option<for<'a> fn(&'a u32, &'a u32) -> &'a u32>`
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/hr-subtype.rs:39:26
|
||||
--> $DIR/hr-subtype.rs:45:26
|
||||
|
|
||||
LL | gimme::<$t1>(None::<$t2>);
|
||||
| ^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter 'a
|
||||
...
|
||||
LL | / check! { bound_a_b_vs_bound_a: (for<'a,'b> fn(&'a u32, &'b u32),
|
||||
LL | | for<'a> fn(&'a u32, &'a u32)) }
|
||||
| |__________________________________________________________________- in this macro invocation
|
||||
LL | | for<'a> fn(&'a u32, &'a u32)) }
|
||||
| |__________________________________- in this macro invocation
|
||||
|
|
||||
= note: expected enum `std::option::Option<for<'a, 'b> fn(&'a u32, &'b u32)>`
|
||||
found enum `std::option::Option<for<'a> fn(&'a u32, &'a u32)>`
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
error: fatal error triggered by #[rustc_error]
|
||||
--> $DIR/hr-subtype.rs:100:1
|
||||
--> $DIR/hr-subtype.rs:104:1
|
||||
|
|
||||
LL | / fn main() {
|
||||
LL | |
|
||||
LL | |
|
||||
LL | |
|
||||
LL | |
|
||||
... |
|
||||
LL | |
|
||||
LL | | }
|
||||
| |_^
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
error: fatal error triggered by #[rustc_error]
|
||||
--> $DIR/hr-subtype.rs:100:1
|
||||
--> $DIR/hr-subtype.rs:104:1
|
||||
|
|
||||
LL | / fn main() {
|
||||
LL | |
|
||||
LL | |
|
||||
LL | |
|
||||
LL | |
|
||||
... |
|
||||
LL | |
|
||||
LL | | }
|
||||
| |_^
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/hr-subtype.rs:39:26
|
||||
--> $DIR/hr-subtype.rs:45:26
|
||||
|
|
||||
LL | gimme::<$t1>(None::<$t2>);
|
||||
| ^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter 'a
|
||||
...
|
||||
LL | / check! { bound_a_vs_free_x: (for<'a> fn(&'a u32),
|
||||
LL | | fn(&'x u32)) }
|
||||
| |___________________________________________- in this macro invocation
|
||||
LL | | fn(&'x u32)) }
|
||||
| |______________- in this macro invocation
|
||||
|
|
||||
= note: expected enum `std::option::Option<for<'a> fn(&'a u32)>`
|
||||
found enum `std::option::Option<fn(&'x u32)>`
|
||||
|
|
|
|||
|
|
@ -1,17 +1,14 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/hr-subtype.rs:39:26
|
||||
error: fatal error triggered by #[rustc_error]
|
||||
--> $DIR/hr-subtype.rs:104:1
|
||||
|
|
||||
LL | gimme::<$t1>(None::<$t2>);
|
||||
| ^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter 'a
|
||||
...
|
||||
LL | / check! { bound_co_a_b_vs_bound_co_a: (for<'a,'b> fn(Co<'a>, Co<'b>),
|
||||
LL | | for<'a> fn(Co<'a>, Co<'a>)) }
|
||||
| |______________________________________________________________________- in this macro invocation
|
||||
|
|
||||
= note: expected enum `std::option::Option<for<'a, 'b> fn(Co<'a>, Co<'b>)>`
|
||||
found enum `std::option::Option<for<'a> fn(Co<'a>, Co<'a>)>`
|
||||
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
LL | / fn main() {
|
||||
LL | |
|
||||
LL | |
|
||||
LL | |
|
||||
... |
|
||||
LL | |
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
||||
|
|
|
|||
|
|
@ -1,17 +1,14 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/hr-subtype.rs:39:26
|
||||
error: fatal error triggered by #[rustc_error]
|
||||
--> $DIR/hr-subtype.rs:104:1
|
||||
|
|
||||
LL | gimme::<$t1>(None::<$t2>);
|
||||
| ^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter 'a
|
||||
...
|
||||
LL | / check! { bound_co_a_co_b_ret_contra_a: (for<'a,'b> fn(Co<'a>, Co<'b>) -> Contra<'a>,
|
||||
LL | | for<'a> fn(Co<'a>, Co<'a>) -> Contra<'a>) }
|
||||
| |______________________________________________________________________________________- in this macro invocation
|
||||
|
|
||||
= note: expected enum `std::option::Option<for<'a, 'b> fn(Co<'a>, Co<'b>) -> Contra<'a>>`
|
||||
found enum `std::option::Option<for<'a> fn(Co<'a>, Co<'a>) -> Contra<'a>>`
|
||||
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
LL | / fn main() {
|
||||
LL | |
|
||||
LL | |
|
||||
LL | |
|
||||
... |
|
||||
LL | |
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
error: fatal error triggered by #[rustc_error]
|
||||
--> $DIR/hr-subtype.rs:100:1
|
||||
--> $DIR/hr-subtype.rs:104:1
|
||||
|
|
||||
LL | / fn main() {
|
||||
LL | |
|
||||
LL | |
|
||||
LL | |
|
||||
LL | |
|
||||
... |
|
||||
LL | |
|
||||
LL | | }
|
||||
| |_^
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/hr-subtype.rs:39:26
|
||||
--> $DIR/hr-subtype.rs:45:26
|
||||
|
|
||||
LL | gimme::<$t1>(None::<$t2>);
|
||||
| ^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter 'a
|
||||
...
|
||||
LL | / check! { bound_contra_a_contra_b_ret_co_a: (for<'a,'b> fn(Contra<'a>, Contra<'b>) -> Co<'a>,
|
||||
LL | | for<'a> fn(Contra<'a>, Contra<'a>) -> Co<'a>) }
|
||||
| |______________________________________________________________________________________________- in this macro invocation
|
||||
LL | | for<'a> fn(Contra<'a>, Contra<'a>) -> Co<'a>) }
|
||||
| |__________________________________________________- in this macro invocation
|
||||
|
|
||||
= note: expected enum `std::option::Option<for<'a, 'b> fn(Contra<'a>, Contra<'b>) -> Co<'a>>`
|
||||
found enum `std::option::Option<for<'a> fn(Contra<'a>, Contra<'a>) -> Co<'a>>`
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/hr-subtype.rs:39:26
|
||||
--> $DIR/hr-subtype.rs:45:26
|
||||
|
|
||||
LL | gimme::<$t1>(None::<$t2>);
|
||||
| ^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter 'a
|
||||
...
|
||||
LL | / check! { bound_inv_a_b_vs_bound_inv_a: (for<'a,'b> fn(Inv<'a>, Inv<'b>),
|
||||
LL | | for<'a> fn(Inv<'a>, Inv<'a>)) }
|
||||
| |__________________________________________________________________________- in this macro invocation
|
||||
LL | | for<'a> fn(Inv<'a>, Inv<'a>)) }
|
||||
| |__________________________________- in this macro invocation
|
||||
|
|
||||
= note: expected enum `std::option::Option<for<'a, 'b> fn(Inv<'a>, Inv<'b>)>`
|
||||
found enum `std::option::Option<for<'a> fn(Inv<'a>, Inv<'a>)>`
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
error: fatal error triggered by #[rustc_error]
|
||||
--> $DIR/hr-subtype.rs:100:1
|
||||
--> $DIR/hr-subtype.rs:104:1
|
||||
|
|
||||
LL | / fn main() {
|
||||
LL | |
|
||||
LL | |
|
||||
LL | |
|
||||
LL | |
|
||||
... |
|
||||
LL | |
|
||||
LL | | }
|
||||
| |_^
|
||||
|
|
|
|||
|
|
@ -1,65 +1,65 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/hr-subtype.rs:33:26
|
||||
--> $DIR/hr-subtype.rs:39:26
|
||||
|
|
||||
LL | gimme::<$t2>(None::<$t1>);
|
||||
| ^^^^^^^^^^^ lifetime mismatch
|
||||
...
|
||||
LL | / check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>),
|
||||
LL | | fn(Inv<'y>)) }
|
||||
| |__________________________________________________- in this macro invocation
|
||||
LL | | fn(Inv<'y>)) }
|
||||
| |______________- in this macro invocation
|
||||
|
|
||||
= note: expected enum `std::option::Option<fn(Inv<'y>)>`
|
||||
found enum `std::option::Option<fn(Inv<'x>)>`
|
||||
note: the lifetime `'x` as defined on the function body at 32:20...
|
||||
--> $DIR/hr-subtype.rs:32:20
|
||||
note: the lifetime `'x` as defined on the function body at 38:20...
|
||||
--> $DIR/hr-subtype.rs:38:20
|
||||
|
|
||||
LL | fn subtype<'x,'y:'x,'z:'y>() {
|
||||
LL | fn subtype<'x, 'y: 'x, 'z: 'y>() {
|
||||
| ^^
|
||||
...
|
||||
LL | / check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>),
|
||||
LL | | fn(Inv<'y>)) }
|
||||
| |__________________________________________________- in this macro invocation
|
||||
note: ...does not necessarily outlive the lifetime `'y` as defined on the function body at 32:23
|
||||
--> $DIR/hr-subtype.rs:32:23
|
||||
LL | | fn(Inv<'y>)) }
|
||||
| |______________- in this macro invocation
|
||||
note: ...does not necessarily outlive the lifetime `'y` as defined on the function body at 38:24
|
||||
--> $DIR/hr-subtype.rs:38:24
|
||||
|
|
||||
LL | fn subtype<'x,'y:'x,'z:'y>() {
|
||||
| ^^
|
||||
LL | fn subtype<'x, 'y: 'x, 'z: 'y>() {
|
||||
| ^^
|
||||
...
|
||||
LL | / check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>),
|
||||
LL | | fn(Inv<'y>)) }
|
||||
| |__________________________________________________- in this macro invocation
|
||||
LL | | fn(Inv<'y>)) }
|
||||
| |______________- in this macro invocation
|
||||
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/hr-subtype.rs:39:26
|
||||
--> $DIR/hr-subtype.rs:45:26
|
||||
|
|
||||
LL | gimme::<$t1>(None::<$t2>);
|
||||
| ^^^^^^^^^^^ lifetime mismatch
|
||||
...
|
||||
LL | / check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>),
|
||||
LL | | fn(Inv<'y>)) }
|
||||
| |__________________________________________________- in this macro invocation
|
||||
LL | | fn(Inv<'y>)) }
|
||||
| |______________- in this macro invocation
|
||||
|
|
||||
= note: expected enum `std::option::Option<fn(Inv<'x>)>`
|
||||
found enum `std::option::Option<fn(Inv<'y>)>`
|
||||
note: the lifetime `'x` as defined on the function body at 38:22...
|
||||
--> $DIR/hr-subtype.rs:38:22
|
||||
note: the lifetime `'x` as defined on the function body at 44:22...
|
||||
--> $DIR/hr-subtype.rs:44:22
|
||||
|
|
||||
LL | fn supertype<'x,'y:'x,'z:'y>() {
|
||||
LL | fn supertype<'x, 'y: 'x, 'z: 'y>() {
|
||||
| ^^
|
||||
...
|
||||
LL | / check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>),
|
||||
LL | | fn(Inv<'y>)) }
|
||||
| |__________________________________________________- in this macro invocation
|
||||
note: ...does not necessarily outlive the lifetime `'y` as defined on the function body at 38:25
|
||||
--> $DIR/hr-subtype.rs:38:25
|
||||
LL | | fn(Inv<'y>)) }
|
||||
| |______________- in this macro invocation
|
||||
note: ...does not necessarily outlive the lifetime `'y` as defined on the function body at 44:26
|
||||
--> $DIR/hr-subtype.rs:44:26
|
||||
|
|
||||
LL | fn supertype<'x,'y:'x,'z:'y>() {
|
||||
| ^^
|
||||
LL | fn supertype<'x, 'y: 'x, 'z: 'y>() {
|
||||
| ^^
|
||||
...
|
||||
LL | / check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>),
|
||||
LL | | fn(Inv<'y>)) }
|
||||
| |__________________________________________________- in this macro invocation
|
||||
LL | | fn(Inv<'y>)) }
|
||||
| |______________- in this macro invocation
|
||||
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
error: fatal error triggered by #[rustc_error]
|
||||
--> $DIR/hr-subtype.rs:100:1
|
||||
--> $DIR/hr-subtype.rs:104:1
|
||||
|
|
||||
LL | / fn main() {
|
||||
LL | |
|
||||
LL | |
|
||||
LL | |
|
||||
LL | |
|
||||
... |
|
||||
LL | |
|
||||
LL | | }
|
||||
| |_^
|
||||
|
|
|
|||
|
|
@ -1,33 +1,33 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/hr-subtype.rs:39:26
|
||||
--> $DIR/hr-subtype.rs:45:26
|
||||
|
|
||||
LL | gimme::<$t1>(None::<$t2>);
|
||||
| ^^^^^^^^^^^ lifetime mismatch
|
||||
...
|
||||
LL | / check! { free_x_vs_free_y: (fn(&'x u32),
|
||||
LL | | fn(&'y u32)) }
|
||||
| |__________________________________________- in this macro invocation
|
||||
LL | | fn(&'y u32)) }
|
||||
| |______________- in this macro invocation
|
||||
|
|
||||
= note: expected enum `std::option::Option<fn(&'x u32)>`
|
||||
found enum `std::option::Option<fn(&'y u32)>`
|
||||
note: the lifetime `'x` as defined on the function body at 38:22...
|
||||
--> $DIR/hr-subtype.rs:38:22
|
||||
note: the lifetime `'x` as defined on the function body at 44:22...
|
||||
--> $DIR/hr-subtype.rs:44:22
|
||||
|
|
||||
LL | fn supertype<'x,'y:'x,'z:'y>() {
|
||||
LL | fn supertype<'x, 'y: 'x, 'z: 'y>() {
|
||||
| ^^
|
||||
...
|
||||
LL | / check! { free_x_vs_free_y: (fn(&'x u32),
|
||||
LL | | fn(&'y u32)) }
|
||||
| |__________________________________________- in this macro invocation
|
||||
note: ...does not necessarily outlive the lifetime `'y` as defined on the function body at 38:25
|
||||
--> $DIR/hr-subtype.rs:38:25
|
||||
LL | | fn(&'y u32)) }
|
||||
| |______________- in this macro invocation
|
||||
note: ...does not necessarily outlive the lifetime `'y` as defined on the function body at 44:26
|
||||
--> $DIR/hr-subtype.rs:44:26
|
||||
|
|
||||
LL | fn supertype<'x,'y:'x,'z:'y>() {
|
||||
| ^^
|
||||
LL | fn supertype<'x, 'y: 'x, 'z: 'y>() {
|
||||
| ^^
|
||||
...
|
||||
LL | / check! { free_x_vs_free_y: (fn(&'x u32),
|
||||
LL | | fn(&'y u32)) }
|
||||
| |__________________________________________- in this macro invocation
|
||||
LL | | fn(&'y u32)) }
|
||||
| |______________- in this macro invocation
|
||||
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to previous error
|
||||
|
|
|
|||
|
|
@ -18,24 +18,30 @@
|
|||
// revisions: bound_inv_a_b_vs_bound_inv_a
|
||||
// revisions: bound_a_b_ret_a_vs_bound_a_ret_a
|
||||
|
||||
fn gimme<T>(_: Option<T>) { }
|
||||
fn gimme<T>(_: Option<T>) {}
|
||||
|
||||
struct Inv<'a> { x: *mut &'a u32 }
|
||||
struct Inv<'a> {
|
||||
x: *mut &'a u32,
|
||||
}
|
||||
|
||||
struct Co<'a> { x: fn(&'a u32) }
|
||||
struct Co<'a> {
|
||||
x: fn(&'a u32),
|
||||
}
|
||||
|
||||
struct Contra<'a> { x: &'a u32 }
|
||||
struct Contra<'a> {
|
||||
x: &'a u32,
|
||||
}
|
||||
|
||||
macro_rules! check {
|
||||
($rev:ident: ($t1:ty, $t2:ty)) => {
|
||||
#[cfg($rev)]
|
||||
fn subtype<'x,'y:'x,'z:'y>() {
|
||||
fn subtype<'x, 'y: 'x, 'z: 'y>() {
|
||||
gimme::<$t2>(None::<$t1>);
|
||||
//[free_inv_x_vs_free_inv_y]~^ ERROR
|
||||
}
|
||||
|
||||
#[cfg($rev)]
|
||||
fn supertype<'x,'y:'x,'z:'y>() {
|
||||
fn supertype<'x, 'y: 'x, 'z: 'y>() {
|
||||
gimme::<$t1>(None::<$t2>);
|
||||
//[bound_a_vs_free_x]~^ ERROR
|
||||
//[free_x_vs_free_y]~^^ ERROR
|
||||
|
|
@ -43,35 +49,33 @@ macro_rules! check {
|
|||
//[bound_a_b_ret_a_vs_bound_a_ret_a]~^^^^ ERROR
|
||||
//[free_inv_x_vs_free_inv_y]~^^^^^ ERROR
|
||||
//[bound_a_b_vs_bound_a]~^^^^^^ ERROR mismatched types
|
||||
//[bound_co_a_co_b_ret_contra_a]~^^^^^^^ ERROR
|
||||
//[bound_contra_a_contra_b_ret_co_a]~^^^^^^^^ ERROR
|
||||
//[bound_co_a_b_vs_bound_co_a]~^^^^^^^^^ ERROR
|
||||
//[bound_contra_a_contra_b_ret_co_a]~^^^^^^^ ERROR
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// If both have bound regions, they are equivalent, regardless of
|
||||
// variant.
|
||||
check! { bound_a_vs_bound_a: (for<'a> fn(&'a u32),
|
||||
for<'a> fn(&'a u32)) }
|
||||
for<'a> fn(&'a u32)) }
|
||||
check! { bound_a_vs_bound_b: (for<'a> fn(&'a u32),
|
||||
for<'b> fn(&'b u32)) }
|
||||
for<'b> fn(&'b u32)) }
|
||||
check! { bound_inv_a_vs_bound_inv_b: (for<'a> fn(Inv<'a>),
|
||||
for<'b> fn(Inv<'b>)) }
|
||||
for<'b> fn(Inv<'b>)) }
|
||||
check! { bound_co_a_vs_bound_co_b: (for<'a> fn(Co<'a>),
|
||||
for<'b> fn(Co<'b>)) }
|
||||
for<'b> fn(Co<'b>)) }
|
||||
|
||||
// Bound is a subtype of free.
|
||||
check! { bound_a_vs_free_x: (for<'a> fn(&'a u32),
|
||||
fn(&'x u32)) }
|
||||
fn(&'x u32)) }
|
||||
|
||||
// Two free regions are relatable if subtyping holds.
|
||||
check! { free_x_vs_free_x: (fn(&'x u32),
|
||||
fn(&'x u32)) }
|
||||
fn(&'x u32)) }
|
||||
check! { free_x_vs_free_y: (fn(&'x u32),
|
||||
fn(&'y u32)) }
|
||||
fn(&'y u32)) }
|
||||
check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>),
|
||||
fn(Inv<'y>)) }
|
||||
fn(Inv<'y>)) }
|
||||
|
||||
// Somewhat surprisingly, a fn taking two distinct bound lifetimes and
|
||||
// a fn taking one bound lifetime can be interchangeable, but only if
|
||||
|
|
@ -82,25 +86,27 @@ check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>),
|
|||
// intersection;
|
||||
// - if we are contravariant, then 'a can be inferred to 'static.
|
||||
check! { bound_a_b_vs_bound_a: (for<'a,'b> fn(&'a u32, &'b u32),
|
||||
for<'a> fn(&'a u32, &'a u32)) }
|
||||
for<'a> fn(&'a u32, &'a u32)) }
|
||||
check! { bound_co_a_b_vs_bound_co_a: (for<'a,'b> fn(Co<'a>, Co<'b>),
|
||||
for<'a> fn(Co<'a>, Co<'a>)) }
|
||||
for<'a> fn(Co<'a>, Co<'a>)) }
|
||||
check! { bound_contra_a_contra_b_ret_co_a: (for<'a,'b> fn(Contra<'a>, Contra<'b>) -> Co<'a>,
|
||||
for<'a> fn(Contra<'a>, Contra<'a>) -> Co<'a>) }
|
||||
for<'a> fn(Contra<'a>, Contra<'a>) -> Co<'a>) }
|
||||
check! { bound_co_a_co_b_ret_contra_a: (for<'a,'b> fn(Co<'a>, Co<'b>) -> Contra<'a>,
|
||||
for<'a> fn(Co<'a>, Co<'a>) -> Contra<'a>) }
|
||||
for<'a> fn(Co<'a>, Co<'a>) -> Contra<'a>) }
|
||||
|
||||
// If we make those lifetimes invariant, then the two types are not interchangeable.
|
||||
check! { bound_inv_a_b_vs_bound_inv_a: (for<'a,'b> fn(Inv<'a>, Inv<'b>),
|
||||
for<'a> fn(Inv<'a>, Inv<'a>)) }
|
||||
for<'a> fn(Inv<'a>, Inv<'a>)) }
|
||||
check! { bound_a_b_ret_a_vs_bound_a_ret_a: (for<'a,'b> fn(&'a u32, &'b u32) -> &'a u32,
|
||||
for<'a> fn(&'a u32, &'a u32) -> &'a u32) }
|
||||
for<'a> fn(&'a u32, &'a u32) -> &'a u32) }
|
||||
|
||||
#[rustc_error]
|
||||
fn main() {
|
||||
//[bound_a_vs_bound_a]~^ ERROR fatal error triggered by #[rustc_error]
|
||||
//[bound_a_vs_bound_b]~^^ ERROR fatal error triggered by #[rustc_error]
|
||||
//[bound_inv_a_vs_bound_inv_b]~^^^ ERROR fatal error triggered by #[rustc_error]
|
||||
//[bound_co_a_vs_bound_co_b]~^^^^ ERROR fatal error triggered by #[rustc_error]
|
||||
//[free_x_vs_free_x]~^^^^^ ERROR fatal error triggered by #[rustc_error]
|
||||
//[bound_a_vs_bound_a]~^ ERROR fatal error triggered by #[rustc_error]
|
||||
//[bound_a_vs_bound_b]~^^ ERROR fatal error triggered by #[rustc_error]
|
||||
//[bound_inv_a_vs_bound_inv_b]~^^^ ERROR fatal error triggered by #[rustc_error]
|
||||
//[bound_co_a_vs_bound_co_b]~^^^^ ERROR fatal error triggered by #[rustc_error]
|
||||
//[free_x_vs_free_x]~^^^^^ ERROR fatal error triggered by #[rustc_error]
|
||||
//[bound_co_a_b_vs_bound_co_a]~^^^^^^ ERROR
|
||||
//[bound_co_a_co_b_ret_contra_a]~^^^^^^^ ERROR
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
//
|
||||
// In particular, we test this pattern in trait solving, where it is not connected
|
||||
// to any part of the source code.
|
||||
//
|
||||
// check-pass
|
||||
|
||||
trait Trait<T> {}
|
||||
|
||||
|
|
@ -30,9 +32,6 @@ fn main() {
|
|||
// - `?b: ?a` -- solveable if `?b` is inferred to `'static`
|
||||
// - So the subtyping check succeeds, somewhat surprisingly.
|
||||
// This is because we can use `'static`.
|
||||
//
|
||||
// NB. *However*, the reinstated leak-check gives an error here.
|
||||
|
||||
foo::<()>();
|
||||
//~^ ERROR not satisfied
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
error[E0277]: the trait bound `(): Trait<for<'b> fn(fn(&'b u32))>` is not satisfied
|
||||
--> $DIR/hrtb-exists-forall-trait-covariant.rs:36:11
|
||||
|
|
||||
LL | fn foo<T>()
|
||||
| --- required by a bound in this
|
||||
LL | where
|
||||
LL | T: Trait<for<'b> fn(fn(&'b u32))>,
|
||||
| ------------------------------ required by this bound in `foo`
|
||||
...
|
||||
LL | foo::<()>();
|
||||
| ^^ the trait `Trait<for<'b> fn(fn(&'b u32))>` is not implemented for `()`
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
<() as Trait<fn(fn(&'a u32))>>
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
||||
|
|
@ -13,11 +13,11 @@ struct S;
|
|||
|
||||
// Given 'cx, return 'cx
|
||||
type F = for<'cx> fn(&'cx S) -> &'cx S;
|
||||
fn want_F(f: F) { }
|
||||
fn want_F(f: F) {}
|
||||
|
||||
// Given anything, return 'static
|
||||
type G = for<'cx> fn(&'cx S) -> &'static S;
|
||||
fn want_G(f: G) { }
|
||||
fn want_G(f: G) {}
|
||||
|
||||
// Should meet both.
|
||||
fn foo(x: &S) -> &'static S {
|
||||
|
|
@ -25,7 +25,7 @@ fn foo(x: &S) -> &'static S {
|
|||
}
|
||||
|
||||
// Should meet both.
|
||||
fn bar<'a,'b>(x: &'a S) -> &'b S {
|
||||
fn bar<'a, 'b>(x: &'a S) -> &'b S {
|
||||
panic!()
|
||||
}
|
||||
|
||||
|
|
@ -37,7 +37,7 @@ fn baz(x: &S) -> &S {
|
|||
fn supply_F() {
|
||||
want_F(foo);
|
||||
|
||||
want_F(bar); //~ ERROR mismatched types
|
||||
want_F(bar);
|
||||
|
||||
want_F(baz);
|
||||
}
|
||||
|
|
@ -48,5 +48,4 @@ fn supply_G() {
|
|||
want_G(baz); //~ ERROR mismatched types
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
}
|
||||
pub fn main() {}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,3 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/regions-fn-subtyping-return-static-fail.rs:40:12
|
||||
|
|
||||
LL | want_F(bar);
|
||||
| ^^^ expected concrete lifetime, found bound lifetime parameter 'cx
|
||||
|
|
||||
= note: expected fn pointer `for<'cx> fn(&'cx S) -> &'cx S`
|
||||
found fn item `for<'a> fn(&'a S) -> &S {bar::<'_>}`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/regions-fn-subtyping-return-static-fail.rs:48:12
|
||||
|
|
||||
|
|
@ -16,6 +7,6 @@ LL | want_G(baz);
|
|||
= note: expected fn pointer `for<'cx> fn(&'cx S) -> &'static S`
|
||||
found fn item `for<'r> fn(&'r S) -> &'r S {baz}`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
// *ANY* lifetime and returns a reference with the 'static lifetime.
|
||||
// This can safely be considered to be an instance of `F` because all
|
||||
// lifetimes are sublifetimes of 'static.
|
||||
//
|
||||
// check-pass
|
||||
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_variables)]
|
||||
|
|
@ -14,11 +16,11 @@ struct S;
|
|||
|
||||
// Given 'cx, return 'cx
|
||||
type F = for<'cx> fn(&'cx S) -> &'cx S;
|
||||
fn want_F(f: F) { }
|
||||
fn want_F(f: F) {}
|
||||
|
||||
// Given anything, return 'static
|
||||
type G = for<'cx> fn(&'cx S) -> &'static S;
|
||||
fn want_G(f: G) { }
|
||||
fn want_G(f: G) {}
|
||||
|
||||
// Should meet both.
|
||||
fn foo(x: &S) -> &'static S {
|
||||
|
|
@ -26,7 +28,7 @@ fn foo(x: &S) -> &'static S {
|
|||
}
|
||||
|
||||
// Should meet both.
|
||||
fn bar<'a,'b>(x: &'a S) -> &'b S {
|
||||
fn bar<'a, 'b>(x: &'a S) -> &'b S {
|
||||
panic!()
|
||||
}
|
||||
|
||||
|
|
@ -38,10 +40,9 @@ fn baz(x: &S) -> &S {
|
|||
fn supply_F() {
|
||||
want_F(foo);
|
||||
|
||||
want_F(bar); //~ ERROR mismatched types
|
||||
want_F(bar);
|
||||
|
||||
want_F(baz);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
}
|
||||
pub fn main() {}
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/regions-fn-subtyping-return-static.rs:41:12
|
||||
|
|
||||
LL | want_F(bar);
|
||||
| ^^^ expected concrete lifetime, found bound lifetime parameter 'cx
|
||||
|
|
||||
= note: expected fn pointer `for<'cx> fn(&'cx S) -> &'cx S`
|
||||
found fn item `for<'a> fn(&'a S) -> &S {bar::<'_>}`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
||||
|
|
@ -36,7 +36,7 @@ error[E0271]: type mismatch resolving `for<'a, 'b> <fn(_) -> _ {id::<_>} as std:
|
|||
--> $DIR/rfc1623.rs:25:8
|
||||
|
|
||||
LL | f: &id,
|
||||
| ^^^ expected bound lifetime parameter 'a, found concrete lifetime
|
||||
| ^^^ expected bound lifetime parameter 'b, found concrete lifetime
|
||||
|
|
||||
= note: required for the cast to the object type `dyn for<'a, 'b> std::ops::Fn(&'a Foo<'b>) -> &'a Foo<'b>`
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue