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:
Niko Matsakis 2020-05-19 01:09:40 +00:00
parent 4199b3ae26
commit f2cf994483
29 changed files with 572 additions and 320 deletions

View file

@ -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,
)
}

View file

@ -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(())
})
}

View file

@ -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))
}
}

View file

@ -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)]

View file

@ -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)
}
}
}

View file

@ -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
}
}
}

View file

@ -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

View file

@ -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>`

View file

@ -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)>`

View file

@ -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 | | }
| |_^

View file

@ -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 | | }
| |_^

View file

@ -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)>`

View file

@ -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`.

View file

@ -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`.

View file

@ -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 | | }
| |_^

View file

@ -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>>`

View file

@ -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>)>`

View file

@ -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 | | }
| |_^

View file

@ -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

View file

@ -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 | | }
| |_^

View file

@ -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

View file

@ -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
}

View file

@ -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
}

View file

@ -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`.

View file

@ -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() {}

View file

@ -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`.

View file

@ -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() {}

View file

@ -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`.

View file

@ -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>`