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:
commit
8498c5f5b0
|
@ -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);
|
||||
|
|
|
@ -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(..) => {
|
||||
|
|
|
@ -577,7 +577,7 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
|
|||
|
||||
ty::RePlaceholder(..)
|
||||
| ty::ReVar(..)
|
||||
| ty::ReEmpty
|
||||
| ty::ReEmpty(_)
|
||||
| ty::ReStatic
|
||||
| ty::ReScope(..)
|
||||
| ty::ReEarlyBound(..)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)?;
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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={:?})",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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()),
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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."
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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"),
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -291,7 +291,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
|||
| ty::ReScope(..)
|
||||
| ty::ReVar(..)
|
||||
| ty::RePlaceholder(..)
|
||||
| ty::ReEmpty
|
||||
| ty::ReEmpty(_)
|
||||
| ty::ReErased
|
||||
| ty::ReClosureBound(..) => None,
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
|
|
@ -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`.
|
|
@ -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`.
|
|
@ -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() {}
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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() {}
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Reference in New Issue