Auto merge of #65232 - nikomatsakis:lazy-norm-anon-const-push-2, r=matthewjasper

replace the leak check with universes, take 2

This PR is an attempt to revive the "universe-based region check", which is an important step towards lazy normalization. Unlike before, we also modify the definition of `'empty` so that it is indexed by a universe. This sidesteps some of the surprising effects we saw before -- at the core, we no longer think that `exists<'a> { forall<'b> { 'b: 'a } }` is solveable. The new region lattice looks like this:

```
static ----------+-----...------+       (greatest)
|                |              |
early-bound and  |              |
free regions     |              |
|                |              |
scope regions    |              |
|                |              |
empty(root)   placeholder(U1)   |
|            /                  |
|           /         placeholder(Un)
empty(U1) --         /
|                   /
...                /
|                 /
empty(Un) --------                      (smallest)
```
This PR has three effects:

* It changes a fair number of error messages, I think for the better.
* It fixes a number of bugs. The old algorithm was too conservative and caused us to reject legal subtypings.
* It also causes two regressions (things that used to compile, but now do not).
    * `coherence-subtyping.rs` gets an additional error. This is expected.
    * `issue-57639.rs` regresses as before, for the reasons covered in #57639.

Both of the regressions stem from the same underlying property: without the leak check, the instantaneous "subtype" check is not able to tell whether higher-ranked subtyping will succeed or not. In both cases, we might be able to fix the problem by doing a 'leak-check like change' at some later point (e.g., as part of coherence).

This is a draft PR because:

* I didn't finish ripping out the leak-check completely.
* We might want to consider a crater run before landing this.
* We might want some kind of design meeting to cover the overall strategy.
* I just remembered I never finished 100% integrating this into the canonicalization code.
* I should also review what happens in NLL region checking -- it probably still has a notion of bottom (empty set).

r? @matthewjasper
This commit is contained in:
bors 2020-02-07 23:08:52 +00:00
commit 8498c5f5b0
45 changed files with 613 additions and 207 deletions

View File

@ -63,9 +63,12 @@ impl<'a> HashStable<StableHashingContext<'a>> for ty::RegionKind {
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
mem::discriminant(self).hash_stable(hcx, hasher);
match *self {
ty::ReErased | ty::ReStatic | ty::ReEmpty => {
ty::ReErased | ty::ReStatic => {
// No variant fields to hash for these ...
}
ty::ReEmpty(universe) => {
universe.hash_stable(hcx, hasher);
}
ty::ReLateBound(db, ty::BrAnon(i)) => {
db.hash_stable(hcx, hasher);
i.hash_stable(hcx, hasher);

View File

@ -167,11 +167,17 @@ impl CanonicalizeRegionMode for CanonicalizeQueryResponse {
r: ty::Region<'tcx>,
) -> ty::Region<'tcx> {
match r {
ty::ReFree(_) | ty::ReEmpty | ty::ReErased | ty::ReStatic | ty::ReEarlyBound(..) => r,
ty::ReFree(_)
| ty::ReErased
| ty::ReStatic
| ty::ReEmpty(ty::UniverseIndex::ROOT)
| ty::ReEarlyBound(..) => r,
ty::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region(
CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderRegion(*placeholder) },
r,
),
ty::ReVar(vid) => {
let universe = canonicalizer.region_var_universe(*vid);
canonicalizer.canonical_var_for_region(
@ -179,6 +185,11 @@ impl CanonicalizeRegionMode for CanonicalizeQueryResponse {
r,
)
}
ty::ReEmpty(ui) => {
bug!("canonicalizing 'empty in universe {:?}", ui) // FIXME
}
_ => {
// Other than `'static` or `'empty`, the query
// response should be executing in a fully
@ -213,7 +224,7 @@ impl CanonicalizeRegionMode for CanonicalizeUserTypeAnnotation {
r: ty::Region<'tcx>,
) -> ty::Region<'tcx> {
match r {
ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReErased | ty::ReEmpty | ty::ReStatic => r,
ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReErased | ty::ReStatic => r,
ty::ReVar(_) => canonicalizer.canonical_var_for_region_in_root_universe(r),
_ => {
// We only expect region names that the user can type.
@ -320,8 +331,8 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
| ty::ReEarlyBound(..)
| ty::ReFree(_)
| ty::ReScope(_)
| ty::ReEmpty(_)
| ty::RePlaceholder(..)
| ty::ReEmpty
| ty::ReErased => self.canonicalize_region_mode.canonicalize_free_region(self, r),
ty::ReClosureBound(..) => {

View File

@ -577,7 +577,7 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
ty::RePlaceholder(..)
| ty::ReVar(..)
| ty::ReEmpty
| ty::ReEmpty(_)
| ty::ReStatic
| ty::ReScope(..)
| ty::ReEarlyBound(..)

View File

@ -138,7 +138,10 @@ pub(super) fn note_and_explain_region(
msg_span_from_free_region(tcx, region)
}
ty::ReEmpty => ("the empty lifetime".to_owned(), None),
ty::ReEmpty(ty::UniverseIndex::ROOT) => ("the empty lifetime".to_owned(), None),
// uh oh, hope no user ever sees THIS
ty::ReEmpty(ui) => (format!("the empty lifetime in universe {:?}", ui), None),
ty::RePlaceholder(_) => (format!("any other region"), None),
@ -181,7 +184,8 @@ fn msg_span_from_free_region(
msg_span_from_early_bound_and_free_regions(tcx, region)
}
ty::ReStatic => ("the static lifetime".to_owned(), None),
ty::ReEmpty => ("an empty lifetime".to_owned(), None),
ty::ReEmpty(ty::UniverseIndex::ROOT) => ("an empty lifetime".to_owned(), None),
ty::ReEmpty(ui) => (format!("an empty lifetime in universe {:?}", ui), None),
_ => bug!("{:?}", region),
}
}
@ -375,6 +379,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
}
}
RegionResolutionError::UpperBoundUniverseConflict(
_,
_,
var_universe,
sup_origin,
sup_r,
) => {
assert!(sup_r.is_placeholder());
// Make a dummy value for the "sub region" --
// this is the initial value of the
// placeholder. In practice, we expect more
// tailored errors that don't really use this
// value.
let sub_r = self.tcx.mk_region(ty::ReEmpty(var_universe));
self.report_placeholder_failure(
region_scope_tree,
sup_origin,
sub_r,
sup_r,
)
.emit();
}
RegionResolutionError::MemberConstraintFailure {
opaque_type_def_id,
hidden_ty,
@ -429,6 +458,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
RegionResolutionError::GenericBoundFailure(..) => true,
RegionResolutionError::ConcreteFailure(..)
| RegionResolutionError::SubSupConflict(..)
| RegionResolutionError::UpperBoundUniverseConflict(..)
| RegionResolutionError::MemberConstraintFailure { .. } => false,
};
@ -443,6 +473,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(),
RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(),
RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _) => rvo.span(),
RegionResolutionError::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(),
RegionResolutionError::MemberConstraintFailure { span, .. } => span,
});
errors

View File

@ -45,7 +45,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
///
/// It will later be extended to trait objects.
pub(super) fn try_report_anon_anon_conflict(&self) -> Option<ErrorReported> {
let (span, sub, sup) = self.regions();
let (span, sub, sup) = self.regions()?;
// Determine whether the sub and sup consist of both anonymous (elided) regions.
let anon_reg_sup = self.tcx().is_suitable_region(sup)?;

View File

@ -17,11 +17,6 @@ mod util;
impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
pub fn try_report_nice_region_error(&self, error: &RegionResolutionError<'tcx>) -> bool {
match *error {
ConcreteFailure(..) | SubSupConflict(..) => {}
_ => return false, // inapplicable
}
if let Some(tables) = self.in_progress_tables {
let tables = tables.borrow();
NiceRegionError::new(self, error.clone(), Some(&tables)).try_report().is_some()
@ -79,13 +74,14 @@ impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> {
.or_else(|| self.try_report_impl_not_conforming_to_trait())
}
pub fn regions(&self) -> (Span, ty::Region<'tcx>, ty::Region<'tcx>) {
pub fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> {
match (&self.error, self.regions) {
(Some(ConcreteFailure(origin, sub, sup)), None) => (origin.span(), sub, sup),
(Some(SubSupConflict(_, _, origin, sub, _, sup)), None) => (origin.span(), sub, sup),
(None, Some((span, sub, sup))) => (span, sub, sup),
(Some(_), Some(_)) => panic!("incorrectly built NiceRegionError"),
_ => panic!("trying to report on an incorrect lifetime failure"),
(Some(ConcreteFailure(origin, sub, sup)), None) => Some((origin.span(), sub, sup)),
(Some(SubSupConflict(_, _, origin, sub, _, sup)), None) => {
Some((origin.span(), sub, sup))
}
(None, Some((span, sub, sup))) => Some((span, sub, sup)),
_ => None,
}
}
}

View File

@ -9,7 +9,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
/// When given a `ConcreteFailure` for a function with parameters containing a named region and
/// an anonymous region, emit an descriptive diagnostic error.
pub(super) fn try_report_named_anon_conflict(&self) -> Option<DiagnosticBuilder<'a>> {
let (span, sub, sup) = self.regions();
let (span, sub, sup) = self.regions()?;
debug!(
"try_report_named_anon_conflict(sub={:?}, sup={:?}, error={:?})",

View File

@ -107,6 +107,25 @@ impl NiceRegionError<'me, 'tcx> {
found.substs,
)),
Some(RegionResolutionError::UpperBoundUniverseConflict(
vid,
_,
_,
SubregionOrigin::Subtype(box TypeTrace {
cause,
values: ValuePairs::TraitRefs(ExpectedFound { expected, found }),
}),
sup_placeholder @ ty::RePlaceholder(_),
)) if expected.def_id == found.def_id => Some(self.try_report_placeholders_trait(
Some(self.tcx().mk_region(ty::ReVar(*vid))),
cause,
None,
Some(*sup_placeholder),
expected.def_id,
expected.substs,
found.substs,
)),
Some(RegionResolutionError::ConcreteFailure(
SubregionOrigin::Subtype(box TypeTrace {
cause,

View File

@ -130,7 +130,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> {
| ty::ReScope(_)
| ty::ReVar(_)
| ty::RePlaceholder(..)
| ty::ReEmpty
| ty::ReEmpty(_)
| ty::ReErased => {
// replace all free regions with 'erased
self.tcx().lifetimes.re_erased

View File

@ -128,6 +128,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
placeholder_map: &PlaceholderMap<'tcx>,
snapshot: &CombinedSnapshot<'_, 'tcx>,
) -> RelateResult<'tcx, ()> {
// If the user gave `-Zno-leak-check`, or we have been
// configured to skip the leak check, then skip the leak check
// completely. The leak check is deprecated. Any legitimate
// subtyping errors that it would have caught will now be
// caught later on, during region checking. However, we
// continue to use it for a transition period.
if self.tcx.sess.opts.debugging_opts.no_leak_check || self.skip_leak_check.get() {
return Ok(());
}
self.borrow_region_constraints().leak_check(
self.tcx,
overly_polymorphic,

View File

@ -82,6 +82,16 @@ pub enum RegionResolutionError<'tcx> {
Region<'tcx>,
),
/// Indicates a `'b: 'a` constraint where `'a` is in a universe that
/// cannot name the placeholder `'b`.
UpperBoundUniverseConflict(
RegionVid,
RegionVariableOrigin,
ty::UniverseIndex, // the universe index of the region variable
SubregionOrigin<'tcx>, // cause of the constraint
Region<'tcx>, // the placeholder `'b`
),
/// Indicates a failure of a `MemberConstraint`. These arise during
/// impl trait processing explicitly -- basically, the impl trait's hidden type
/// included some region that it was not supposed to.
@ -149,7 +159,14 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
fn construct_var_data(&self, tcx: TyCtxt<'tcx>) -> LexicalRegionResolutions<'tcx> {
LexicalRegionResolutions {
error_region: tcx.lifetimes.re_static,
values: IndexVec::from_elem_n(VarValue::Value(tcx.lifetimes.re_empty), self.num_vars()),
values: IndexVec::from_fn_n(
|vid| {
let vid_universe = self.var_infos[vid].universe;
let re_empty = tcx.mk_region(ty::ReEmpty(vid_universe));
VarValue::Value(re_empty)
},
self.num_vars(),
),
}
}
@ -381,8 +398,11 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
// This is a specialized version of the `lub_concrete_regions`
// check below for a common case, here purely as an
// optimization.
if let ReEmpty = a_region {
return false;
let b_universe = self.var_infos[b_vid].universe;
if let ReEmpty(a_universe) = a_region {
if *a_universe == b_universe {
return false;
}
}
let mut lub = self.lub_concrete_regions(a_region, cur_region);
@ -399,7 +419,6 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
// tighter bound than `'static`.
//
// (This might e.g. arise from being asked to prove `for<'a> { 'b: 'a }`.)
let b_universe = self.var_infos[b_vid].universe;
if let ty::RePlaceholder(p) = lub {
if b_universe.cannot_name(p.universe) {
lub = self.tcx().lifetimes.re_static;
@ -420,12 +439,38 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
/// True if `a <= b`, but not defined over inference variables.
fn sub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> bool {
let tcx = self.tcx();
let sub_free_regions = |r1, r2| self.region_rels.free_regions.sub_free_regions(tcx, r1, r2);
// Check for the case where we know that `'b: 'static` -- in that case,
// `a <= b` for all `a`.
let b_free_or_static = self.region_rels.free_regions.is_free_or_static(b);
if b_free_or_static && sub_free_regions(tcx.lifetimes.re_static, b) {
return true;
}
// If both `a` and `b` are free, consult the declared
// relationships. Note that this can be more precise than the
// `lub` relationship defined below, since sometimes the "lub"
// is actually the `postdom_upper_bound` (see
// `TransitiveRelation` for more details).
let a_free_or_static = self.region_rels.free_regions.is_free_or_static(a);
if a_free_or_static && b_free_or_static {
return sub_free_regions(a, b);
}
// For other cases, leverage the LUB code to find the LUB and
// check if it is equal to `b`.
self.lub_concrete_regions(a, b) == b
}
/// Returns the smallest region `c` such that `a <= c` and `b <= c`.
/// Returns the least-upper-bound of `a` and `b`; i.e., the
/// smallest region `c` such that `a <= c` and `b <= c`.
///
/// Neither `a` nor `b` may be an inference variable (hence the
/// term "concrete regions").
fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> {
match (a, b) {
let r = match (a, b) {
(&ty::ReClosureBound(..), _)
| (_, &ty::ReClosureBound(..))
| (&ReLateBound(..), _)
@ -435,14 +480,6 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
bug!("cannot relate region: LUB({:?}, {:?})", a, b);
}
(r @ &ReStatic, _) | (_, r @ &ReStatic) => {
r // nothing lives longer than static
}
(&ReEmpty, r) | (r, &ReEmpty) => {
r // everything lives longer than empty
}
(&ReVar(v_id), _) | (_, &ReVar(v_id)) => {
span_bug!(
self.var_infos[v_id].origin.span(),
@ -453,6 +490,41 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
);
}
(&ReStatic, _) | (_, &ReStatic) => {
// nothing lives longer than `'static`
self.tcx().lifetimes.re_static
}
(&ReEmpty(_), r @ ReEarlyBound(_))
| (r @ ReEarlyBound(_), &ReEmpty(_))
| (&ReEmpty(_), r @ ReFree(_))
| (r @ ReFree(_), &ReEmpty(_))
| (&ReEmpty(_), r @ ReScope(_))
| (r @ ReScope(_), &ReEmpty(_)) => {
// All empty regions are less than early-bound, free,
// and scope regions.
r
}
(&ReEmpty(a_ui), &ReEmpty(b_ui)) => {
// Empty regions are ordered according to the universe
// they are associated with.
let ui = a_ui.min(b_ui);
self.tcx().mk_region(ReEmpty(ui))
}
(&ReEmpty(empty_ui), &RePlaceholder(placeholder))
| (&RePlaceholder(placeholder), &ReEmpty(empty_ui)) => {
// If this empty region is from a universe that can
// name the placeholder, then the placeholder is
// larger; otherwise, the only ancestor is `'static`.
if empty_ui.can_name(placeholder.universe) {
self.tcx().mk_region(RePlaceholder(placeholder))
} else {
self.tcx().lifetimes.re_static
}
}
(&ReEarlyBound(_), &ReScope(s_id))
| (&ReScope(s_id), &ReEarlyBound(_))
| (&ReFree(_), &ReScope(s_id))
@ -509,7 +581,11 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
self.tcx().lifetimes.re_static
}
}
}
};
debug!("lub_concrete_regions({:?}, {:?}) = {:?}", a, b, r);
r
}
/// After expansion is complete, go and check upper bounds (i.e.,
@ -528,7 +604,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
}
Constraint::RegSubReg(sub, sup) => {
if self.region_rels.is_subregion_of(sub, sup) {
if self.sub_concrete_regions(sub, sup) {
continue;
}
@ -557,7 +633,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
// Do not report these errors immediately:
// instead, set the variable value to error and
// collect them later.
if !self.region_rels.is_subregion_of(a_region, b_region) {
if !self.sub_concrete_regions(a_region, b_region) {
debug!(
"collect_errors: region error at {:?}: \
cannot verify that {:?}={:?} <= {:?}",
@ -592,12 +668,6 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
debug!("collect_errors: verify={:?}", verify);
let sub = var_data.normalize(self.tcx(), verify.region);
// This was an inference variable which didn't get
// constrained, therefore it can be assume to hold.
if let ty::ReEmpty = *sub {
continue;
}
let verify_kind_ty = verify.kind.to_ty(self.tcx());
if self.bound_is_met(&verify.bound, var_data, verify_kind_ty, sub) {
continue;
@ -760,7 +830,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
};
for upper_bound in &upper_bounds {
if !self.region_rels.is_subregion_of(effective_lower_bound, upper_bound.region) {
if !self.sub_concrete_regions(effective_lower_bound, upper_bound.region) {
let origin = self.var_infos[node_idx].origin;
debug!(
"region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \
@ -780,6 +850,26 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
}
}
// If we have a scenario like `exists<'a> { forall<'b> { 'b:
// 'a } }`, we wind up without any lower-bound -- all we have
// are placeholders as upper bounds, but the universe of the
// variable `'a` doesn't permit those placeholders.
for upper_bound in &upper_bounds {
if let ty::RePlaceholder(p) = upper_bound.region {
if node_universe.cannot_name(p.universe) {
let origin = self.var_infos[node_idx].origin.clone();
errors.push(RegionResolutionError::UpperBoundUniverseConflict(
node_idx,
origin,
node_universe,
upper_bound.origin.clone(),
upper_bound.region,
));
return;
}
}
}
// Errors in earlier passes can yield error variables without
// resolution errors here; delay ICE in favor of those errors.
self.tcx().sess.delay_span_bug(
@ -890,7 +980,15 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
}
VerifyBound::OutlivedBy(r) => {
self.region_rels.is_subregion_of(min, var_values.normalize(self.tcx(), r))
self.sub_concrete_regions(min, var_values.normalize(self.tcx(), r))
}
VerifyBound::IsEmpty => {
if let ty::ReEmpty(_) = min {
true
} else {
false
}
}
VerifyBound::AnyBound(bs) => {

View File

@ -125,6 +125,13 @@ pub struct InferCtxt<'a, 'tcx> {
/// order, represented by its upper and lower bounds.
pub type_variables: RefCell<type_variable::TypeVariableTable<'tcx>>,
/// If set, this flag causes us to skip the 'leak check' during
/// higher-ranked subtyping operations. This flag is a temporary one used
/// to manage the removal of the leak-check: for the time being, we still run the
/// leak-check, but we issue warnings. This flag can only be set to true
/// when entering a snapshot.
skip_leak_check: Cell<bool>,
/// Map from const parameter variable to the kind of const it represents.
const_unification_table: RefCell<ut::UnificationTable<ut::InPlace<ty::ConstVid<'tcx>>>>,
@ -246,7 +253,7 @@ pub enum ValuePairs<'tcx> {
/// encounter an error or subtyping constraint.
///
/// See the `error_reporting` module for more details.
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct TypeTrace<'tcx> {
cause: ObligationCause<'tcx>,
values: ValuePairs<'tcx>,
@ -550,6 +557,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
tainted_by_errors_flag: Cell::new(false),
err_count_on_creation: tcx.sess.err_count(),
in_snapshot: Cell::new(false),
skip_leak_check: Cell::new(false),
region_obligations: RefCell::new(vec![]),
universe: Cell::new(ty::UniverseIndex::ROOT),
})
@ -593,6 +601,7 @@ pub struct CombinedSnapshot<'a, 'tcx> {
region_obligations_snapshot: usize,
universe: ty::UniverseIndex,
was_in_snapshot: bool,
was_skip_leak_check: bool,
_in_progress_tables: Option<Ref<'a, ty::TypeckTables<'tcx>>>,
}
@ -720,6 +729,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
region_obligations_snapshot: self.region_obligations.borrow().len(),
universe: self.universe(),
was_in_snapshot: in_snapshot,
was_skip_leak_check: self.skip_leak_check.get(),
// Borrow tables "in progress" (i.e., during typeck)
// to ban writes from within a snapshot to them.
_in_progress_tables: self.in_progress_tables.map(|tables| tables.borrow()),
@ -738,11 +748,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
region_obligations_snapshot,
universe,
was_in_snapshot,
was_skip_leak_check,
_in_progress_tables,
} = snapshot;
self.in_snapshot.set(was_in_snapshot);
self.universe.set(universe);
self.skip_leak_check.set(was_skip_leak_check);
self.projection_cache.borrow_mut().rollback_to(projection_cache_snapshot);
self.type_variables.borrow_mut().rollback_to(type_snapshot);
@ -765,10 +777,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
region_obligations_snapshot: _,
universe: _,
was_in_snapshot,
was_skip_leak_check,
_in_progress_tables,
} = snapshot;
self.in_snapshot.set(was_in_snapshot);
self.skip_leak_check.set(was_skip_leak_check);
self.projection_cache.borrow_mut().commit(projection_cache_snapshot);
self.type_variables.borrow_mut().commit(type_snapshot);
@ -822,6 +836,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
r
}
/// If `should_skip` is true, then execute `f` then unroll any bindings it creates.
pub fn probe_maybe_skip_leak_check<R, F>(&self, should_skip: bool, f: F) -> R
where
F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R,
{
debug!("probe()");
let snapshot = self.start_snapshot();
let skip_leak_check = should_skip || self.skip_leak_check.get();
self.skip_leak_check.set(skip_leak_check);
let r = f(&snapshot);
self.rollback_to("probe", snapshot);
r
}
/// Scan the constraints produced since `snapshot` began and returns:
///
/// - `None` -- if none of them involve "region outlives" constraints
@ -1647,12 +1675,6 @@ impl<'tcx> TypeTrace<'tcx> {
}
}
impl<'tcx> fmt::Debug for TypeTrace<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "TypeTrace({:?})", self.cause)
}
}
impl<'tcx> SubregionOrigin<'tcx> {
pub fn span(&self) -> Span {
match *self {

View File

@ -384,9 +384,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
match least_region {
None => least_region = Some(subst_arg),
Some(lr) => {
if free_region_relations.sub_free_regions(lr, subst_arg) {
if free_region_relations.sub_free_regions(self.tcx, lr, subst_arg) {
// keep the current least region
} else if free_region_relations.sub_free_regions(subst_arg, lr) {
} else if free_region_relations.sub_free_regions(self.tcx, subst_arg, lr) {
// switch to `subst_arg`
least_region = Some(subst_arg);
} else {
@ -611,7 +611,7 @@ pub fn unexpected_hidden_region_diagnostic(
);
// Explain the region we are capturing.
if let ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic | ty::ReEmpty = hidden_region {
if let ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic | ty::ReEmpty(_) = hidden_region {
// Assuming regionck succeeded (*), we ought to always be
// capturing *some* region from the fn header, and hence it
// ought to be free. So under normal circumstances, we will go
@ -844,7 +844,7 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> {
.emit();
}
}
self.tcx.lifetimes.re_empty
self.tcx.lifetimes.re_root_empty
}
None => {
self.tcx

View File

@ -60,7 +60,18 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
// scope type parameters:
let param_bounds = param_bounds.chain(self.implicit_region_bound);
VerifyBound::AnyBound(param_bounds.map(|r| VerifyBound::OutlivedBy(r)).collect())
let any_bounds: Vec<_> = param_bounds.map(|r| VerifyBound::OutlivedBy(r)).collect();
if any_bounds.is_empty() {
// We know that all types `T` outlive `'empty`, so if we
// can find no other bound, then check that the region
// being tested is `'empty`.
VerifyBound::IsEmpty
} else {
// If we can find any other bound `R` such that `T: R`, then
// we don't need to check for `'empty`, because `R: 'empty`.
VerifyBound::AnyBound(any_bounds)
}
}
/// Given a projection like `T::Item`, searches the environment

View File

@ -33,18 +33,6 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
assert!(self.in_snapshot());
// If the user gave `-Zno-leak-check`, then skip the leak
// check completely. This is wildly unsound and also not
// unlikely to cause an ICE or two. It is intended for use
// only during a transition period, in which the MIR typeck
// uses the "universe-style" check, and the rest of typeck
// uses the more conservative leak check. Since the leak
// check is more conservative, we can't test the
// universe-style check without disabling it.
if tcx.sess.opts.debugging_opts.no_leak_check {
return Ok(());
}
// Go through each placeholder that we created.
for (_, &placeholder_region) in placeholder_map {
// Find the universe this placeholder inhabits.

View File

@ -233,6 +233,9 @@ pub enum VerifyBound<'tcx> {
/// if `R: min`, then by transitivity `G: min`.
OutlivedBy(Region<'tcx>),
/// Given a region `R`, true if it is `'empty`.
IsEmpty,
/// Given a set of bounds `B`, expands to the function:
///
/// ```rust
@ -792,10 +795,10 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
match *region {
ty::ReScope(..)
| ty::ReStatic
| ty::ReEmpty
| ty::ReErased
| ty::ReFree(..)
| ty::ReEarlyBound(..) => ty::UniverseIndex::ROOT,
ty::ReEmpty(ui) => ui,
ty::RePlaceholder(placeholder) => placeholder.universe,
ty::ReClosureBound(vid) | ty::ReVar(vid) => self.var_universe(vid),
ty::ReLateBound(..) => bug!("universe(): encountered bound region {:?}", region),
@ -867,6 +870,7 @@ impl<'tcx> VerifyBound<'tcx> {
VerifyBound::IfEq(..) => false,
VerifyBound::OutlivedBy(ty::ReStatic) => true,
VerifyBound::OutlivedBy(_) => false,
VerifyBound::IsEmpty => false,
VerifyBound::AnyBound(bs) => bs.iter().any(|b| b.must_hold()),
VerifyBound::AllBounds(bs) => bs.iter().all(|b| b.must_hold()),
}
@ -875,7 +879,7 @@ impl<'tcx> VerifyBound<'tcx> {
pub fn cannot_hold(&self) -> bool {
match self {
VerifyBound::IfEq(_, b) => b.cannot_hold(),
VerifyBound::OutlivedBy(ty::ReEmpty) => true,
VerifyBound::IsEmpty => false,
VerifyBound::OutlivedBy(_) => false,
VerifyBound::AnyBound(bs) => bs.iter().all(|b| b.cannot_hold()),
VerifyBound::AllBounds(bs) => bs.iter().any(|b| b.cannot_hold()),

View File

@ -4,8 +4,8 @@
//! and use that to decide when one free region outlives another, and so forth.
use crate::middle::region;
use crate::ty::free_region_map::{FreeRegionMap, FreeRegionRelations};
use crate::ty::{self, Region, TyCtxt};
use crate::ty::free_region_map::FreeRegionMap;
use crate::ty::{Region, TyCtxt};
use rustc_hir::def_id::DefId;
/// Combines a `region::ScopeTree` (which governs relationships between
@ -38,62 +38,6 @@ impl<'a, 'tcx> RegionRelations<'a, 'tcx> {
Self { tcx, context, region_scope_tree, free_regions }
}
/// Determines whether one region is a subregion of another. This is intended to run *after
/// inference* and sadly the logic is somewhat duplicated with the code in infer.rs.
pub fn is_subregion_of(
&self,
sub_region: ty::Region<'tcx>,
super_region: ty::Region<'tcx>,
) -> bool {
let result = sub_region == super_region || {
match (sub_region, super_region) {
(ty::ReEmpty, _) | (_, ty::ReStatic) => true,
(ty::ReScope(sub_scope), ty::ReScope(super_scope)) => {
self.region_scope_tree.is_subscope_of(*sub_scope, *super_scope)
}
(ty::ReScope(sub_scope), ty::ReEarlyBound(ref br)) => {
let fr_scope = self.region_scope_tree.early_free_scope(self.tcx, br);
self.region_scope_tree.is_subscope_of(*sub_scope, fr_scope)
}
(ty::ReScope(sub_scope), ty::ReFree(fr)) => {
let fr_scope = self.region_scope_tree.free_scope(self.tcx, fr);
self.region_scope_tree.is_subscope_of(*sub_scope, fr_scope)
}
(ty::ReEarlyBound(_), ty::ReEarlyBound(_))
| (ty::ReFree(_), ty::ReEarlyBound(_))
| (ty::ReEarlyBound(_), ty::ReFree(_))
| (ty::ReFree(_), ty::ReFree(_)) => {
self.free_regions.sub_free_regions(sub_region, super_region)
}
_ => false,
}
};
let result = result || self.is_static(super_region);
debug!(
"is_subregion_of(sub_region={:?}, super_region={:?}) = {:?}",
sub_region, super_region, result
);
result
}
/// Determines whether this free region is required to be `'static`.
fn is_static(&self, super_region: ty::Region<'tcx>) -> bool {
debug!("is_static(super_region={:?})", super_region);
match *super_region {
ty::ReStatic => true,
ty::ReEarlyBound(_) | ty::ReFree(_) => {
let re_static = self.tcx.mk_region(ty::ReStatic);
self.free_regions.sub_free_regions(&re_static, &super_region)
}
_ => false,
}
}
pub fn lub_free_regions(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> Region<'tcx> {
self.free_regions.lub_free_regions(self.tcx, r_a, r_b)
}

View File

@ -7,6 +7,7 @@
use crate::infer::{CombinedSnapshot, InferOk};
use crate::traits::select::IntercrateAmbiguityCause;
use crate::traits::IntercrateMode;
use crate::traits::SkipLeakCheck;
use crate::traits::{self, Normalized, Obligation, ObligationCause, SelectionContext};
use crate::ty::fold::TypeFoldable;
use crate::ty::subst::Subst;
@ -53,6 +54,7 @@ pub fn overlapping_impls<F1, F2, R>(
impl1_def_id: DefId,
impl2_def_id: DefId,
intercrate_mode: IntercrateMode,
skip_leak_check: SkipLeakCheck,
on_overlap: F1,
no_overlap: F2,
) -> R
@ -70,7 +72,7 @@ where
let overlaps = tcx.infer_ctxt().enter(|infcx| {
let selcx = &mut SelectionContext::intercrate(&infcx, intercrate_mode);
overlap(selcx, impl1_def_id, impl2_def_id).is_some()
overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id).is_some()
});
if !overlaps {
@ -83,7 +85,7 @@ where
tcx.infer_ctxt().enter(|infcx| {
let selcx = &mut SelectionContext::intercrate(&infcx, intercrate_mode);
selcx.enable_tracking_intercrate_ambiguity_causes();
on_overlap(overlap(selcx, impl1_def_id, impl2_def_id).unwrap())
on_overlap(overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id).unwrap())
})
}
@ -113,12 +115,15 @@ fn with_fresh_ty_vars<'cx, 'tcx>(
/// where-clauses)? If so, returns an `ImplHeader` that unifies the two impls.
fn overlap<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
skip_leak_check: SkipLeakCheck,
a_def_id: DefId,
b_def_id: DefId,
) -> Option<OverlapResult<'tcx>> {
debug!("overlap(a_def_id={:?}, b_def_id={:?})", a_def_id, b_def_id);
selcx.infcx().probe(|snapshot| overlap_within_probe(selcx, a_def_id, b_def_id, snapshot))
selcx.infcx().probe_maybe_skip_leak_check(skip_leak_check.is_yes(), |snapshot| {
overlap_within_probe(selcx, a_def_id, b_def_id, snapshot)
})
}
fn overlap_within_probe(
@ -146,7 +151,9 @@ fn overlap_within_probe(
.eq_impl_headers(&a_impl_header, &b_impl_header)
{
Ok(InferOk { obligations, value: () }) => obligations,
Err(_) => return None,
Err(_) => {
return None;
}
};
debug!("overlap: unification check succeeded");

View File

@ -83,6 +83,28 @@ pub enum IntercrateMode {
Fixed,
}
/// Whether to skip the leak check, as part of a future compatibility warning step.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum SkipLeakCheck {
Yes,
No,
}
impl SkipLeakCheck {
fn is_yes(self) -> bool {
self == SkipLeakCheck::Yes
}
}
/// The "default" for skip-leak-check corresponds to the current
/// behavior (do not skip the leak check) -- not the behavior we are
/// transitioning into.
impl Default for SkipLeakCheck {
fn default() -> Self {
SkipLeakCheck::No
}
}
/// The mode that trait queries run in.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum TraitQueryMode {

View File

@ -19,6 +19,7 @@ use crate::ty::{self, TyCtxt, TypeFoldable};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::struct_span_err;
use rustc_hir::def_id::DefId;
use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK;
use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS;
use rustc_span::DUMMY_SP;
@ -97,7 +98,7 @@ pub fn translate_substs<'a, 'tcx>(
|_| {
bug!(
"When translating substitutions for specialization, the expected \
specialization failed to hold"
specialization failed to hold"
)
},
)
@ -268,7 +269,7 @@ fn fulfill_implication<'a, 'tcx>(
// no dice!
debug!(
"fulfill_implication: for impls on {:?} and {:?}, \
could not fulfill: {:?} given {:?}",
could not fulfill: {:?} given {:?}",
source_trait_ref, target_trait_ref, errors, param_env.caller_bounds
);
Err(())
@ -342,6 +343,7 @@ pub(super) fn specialization_graph_provider(
FutureCompatOverlapErrorKind::Issue33140 => {
ORDER_DEPENDENT_TRAIT_OBJECTS
}
FutureCompatOverlapErrorKind::LeakCheck => COHERENCE_LEAK_CHECK,
};
tcx.struct_span_lint_hir(
lint,

View File

@ -11,6 +11,7 @@ pub use rustc::traits::types::specialization_graph::*;
pub enum FutureCompatOverlapErrorKind {
Issue43355,
Issue33140,
LeakCheck,
}
#[derive(Debug)]
@ -111,6 +112,7 @@ impl<'tcx> Children {
possible_sibling,
impl_def_id,
traits::IntercrateMode::Issue43355,
traits::SkipLeakCheck::default(),
|overlap| {
if let Some(overlap_kind) =
tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling)
@ -161,6 +163,7 @@ impl<'tcx> Children {
possible_sibling,
impl_def_id,
traits::IntercrateMode::Fixed,
traits::SkipLeakCheck::default(),
|overlap| {
last_lint = Some(FutureCompatOverlapError {
error: overlap_error(overlap),
@ -169,6 +172,23 @@ impl<'tcx> Children {
},
|| (),
);
if last_lint.is_none() {
traits::overlapping_impls(
tcx,
possible_sibling,
impl_def_id,
traits::IntercrateMode::Fixed,
traits::SkipLeakCheck::Yes,
|overlap| {
last_lint = Some(FutureCompatOverlapError {
error: overlap_error(overlap),
kind: FutureCompatOverlapErrorKind::LeakCheck,
});
},
|| (),
);
}
}
// no overlap (error bailed already via ?)
@ -247,7 +267,7 @@ impl<'tcx> Graph {
if trait_ref.references_error() {
debug!(
"insert: inserting dummy node for erroneous TraitRef {:?}, \
impl_def_id={:?}, trait_def_id={:?}",
impl_def_id={:?}, trait_def_id={:?}",
trait_ref, impl_def_id, trait_def_id
);
@ -326,7 +346,7 @@ impl<'tcx> Graph {
if self.parent.insert(child, parent).is_some() {
bug!(
"When recording an impl from the crate store, information about its parent \
was already present."
was already present."
);
}

View File

@ -173,8 +173,13 @@ pub struct CommonTypes<'tcx> {
}
pub struct CommonLifetimes<'tcx> {
pub re_empty: Region<'tcx>,
/// `ReEmpty` in the root universe.
pub re_root_empty: Region<'tcx>,
/// `ReStatic`
pub re_static: Region<'tcx>,
/// Erased region, used after type-checking
pub re_erased: Region<'tcx>,
}
@ -876,7 +881,7 @@ impl<'tcx> CommonLifetimes<'tcx> {
let mk = |r| interners.region.intern(r, |r| Interned(interners.arena.alloc(r))).0;
CommonLifetimes {
re_empty: mk(RegionKind::ReEmpty),
re_root_empty: mk(RegionKind::ReEmpty(ty::UniverseIndex::ROOT)),
re_static: mk(RegionKind::ReStatic),
re_erased: mk(RegionKind::ReErased),
}

View File

@ -23,11 +23,61 @@ impl<'tcx> FreeRegionMap<'tcx> {
// (with the exception that `'static: 'x` is not notable)
pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) {
debug!("relate_regions(sub={:?}, sup={:?})", sub, sup);
if is_free_or_static(sub) && is_free(sup) {
if self.is_free_or_static(sub) && self.is_free(sup) {
self.relation.add(sub, sup)
}
}
/// Tests whether `r_a <= r_b`.
///
/// Both regions must meet `is_free_or_static`.
///
/// Subtle: one tricky case that this code gets correct is as
/// follows. If we know that `r_b: 'static`, then this function
/// will return true, even though we don't know anything that
/// directly relates `r_a` and `r_b`.
///
/// Also available through the `FreeRegionRelations` trait below.
pub fn sub_free_regions(
&self,
tcx: TyCtxt<'tcx>,
r_a: Region<'tcx>,
r_b: Region<'tcx>,
) -> bool {
assert!(self.is_free_or_static(r_a) && self.is_free_or_static(r_b));
let re_static = tcx.lifetimes.re_static;
if self.check_relation(re_static, r_b) {
// `'a <= 'static` is always true, and not stored in the
// relation explicitly, so check if `'b` is `'static` (or
// equivalent to it)
true
} else {
self.check_relation(r_a, r_b)
}
}
/// Check whether `r_a <= r_b` is found in the relation.
fn check_relation(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool {
r_a == r_b || self.relation.contains(&r_a, &r_b)
}
/// True for free regions other than `'static`.
pub fn is_free(&self, r: Region<'_>) -> bool {
match *r {
ty::ReEarlyBound(_) | ty::ReFree(_) => true,
_ => false,
}
}
/// True if `r` is a free region or static of the sort that this
/// free region map can be used with.
pub fn is_free_or_static(&self, r: Region<'_>) -> bool {
match *r {
ty::ReStatic => true,
_ => self.is_free(r),
}
}
/// Computes the least-upper-bound of two free regions. In some
/// cases, this is more conservative than necessary, in order to
/// avoid making arbitrary choices. See
@ -39,13 +89,13 @@ impl<'tcx> FreeRegionMap<'tcx> {
r_b: Region<'tcx>,
) -> Region<'tcx> {
debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b);
assert!(is_free(r_a));
assert!(is_free(r_b));
assert!(self.is_free(r_a));
assert!(self.is_free(r_b));
let result = if r_a == r_b {
r_a
} else {
match self.relation.postdom_upper_bound(&r_a, &r_b) {
None => tcx.mk_region(ty::ReStatic),
None => tcx.lifetimes.re_static,
Some(r) => *r,
}
};
@ -60,31 +110,18 @@ impl<'tcx> FreeRegionMap<'tcx> {
pub trait FreeRegionRelations<'tcx> {
/// Tests whether `r_a <= r_b`. Both must be free regions or
/// `'static`.
fn sub_free_regions(&self, shorter: ty::Region<'tcx>, longer: ty::Region<'tcx>) -> bool;
fn sub_free_regions(
&self,
tcx: TyCtxt<'tcx>,
shorter: ty::Region<'tcx>,
longer: ty::Region<'tcx>,
) -> bool;
}
impl<'tcx> FreeRegionRelations<'tcx> for FreeRegionMap<'tcx> {
fn sub_free_regions(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool {
assert!(is_free_or_static(r_a) && is_free_or_static(r_b));
if let ty::ReStatic = r_b {
true // `'a <= 'static` is just always true, and not stored in the relation explicitly
} else {
r_a == r_b || self.relation.contains(&r_a, &r_b)
}
}
}
fn is_free(r: Region<'_>) -> bool {
match *r {
ty::ReEarlyBound(_) | ty::ReFree(_) => true,
_ => false,
}
}
fn is_free_or_static(r: Region<'_>) -> bool {
match *r {
ty::ReStatic => true,
_ => is_free(r),
fn sub_free_regions(&self, tcx: TyCtxt<'tcx>, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool {
// invoke the "inherent method"
self.sub_free_regions(tcx, r_a, r_b)
}
}

View File

@ -1382,7 +1382,7 @@ impl<F: fmt::Write> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx, F> {
ty::ReVar(_) | ty::ReScope(_) | ty::ReErased => false,
ty::ReStatic | ty::ReEmpty | ty::ReClosureBound(_) => true,
ty::ReStatic | ty::ReEmpty(_) | ty::ReClosureBound(_) => true,
}
}
}
@ -1464,10 +1464,14 @@ impl<F: fmt::Write> FmtPrinter<'_, '_, F> {
p!(write("'static"));
return Ok(self);
}
ty::ReEmpty => {
ty::ReEmpty(ty::UniverseIndex::ROOT) => {
p!(write("'<empty>"));
return Ok(self);
}
ty::ReEmpty(ui) => {
p!(write("'<empty:{:?}>", ui));
return Ok(self);
}
// The user should never encounter these in unsubstituted form.
ty::ReClosureBound(vid) => {

View File

@ -108,7 +108,7 @@ impl fmt::Debug for ty::RegionKind {
ty::RePlaceholder(placeholder) => write!(f, "RePlaceholder({:?})", placeholder),
ty::ReEmpty => write!(f, "ReEmpty"),
ty::ReEmpty(ui) => write!(f, "ReEmpty({:?})", ui),
ty::ReErased => write!(f, "ReErased"),
}

View File

@ -1292,11 +1292,67 @@ rustc_index::newtype_index! {
pub type Region<'tcx> = &'tcx RegionKind;
/// Representation of regions.
/// Representation of (lexical) regions. Note that the NLL checker
/// uses a distinct representation of regions. For this reason, it
/// internally replaces all the regions with inference variables --
/// the index of the variable is then used to index into internal NLL
/// data structures. See `rustc_mir::borrow_check` module for more
/// information.
///
/// Unlike types, most region variants are "fictitious", not concrete,
/// regions. Among these, `ReStatic`, `ReEmpty` and `ReScope` are the only
/// ones representing concrete regions.
/// ## The Region lattice within a given function
///
/// In general, the (lexical, and hence deprecated) region lattice
/// looks like
///
/// ```
/// static ----------+-----...------+ (greatest)
/// | | |
/// early-bound and | |
/// free regions | |
/// | | |
/// scope regions | |
/// | | |
/// empty(root) placeholder(U1) |
/// | / |
/// | / placeholder(Un)
/// empty(U1) -- /
/// | /
/// ... /
/// | /
/// empty(Un) -------- (smallest)
/// ```
///
/// Early-bound/free regions are the named lifetimes in scope from the
/// function declaration. They have relationships to one another
/// determined based on the declared relationships from the
/// function. They all collectively outlive the scope regions. (See
/// `RegionRelations` type, and particularly
/// `crate::infer::outlives::free_region_map::FreeRegionMap`.)
///
/// The scope regions are related to one another based on the AST
/// structure. (See `RegionRelations` type, and particularly the
/// `rustc::middle::region::ScopeTree`.)
///
/// Note that inference variables and bound regions are not included
/// in this diagram. In the case of inference variables, they should
/// be inferred to some other region from the diagram. In the case of
/// bound regions, they are excluded because they don't make sense to
/// include -- the diagram indicates the relationship between free
/// regions.
///
/// ## Inference variables
///
/// During region inference, we sometimes create inference variables,
/// represented as `ReVar`. These will be inferred by the code in
/// `infer::lexical_region_resolve` to some free region from the
/// lattice above (the minimal region that meets the
/// constraints).
///
/// During NLL checking, where regions are defined differently, we
/// also use `ReVar` -- in that case, the index is used to index into
/// the NLL region checker's data structures. The variable may in fact
/// represent either a free region or an inference variable, in that
/// case.
///
/// ## Bound Regions
///
@ -1379,14 +1435,13 @@ pub enum RegionKind {
/// Should not exist after typeck.
RePlaceholder(ty::PlaceholderRegion),
/// Empty lifetime is for data that is never accessed.
/// Bottom in the region lattice. We treat ReEmpty somewhat
/// specially; at least right now, we do not generate instances of
/// it during the GLB computations, but rather
/// generate an error instead. This is to improve error messages.
/// The only way to get an instance of ReEmpty is to have a region
/// variable with no constraints.
ReEmpty,
/// Empty lifetime is for data that is never accessed. We tag the
/// empty lifetime with a universe -- the idea is that we don't
/// want `exists<'a> { forall<'b> { 'b: 'a } }` to be satisfiable.
/// Therefore, the `'empty` in a universe `U` is less than all
/// regions visible from `U`, but not less than regions not visible
/// from `U`.
ReEmpty(ty::UniverseIndex),
/// Erased region, used by trait selection, in MIR and during codegen.
ReErased,
@ -1635,7 +1690,7 @@ impl RegionKind {
RegionKind::ReStatic => true,
RegionKind::ReVar(..) => false,
RegionKind::RePlaceholder(placeholder) => placeholder.name.is_named(),
RegionKind::ReEmpty => false,
RegionKind::ReEmpty(_) => false,
RegionKind::ReErased => false,
RegionKind::ReClosureBound(..) => false,
}
@ -1718,7 +1773,7 @@ impl RegionKind {
flags = flags | TypeFlags::HAS_FREE_REGIONS;
flags = flags | TypeFlags::HAS_RE_EARLY_BOUND;
}
ty::ReEmpty | ty::ReStatic | ty::ReFree { .. } | ty::ReScope { .. } => {
ty::ReEmpty(_) | ty::ReStatic | ty::ReFree { .. } | ty::ReScope { .. } => {
flags = flags | TypeFlags::HAS_FREE_REGIONS;
}
ty::ReErased => {}
@ -1728,7 +1783,7 @@ impl RegionKind {
}
match *self {
ty::ReStatic | ty::ReEmpty | ty::ReErased | ty::ReLateBound(..) => (),
ty::ReStatic | ty::ReEmpty(_) | ty::ReErased | ty::ReLateBound(..) => (),
_ => flags = flags | TypeFlags::HAS_FREE_LOCAL_NAMES,
}

View File

@ -574,6 +574,14 @@ impl<I: Idx, T> IndexVec<I, T> {
IndexVec { raw: vec![elem; n], _marker: PhantomData }
}
/// Create an `IndexVec` with `n` elements, where the value of each
/// element is the result of `func(i)`
#[inline]
pub fn from_fn_n(func: impl FnMut(I) -> T, n: usize) -> Self {
let indices = (0..n).map(I::new);
Self::from_raw(indices.map(func).collect())
}
#[inline]
pub fn push(&mut self, d: T) -> I {
let idx = I::new(self.len());

View File

@ -291,7 +291,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
| ty::ReScope(..)
| ty::ReVar(..)
| ty::RePlaceholder(..)
| ty::ReEmpty
| ty::ReEmpty(_)
| ty::ReErased
| ty::ReClosureBound(..) => None,
}

View File

@ -1108,6 +1108,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
self.eval_if_eq(tcx, body, generic_ty, lower_bound, test_ty, verify_bound1)
}
VerifyBound::IsEmpty => {
let lower_bound_scc = self.constraint_sccs.scc(lower_bound);
self.scc_values.elements_contained_in(lower_bound_scc).next().is_none()
}
VerifyBound::OutlivedBy(r) => {
let r_vid = self.to_region_vid(r);
self.eval_outlives(r_vid, lower_bound)

View File

@ -160,7 +160,8 @@ impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<'
a: ty::Region<'tcx>,
b: ty::Region<'tcx>,
) {
if let ty::ReEmpty = a {
// FIXME -- this is not the fix I would prefer
if let ty::ReEmpty(ty::UniverseIndex::ROOT) = a {
return;
}
let b = self.to_region_vid(b);
@ -175,7 +176,8 @@ impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<'
a: ty::Region<'tcx>,
bound: VerifyBound<'tcx>,
) {
if let ty::ReEmpty = a {
// FIXME: I'd prefer if NLL had a notion of empty
if let ty::ReEmpty(ty::UniverseIndex::ROOT) = a {
return;
}
let type_test = self.verify_to_type_test(kind, a, bound);

View File

@ -5,7 +5,7 @@ use rustc::mir::ConstraintCategory;
use rustc::traits::query::outlives_bounds::{self, OutlivesBound};
use rustc::traits::query::type_op::{self, TypeOp};
use rustc::ty::free_region_map::FreeRegionRelations;
use rustc::ty::{self, RegionVid, Ty};
use rustc::ty::{self, RegionVid, Ty, TyCtxt};
use rustc_data_structures::transitive_relation::TransitiveRelation;
use rustc_span::DUMMY_SP;
use std::rc::Rc;
@ -333,7 +333,7 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> {
// `where Type:` is lowered to `where Type: 'empty` so that
// we check `Type` is well formed, but there's no use for
// this bound here.
if let ty::ReEmpty = r1 {
if let ty::ReEmpty(_) = r1 {
return;
}
@ -359,7 +359,12 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> {
/// over the `FreeRegionMap` from lexical regions and
/// `UniversalRegions` (from NLL)`.
impl<'tcx> FreeRegionRelations<'tcx> for UniversalRegionRelations<'tcx> {
fn sub_free_regions(&self, shorter: ty::Region<'tcx>, longer: ty::Region<'tcx>) -> bool {
fn sub_free_regions(
&self,
_tcx: TyCtxt<'tcx>,
shorter: ty::Region<'tcx>,
longer: ty::Region<'tcx>,
) -> bool {
let shorter = shorter.to_region_vid();
assert!(self.universal_regions.is_universal_region(shorter));
let longer = longer.to_region_vid();

View File

@ -260,6 +260,16 @@ declare_lint! {
};
}
declare_lint! {
pub COHERENCE_LEAK_CHECK,
Warn,
"distinct impls distinguished only by the leak-check code",
@future_incompatible = FutureIncompatibleInfo {
reference: "issue #56105 <https://github.com/rust-lang/rust/issues/56105>",
edition: None,
};
}
declare_lint! {
pub DEPRECATED,
Warn,
@ -515,6 +525,7 @@ declare_lint_pass! {
MISSING_FRAGMENT_SPECIFIER,
LATE_BOUND_LIFETIME_ARGUMENTS,
ORDER_DEPENDENT_TRAIT_OBJECTS,
COHERENCE_LEAK_CHECK,
DEPRECATED,
UNUSED_UNSAFE,
UNUSED_MUT,

View File

@ -246,9 +246,11 @@ impl TypeRelation<'tcx> for AnswerSubstitutor<'cx, 'tcx> {
assert_eq!(a_bound.assert_bound_var(), b_bound.assert_bound_var());
}
(ty::ReStatic, ty::ReStatic)
| (ty::ReErased, ty::ReErased)
| (ty::ReEmpty, ty::ReEmpty) => (),
(ty::ReStatic, ty::ReStatic) | (ty::ReErased, ty::ReErased) => (),
(ty::ReEmpty(a_ui), ty::ReEmpty(b_ui)) => {
assert_eq!(a_ui, b_ui);
}
(&ty::ReFree(a_free), &ty::ReFree(b_free)) => {
assert_eq!(a_free, b_free);

View File

@ -1,5 +1,5 @@
use crate::namespace::Namespace;
use rustc::traits::{self, IntercrateMode};
use rustc::traits::{self, IntercrateMode, SkipLeakCheck};
use rustc::ty::TyCtxt;
use rustc_errors::struct_span_err;
use rustc_hir as hir;
@ -76,6 +76,9 @@ impl InherentOverlapChecker<'tcx> {
impl1_def_id,
impl2_def_id,
IntercrateMode::Issue43355,
// We go ahead and just skip the leak check for
// inherent impls without warning.
SkipLeakCheck::Yes,
|overlap| {
self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap);
false

View File

@ -2324,7 +2324,8 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
// compiler/tooling bugs from not handling WF predicates.
} else {
let span = bound_pred.bounded_ty.span;
let predicate = ty::OutlivesPredicate(ty, tcx.mk_region(ty::ReEmpty));
let re_root_empty = tcx.lifetimes.re_root_empty;
let predicate = ty::OutlivesPredicate(ty, re_root_empty);
predicates.push((
ty::Predicate::TypeOutlives(ty::Binder::dummy(predicate)),
span,

View File

@ -166,7 +166,7 @@ fn is_free_region(tcx: TyCtxt<'_>, region: Region<'_>) -> bool {
//
// struct Bar<T>(<Self as Foo>::Type) where Self: ;
// struct Baz<'a>(&'a Self) where Self: ;
RegionKind::ReEmpty => false,
RegionKind::ReEmpty(_) => false,
// These regions don't appear in types from type declarations:
RegionKind::ReErased

View File

@ -453,7 +453,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
| ty::ReScope(..)
| ty::ReVar(..)
| ty::RePlaceholder(..)
| ty::ReEmpty
| ty::ReEmpty(_)
| ty::ReErased => {
// We don't expect to see anything but 'static or bound
// regions when visiting member types or method types.

View File

@ -447,7 +447,7 @@ impl Clean<Option<Lifetime>> for ty::RegionKind {
| ty::ReScope(..)
| ty::ReVar(..)
| ty::RePlaceholder(..)
| ty::ReEmpty
| ty::ReEmpty(_)
| ty::ReClosureBound(_)
| ty::ReErased => {
debug!("cannot clean region {:?}", self);
@ -521,7 +521,7 @@ impl<'tcx> Clean<Option<WherePredicate>>
let ty::OutlivesPredicate(ref a, ref b) = *self;
match (a, b) {
(ty::ReEmpty, ty::ReEmpty) => {
(ty::ReEmpty(_), ty::ReEmpty(_)) => {
return None;
}
_ => {}
@ -539,7 +539,7 @@ impl<'tcx> Clean<Option<WherePredicate>> for ty::OutlivesPredicate<Ty<'tcx>, ty:
let ty::OutlivesPredicate(ref ty, ref lt) = *self;
match lt {
ty::ReEmpty => return None,
ty::ReEmpty(_) => return None,
_ => {}
}

View File

@ -0,0 +1,14 @@
error[E0592]: duplicate definitions with name `method1`
--> $DIR/coherence-inherited-subtyping.rs:14:5
|
LL | fn method1(&self) {}
| ^^^^^^^^^^^^^^^^^^^^ duplicate definitions for `method1`
...
LL | fn method1(&self) {}
| -------------------- other definition for `method1`
|
= note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details
error: aborting due to previous error
For more information about this error, try `rustc --explain E0592`.

View File

@ -0,0 +1,14 @@
error[E0592]: duplicate definitions with name `method1`
--> $DIR/coherence-inherited-subtyping.rs:14:5
|
LL | fn method1(&self) {}
| ^^^^^^^^^^^^^^^^^^^^ duplicate definitions for `method1`
...
LL | fn method1(&self) {}
| -------------------- other definition for `method1`
|
= note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details
error: aborting due to previous error
For more information about this error, try `rustc --explain E0592`.

View File

@ -0,0 +1,21 @@
// Test that two distinct impls which match subtypes of one another
// yield coherence errors (or not) depending on the variance.
//
// Note: This scenario is currently accepted, but as part of the
// universe transition (#56105) may eventually become an error.
// revisions: old re
struct Foo<T> {
t: T,
}
impl Foo<for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8> {
fn method1(&self) {} //~ ERROR duplicate definitions with name `method1`
}
impl Foo<for<'a> fn(&'a u8, &'a u8) -> &'a u8> {
fn method1(&self) {}
}
fn main() {}

View File

@ -0,0 +1,14 @@
warning: conflicting implementations of trait `TheTrait` for type `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`:
--> $DIR/coherence-subtyping.rs:16:1
|
LL | impl TheTrait for for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8 {}
| ---------------------------------------------------------- first implementation here
LL |
LL | impl TheTrait for for<'a> fn(&'a u8, &'a u8) -> &'a u8 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`
|
= note: `#[warn(coherence_leak_check)]` on by default
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #56105 <https://github.com/rust-lang/rust/issues/56105>
= note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details

View File

@ -0,0 +1,14 @@
warning: conflicting implementations of trait `TheTrait` for type `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`:
--> $DIR/coherence-subtyping.rs:16:1
|
LL | impl TheTrait for for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8 {}
| ---------------------------------------------------------- first implementation here
LL |
LL | impl TheTrait for for<'a> fn(&'a u8, &'a u8) -> &'a u8 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`
|
= note: `#[warn(coherence_leak_check)]` on by default
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #56105 <https://github.com/rust-lang/rust/issues/56105>
= note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details

View File

@ -5,16 +5,19 @@
// universe transition (#56105) may eventually become an error.
// revisions: old re
// build-pass (FIXME(62277): could be check-pass?)
// check-pass
trait TheTrait {
fn foo(&self) { }
fn foo(&self) {}
}
impl TheTrait for for<'a,'b> fn(&'a u8, &'b u8) -> &'a u8 {
}
impl TheTrait for for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8 {}
impl TheTrait for for<'a> fn(&'a u8, &'a u8) -> &'a u8 {
//[re]~^ WARNING conflicting implementation
//[re]~^^ WARNING this was previously accepted by the compiler but is being phased out
//[old]~^^^ WARNING conflicting implementation
//[old]~^^^^ WARNING this was previously accepted by the compiler but is being phased out
}
fn main() { }
fn main() {}

View File

@ -36,7 +36,7 @@ where
T: Anything<'b, 'c>,
{
with_signature(cell, t, |cell, t| require(cell, t));
//~^ ERROR associated type `<T as Anything<'_#5r, '_#6r>>::AssocType` may not live long enough
//~^ ERROR may not live long enough
}
#[rustc_regions]
@ -46,7 +46,7 @@ where
'a: 'a,
{
with_signature(cell, t, |cell, t| require(cell, t));
//~^ ERROR associated type `<T as Anything<'_#6r, '_#7r>>::AssocType` may not live long enough
//~^ ERROR may not live long enough
}
#[rustc_regions]