1
Fork 0

Auto merge of #54453 - nikomatsakis:nll-issue-53121-shred-outlives, r=pnkfelix

rework how we handle outlives relationships

When we encounter an outlives relationship involving a projection, we use to over-constrain in some cases with region constraints. We also used to evaluate whether the where-clauses in the environment might apply **before** running inference.

We now avoid doing both of those things:

- If there are where-clauses in the environment that might be useful, we add no constraints.
- After inference is done, we check if we wound up inferring values compatible with the where-clause, and make use of them if so.

I realize now that this PR includes some meandering commits and refactorings from when I expected to go in a different direction. If desired, I could try to remove some of those.

Fixes #53121
Fixes #53789

r? @pnkfelix
This commit is contained in:
bors 2018-09-26 18:16:55 +00:00
commit c7df1f530b
44 changed files with 2574 additions and 1604 deletions

View file

@ -55,23 +55,22 @@
//! ported to this system, and which relies on string concatenation at the
//! time of error detection.
use infer;
use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs};
use super::region_constraints::GenericKind;
use super::lexical_region_resolve::RegionResolutionError;
use super::region_constraints::GenericKind;
use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs};
use infer::{self, SuppressRegionErrors};
use std::{cmp, fmt};
use errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
use hir;
use hir::Node;
use hir::def_id::DefId;
use hir::Node;
use middle::region;
use traits::{ObligationCause, ObligationCauseCode};
use ty::{self, subst::Subst, Region, Ty, TyCtxt, TypeFoldable, TyKind};
use ty::error::TypeError;
use session::config::BorrowckMode;
use std::{cmp, fmt};
use syntax::ast::DUMMY_NODE_ID;
use syntax_pos::{Pos, Span};
use errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
use traits::{ObligationCause, ObligationCauseCode};
use ty::error::TypeError;
use ty::{self, subst::Subst, Region, Ty, TyCtxt, TyKind, TypeFoldable};
mod note;
@ -153,8 +152,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
}
// We shouldn't encounter an error message with ReClosureBound.
ty::ReCanonical(..) |
ty::ReClosureBound(..) => {
ty::ReCanonical(..) | ty::ReClosureBound(..) => {
bug!("encountered unexpected ReClosureBound: {:?}", region,);
}
};
@ -176,9 +174,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
fn msg_span_from_free_region(self, region: ty::Region<'tcx>) -> (String, Option<Span>) {
match *region {
ty::ReEarlyBound(_) | ty::ReFree(_) => {
ty::ReEarlyBound(_) | ty::ReFree(_) => {
self.msg_span_from_early_bound_and_free_regions(region)
},
}
ty::ReStatic => ("the static lifetime".to_owned(), None),
_ => bug!("{:?}", region),
}
@ -197,25 +195,28 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
Some(Node::Item(it)) => Self::item_scope_tag(&it),
Some(Node::TraitItem(it)) => Self::trait_item_scope_tag(&it),
Some(Node::ImplItem(it)) => Self::impl_item_scope_tag(&it),
_ => unreachable!()
_ => unreachable!(),
};
let (prefix, span) = match *region {
ty::ReEarlyBound(ref br) => {
let mut sp = cm.def_span(self.hir.span(node));
if let Some(param) = self.hir.get_generics(scope).and_then(|generics| {
generics.get_named(&br.name)
}) {
if let Some(param) = self.hir
.get_generics(scope)
.and_then(|generics| generics.get_named(&br.name))
{
sp = param.span;
}
(format!("the lifetime {} as defined on", br.name), sp)
}
ty::ReFree(ty::FreeRegion {
bound_region: ty::BoundRegion::BrNamed(_, ref name), ..
bound_region: ty::BoundRegion::BrNamed(_, ref name),
..
}) => {
let mut sp = cm.def_span(self.hir.span(node));
if let Some(param) = self.hir.get_generics(scope).and_then(|generics| {
generics.get_named(&name)
}) {
if let Some(param) = self.hir
.get_generics(scope)
.and_then(|generics| generics.get_named(&name))
{
sp = param.span;
}
(format!("the lifetime {} as defined on", name), sp)
@ -278,9 +279,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
fn impl_item_scope_tag(item: &hir::ImplItem) -> &'static str {
match item.node {
hir::ImplItemKind::Method(..) => "method body",
hir::ImplItemKind::Const(..) |
hir::ImplItemKind::Existential(..) |
hir::ImplItemKind::Type(..) => "associated item",
hir::ImplItemKind::Const(..)
| hir::ImplItemKind::Existential(..)
| hir::ImplItemKind::Type(..) => "associated item",
}
}
@ -298,20 +299,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
&self,
region_scope_tree: &region::ScopeTree,
errors: &Vec<RegionResolutionError<'tcx>>,
will_later_be_reported_by_nll: bool,
suppress: SuppressRegionErrors,
) {
debug!("report_region_errors(): {} errors to start", errors.len());
debug!(
"report_region_errors(): {} errors to start, suppress = {:?}",
errors.len(),
suppress
);
// If the errors will later be reported by NLL, choose wether to display them or not based
// on the borrowck mode
if will_later_be_reported_by_nll {
match self.tcx.borrowck_mode() {
// If we're on AST or Migrate mode, report AST region errors
BorrowckMode::Ast | BorrowckMode::Migrate => {},
// If we're on MIR or Compare mode, don't report AST region errors as they should
// be reported by NLL
BorrowckMode::Compare | BorrowckMode::Mir => return,
}
if suppress.suppressed() {
return;
}
// try to pre-process the errors, which will group some of them
@ -482,17 +479,18 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
} else {
err.span_label(arm_span, msg);
}
},
hir::MatchSource::TryDesugar => { // Issue #51632
}
hir::MatchSource::TryDesugar => {
// Issue #51632
if let Ok(try_snippet) = self.tcx.sess.source_map().span_to_snippet(arm_span) {
err.span_suggestion_with_applicability(
arm_span,
"try wrapping with a success variant",
format!("Ok({})", try_snippet),
Applicability::MachineApplicable
Applicability::MachineApplicable,
);
}
},
}
_ => {
let msg = "match arm with an incompatible type";
if self.tcx.sess.source_map().is_multiline(arm_span) {
@ -641,16 +639,21 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
fn strip_generic_default_params(
&self,
def_id: DefId,
substs: &ty::subst::Substs<'tcx>
substs: &ty::subst::Substs<'tcx>,
) -> &'tcx ty::subst::Substs<'tcx> {
let generics = self.tcx.generics_of(def_id);
let mut num_supplied_defaults = 0;
let mut type_params = generics.params.iter().rev().filter_map(|param| match param.kind {
ty::GenericParamDefKind::Lifetime => None,
ty::GenericParamDefKind::Type { has_default, .. } => {
Some((param.def_id, has_default))
}
}).peekable();
let mut type_params = generics
.params
.iter()
.rev()
.filter_map(|param| match param.kind {
ty::GenericParamDefKind::Lifetime => None,
ty::GenericParamDefKind::Type { has_default, .. } => {
Some((param.def_id, has_default))
}
})
.peekable();
let has_default = {
let has_default = type_params.peek().map(|(_, has_default)| has_default);
*has_default.unwrap_or(&false)
@ -684,10 +687,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
| (&ty::Infer(ty::InferTy::IntVar(_)), &ty::Infer(ty::InferTy::IntVar(_)))
| (&ty::Float(_), &ty::Infer(ty::InferTy::FloatVar(_)))
| (&ty::Infer(ty::InferTy::FloatVar(_)), &ty::Float(_))
| (
&ty::Infer(ty::InferTy::FloatVar(_)),
&ty::Infer(ty::InferTy::FloatVar(_)),
) => true,
| (&ty::Infer(ty::InferTy::FloatVar(_)), &ty::Infer(ty::InferTy::FloatVar(_))) => {
true
}
_ => false,
}
}
@ -703,11 +705,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
"&{}{}{}",
r,
if r == "" { "" } else { " " },
if mutbl == hir::MutMutable {
"mut "
} else {
""
}
if mutbl == hir::MutMutable { "mut " } else { "" }
));
s.push_normal(ty.to_string());
}
@ -738,9 +736,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
let common_len = cmp::min(len1, len2);
let remainder1: Vec<_> = sub1.types().skip(common_len).collect();
let remainder2: Vec<_> = sub2.types().skip(common_len).collect();
let common_default_params =
remainder1.iter().rev().zip(remainder2.iter().rev())
.filter(|(a, b)| a == b).count();
let common_default_params = remainder1
.iter()
.rev()
.zip(remainder2.iter().rev())
.filter(|(a, b)| a == b)
.count();
let len = sub1.len() - common_default_params;
// Only draw `<...>` if there're lifetime/type arguments.
@ -866,8 +867,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}
// When encountering &T != &mut T, highlight only the borrow
(&ty::Ref(r1, ref_ty1, mutbl1),
&ty::Ref(r2, ref_ty2, mutbl2)) if equals(&ref_ty1, &ref_ty2) => {
(&ty::Ref(r1, ref_ty1, mutbl1), &ty::Ref(r2, ref_ty2, mutbl2))
if equals(&ref_ty1, &ref_ty2) =>
{
let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new());
push_ty_ref(&r1, ref_ty1, mutbl1, &mut values.0);
push_ty_ref(&r2, ref_ty2, mutbl2, &mut values.1);
@ -1068,11 +1070,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
bound_kind: GenericKind<'tcx>,
sub: Region<'tcx>,
) {
self.construct_generic_bound_failure(region_scope_tree,
span,
origin,
bound_kind,
sub)
self.construct_generic_bound_failure(region_scope_tree, span, origin, bound_kind, sub)
.emit()
}
@ -1083,8 +1081,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
origin: Option<SubregionOrigin<'tcx>>,
bound_kind: GenericKind<'tcx>,
sub: Region<'tcx>,
) -> DiagnosticBuilder<'a>
{
) -> DiagnosticBuilder<'a> {
// Attempt to obtain the span of the parameter so we can
// suggest adding an explicit lifetime bound to it.
let type_param_span = match (self.in_progress_tables, bound_kind) {
@ -1161,8 +1158,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
let tail = if has_lifetimes { " + " } else { "" };
let suggestion = format!("{}: {}{}", bound_kind, sub, tail);
err.span_suggestion_short_with_applicability(
sp, consider, suggestion,
Applicability::MaybeIncorrect // Issue #41966
sp,
consider,
suggestion,
Applicability::MaybeIncorrect, // Issue #41966
);
} else {
err.help(consider);
@ -1358,12 +1357,10 @@ impl<'tcx> ObligationCause<'tcx> {
match self.code {
CompareImplMethodObligation { .. } => Error0308("method not compatible with trait"),
MatchExpressionArm { source, .. } => Error0308(match source {
hir::MatchSource::IfLetDesugar { .. } => {
"`if let` arms have incompatible types"
},
hir::MatchSource::IfLetDesugar { .. } => "`if let` arms have incompatible types",
hir::MatchSource::TryDesugar => {
"try expression alternatives have incompatible types"
},
}
_ => "match arms have incompatible types",
}),
IfExpression => Error0308("if and else have incompatible types"),

View file

@ -10,23 +10,26 @@
//! The code to do lexical region resolution.
use infer::SubregionOrigin;
use infer::RegionVariableOrigin;
use infer::region_constraints::Constraint;
use infer::region_constraints::GenericKind;
use infer::region_constraints::RegionConstraintData;
use infer::region_constraints::VarInfos;
use infer::region_constraints::VerifyBound;
use infer::RegionVariableOrigin;
use infer::SubregionOrigin;
use middle::free_region::RegionRelations;
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::graph::implementation::{Graph, Direction, NodeIndex, INCOMING, OUTGOING};
use rustc_data_structures::graph::implementation::{
Direction, Graph, NodeIndex, INCOMING, OUTGOING,
};
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use std::fmt;
use std::u32;
use ty::{self, TyCtxt};
use ty::{Region, RegionVid};
use ty::fold::TypeFoldable;
use ty::{self, Ty, TyCtxt};
use ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic};
use ty::{ReLateBound, ReScope, ReSkolemized, ReVar};
use ty::{Region, RegionVid};
mod graphviz;
@ -108,11 +111,15 @@ struct LexicalResolver<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
}
impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
fn tcx(&self) -> TyCtxt<'cx, 'gcx, 'tcx> {
self.region_rels.tcx
}
fn infer_variable_values(
&mut self,
errors: &mut Vec<RegionResolutionError<'tcx>>,
) -> LexicalRegionResolutions<'tcx> {
let mut var_data = self.construct_var_data(self.region_rels.tcx);
let mut var_data = self.construct_var_data(self.tcx());
// Dorky hack to cause `dump_constraints` to only get called
// if debug mode is enabled:
@ -239,9 +246,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
debug!(
"Expanding value of {:?} from {:?} to {:?}",
b_vid,
cur_region,
lub
b_vid, cur_region, lub
);
*b_data = VarValue::Value(lub);
@ -254,18 +259,17 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
}
}
fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> {
let tcx = self.region_rels.tcx;
let tcx = self.tcx();
match (a, b) {
(&ty::ReCanonical(..), _) |
(_, &ty::ReCanonical(..)) |
(&ty::ReClosureBound(..), _) |
(_, &ty::ReClosureBound(..)) |
(&ReLateBound(..), _) |
(_, &ReLateBound(..)) |
(&ReErased, _) |
(_, &ReErased) => {
(&ty::ReCanonical(..), _)
| (_, &ty::ReCanonical(..))
| (&ty::ReClosureBound(..), _)
| (_, &ty::ReClosureBound(..))
| (&ReLateBound(..), _)
| (_, &ReLateBound(..))
| (&ReErased, _)
| (_, &ReErased) => {
bug!("cannot relate region: LUB({:?}, {:?})", a, b);
}
@ -287,20 +291,20 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
);
}
(&ReEarlyBound(_), &ReScope(s_id)) |
(&ReScope(s_id), &ReEarlyBound(_)) |
(&ReFree(_), &ReScope(s_id)) |
(&ReScope(s_id), &ReFree(_)) => {
(&ReEarlyBound(_), &ReScope(s_id))
| (&ReScope(s_id), &ReEarlyBound(_))
| (&ReFree(_), &ReScope(s_id))
| (&ReScope(s_id), &ReFree(_)) => {
// A "free" region can be interpreted as "some region
// at least as big as fr.scope". So, we can
// reasonably compare free regions and scopes:
let fr_scope = match (a, b) {
(&ReEarlyBound(ref br), _) | (_, &ReEarlyBound(ref br)) => self.region_rels
.region_scope_tree
.early_free_scope(self.region_rels.tcx, br),
.early_free_scope(self.tcx(), br),
(&ReFree(ref fr), _) | (_, &ReFree(ref fr)) => self.region_rels
.region_scope_tree
.free_scope(self.region_rels.tcx, fr),
.free_scope(self.tcx(), fr),
_ => bug!(),
};
let r_id = self.region_rels
@ -332,10 +336,10 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
tcx.mk_region(ReScope(lub))
}
(&ReEarlyBound(_), &ReEarlyBound(_)) |
(&ReFree(_), &ReEarlyBound(_)) |
(&ReEarlyBound(_), &ReFree(_)) |
(&ReFree(_), &ReFree(_)) => self.region_rels.lub_free_regions(a, b),
(&ReEarlyBound(_), &ReEarlyBound(_))
| (&ReFree(_), &ReEarlyBound(_))
| (&ReEarlyBound(_), &ReFree(_))
| (&ReFree(_), &ReFree(_)) => self.region_rels.lub_free_regions(a, b),
// For these types, we cannot define any additional
// relationship:
@ -358,8 +362,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
for (constraint, origin) in &self.data.constraints {
debug!(
"collect_errors: constraint={:?} origin={:?}",
constraint,
origin
constraint, origin
);
match *constraint {
Constraint::RegSubVar(..) | Constraint::VarSubVar(..) => {
@ -374,9 +377,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
debug!(
"collect_errors: region error at {:?}: \
cannot verify that {:?} <= {:?}",
origin,
sub,
sup
origin, sub, sup
);
errors.push(RegionResolutionError::ConcreteFailure(
@ -402,10 +403,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
debug!(
"collect_errors: region error at {:?}: \
cannot verify that {:?}={:?} <= {:?}",
origin,
a_vid,
a_region,
b_region
origin, a_vid, a_region, b_region
);
*a_data = VarValue::ErrorValue;
}
@ -415,7 +413,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
for verify in &self.data.verifys {
debug!("collect_errors: verify={:?}", verify);
let sub = var_data.normalize(verify.region);
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.
@ -423,16 +421,15 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
continue;
}
if self.bound_is_met(&verify.bound, var_data, sub) {
let verify_kind_ty = verify.kind.to_ty(self.tcx());
if self.bound_is_met(&verify.bound, var_data, verify_kind_ty, sub) {
continue;
}
debug!(
"collect_errors: region error at {:?}: \
cannot verify that {:?} <= {:?}",
verify.origin,
verify.region,
verify.bound
verify.origin, verify.region, verify.bound
);
errors.push(RegionResolutionError::GenericBoundFailure(
@ -580,10 +577,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
debug!(
"region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \
sup: {:?}",
origin,
node_idx,
lower_bound.region,
upper_bound.region
origin, node_idx, lower_bound.region, upper_bound.region
);
errors.push(RegionResolutionError::SubSupConflict(
origin,
@ -645,8 +639,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
debug!(
"collect_concrete_regions(orig_node_idx={:?}, node_idx={:?})",
orig_node_idx,
node_idx
orig_node_idx, node_idx
);
process_edges(&self.data, &mut state, graph, node_idx, dir);
@ -721,20 +714,26 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
&self,
bound: &VerifyBound<'tcx>,
var_values: &LexicalRegionResolutions<'tcx>,
generic_ty: Ty<'tcx>,
min: ty::Region<'tcx>,
) -> bool {
match bound {
VerifyBound::AnyRegion(rs) => rs.iter()
.map(|&r| var_values.normalize(r))
.any(|r| self.region_rels.is_subregion_of(min, r)),
VerifyBound::IfEq(k, b) => {
(var_values.normalize(self.region_rels.tcx, *k) == generic_ty)
&& self.bound_is_met(b, var_values, generic_ty, min)
}
VerifyBound::AllRegions(rs) => rs.iter()
.map(|&r| var_values.normalize(r))
.all(|r| self.region_rels.is_subregion_of(min, r)),
VerifyBound::OutlivedBy(r) =>
self.region_rels.is_subregion_of(
min,
var_values.normalize(self.tcx(), r),
),
VerifyBound::AnyBound(bs) => bs.iter().any(|b| self.bound_is_met(b, var_values, min)),
VerifyBound::AnyBound(bs) => bs.iter()
.any(|b| self.bound_is_met(b, var_values, generic_ty, min)),
VerifyBound::AllBounds(bs) => bs.iter().all(|b| self.bound_is_met(b, var_values, min)),
VerifyBound::AllBounds(bs) => bs.iter()
.all(|b| self.bound_is_met(b, var_values, generic_ty, min)),
}
}
}
@ -745,13 +744,15 @@ impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> {
}
}
impl<'tcx> LexicalRegionResolutions<'tcx> {
fn normalize(&self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
match *r {
ty::ReVar(rid) => self.resolve_var(rid),
fn normalize<T>(&self, tcx: TyCtxt<'_, '_, 'tcx>, value: T) -> T
where
T: TypeFoldable<'tcx>,
{
tcx.fold_regions(&value, &mut false, |r, _db| match r {
ty::ReVar(rid) => self.resolve_var(*rid),
_ => r,
}
})
}
fn value(&self, rid: RegionVid) -> &VarValue<'tcx> {

View file

@ -24,6 +24,7 @@ use middle::free_region::RegionRelations;
use middle::lang_items;
use middle::region;
use rustc_data_structures::unify as ut;
use session::config::BorrowckMode;
use std::cell::{Cell, Ref, RefCell, RefMut};
use std::collections::BTreeMap;
use std::fmt;
@ -80,6 +81,38 @@ pub type Bound<T> = Option<T>;
pub type UnitResult<'tcx> = RelateResult<'tcx, ()>; // "unify result"
pub type FixupResult<T> = Result<T, FixupError>; // "fixup result"
/// A flag that is used to suppress region errors. This is normally
/// false, but sometimes -- when we are doing region checks that the
/// NLL borrow checker will also do -- it might be set to true.
#[derive(Copy, Clone, Default, Debug)]
pub struct SuppressRegionErrors {
suppressed: bool
}
impl SuppressRegionErrors {
pub fn suppressed(self) -> bool {
self.suppressed
}
/// Indicates that the MIR borrowck will repeat these region
/// checks, so we should ignore errors if NLL is (unconditionally)
/// enabled.
pub fn when_nll_is_enabled(tcx: TyCtxt<'_, '_, '_>) -> Self {
match tcx.borrowck_mode() {
// If we're on AST or Migrate mode, report AST region errors
BorrowckMode::Ast | BorrowckMode::Migrate => SuppressRegionErrors {
suppressed: false
},
// If we're on MIR or Compare mode, don't report AST region errors as they should
// be reported by NLL
BorrowckMode::Compare | BorrowckMode::Mir => SuppressRegionErrors {
suppressed: true
},
}
}
}
pub struct InferCtxt<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
pub tcx: TyCtxt<'a, 'gcx, 'tcx>,
@ -408,7 +441,7 @@ pub enum FixupError {
pub struct RegionObligation<'tcx> {
pub sub_region: ty::Region<'tcx>,
pub sup_type: Ty<'tcx>,
pub cause: ObligationCause<'tcx>,
pub origin: SubregionOrigin<'tcx>,
}
impl fmt::Display for FixupError {
@ -1039,34 +1072,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
region_context: DefId,
region_map: &region::ScopeTree,
outlives_env: &OutlivesEnvironment<'tcx>,
) {
self.resolve_regions_and_report_errors_inner(
region_context,
region_map,
outlives_env,
false,
)
}
/// Like `resolve_regions_and_report_errors`, but skips error
/// reporting if NLL is enabled. This is used for fn bodies where
/// the same error may later be reported by the NLL-based
/// inference.
pub fn resolve_regions_and_report_errors_unless_nll(
&self,
region_context: DefId,
region_map: &region::ScopeTree,
outlives_env: &OutlivesEnvironment<'tcx>,
) {
self.resolve_regions_and_report_errors_inner(region_context, region_map, outlives_env, true)
}
fn resolve_regions_and_report_errors_inner(
&self,
region_context: DefId,
region_map: &region::ScopeTree,
outlives_env: &OutlivesEnvironment<'tcx>,
will_later_be_reported_by_nll: bool,
suppress: SuppressRegionErrors,
) {
assert!(
self.is_tainted_by_errors() || self.region_obligations.borrow().is_empty(),
@ -1098,7 +1104,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
// this infcx was in use. This is totally hokey but
// otherwise we have a hard time separating legit region
// errors from silly ones.
self.report_region_errors(region_map, &errors, will_later_be_reported_by_nll);
self.report_region_errors(region_map, &errors, suppress);
}
}

View file

@ -8,13 +8,13 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use infer::{GenericKind, InferCtxt};
use infer::outlives::free_region_map::FreeRegionMap;
use traits::query::outlives_bounds::{self, OutlivesBound};
use ty::{self, Ty};
use infer::{GenericKind, InferCtxt};
use rustc_data_structures::fx::FxHashMap;
use syntax::ast;
use syntax_pos::Span;
use traits::query::outlives_bounds::{self, OutlivesBound};
use ty::{self, Ty};
/// The `OutlivesEnvironment` collects information about what outlives
/// what in a given type-checking setting. For example, if we have a
@ -39,15 +39,51 @@ use syntax_pos::Span;
pub struct OutlivesEnvironment<'tcx> {
param_env: ty::ParamEnv<'tcx>,
free_region_map: FreeRegionMap<'tcx>,
region_bound_pairs: Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>,
// Contains, for each body B that we are checking (that is, the fn
// item, but also any nested closures), the set of implied region
// bounds that are in scope in that particular body.
//
// Example:
//
// ```
// fn foo<'a, 'b, T>(x: &'a T, y: &'b ()) {
// bar(x, y, |y: &'b T| { .. } // body B1)
// } // body B0
// ```
//
// Here, for body B0, the list would be `[T: 'a]`, because we
// infer that `T` must outlive `'a` from the implied bounds on the
// fn declaration.
//
// For the body B1, the list would be `[T: 'a, T: 'b]`, because we
// also can see that -- within the closure body! -- `T` must
// outlive `'b`. This is not necessarily true outside the closure
// body, since the closure may never be called.
//
// We collect this map as we descend the tree. We then use the
// results when proving outlives obligations like `T: 'x` later
// (e.g., if `T: 'x` must be proven within the body B1, then we
// know it is true if either `'a: 'x` or `'b: 'x`).
region_bound_pairs_map: FxHashMap<ast::NodeId, RegionBoundPairs<'tcx>>,
// Used to compute `region_bound_pairs_map`: contains the set of
// in-scope region-bound pairs thus far.
region_bound_pairs_accum: RegionBoundPairs<'tcx>,
}
/// "Region-bound pairs" tracks outlives relations that are known to
/// be true, either because of explicit where clauses like `T: 'a` or
/// because of implied bounds.
pub type RegionBoundPairs<'tcx> = Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>;
impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> {
pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self {
let mut env = OutlivesEnvironment {
param_env,
free_region_map: FreeRegionMap::new(),
region_bound_pairs: vec![],
region_bound_pairs_map: FxHashMap::default(),
region_bound_pairs_accum: vec![],
};
env.add_outlives_bounds(None, outlives_bounds::explicit_outlives_bounds(param_env));
@ -61,8 +97,8 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> {
}
/// Borrows current value of the `region_bound_pairs`.
pub fn region_bound_pairs(&self) -> &[(ty::Region<'tcx>, GenericKind<'tcx>)] {
&self.region_bound_pairs
pub fn region_bound_pairs_map(&self) -> &FxHashMap<ast::NodeId, RegionBoundPairs<'tcx>> {
&self.region_bound_pairs_map
}
/// Returns ownership of the `free_region_map`.
@ -108,12 +144,12 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> {
/// similar leaks around givens that seem equally suspicious, to
/// be honest. --nmatsakis
pub fn push_snapshot_pre_closure(&self) -> usize {
self.region_bound_pairs.len()
self.region_bound_pairs_accum.len()
}
/// See `push_snapshot_pre_closure`.
pub fn pop_snapshot_post_closure(&mut self, len: usize) {
self.region_bound_pairs.truncate(len);
self.region_bound_pairs_accum.truncate(len);
}
/// This method adds "implied bounds" into the outlives environment.
@ -149,6 +185,15 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> {
}
}
/// Save the current set of region-bound pairs under the given `body_id`.
pub fn save_implied_bounds(&mut self, body_id: ast::NodeId) {
let old = self.region_bound_pairs_map.insert(
body_id,
self.region_bound_pairs_accum.clone(),
);
assert!(old.is_none());
}
/// Processes outlives bounds that are known to hold, whether from implied or other sources.
///
/// The `infcx` parameter is optional; if the implied bounds may
@ -167,16 +212,18 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> {
for outlives_bound in outlives_bounds {
debug!("add_outlives_bounds: outlives_bound={:?}", outlives_bound);
match outlives_bound {
OutlivesBound::RegionSubRegion(r_a @ &ty::ReEarlyBound(_), &ty::ReVar(vid_b)) |
OutlivesBound::RegionSubRegion(r_a @ &ty::ReFree(_), &ty::ReVar(vid_b)) => {
infcx.expect("no infcx provided but region vars found").add_given(r_a, vid_b);
OutlivesBound::RegionSubRegion(r_a @ &ty::ReEarlyBound(_), &ty::ReVar(vid_b))
| OutlivesBound::RegionSubRegion(r_a @ &ty::ReFree(_), &ty::ReVar(vid_b)) => {
infcx
.expect("no infcx provided but region vars found")
.add_given(r_a, vid_b);
}
OutlivesBound::RegionSubParam(r_a, param_b) => {
self.region_bound_pairs
self.region_bound_pairs_accum
.push((r_a, GenericKind::Param(param_b)));
}
OutlivesBound::RegionSubProjection(r_a, projection_b) => {
self.region_bound_pairs
self.region_bound_pairs_accum
.push((r_a, GenericKind::Projection(projection_b)));
}
OutlivesBound::RegionSubRegion(r_a, r_b) => {

View file

@ -13,3 +13,4 @@
pub mod env;
pub mod free_region_map;
pub mod obligations;
pub mod verify;

View file

@ -69,13 +69,14 @@
//! might later infer `?U` to something like `&'b u32`, which would
//! imply that `'b: 'a`.
use hir::def_id::DefId;
use infer::outlives::env::RegionBoundPairs;
use infer::outlives::verify::VerifyBoundCx;
use infer::{self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, VerifyBound};
use rustc_data_structures::fx::FxHashMap;
use syntax::ast;
use traits;
use traits::ObligationCause;
use ty::outlives::Component;
use ty::subst::{Subst, Substs};
use ty::{self, Ty, TyCtxt, TypeFoldable};
use ty::{self, Region, Ty, TyCtxt, TypeFoldable};
impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
/// Registers that the given region obligation must be resolved
@ -98,6 +99,26 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
.push((body_id, obligation));
}
pub fn register_region_obligation_with_cause(
&self,
sup_type: Ty<'tcx>,
sub_region: Region<'tcx>,
cause: &ObligationCause<'tcx>,
) {
let origin = SubregionOrigin::from_obligation_cause(cause, || {
infer::RelateParamBound(cause.span, sup_type)
});
self.register_region_obligation(
cause.body_id,
RegionObligation {
sup_type,
sub_region,
origin,
},
);
}
/// Trait queries just want to pass back type obligations "as is"
pub fn take_registered_region_obligations(&self) -> Vec<(ast::NodeId, RegionObligation<'tcx>)> {
::std::mem::replace(&mut *self.region_obligations.borrow_mut(), vec![])
@ -138,10 +159,9 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
/// processed.
pub fn process_registered_region_obligations(
&self,
region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)],
region_bound_pairs_map: &FxHashMap<ast::NodeId, RegionBoundPairs<'tcx>>,
implicit_region_bound: Option<ty::Region<'tcx>>,
param_env: ty::ParamEnv<'tcx>,
body_id: ast::NodeId,
) {
assert!(
!self.in_snapshot.get(),
@ -150,41 +170,39 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
debug!("process_registered_region_obligations()");
// pull out the region obligations with the given `body_id` (leaving the rest)
let mut my_region_obligations = Vec::with_capacity(self.region_obligations.borrow().len());
{
let mut r_o = self.region_obligations.borrow_mut();
my_region_obligations.extend(
r_o.drain_filter(|(ro_body_id, _)| *ro_body_id == body_id)
.map(|(_, obligation)| obligation)
);
}
let my_region_obligations = self.take_registered_region_obligations();
let outlives = &mut TypeOutlives::new(
self,
self.tcx,
region_bound_pairs,
implicit_region_bound,
param_env,
);
for RegionObligation {
sup_type,
sub_region,
cause,
} in my_region_obligations
for (
body_id,
RegionObligation {
sup_type,
sub_region,
origin,
},
) in my_region_obligations
{
debug!(
"process_registered_region_obligations: sup_type={:?} sub_region={:?} cause={:?}",
sup_type, sub_region, cause
"process_registered_region_obligations: sup_type={:?} sub_region={:?} origin={:?}",
sup_type, sub_region, origin
);
let origin = SubregionOrigin::from_obligation_cause(&cause, || {
infer::RelateParamBound(cause.span, sup_type)
});
let sup_type = self.resolve_type_vars_if_possible(&sup_type);
outlives.type_must_outlive(origin, sup_type, sub_region);
if let Some(region_bound_pairs) = region_bound_pairs_map.get(&body_id) {
let outlives = &mut TypeOutlives::new(
self,
self.tcx,
&region_bound_pairs,
implicit_region_bound,
param_env,
);
outlives.type_must_outlive(origin, sup_type, sub_region);
} else {
self.tcx.sess.delay_span_bug(
origin.span(),
&format!("no region-bound-pairs for {:?}", body_id),
)
}
}
}
@ -192,7 +210,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
/// registered in advance.
pub fn type_must_outlive(
&self,
region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)],
region_bound_pairs: &RegionBoundPairs<'tcx>,
implicit_region_bound: Option<ty::Region<'tcx>>,
param_env: ty::ParamEnv<'tcx>,
origin: infer::SubregionOrigin<'tcx>,
@ -225,9 +243,7 @@ where
// of these fields.
delegate: D,
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)],
implicit_region_bound: Option<ty::Region<'tcx>>,
param_env: ty::ParamEnv<'tcx>,
verify_bound: VerifyBoundCx<'cx, 'gcx, 'tcx>,
}
pub trait TypeOutlivesDelegate<'tcx> {
@ -254,16 +270,19 @@ where
pub fn new(
delegate: D,
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)],
region_bound_pairs: &'cx RegionBoundPairs<'tcx>,
implicit_region_bound: Option<ty::Region<'tcx>>,
param_env: ty::ParamEnv<'tcx>,
) -> Self {
Self {
delegate,
tcx,
region_bound_pairs,
implicit_region_bound,
param_env,
verify_bound: VerifyBoundCx::new(
tcx,
region_bound_pairs,
implicit_region_bound,
param_env,
),
}
}
@ -302,7 +321,8 @@ where
let origin = origin.clone();
match component {
Component::Region(region1) => {
self.delegate.push_sub_region_constraint(origin, region, region1);
self.delegate
.push_sub_region_constraint(origin, region, region1);
}
Component::Param(param_ty) => {
self.param_ty_must_outlive(origin, region, param_ty);
@ -337,8 +357,8 @@ where
region, param_ty, origin
);
let verify_bound = self.param_bound(param_ty);
let generic = GenericKind::Param(param_ty);
let verify_bound = self.verify_bound.generic_bound(generic);
self.delegate
.push_verify(origin, generic, region, verify_bound);
}
@ -368,19 +388,22 @@ where
// rule might not apply (but another rule might). For now, we err
// on the side of adding too few edges into the graph.
// Compute the bounds we can derive from the environment or trait
// definition. We know that the projection outlives all the
// regions in this list.
let env_bounds = self.projection_declared_bounds(projection_ty);
// Compute the bounds we can derive from the environment. This
// is an "approximate" match -- in some cases, these bounds
// may not apply.
let approx_env_bounds = self.verify_bound
.projection_approx_declared_bounds_from_env(projection_ty);
debug!(
"projection_must_outlive: approx_env_bounds={:?}",
approx_env_bounds
);
debug!("projection_must_outlive: env_bounds={:?}", env_bounds);
// If we know that the projection outlives 'static, then we're
// done here.
if env_bounds.contains(&&ty::ReStatic) {
debug!("projection_must_outlive: 'static as declared bound");
return;
}
// Compute the bounds we can derive from the trait definition.
// These are guaranteed to apply, no matter the inference
// results.
let trait_bounds: Vec<_> = self.verify_bound
.projection_declared_bounds_from_trait(projection_ty)
.collect();
// If declared bounds list is empty, the only applicable rule is
// OutlivesProjectionComponent. If there are inference variables,
@ -397,7 +420,7 @@ where
// inference variables, we use a verify constraint instead of adding
// edges, which winds up enforcing the same condition.
let needs_infer = projection_ty.needs_infer();
if env_bounds.is_empty() && needs_infer {
if approx_env_bounds.is_empty() && trait_bounds.is_empty() && needs_infer {
debug!("projection_must_outlive: no declared bounds");
for component_ty in projection_ty.substs.types() {
@ -405,36 +428,38 @@ where
}
for r in projection_ty.substs.regions() {
self.delegate.push_sub_region_constraint(origin.clone(), region, r);
self.delegate
.push_sub_region_constraint(origin.clone(), region, r);
}
return;
}
// If we find that there is a unique declared bound `'b`, and this bound
// appears in the trait reference, then the best action is to require that `'b:'r`,
// so do that. This is best no matter what rule we use:
// If we found a unique bound `'b` from the trait, and we
// found nothing else from the environment, then the best
// action is to require that `'b: 'r`, so do that.
//
// - OutlivesProjectionEnv or OutlivesProjectionTraitDef: these would translate to
// the requirement that `'b:'r`
// - OutlivesProjectionComponent: this would require `'b:'r` in addition to
// other conditions
if !env_bounds.is_empty() && env_bounds[1..].iter().all(|b| *b == env_bounds[0]) {
let unique_bound = env_bounds[0];
// This is best no matter what rule we use:
//
// - OutlivesProjectionEnv: these would translate to the requirement that `'b:'r`
// - OutlivesProjectionTraitDef: these would translate to the requirement that `'b:'r`
// - OutlivesProjectionComponent: this would require `'b:'r`
// in addition to other conditions
if !trait_bounds.is_empty()
&& trait_bounds[1..]
.iter()
.chain(approx_env_bounds.iter().map(|b| &b.1))
.all(|b| *b == trait_bounds[0])
{
let unique_bound = trait_bounds[0];
debug!(
"projection_must_outlive: unique declared bound = {:?}",
"projection_must_outlive: unique trait bound = {:?}",
unique_bound
);
if projection_ty
.substs
.regions()
.any(|r| env_bounds.contains(&r))
{
debug!("projection_must_outlive: unique declared bound appears in trait ref");
self.delegate
.push_sub_region_constraint(origin.clone(), region, unique_bound);
return;
}
debug!("projection_must_outlive: unique declared bound appears in trait ref");
self.delegate
.push_sub_region_constraint(origin.clone(), region, unique_bound);
return;
}
// Fallback to verifying after the fact that there exists a
@ -442,216 +467,11 @@ where
// projection outlive; in some cases, this may add insufficient
// edges into the inference graph, leading to inference failures
// even though a satisfactory solution exists.
let verify_bound = self.projection_bound(env_bounds, projection_ty);
let generic = GenericKind::Projection(projection_ty);
let verify_bound = self.verify_bound.generic_bound(generic);
self.delegate
.push_verify(origin, generic.clone(), region, verify_bound);
}
fn type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> {
match ty.sty {
ty::Param(p) => self.param_bound(p),
ty::Projection(data) => {
let declared_bounds = self.projection_declared_bounds(data);
self.projection_bound(declared_bounds, data)
}
_ => self.recursive_type_bound(ty),
}
}
fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> {
debug!("param_bound(param_ty={:?})", param_ty);
let mut param_bounds = self.declared_generic_bounds_from_env(GenericKind::Param(param_ty));
// Add in the default bound of fn body that applies to all in
// scope type parameters:
param_bounds.extend(self.implicit_region_bound);
VerifyBound::AnyRegion(param_bounds)
}
fn projection_declared_bounds(
&self,
projection_ty: ty::ProjectionTy<'tcx>,
) -> Vec<ty::Region<'tcx>> {
// First assemble bounds from where clauses and traits.
let mut declared_bounds =
self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty));
declared_bounds
.extend_from_slice(&self.declared_projection_bounds_from_trait(projection_ty));
declared_bounds
}
fn projection_bound(
&self,
declared_bounds: Vec<ty::Region<'tcx>>,
projection_ty: ty::ProjectionTy<'tcx>,
) -> VerifyBound<'tcx> {
debug!(
"projection_bound(declared_bounds={:?}, projection_ty={:?})",
declared_bounds, projection_ty
);
// see the extensive comment in projection_must_outlive
let ty = self
.tcx
.mk_projection(projection_ty.item_def_id, projection_ty.substs);
let recursive_bound = self.recursive_type_bound(ty);
VerifyBound::AnyRegion(declared_bounds).or(recursive_bound)
}
fn recursive_type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> {
let mut bounds = ty.walk_shallow().map(|subty| self.type_bound(subty)).collect::<Vec<_>>();
let mut regions = ty.regions();
regions.retain(|r| !r.is_late_bound()); // ignore late-bound regions
bounds.push(VerifyBound::AllRegions(regions));
// remove bounds that must hold, since they are not interesting
bounds.retain(|b| !b.must_hold());
if bounds.len() == 1 {
bounds.pop().unwrap()
} else {
VerifyBound::AllBounds(bounds)
}
}
fn declared_generic_bounds_from_env(
&self,
generic: GenericKind<'tcx>,
) -> Vec<ty::Region<'tcx>> {
let tcx = self.tcx;
// To start, collect bounds from user environment. Note that
// parameter environments are already elaborated, so we don't
// have to worry about that. Comparing using `==` is a bit
// dubious for projections, but it will work for simple cases
// like `T` and `T::Item`. It may not work as well for things
// like `<T as Foo<'a>>::Item`.
let generic_ty = generic.to_ty(tcx);
let c_b = self.param_env.caller_bounds;
let mut param_bounds = self.collect_outlives_from_predicate_list(generic_ty, c_b);
// Next, collect regions we scraped from the well-formedness
// constraints in the fn signature. To do that, we walk the list
// of known relations from the fn ctxt.
//
// This is crucial because otherwise code like this fails:
//
// fn foo<'a, A>(x: &'a A) { x.bar() }
//
// The problem is that the type of `x` is `&'a A`. To be
// well-formed, then, A must be lower-generic by `'a`, but we
// don't know that this holds from first principles.
for &(r, p) in self.region_bound_pairs {
debug!("generic={:?} p={:?}", generic, p);
if generic == p {
param_bounds.push(r);
}
}
param_bounds
}
/// Given a projection like `<T as Foo<'x>>::Bar`, returns any bounds
/// declared in the trait definition. For example, if the trait were
///
/// ```rust
/// trait Foo<'a> {
/// type Bar: 'a;
/// }
/// ```
///
/// then this function would return `'x`. This is subject to the
/// limitations around higher-ranked bounds described in
/// `region_bounds_declared_on_associated_item`.
fn declared_projection_bounds_from_trait(
&self,
projection_ty: ty::ProjectionTy<'tcx>,
) -> Vec<ty::Region<'tcx>> {
debug!("projection_bounds(projection_ty={:?})", projection_ty);
let mut bounds = self.region_bounds_declared_on_associated_item(projection_ty.item_def_id);
for r in &mut bounds {
*r = r.subst(self.tcx, projection_ty.substs);
}
bounds
}
/// Given the def-id of an associated item, returns any region
/// bounds attached to that associated item from the trait definition.
///
/// For example:
///
/// ```rust
/// trait Foo<'a> {
/// type Bar: 'a;
/// }
/// ```
///
/// If we were given the def-id of `Foo::Bar`, we would return
/// `'a`. You could then apply the substitutions from the
/// projection to convert this into your namespace. This also
/// works if the user writes `where <Self as Foo<'a>>::Bar: 'a` on
/// the trait. In fact, it works by searching for just such a
/// where-clause.
///
/// It will not, however, work for higher-ranked bounds like:
///
/// ```rust
/// trait Foo<'a, 'b>
/// where for<'x> <Self as Foo<'x, 'b>>::Bar: 'x
/// {
/// type Bar;
/// }
/// ```
///
/// This is for simplicity, and because we are not really smart
/// enough to cope with such bounds anywhere.
fn region_bounds_declared_on_associated_item(
&self,
assoc_item_def_id: DefId,
) -> Vec<ty::Region<'tcx>> {
let tcx = self.tcx;
let assoc_item = tcx.associated_item(assoc_item_def_id);
let trait_def_id = assoc_item.container.assert_trait();
let trait_predicates = tcx.predicates_of(trait_def_id);
let identity_substs = Substs::identity_for_item(tcx, assoc_item_def_id);
let identity_proj = tcx.mk_projection(assoc_item_def_id, identity_substs);
self.collect_outlives_from_predicate_list(
identity_proj,
traits::elaborate_predicates(tcx, trait_predicates.predicates),
)
}
/// Searches through a predicate list for a predicate `T: 'a`.
///
/// Careful: does not elaborate predicates, and just uses `==`
/// when comparing `ty` for equality, so `ty` must be something
/// that does not involve inference variables and where you
/// otherwise want a precise match.
fn collect_outlives_from_predicate_list<I, P>(
&self,
ty: Ty<'tcx>,
predicates: I,
) -> Vec<ty::Region<'tcx>>
where
I: IntoIterator<Item = P>,
P: AsRef<ty::Predicate<'tcx>>,
{
predicates
.into_iter()
.filter_map(|p| p.as_ref().to_opt_type_outlives())
.filter_map(|p| p.no_late_bound_regions())
.filter(|p| p.0 == ty)
.map(|p| p.1)
.collect()
}
}
impl<'cx, 'gcx, 'tcx> TypeOutlivesDelegate<'tcx> for &'cx InferCtxt<'cx, 'gcx, 'tcx> {
@ -674,4 +494,3 @@ impl<'cx, 'gcx, 'tcx> TypeOutlivesDelegate<'tcx> for &'cx InferCtxt<'cx, 'gcx, '
self.verify_generic_bound(origin, kind, a, bound)
}
}

View file

@ -0,0 +1,326 @@
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use hir::def_id::DefId;
use infer::outlives::env::RegionBoundPairs;
use infer::{GenericKind, VerifyBound};
use traits;
use ty::subst::{Subst, Substs};
use ty::{self, Ty, TyCtxt};
use util::captures::Captures;
/// The `TypeOutlives` struct has the job of "lowering" a `T: 'a`
/// obligation into a series of `'a: 'b` constraints and "verifys", as
/// described on the module comment. The final constraints are emitted
/// via a "delegate" of type `D` -- this is usually the `infcx`, which
/// accrues them into the `region_obligations` code, but for NLL we
/// use something else.
pub struct VerifyBoundCx<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
region_bound_pairs: &'cx RegionBoundPairs<'tcx>,
implicit_region_bound: Option<ty::Region<'tcx>>,
param_env: ty::ParamEnv<'tcx>,
}
impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> {
pub fn new(
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
region_bound_pairs: &'cx RegionBoundPairs<'tcx>,
implicit_region_bound: Option<ty::Region<'tcx>>,
param_env: ty::ParamEnv<'tcx>,
) -> Self {
Self {
tcx,
region_bound_pairs,
implicit_region_bound,
param_env,
}
}
/// Returns a "verify bound" that encodes what we know about
/// `generic` and the regions it outlives.
pub fn generic_bound(&self, generic: GenericKind<'tcx>) -> VerifyBound<'tcx> {
match generic {
GenericKind::Param(param_ty) => self.param_bound(param_ty),
GenericKind::Projection(projection_ty) => self.projection_bound(projection_ty),
}
}
fn type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> {
match ty.sty {
ty::Param(p) => self.param_bound(p),
ty::Projection(data) => self.projection_bound(data),
_ => self.recursive_type_bound(ty),
}
}
fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> {
debug!("param_bound(param_ty={:?})", param_ty);
// Start with anything like `T: 'a` we can scrape from the
// environment
let param_bounds = self.declared_generic_bounds_from_env(GenericKind::Param(param_ty))
.into_iter()
.map(|outlives| outlives.1);
// Add in the default bound of fn body that applies to all in
// scope type parameters:
let param_bounds = param_bounds.chain(self.implicit_region_bound);
VerifyBound::AnyBound(param_bounds.map(|r| VerifyBound::OutlivedBy(r)).collect())
}
/// Given a projection like `T::Item`, searches the environment
/// for where-clauses like `T::Item: 'a`. Returns the set of
/// regions `'a` that it finds.
///
/// This is an "approximate" check -- it may not find all
/// applicable bounds, and not all the bounds it returns can be
/// relied upon. In particular, this check ignores region
/// identity. So, for example, if we have `<T as
/// Trait<'0>>::Item` where `'0` is a region variable, and the
/// user has `<T as Trait<'a>>::Item: 'b` in the environment, then
/// the clause from the environment only applies if `'0 = 'a`,
/// which we don't know yet. But we would still include `'b` in
/// this list.
pub fn projection_approx_declared_bounds_from_env(
&self,
projection_ty: ty::ProjectionTy<'tcx>,
) -> Vec<ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
let projection_ty = GenericKind::Projection(projection_ty).to_ty(self.tcx);
let erased_projection_ty = self.tcx.erase_regions(&projection_ty);
self.declared_generic_bounds_from_env_with_compare_fn(|ty| {
if let ty::Projection(..) = ty.sty {
let erased_ty = self.tcx.erase_regions(&ty);
erased_ty == erased_projection_ty
} else {
false
}
})
}
/// Searches the where clauses in scope for regions that
/// `projection_ty` is known to outlive. Currently requires an
/// exact match.
pub fn projection_declared_bounds_from_trait(
&self,
projection_ty: ty::ProjectionTy<'tcx>,
) -> impl Iterator<Item = ty::Region<'tcx>> + 'cx + Captures<'gcx> {
self.declared_projection_bounds_from_trait(projection_ty)
}
pub fn projection_bound(&self, projection_ty: ty::ProjectionTy<'tcx>) -> VerifyBound<'tcx> {
debug!("projection_bound(projection_ty={:?})", projection_ty);
let projection_ty_as_ty =
self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs);
// Search the env for where clauses like `P: 'a`.
let env_bounds = self.projection_approx_declared_bounds_from_env(projection_ty)
.into_iter()
.map(|ty::OutlivesPredicate(ty, r)| {
let vb = VerifyBound::OutlivedBy(r);
if ty == projection_ty_as_ty {
// Micro-optimize if this is an exact match (this
// occurs often when there are no region variables
// involved).
vb
} else {
VerifyBound::IfEq(ty, Box::new(vb))
}
});
// Extend with bounds that we can find from the trait.
let trait_bounds = self.projection_declared_bounds_from_trait(projection_ty)
.into_iter()
.map(|r| VerifyBound::OutlivedBy(r));
// see the extensive comment in projection_must_outlive
let ty = self.tcx
.mk_projection(projection_ty.item_def_id, projection_ty.substs);
let recursive_bound = self.recursive_type_bound(ty);
VerifyBound::AnyBound(env_bounds.chain(trait_bounds).collect()).or(recursive_bound)
}
fn recursive_type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> {
let mut bounds = ty.walk_shallow()
.map(|subty| self.type_bound(subty))
.collect::<Vec<_>>();
let mut regions = ty.regions();
regions.retain(|r| !r.is_late_bound()); // ignore late-bound regions
bounds.push(VerifyBound::AllBounds(
regions
.into_iter()
.map(|r| VerifyBound::OutlivedBy(r))
.collect(),
));
// remove bounds that must hold, since they are not interesting
bounds.retain(|b| !b.must_hold());
if bounds.len() == 1 {
bounds.pop().unwrap()
} else {
VerifyBound::AllBounds(bounds)
}
}
/// Searches the environment for where-clauses like `G: 'a` where
/// `G` is either some type parameter `T` or a projection like
/// `T::Item`. Returns a vector of the `'a` bounds it can find.
///
/// This is a conservative check -- it may not find all applicable
/// bounds, but all the bounds it returns can be relied upon.
fn declared_generic_bounds_from_env(
&self,
generic: GenericKind<'tcx>,
) -> Vec<ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
let generic_ty = generic.to_ty(self.tcx);
self.declared_generic_bounds_from_env_with_compare_fn(|ty| ty == generic_ty)
}
fn declared_generic_bounds_from_env_with_compare_fn(
&self,
compare_ty: impl Fn(Ty<'tcx>) -> bool,
) -> Vec<ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
let tcx = self.tcx;
// To start, collect bounds from user environment. Note that
// parameter environments are already elaborated, so we don't
// have to worry about that. Comparing using `==` is a bit
// dubious for projections, but it will work for simple cases
// like `T` and `T::Item`. It may not work as well for things
// like `<T as Foo<'a>>::Item`.
let c_b = self.param_env.caller_bounds;
let param_bounds = self.collect_outlives_from_predicate_list(&compare_ty, c_b);
// Next, collect regions we scraped from the well-formedness
// constraints in the fn signature. To do that, we walk the list
// of known relations from the fn ctxt.
//
// This is crucial because otherwise code like this fails:
//
// fn foo<'a, A>(x: &'a A) { x.bar() }
//
// The problem is that the type of `x` is `&'a A`. To be
// well-formed, then, A must be lower-generic by `'a`, but we
// don't know that this holds from first principles.
let from_region_bound_pairs = self.region_bound_pairs.iter().filter_map(|&(r, p)| {
debug!(
"declared_generic_bounds_from_env_with_compare_fn: region_bound_pair = {:?}",
(r, p)
);
let p_ty = p.to_ty(tcx);
if compare_ty(p_ty) {
Some(ty::OutlivesPredicate(p_ty, r))
} else {
None
}
});
param_bounds
.chain(from_region_bound_pairs)
.inspect(|bound| {
debug!(
"declared_generic_bounds_from_env_with_compare_fn: result predicate = {:?}",
bound
)
})
.collect()
}
/// Given a projection like `<T as Foo<'x>>::Bar`, returns any bounds
/// declared in the trait definition. For example, if the trait were
///
/// ```rust
/// trait Foo<'a> {
/// type Bar: 'a;
/// }
/// ```
///
/// then this function would return `'x`. This is subject to the
/// limitations around higher-ranked bounds described in
/// `region_bounds_declared_on_associated_item`.
fn declared_projection_bounds_from_trait(
&self,
projection_ty: ty::ProjectionTy<'tcx>,
) -> impl Iterator<Item = ty::Region<'tcx>> + 'cx + Captures<'gcx> {
debug!("projection_bounds(projection_ty={:?})", projection_ty);
let tcx = self.tcx;
self.region_bounds_declared_on_associated_item(projection_ty.item_def_id)
.map(move |r| r.subst(tcx, projection_ty.substs))
}
/// Given the def-id of an associated item, returns any region
/// bounds attached to that associated item from the trait definition.
///
/// For example:
///
/// ```rust
/// trait Foo<'a> {
/// type Bar: 'a;
/// }
/// ```
///
/// If we were given the def-id of `Foo::Bar`, we would return
/// `'a`. You could then apply the substitutions from the
/// projection to convert this into your namespace. This also
/// works if the user writes `where <Self as Foo<'a>>::Bar: 'a` on
/// the trait. In fact, it works by searching for just such a
/// where-clause.
///
/// It will not, however, work for higher-ranked bounds like:
///
/// ```rust
/// trait Foo<'a, 'b>
/// where for<'x> <Self as Foo<'x, 'b>>::Bar: 'x
/// {
/// type Bar;
/// }
/// ```
///
/// This is for simplicity, and because we are not really smart
/// enough to cope with such bounds anywhere.
fn region_bounds_declared_on_associated_item(
&self,
assoc_item_def_id: DefId,
) -> impl Iterator<Item = ty::Region<'tcx>> + 'cx + Captures<'gcx> {
let tcx = self.tcx;
let assoc_item = tcx.associated_item(assoc_item_def_id);
let trait_def_id = assoc_item.container.assert_trait();
let trait_predicates = tcx.predicates_of(trait_def_id);
let identity_substs = Substs::identity_for_item(tcx, assoc_item_def_id);
let identity_proj = tcx.mk_projection(assoc_item_def_id, identity_substs);
self.collect_outlives_from_predicate_list(
move |ty| ty == identity_proj,
traits::elaborate_predicates(tcx, trait_predicates.predicates),
).map(|b| b.1)
}
/// Searches through a predicate list for a predicate `T: 'a`.
///
/// Careful: does not elaborate predicates, and just uses `==`
/// when comparing `ty` for equality, so `ty` must be something
/// that does not involve inference variables and where you
/// otherwise want a precise match.
fn collect_outlives_from_predicate_list(
&self,
compare_ty: impl Fn(Ty<'tcx>) -> bool,
predicates: impl IntoIterator<Item = impl AsRef<ty::Predicate<'tcx>>>,
) -> impl Iterator<Item = ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
predicates
.into_iter()
.filter_map(|p| p.as_ref().to_opt_type_outlives())
.filter_map(|p| p.no_late_bound_regions())
.filter(move |p| compare_ty(p.0))
}
}

View file

@ -155,29 +155,94 @@ pub enum GenericKind<'tcx> {
Projection(ty::ProjectionTy<'tcx>),
}
/// When we introduce a verification step, we wish to test that a
/// particular region (let's call it `'min`) meets some bound.
/// The bound is described the by the following grammar:
EnumTypeFoldableImpl! {
impl<'tcx> TypeFoldable<'tcx> for GenericKind<'tcx> {
(GenericKind::Param)(a),
(GenericKind::Projection)(a),
}
}
/// Describes the things that some `GenericKind` value G is known to
/// outlive. Each variant of `VerifyBound` can be thought of as a
/// function:
///
/// fn(min: Region) -> bool { .. }
///
/// where `true` means that the region `min` meets that `G: min`.
/// (False means nothing.)
///
/// So, for example, if we have the type `T` and we have in scope that
/// `T: 'a` and `T: 'b`, then the verify bound might be:
///
/// fn(min: Region) -> bool {
/// ('a: min) || ('b: min)
/// }
///
/// This is described with a `AnyRegion('a, 'b)` node.
#[derive(Debug, Clone)]
pub enum VerifyBound<'tcx> {
/// B = exists {R} --> some 'r in {R} must outlive 'min
/// Given a kind K and a bound B, expands to a function like the
/// following, where `G` is the generic for which this verify
/// bound was created:
///
/// Put another way, the subject value is known to outlive all
/// regions in {R}, so if any of those outlives 'min, then the
/// bound is met.
AnyRegion(Vec<Region<'tcx>>),
/// B = forall {R} --> all 'r in {R} must outlive 'min
/// fn(min) -> bool {
/// if G == K {
/// B(min)
/// } else {
/// false
/// }
/// }
///
/// Put another way, the subject value is known to outlive some
/// region in {R}, so if all of those outlives 'min, then the bound
/// is met.
AllRegions(Vec<Region<'tcx>>),
/// In other words, if the generic `G` that we are checking is
/// equal to `K`, then check the associated verify bound
/// (otherwise, false).
///
/// This is used when we have something in the environment that
/// may or may not be relevant, depending on the region inference
/// results. For example, we may have `where <T as
/// Trait<'a>>::Item: 'b` in our where clauses. If we are
/// generating the verify-bound for `<T as Trait<'0>>::Item`, then
/// this where-clause is only relevant if `'0` winds up inferred
/// to `'a`.
///
/// So we would compile to a verify-bound like
///
/// IfEq(<T as Trait<'a>>::Item, AnyRegion('a))
///
/// meaning, if the subject G is equal to `<T as Trait<'a>>::Item`
/// (after inference), and `'a: min`, then `G: min`.
IfEq(Ty<'tcx>, Box<VerifyBound<'tcx>>),
/// B = exists {B} --> 'min must meet some bound b in {B}
/// Given a region `R`, expands to the function:
///
/// fn(min) -> bool {
/// R: min
/// }
///
/// This is used when we can establish that `G: R` -- therefore,
/// if `R: min`, then by transitivity `G: min`.
OutlivedBy(Region<'tcx>),
/// Given a set of bounds `B`, expands to the function:
///
/// fn(min) -> bool {
/// exists (b in B) { b(min) }
/// }
///
/// In other words, if we meet some bound in `B`, that suffices.
/// This is used when all the bounds in `B` are known to apply to
/// G.
AnyBound(Vec<VerifyBound<'tcx>>),
/// B = forall {B} --> 'min must meet all bounds b in {B}
/// Given a set of bounds `B`, expands to the function:
///
/// fn(min) -> bool {
/// forall (b in B) { b(min) }
/// }
///
/// In other words, if we meet *all* bounds in `B`, that suffices.
/// This is used when *some* bound in `B` is known to suffice, but
/// we don't know which.
AllBounds(Vec<VerifyBound<'tcx>>),
}
@ -882,33 +947,23 @@ impl<'a, 'gcx, 'tcx> GenericKind<'tcx> {
}
impl<'a, 'gcx, 'tcx> VerifyBound<'tcx> {
fn for_each_region(&self, f: &mut dyn FnMut(ty::Region<'tcx>)) {
match self {
&VerifyBound::AnyRegion(ref rs) | &VerifyBound::AllRegions(ref rs) => for &r in rs {
f(r);
},
&VerifyBound::AnyBound(ref bs) | &VerifyBound::AllBounds(ref bs) => for b in bs {
b.for_each_region(f);
},
}
}
pub fn must_hold(&self) -> bool {
match self {
&VerifyBound::AnyRegion(ref bs) => bs.contains(&&ty::ReStatic),
&VerifyBound::AllRegions(ref bs) => bs.is_empty(),
&VerifyBound::AnyBound(ref bs) => bs.iter().any(|b| b.must_hold()),
&VerifyBound::AllBounds(ref bs) => bs.iter().all(|b| b.must_hold()),
VerifyBound::IfEq(..) => false,
VerifyBound::OutlivedBy(ty::ReStatic) => true,
VerifyBound::OutlivedBy(_) => false,
VerifyBound::AnyBound(bs) => bs.iter().any(|b| b.must_hold()),
VerifyBound::AllBounds(bs) => bs.iter().all(|b| b.must_hold()),
}
}
pub fn cannot_hold(&self) -> bool {
match self {
&VerifyBound::AnyRegion(ref bs) => bs.is_empty(),
&VerifyBound::AllRegions(ref bs) => bs.contains(&&ty::ReEmpty),
&VerifyBound::AnyBound(ref bs) => bs.iter().all(|b| b.cannot_hold()),
&VerifyBound::AllBounds(ref bs) => bs.iter().any(|b| b.cannot_hold()),
VerifyBound::IfEq(_, b) => b.cannot_hold(),
VerifyBound::OutlivedBy(ty::ReEmpty) => true,
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

@ -13,34 +13,39 @@ use super::*;
#[derive(Debug)]
pub(super) struct TaintSet<'tcx> {
directions: TaintDirections,
regions: FxHashSet<ty::Region<'tcx>>
regions: FxHashSet<ty::Region<'tcx>>,
}
impl<'tcx> TaintSet<'tcx> {
pub(super) fn new(directions: TaintDirections,
initial_region: ty::Region<'tcx>)
-> Self {
pub(super) fn new(directions: TaintDirections, initial_region: ty::Region<'tcx>) -> Self {
let mut regions = FxHashSet();
regions.insert(initial_region);
TaintSet { directions: directions, regions: regions }
TaintSet {
directions: directions,
regions: regions,
}
}
pub(super) fn fixed_point(&mut self,
tcx: TyCtxt<'_, '_, 'tcx>,
undo_log: &[UndoLogEntry<'tcx>],
verifys: &[Verify<'tcx>]) {
pub(super) fn fixed_point(
&mut self,
tcx: TyCtxt<'_, '_, 'tcx>,
undo_log: &[UndoLogEntry<'tcx>],
verifys: &[Verify<'tcx>],
) {
let mut prev_len = 0;
while prev_len < self.len() {
debug!("tainted: prev_len = {:?} new_len = {:?}",
prev_len, self.len());
debug!(
"tainted: prev_len = {:?} new_len = {:?}",
prev_len,
self.len()
);
prev_len = self.len();
for undo_entry in undo_log {
match undo_entry {
&AddConstraint(Constraint::VarSubVar(a, b)) => {
self.add_edge(tcx.mk_region(ReVar(a)),
tcx.mk_region(ReVar(b)));
self.add_edge(tcx.mk_region(ReVar(a)), tcx.mk_region(ReVar(b)));
}
&AddConstraint(Constraint::RegSubVar(a, b)) => {
self.add_edge(a, tcx.mk_region(ReVar(b)));
@ -55,15 +60,13 @@ impl<'tcx> TaintSet<'tcx> {
self.add_edge(a, tcx.mk_region(ReVar(b)));
}
&AddVerify(i) => {
verifys[i].bound.for_each_region(&mut |b| {
self.add_edge(verifys[i].region, b);
});
span_bug!(
verifys[i].origin.span(),
"we never add verifications while doing higher-ranked things",
)
}
&Purged |
&AddCombination(..) |
&AddVar(..) |
&OpenSnapshot |
&CommitedSnapshot => {}
&Purged | &AddCombination(..) | &AddVar(..) | &OpenSnapshot
| &CommitedSnapshot => {}
}
}
}
@ -77,9 +80,7 @@ impl<'tcx> TaintSet<'tcx> {
self.regions.len()
}
fn add_edge(&mut self,
source: ty::Region<'tcx>,
target: ty::Region<'tcx>) {
fn add_edge(&mut self, source: ty::Region<'tcx>, target: ty::Region<'tcx>) {
if self.directions.incoming {
if self.regions.contains(&target) {
self.regions.insert(source);
@ -93,4 +94,3 @@ impl<'tcx> TaintSet<'tcx> {
}
}
}

View file

@ -16,10 +16,9 @@ use super::*;
use std::collections::hash_map::Entry;
use std::collections::VecDeque;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use infer::region_constraints::{Constraint, RegionConstraintData};
use infer::{InferCtxt, RegionObligation};
use infer::InferCtxt;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use ty::fold::TypeFolder;
use ty::{Region, RegionVid};
@ -227,20 +226,18 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
.iter()
.filter_map(|param| match param.kind {
ty::GenericParamDefKind::Lifetime => Some(param.name.to_string()),
_ => None
_ => None,
})
.collect();
let body_ids: FxHashSet<_> = infcx
let body_id_map: FxHashMap<_, _> = infcx
.region_obligations
.borrow()
.iter()
.map(|&(id, _)| id)
.map(|&(id, _)| (id, vec![]))
.collect();
for id in body_ids {
infcx.process_registered_region_obligations(&[], None, full_env.clone(), id);
}
infcx.process_registered_region_obligations(&body_id_map, None, full_env.clone());
let region_data = infcx
.borrow_region_constraints()
@ -359,8 +356,10 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
&Err(SelectionError::Unimplemented) => {
if self.is_of_param(pred.skip_binder().trait_ref.substs) {
already_visited.remove(&pred);
self.add_user_pred(&mut user_computed_preds,
ty::Predicate::Trait(pred.clone()));
self.add_user_pred(
&mut user_computed_preds,
ty::Predicate::Trait(pred.clone()),
);
predicates.push_back(pred);
} else {
debug!(
@ -418,8 +417,11 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
// under which a type implements an auto trait. A trait predicate involving
// a HRTB means that the type needs to work with any choice of lifetime,
// not just one specific lifetime (e.g. 'static).
fn add_user_pred<'c>(&self, user_computed_preds: &mut FxHashSet<ty::Predicate<'c>>,
new_pred: ty::Predicate<'c>) {
fn add_user_pred<'c>(
&self,
user_computed_preds: &mut FxHashSet<ty::Predicate<'c>>,
new_pred: ty::Predicate<'c>,
) {
let mut should_add_new = true;
user_computed_preds.retain(|&old_pred| {
match (&new_pred, old_pred) {
@ -431,20 +433,19 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
if !new_substs.types().eq(old_substs.types()) {
// We can't compare lifetimes if the types are different,
// so skip checking old_pred
return true
return true;
}
for (new_region, old_region) in new_substs
.regions()
.zip(old_substs.regions()) {
for (new_region, old_region) in
new_substs.regions().zip(old_substs.regions())
{
match (new_region, old_region) {
// If both predicates have an 'ReLateBound' (a HRTB) in the
// same spot, we do nothing
(
ty::RegionKind::ReLateBound(_, _),
ty::RegionKind::ReLateBound(_, _)
) => {},
ty::RegionKind::ReLateBound(_, _),
) => {}
(ty::RegionKind::ReLateBound(_, _), _) => {
// The new predicate has a HRTB in a spot where the old
@ -458,7 +459,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
// so we return 'false' to remove the old predicate from
// user_computed_preds
return false;
},
}
(_, ty::RegionKind::ReLateBound(_, _)) => {
// This is the opposite situation as the previous arm - the
// old predicate has a HRTB lifetime in a place where the
@ -471,10 +472,10 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
}
}
}
},
}
_ => {}
}
return true
return true;
});
if should_add_new {
@ -513,28 +514,20 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
match constraint {
&Constraint::VarSubVar(r1, r2) => {
{
let deps1 = vid_map
.entry(RegionTarget::RegionVid(r1))
.or_default();
let deps1 = vid_map.entry(RegionTarget::RegionVid(r1)).or_default();
deps1.larger.insert(RegionTarget::RegionVid(r2));
}
let deps2 = vid_map
.entry(RegionTarget::RegionVid(r2))
.or_default();
let deps2 = vid_map.entry(RegionTarget::RegionVid(r2)).or_default();
deps2.smaller.insert(RegionTarget::RegionVid(r1));
}
&Constraint::RegSubVar(region, vid) => {
{
let deps1 = vid_map
.entry(RegionTarget::Region(region))
.or_default();
let deps1 = vid_map.entry(RegionTarget::Region(region)).or_default();
deps1.larger.insert(RegionTarget::RegionVid(vid));
}
let deps2 = vid_map
.entry(RegionTarget::RegionVid(vid))
.or_default();
let deps2 = vid_map.entry(RegionTarget::RegionVid(vid)).or_default();
deps2.smaller.insert(RegionTarget::Region(region));
}
&Constraint::VarSubReg(vid, region) => {
@ -542,15 +535,11 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
}
&Constraint::RegSubReg(r1, r2) => {
{
let deps1 = vid_map
.entry(RegionTarget::Region(r1))
.or_default();
let deps1 = vid_map.entry(RegionTarget::Region(r1)).or_default();
deps1.larger.insert(RegionTarget::Region(r2));
}
let deps2 = vid_map
.entry(RegionTarget::Region(r2))
.or_default();
let deps2 = vid_map.entry(RegionTarget::Region(r2)).or_default();
deps2.smaller.insert(RegionTarget::Region(r1));
}
}
@ -683,7 +672,11 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
}
}
&ty::Predicate::RegionOutlives(ref binder) => {
if select.infcx().region_outlives_predicate(&dummy_cause, binder).is_err() {
if select
.infcx()
.region_outlives_predicate(&dummy_cause, binder)
.is_err()
{
return false;
}
}
@ -693,23 +686,17 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
binder.map_bound_ref(|pred| pred.0).no_late_bound_regions(),
) {
(None, Some(t_a)) => {
select.infcx().register_region_obligation(
ast::DUMMY_NODE_ID,
RegionObligation {
sup_type: t_a,
sub_region: select.infcx().tcx.types.re_static,
cause: dummy_cause.clone(),
},
select.infcx().register_region_obligation_with_cause(
t_a,
select.infcx().tcx.types.re_static,
&dummy_cause,
);
}
(Some(ty::OutlivesPredicate(t_a, r_b)), _) => {
select.infcx().register_region_obligation(
ast::DUMMY_NODE_ID,
RegionObligation {
sup_type: t_a,
sub_region: r_b,
cause: dummy_cause.clone(),
},
select.infcx().register_region_obligation_with_cause(
t_a,
r_b,
&dummy_cause,
);
}
_ => {}

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use infer::{RegionObligation, InferCtxt};
use infer::InferCtxt;
use mir::interpret::GlobalId;
use ty::{self, Ty, TypeFoldable, ToPolyTraitRef, ToPredicate};
use ty::error::ExpectedFound;
@ -372,13 +372,11 @@ impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx,
Some(t_a) => {
let r_static = self.selcx.tcx().types.re_static;
if self.register_region_obligations {
self.selcx.infcx().register_region_obligation(
obligation.cause.body_id,
RegionObligation {
sup_type: t_a,
sub_region: r_static,
cause: obligation.cause.clone(),
});
self.selcx.infcx().register_region_obligation_with_cause(
t_a,
r_static,
&obligation.cause,
);
}
ProcessResult::Changed(vec![])
}
@ -387,13 +385,11 @@ impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx,
// If there aren't, register the obligation.
Some(ty::OutlivesPredicate(t_a, r_b)) => {
if self.register_region_obligations {
self.selcx.infcx().register_region_obligation(
obligation.cause.body_id,
RegionObligation {
sup_type: t_a,
sub_region: r_b,
cause: obligation.cause.clone()
});
self.selcx.infcx().register_region_obligation_with_cause(
t_a,
r_b,
&obligation.cause,
);
}
ProcessResult::Changed(vec![])
}

View file

@ -20,6 +20,7 @@ pub use self::ObligationCauseCode::*;
use chalk_engine;
use hir;
use hir::def_id::DefId;
use infer::SuppressRegionErrors;
use infer::outlives::env::OutlivesEnvironment;
use middle::region;
use mir::interpret::ConstEvalErr;
@ -715,7 +716,12 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
// cares about declarations like `'a: 'b`.
let outlives_env = OutlivesEnvironment::new(elaborated_env);
infcx.resolve_regions_and_report_errors(region_context, &region_scope_tree, &outlives_env);
infcx.resolve_regions_and_report_errors(
region_context,
&region_scope_tree,
&outlives_env,
SuppressRegionErrors::default(),
);
let predicates = match infcx.fully_resolve(&predicates) {
Ok(predicates) => predicates,

View file

@ -535,6 +535,13 @@ impl<I: Idx, T> IndexVec<I, T> {
self.raw.len()
}
/// Gives the next index that will be assigned when `push` is
/// called.
#[inline]
pub fn next_index(&self) -> I {
I::new(self.len())
}
#[inline]
pub fn is_empty(&self) -> bool {
self.raw.is_empty()

View file

@ -14,29 +14,29 @@ use std::path::PathBuf;
use std::sync::mpsc;
use driver;
use rustc_lint;
use rustc_resolve::MakeGlobMap;
use rustc::middle::region;
use rustc::ty::subst::Subst;
use rustc::traits::ObligationCause;
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc::ty::query::OnDiskCache;
use rustc::infer::{self, InferOk, InferResult};
use rustc::infer::outlives::env::OutlivesEnvironment;
use rustc::infer::type_variable::TypeVariableOrigin;
use rustc_metadata::cstore::CStore;
use rustc::hir::map as hir_map;
use rustc::session::{self, config};
use rustc::session::config::{OutputFilenames, OutputTypes};
use rustc_data_structures::sync::{self, Lrc};
use syntax;
use syntax::ast;
use rustc_target::spec::abi::Abi;
use syntax::source_map::{SourceMap, FilePathMapping, FileName};
use errors;
use errors::emitter::Emitter;
use errors::{Level, DiagnosticBuilder};
use errors::{DiagnosticBuilder, Level};
use rustc::hir::map as hir_map;
use rustc::infer::outlives::env::OutlivesEnvironment;
use rustc::infer::type_variable::TypeVariableOrigin;
use rustc::infer::{self, InferOk, InferResult, SuppressRegionErrors};
use rustc::middle::region;
use rustc::session::config::{OutputFilenames, OutputTypes};
use rustc::session::{self, config};
use rustc::traits::ObligationCause;
use rustc::ty::query::OnDiskCache;
use rustc::ty::subst::Subst;
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc_data_structures::sync::{self, Lrc};
use rustc_lint;
use rustc_metadata::cstore::CStore;
use rustc_resolve::MakeGlobMap;
use rustc_target::spec::abi::Abi;
use syntax;
use syntax::ast;
use syntax::feature_gate::UnstableFeatures;
use syntax::source_map::{FileName, FilePathMapping, SourceMap};
use syntax::symbol::Symbol;
use syntax_pos::DUMMY_SP;
@ -90,13 +90,15 @@ impl Emitter for ExpectErrorEmitter {
fn errors(msgs: &[&str]) -> (Box<dyn Emitter + sync::Send>, usize) {
let v = msgs.iter().map(|m| m.to_string()).collect();
(box ExpectErrorEmitter { messages: v } as Box<dyn Emitter + sync::Send>, msgs.len())
(
box ExpectErrorEmitter { messages: v } as Box<dyn Emitter + sync::Send>,
msgs.len(),
)
}
fn test_env<F>(source_string: &str,
args: (Box<dyn Emitter + sync::Send>, usize),
body: F)
where F: FnOnce(Env)
fn test_env<F>(source_string: &str, args: (Box<dyn Emitter + sync::Send>, usize), body: F)
where
F: FnOnce(Env),
{
syntax::with_globals(|| {
let mut options = config::Options::default();
@ -113,34 +115,41 @@ fn test_env_with_pool<F>(
options: config::Options,
source_string: &str,
(emitter, expected_err_count): (Box<dyn Emitter + sync::Send>, usize),
body: F
)
where F: FnOnce(Env)
body: F,
) where
F: FnOnce(Env),
{
let diagnostic_handler = errors::Handler::with_emitter(true, false, emitter);
let sess = session::build_session_(options,
None,
diagnostic_handler,
Lrc::new(SourceMap::new(FilePathMapping::empty())));
let sess = session::build_session_(
options,
None,
diagnostic_handler,
Lrc::new(SourceMap::new(FilePathMapping::empty())),
);
let cstore = CStore::new(::get_codegen_backend(&sess).metadata_loader());
rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
let input = config::Input::Str {
name: FileName::Anon,
input: source_string.to_string(),
};
let krate = driver::phase_1_parse_input(&driver::CompileController::basic(),
&sess,
&input).unwrap();
let driver::ExpansionResult { defs, resolutions, mut hir_forest, .. } = {
driver::phase_2_configure_and_expand(&sess,
&cstore,
krate,
None,
"test",
None,
MakeGlobMap::No,
|_| Ok(()))
.expect("phase 2 aborted")
let krate =
driver::phase_1_parse_input(&driver::CompileController::basic(), &sess, &input).unwrap();
let driver::ExpansionResult {
defs,
resolutions,
mut hir_forest,
..
} = {
driver::phase_2_configure_and_expand(
&sess,
&cstore,
krate,
None,
"test",
None,
MakeGlobMap::No,
|_| Ok(()),
).expect("phase 2 aborted")
};
let arenas = ty::AllArenas::new();
@ -155,32 +164,39 @@ fn test_env_with_pool<F>(
extra: String::new(),
outputs: OutputTypes::new(&[]),
};
TyCtxt::create_and_enter(&sess,
&cstore,
ty::query::Providers::default(),
ty::query::Providers::default(),
&arenas,
resolutions,
hir_map,
OnDiskCache::new_empty(sess.source_map()),
"test_crate",
tx,
&outputs,
|tcx| {
tcx.infer_ctxt().enter(|infcx| {
let mut region_scope_tree = region::ScopeTree::default();
let param_env = ty::ParamEnv::empty();
body(Env {
infcx: &infcx,
region_scope_tree: &mut region_scope_tree,
param_env: param_env,
TyCtxt::create_and_enter(
&sess,
&cstore,
ty::query::Providers::default(),
ty::query::Providers::default(),
&arenas,
resolutions,
hir_map,
OnDiskCache::new_empty(sess.source_map()),
"test_crate",
tx,
&outputs,
|tcx| {
tcx.infer_ctxt().enter(|infcx| {
let mut region_scope_tree = region::ScopeTree::default();
let param_env = ty::ParamEnv::empty();
body(Env {
infcx: &infcx,
region_scope_tree: &mut region_scope_tree,
param_env: param_env,
});
let outlives_env = OutlivesEnvironment::new(param_env);
let def_id = tcx.hir.local_def_id(ast::CRATE_NODE_ID);
infcx.resolve_regions_and_report_errors(
def_id,
&region_scope_tree,
&outlives_env,
SuppressRegionErrors::default(),
);
assert_eq!(tcx.sess.err_count(), expected_err_count);
});
let outlives_env = OutlivesEnvironment::new(param_env);
let def_id = tcx.hir.local_def_id(ast::CRATE_NODE_ID);
infcx.resolve_regions_and_report_errors(def_id, &region_scope_tree, &outlives_env);
assert_eq!(tcx.sess.err_count(), expected_err_count);
});
});
},
);
}
fn d1() -> ty::DebruijnIndex {
@ -196,9 +212,15 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
self.infcx.tcx
}
pub fn create_region_hierarchy(&mut self, rh: &RH,
parent: (region::Scope, region::ScopeDepth)) {
let me = region::Scope { id: rh.id, data: region::ScopeData::Node };
pub fn create_region_hierarchy(
&mut self,
rh: &RH,
parent: (region::Scope, region::ScopeDepth),
) {
let me = region::Scope {
id: rh.id,
data: region::ScopeData::Node,
};
self.region_scope_tree.record_scope_parent(me, Some(parent));
for child_rh in rh.sub {
self.create_region_hierarchy(child_rh, (me, parent.1 + 1));
@ -211,20 +233,25 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
let dscope = region::Scope {
id: hir::ItemLocalId(1),
data: region::ScopeData::Destruction
data: region::ScopeData::Destruction,
};
self.region_scope_tree.record_scope_parent(dscope, None);
self.create_region_hierarchy(&RH {
id: hir::ItemLocalId(1),
sub: &[RH {
id: hir::ItemLocalId(10),
sub: &[],
self.create_region_hierarchy(
&RH {
id: hir::ItemLocalId(1),
sub: &[
RH {
id: hir::ItemLocalId(10),
sub: &[],
},
RH {
id: hir::ItemLocalId(11),
sub: &[],
},
],
},
RH {
id: hir::ItemLocalId(11),
sub: &[],
}],
}, (dscope, 1));
(dscope, 1),
);
}
#[allow(dead_code)] // this seems like it could be useful, even if we don't use it now
@ -236,11 +263,12 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
}
};
fn search_mod(this: &Env,
m: &hir::Mod,
idx: usize,
names: &[String])
-> Option<ast::NodeId> {
fn search_mod(
this: &Env,
m: &hir::Mod,
idx: usize,
names: &[String],
) -> Option<ast::NodeId> {
assert!(idx < names.len());
for item in &m.item_ids {
let item = this.infcx.tcx.hir.expect_item(item.id);
@ -257,22 +285,22 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
}
return match it.node {
hir::ItemKind::Use(..) |
hir::ItemKind::ExternCrate(..) |
hir::ItemKind::Const(..) |
hir::ItemKind::Static(..) |
hir::ItemKind::Fn(..) |
hir::ItemKind::ForeignMod(..) |
hir::ItemKind::GlobalAsm(..) |
hir::ItemKind::Existential(..) |
hir::ItemKind::Ty(..) => None,
hir::ItemKind::Use(..)
| hir::ItemKind::ExternCrate(..)
| hir::ItemKind::Const(..)
| hir::ItemKind::Static(..)
| hir::ItemKind::Fn(..)
| hir::ItemKind::ForeignMod(..)
| hir::ItemKind::GlobalAsm(..)
| hir::ItemKind::Existential(..)
| hir::ItemKind::Ty(..) => None,
hir::ItemKind::Enum(..) |
hir::ItemKind::Struct(..) |
hir::ItemKind::Union(..) |
hir::ItemKind::Trait(..) |
hir::ItemKind::TraitAlias(..) |
hir::ItemKind::Impl(..) => None,
hir::ItemKind::Enum(..)
| hir::ItemKind::Struct(..)
| hir::ItemKind::Union(..)
| hir::ItemKind::Trait(..)
| hir::ItemKind::TraitAlias(..)
| hir::ItemKind::Impl(..) => None,
hir::ItemKind::Mod(ref m) => search_mod(this, m, idx, names),
};
@ -280,7 +308,10 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
}
pub fn make_subtype(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
match self.infcx.at(&ObligationCause::dummy(), self.param_env).sub(a, b) {
match self.infcx
.at(&ObligationCause::dummy(), self.param_env)
.sub(a, b)
{
Ok(_) => true,
Err(ref e) => panic!("Encountered error: {}", e),
}
@ -302,13 +333,15 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
}
pub fn t_fn(&self, input_tys: &[Ty<'tcx>], output_ty: Ty<'tcx>) -> Ty<'tcx> {
self.infcx.tcx.mk_fn_ptr(ty::Binder::bind(self.infcx.tcx.mk_fn_sig(
input_tys.iter().cloned(),
output_ty,
false,
hir::Unsafety::Normal,
Abi::Rust
)))
self.infcx
.tcx
.mk_fn_ptr(ty::Binder::bind(self.infcx.tcx.mk_fn_sig(
input_tys.iter().cloned(),
output_ty,
false,
hir::Unsafety::Normal,
Abi::Rust,
)))
}
pub fn t_nil(&self) -> Ty<'tcx> {
@ -321,23 +354,30 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
pub fn t_param(&self, index: u32) -> Ty<'tcx> {
let name = format!("T{}", index);
self.infcx.tcx.mk_ty_param(index, Symbol::intern(&name).as_interned_str())
self.infcx
.tcx
.mk_ty_param(index, Symbol::intern(&name).as_interned_str())
}
pub fn re_early_bound(&self, index: u32, name: &'static str) -> ty::Region<'tcx> {
let name = Symbol::intern(name).as_interned_str();
self.infcx.tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion {
def_id: self.infcx.tcx.hir.local_def_id(ast::CRATE_NODE_ID),
index,
name,
}))
self.infcx
.tcx
.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion {
def_id: self.infcx.tcx.hir.local_def_id(ast::CRATE_NODE_ID),
index,
name,
}))
}
pub fn re_late_bound_with_debruijn(&self,
id: u32,
debruijn: ty::DebruijnIndex)
-> ty::Region<'tcx> {
self.infcx.tcx.mk_region(ty::ReLateBound(debruijn, ty::BrAnon(id)))
pub fn re_late_bound_with_debruijn(
&self,
id: u32,
debruijn: ty::DebruijnIndex,
) -> ty::Region<'tcx> {
self.infcx
.tcx
.mk_region(ty::ReLateBound(debruijn, ty::BrAnon(id)))
}
pub fn t_rptr(&self, r: ty::Region<'tcx>) -> Ty<'tcx> {
@ -349,10 +389,11 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
self.infcx.tcx.mk_imm_ref(r, self.tcx().types.isize)
}
pub fn t_rptr_late_bound_with_debruijn(&self,
id: u32,
debruijn: ty::DebruijnIndex)
-> Ty<'tcx> {
pub fn t_rptr_late_bound_with_debruijn(
&self,
id: u32,
debruijn: ty::DebruijnIndex,
) -> Ty<'tcx> {
let r = self.re_late_bound_with_debruijn(id, debruijn);
self.infcx.tcx.mk_imm_ref(r, self.tcx().types.isize)
}
@ -360,9 +401,11 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
pub fn t_rptr_scope(&self, id: u32) -> Ty<'tcx> {
let r = ty::ReScope(region::Scope {
id: hir::ItemLocalId(id),
data: region::ScopeData::Node
data: region::ScopeData::Node,
});
self.infcx.tcx.mk_imm_ref(self.infcx.tcx.mk_region(r), self.tcx().types.isize)
self.infcx
.tcx
.mk_imm_ref(self.infcx.tcx.mk_region(r), self.tcx().types.isize)
}
pub fn re_free(&self, id: u32) -> ty::Region<'tcx> {
@ -378,14 +421,19 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
}
pub fn sub(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> InferResult<'tcx, ()> {
self.infcx.at(&ObligationCause::dummy(), self.param_env).sub(t1, t2)
self.infcx
.at(&ObligationCause::dummy(), self.param_env)
.sub(t1, t2)
}
/// Checks that `t1 <: t2` is true (this may register additional
/// region checks).
pub fn check_sub(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) {
match self.sub(t1, t2) {
Ok(InferOk { obligations, value: () }) => {
Ok(InferOk {
obligations,
value: (),
}) => {
// None of these tests should require nested obligations:
assert!(obligations.is_empty());
}
@ -445,8 +493,10 @@ fn sub_free_bound_false() {
env.create_simple_region_hierarchy();
let t_rptr_free1 = env.t_rptr_free(1);
let t_rptr_bound1 = env.t_rptr_late_bound(1);
env.check_not_sub(env.t_fn(&[t_rptr_free1], env.tcx().types.isize),
env.t_fn(&[t_rptr_bound1], env.tcx().types.isize));
env.check_not_sub(
env.t_fn(&[t_rptr_free1], env.tcx().types.isize),
env.t_fn(&[t_rptr_bound1], env.tcx().types.isize),
);
})
}
@ -462,8 +512,10 @@ fn sub_bound_free_true() {
env.create_simple_region_hierarchy();
let t_rptr_bound1 = env.t_rptr_late_bound(1);
let t_rptr_free1 = env.t_rptr_free(1);
env.check_sub(env.t_fn(&[t_rptr_bound1], env.tcx().types.isize),
env.t_fn(&[t_rptr_free1], env.tcx().types.isize));
env.check_sub(
env.t_fn(&[t_rptr_bound1], env.tcx().types.isize),
env.t_fn(&[t_rptr_free1], env.tcx().types.isize),
);
})
}
@ -476,10 +528,13 @@ fn sub_free_bound_false_infer() {
//! does NOT hold for any instantiation of `_#1`.
test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
let t_infer1 = env.infcx.next_ty_var(TypeVariableOrigin::MiscVariable(DUMMY_SP));
let t_infer1 = env.infcx
.next_ty_var(TypeVariableOrigin::MiscVariable(DUMMY_SP));
let t_rptr_bound1 = env.t_rptr_late_bound(1);
env.check_not_sub(env.t_fn(&[t_infer1], env.tcx().types.isize),
env.t_fn(&[t_rptr_bound1], env.tcx().types.isize));
env.check_not_sub(
env.t_fn(&[t_infer1], env.tcx().types.isize),
env.t_fn(&[t_rptr_bound1], env.tcx().types.isize),
);
})
}
@ -487,7 +542,6 @@ fn sub_free_bound_false_infer() {
/// This requires adjusting the Debruijn index.
#[test]
fn subst_ty_renumber_bound() {
test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
// Situation:
// Theta = [A -> &'a foo]
@ -509,11 +563,10 @@ fn subst_ty_renumber_bound() {
env.t_fn(&[t_ptr_bound2], env.t_nil())
};
debug!("subst_bound: t_source={:?} substs={:?} t_substituted={:?} t_expected={:?}",
t_source,
substs,
t_substituted,
t_expected);
debug!(
"subst_bound: t_source={:?} substs={:?} t_substituted={:?} t_expected={:?}",
t_source, substs, t_substituted, t_expected
);
assert_eq!(t_substituted, t_expected);
})
@ -546,11 +599,10 @@ fn subst_ty_renumber_some_bounds() {
env.t_pair(t_rptr_bound1, env.t_fn(&[t_rptr_bound2], env.t_nil()))
};
debug!("subst_bound: t_source={:?} substs={:?} t_substituted={:?} t_expected={:?}",
t_source,
substs,
t_substituted,
t_expected);
debug!(
"subst_bound: t_source={:?} substs={:?} t_substituted={:?} t_expected={:?}",
t_source, substs, t_substituted, t_expected
);
assert_eq!(t_substituted, t_expected);
})
@ -559,7 +611,6 @@ fn subst_ty_renumber_some_bounds() {
/// Test that we correctly compute whether a type has escaping regions or not.
#[test]
fn escaping() {
test_env(EMPTY_SOURCE_STR, errors(&[]), |mut env| {
// Situation:
// Theta = [A -> &'a foo]
@ -608,11 +659,10 @@ fn subst_region_renumber_region() {
env.t_fn(&[t_rptr_bound2], env.t_nil())
};
debug!("subst_bound: t_source={:?} substs={:?} t_substituted={:?} t_expected={:?}",
t_source,
substs,
t_substituted,
t_expected);
debug!(
"subst_bound: t_source={:?} substs={:?} t_substituted={:?} t_expected={:?}",
t_source, substs, t_substituted, t_expected
);
assert_eq!(t_substituted, t_expected);
})
@ -627,9 +677,13 @@ fn walk_ty() {
let tup1_ty = tcx.intern_tup(&[int_ty, usize_ty, int_ty, usize_ty]);
let tup2_ty = tcx.intern_tup(&[tup1_ty, tup1_ty, usize_ty]);
let walked: Vec<_> = tup2_ty.walk().collect();
assert_eq!(walked,
[tup2_ty, tup1_ty, int_ty, usize_ty, int_ty, usize_ty, tup1_ty, int_ty,
usize_ty, int_ty, usize_ty, usize_ty]);
assert_eq!(
walked,
[
tup2_ty, tup1_ty, int_ty, usize_ty, int_ty, usize_ty, tup1_ty, int_ty, usize_ty,
int_ty, usize_ty, usize_ty
]
);
})
}
@ -644,14 +698,16 @@ fn walk_ty_skip_subtree() {
// types we expect to see (in order), plus a boolean saying
// whether to skip the subtree.
let mut expected = vec![(tup2_ty, false),
(tup1_ty, false),
(int_ty, false),
(usize_ty, false),
(int_ty, false),
(usize_ty, false),
(tup1_ty, true), // skip the isize/usize/isize/usize
(usize_ty, false)];
let mut expected = vec![
(tup2_ty, false),
(tup1_ty, false),
(int_ty, false),
(usize_ty, false),
(int_ty, false),
(usize_ty, false),
(tup1_ty, true), // skip the isize/usize/isize/usize
(usize_ty, false),
];
expected.reverse();
let mut walker = tup2_ty.walk();

View file

@ -10,15 +10,13 @@
use super::universal_regions::UniversalRegions;
use borrow_check::nll::constraints::graph::NormalConstraintGraph;
use borrow_check::nll::constraints::{
ConstraintSccIndex, ConstraintSet, OutlivesConstraint,
};
use borrow_check::nll::constraints::{ConstraintSccIndex, ConstraintSet, OutlivesConstraint};
use borrow_check::nll::region_infer::values::{RegionElement, ToElementIndex};
use borrow_check::nll::type_check::free_region_relations::UniversalRegionRelations;
use borrow_check::nll::type_check::Locations;
use rustc::hir::def_id::DefId;
use rustc::infer::canonical::QueryRegionConstraint;
use rustc::infer::region_constraints::{GenericKind, VarInfos};
use rustc::infer::region_constraints::{GenericKind, VarInfos, VerifyBound};
use rustc::infer::{InferCtxt, NLLRegionVariableOrigin, RegionVariableOrigin};
use rustc::mir::{
ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, Local, Location,
@ -29,7 +27,7 @@ use rustc::util::common;
use rustc_data_structures::bit_set::BitSet;
use rustc_data_structures::graph::scc::Sccs;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_errors::{DiagnosticBuilder, Diagnostic};
use rustc_errors::{Diagnostic, DiagnosticBuilder};
use std::rc::Rc;
@ -71,6 +69,15 @@ pub struct RegionInferenceContext<'tcx> {
/// visible from this index.
scc_universes: IndexVec<ConstraintSccIndex, ty::UniverseIndex>,
/// Contains a "representative" from each SCC. This will be the
/// minimal RegionVid belonging to that universe. It is used as a
/// kind of hacky way to manage checking outlives relationships,
/// since we can 'canonicalize' each region to the representative
/// of its SCC and be sure that -- if they have the same repr --
/// they *must* be equal (though not having the same repr does not
/// mean they are unequal).
scc_representatives: IndexVec<ConstraintSccIndex, ty::RegionVid>,
/// The final inferred values of the region variables; we compute
/// one value per SCC. To get the value for any given *region*,
/// you first find which scc it is a part of.
@ -162,42 +169,7 @@ pub struct TypeTest<'tcx> {
/// A test which, if met by the region `'x`, proves that this type
/// constraint is satisfied.
pub test: RegionTest,
}
/// A "test" that can be applied to some "subject region" `'x`. These are used to
/// describe type constraints. Tests do not presently affect the
/// region values that get inferred for each variable; they only
/// examine the results *after* inference. This means they can
/// conveniently include disjuction ("a or b must be true").
#[derive(Clone, Debug)]
pub enum RegionTest {
/// The subject region `'x` must by outlived by *some* region in
/// the given set of regions.
///
/// This test comes from e.g. a where clause like `T: 'a + 'b`,
/// which implies that we know that `T: 'a` and that `T:
/// 'b`. Therefore, if we are trying to prove that `T: 'x`, we can
/// do so by showing that `'a: 'x` *or* `'b: 'x`.
IsOutlivedByAnyRegionIn(Vec<RegionVid>),
/// The subject region `'x` must by outlived by *all* regions in
/// the given set of regions.
///
/// This test comes from e.g. a projection type like `T = <u32 as
/// Trait<'a, 'b>>::Foo`, which must outlive `'a` or `'b`, and
/// maybe both. Therefore we can prove that `T: 'x` if we know
/// that `'a: 'x` *and* `'b: 'x`.
IsOutlivedByAllRegionsIn(Vec<RegionVid>),
/// Any of the given tests are true.
///
/// This arises from projections, for which there are multiple
/// ways to prove an outlives relationship.
Any(Vec<RegionTest>),
/// All of the given tests are true.
All(Vec<RegionTest>),
pub verify_bound: VerifyBound<'tcx>,
}
impl<'tcx> RegionInferenceContext<'tcx> {
@ -245,6 +217,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let scc_universes = Self::compute_scc_universes(&constraint_sccs, &definitions);
let scc_representatives = Self::compute_scc_representatives(&constraint_sccs, &definitions);
let mut result = Self {
definitions,
liveness_constraints,
@ -252,6 +226,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
constraint_graph,
constraint_sccs,
scc_universes,
scc_representatives,
scc_values,
type_tests,
universal_regions,
@ -288,6 +263,27 @@ impl<'tcx> RegionInferenceContext<'tcx> {
scc_universes
}
/// For each SCC, we compute a unique `RegionVid` (in fact, the
/// minimal one that belongs to the SCC). See
/// `scc_representatives` field of `RegionInferenceContext` for
/// more details.
fn compute_scc_representatives(
constraints_scc: &Sccs<RegionVid, ConstraintSccIndex>,
definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
) -> IndexVec<ConstraintSccIndex, ty::RegionVid> {
let num_sccs = constraints_scc.num_sccs();
let next_region_vid = definitions.next_index();
let mut scc_representatives = IndexVec::from_elem_n(next_region_vid, num_sccs);
for region_vid in definitions.indices() {
let scc = constraints_scc.scc(region_vid);
let prev_min = scc_representatives[scc];
scc_representatives[scc] = region_vid.min(prev_min);
}
scc_representatives
}
/// Initializes the region variables for each universally
/// quantified region (lifetime parameter). The first N variables
/// always correspond to the regions appearing in the function
@ -582,7 +578,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
for type_test in &self.type_tests {
debug!("check_type_test: {:?}", type_test);
if self.eval_region_test(mir, type_test.lower_bound, &type_test.test) {
let generic_ty = type_test.generic_kind.to_ty(tcx);
if self.eval_verify_bound(
tcx,
mir,
generic_ty,
type_test.lower_bound,
&type_test.verify_bound,
) {
continue;
}
@ -689,7 +692,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
generic_kind,
lower_bound,
locations,
test: _,
verify_bound: _,
} = type_test;
let generic_ty = generic_kind.to_ty(tcx);
@ -716,7 +719,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// where `ur` is a local bound -- we are sometimes in a
// position to prove things that our caller cannot. See
// #53570 for an example.
if self.eval_region_test(mir, ur, &type_test.test) {
if self.eval_verify_bound(tcx, mir, generic_ty, ur, &type_test.verify_bound) {
continue;
}
@ -888,31 +891,99 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// Test if `test` is true when applied to `lower_bound` at
/// `point`, and returns true or false.
fn eval_region_test(&self, mir: &Mir<'tcx>, lower_bound: RegionVid, test: &RegionTest) -> bool {
fn eval_verify_bound(
&self,
tcx: TyCtxt<'_, '_, 'tcx>,
mir: &Mir<'tcx>,
generic_ty: Ty<'tcx>,
lower_bound: RegionVid,
verify_bound: &VerifyBound<'tcx>,
) -> bool {
debug!(
"eval_region_test(lower_bound={:?}, test={:?})",
lower_bound, test
"eval_verify_bound(lower_bound={:?}, verify_bound={:?})",
lower_bound, verify_bound
);
match test {
RegionTest::IsOutlivedByAllRegionsIn(regions) => regions
.iter()
.all(|&r| self.eval_outlives(mir, r, lower_bound)),
match verify_bound {
VerifyBound::IfEq(test_ty, verify_bound1) => {
self.eval_if_eq(tcx, mir, generic_ty, lower_bound, test_ty, verify_bound1)
}
RegionTest::IsOutlivedByAnyRegionIn(regions) => regions
.iter()
.any(|&r| self.eval_outlives(mir, r, lower_bound)),
VerifyBound::OutlivedBy(r) => {
let r_vid = self.to_region_vid(r);
self.eval_outlives(mir, r_vid, lower_bound)
}
RegionTest::Any(tests) => tests
.iter()
.any(|test| self.eval_region_test(mir, lower_bound, test)),
VerifyBound::AnyBound(verify_bounds) => verify_bounds.iter().any(|verify_bound| {
self.eval_verify_bound(tcx, mir, generic_ty, lower_bound, verify_bound)
}),
RegionTest::All(tests) => tests
.iter()
.all(|test| self.eval_region_test(mir, lower_bound, test)),
VerifyBound::AllBounds(verify_bounds) => verify_bounds.iter().all(|verify_bound| {
self.eval_verify_bound(tcx, mir, generic_ty, lower_bound, verify_bound)
}),
}
}
fn eval_if_eq(
&self,
tcx: TyCtxt<'_, '_, 'tcx>,
mir: &Mir<'tcx>,
generic_ty: Ty<'tcx>,
lower_bound: RegionVid,
test_ty: Ty<'tcx>,
verify_bound: &VerifyBound<'tcx>,
) -> bool {
let generic_ty_normalized = self.normalize_to_scc_representatives(tcx, generic_ty);
let test_ty_normalized = self.normalize_to_scc_representatives(tcx, test_ty);
if generic_ty_normalized == test_ty_normalized {
self.eval_verify_bound(tcx, mir, generic_ty, lower_bound, verify_bound)
} else {
false
}
}
/// This is a conservative normalization procedure. It takes every
/// free region in `value` and replaces it with the
/// "representative" of its SCC (see `scc_representatives` field).
/// We are guaranteed that if two values normalize to the same
/// thing, then they are equal; this is a conservative check in
/// that they could still be equal even if they normalize to
/// different results. (For example, there might be two regions
/// with the same value that are not in the same SCC).
///
/// NB. This is not an ideal approach and I would like to revisit
/// it. However, it works pretty well in practice. In particular,
/// this is needed to deal with projection outlives bounds like
///
/// <T as Foo<'0>>::Item: '1
///
/// In particular, this routine winds up being important when
/// there are bounds like `where <T as Foo<'a>>::Item: 'b` in the
/// environment. In this case, if we can show that `'0 == 'a`,
/// and that `'b: '1`, then we know that the clause is
/// satisfied. In such cases, particularly due to limitations of
/// the trait solver =), we usually wind up with a where-clause like
/// `T: Foo<'a>` in scope, which thus forces `'0 == 'a` to be added as
/// a constraint, and thus ensures that they are in the same SCC.
///
/// So why can't we do a more correct routine? Well, we could
/// *almost* use the `relate_tys` code, but the way it is
/// currently setup it creates inference variables to deal with
/// higher-ranked things and so forth, and right now the inference
/// context is not permitted to make more inference variables. So
/// we use this kind of hacky solution.
fn normalize_to_scc_representatives<T>(&self, tcx: TyCtxt<'_, '_, 'tcx>, value: T) -> T
where
T: TypeFoldable<'tcx>,
{
tcx.fold_regions(&value, &mut false, |r, _db| {
let vid = self.to_region_vid(r);
let scc = self.constraint_sccs.scc(vid);
let repr = self.scc_representatives[scc];
tcx.mk_region(ty::ReVar(repr))
})
}
// Evaluate whether `sup_region: sub_region @ point`.
fn eval_outlives(
&self,

View file

@ -11,10 +11,11 @@
use borrow_check::location::LocationTable;
use borrow_check::nll::constraints::{ConstraintCategory, ConstraintSet, OutlivesConstraint};
use borrow_check::nll::facts::AllFacts;
use borrow_check::nll::region_infer::{RegionTest, TypeTest};
use borrow_check::nll::region_infer::TypeTest;
use borrow_check::nll::type_check::Locations;
use borrow_check::nll::universal_regions::UniversalRegions;
use rustc::infer::canonical::QueryRegionConstraint;
use rustc::infer::outlives::env::RegionBoundPairs;
use rustc::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate};
use rustc::infer::region_constraints::{GenericKind, VerifyBound};
use rustc::infer::{self, SubregionOrigin};
@ -26,7 +27,7 @@ crate struct ConstraintConversion<'a, 'gcx: 'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'tcx>,
universal_regions: &'a UniversalRegions<'tcx>,
location_table: &'a LocationTable,
region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)],
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
implicit_region_bound: Option<ty::Region<'tcx>>,
param_env: ty::ParamEnv<'tcx>,
locations: Locations,
@ -41,7 +42,7 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> {
tcx: TyCtxt<'a, 'gcx, 'tcx>,
universal_regions: &'a UniversalRegions<'tcx>,
location_table: &'a LocationTable,
region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)],
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
implicit_region_bound: Option<ty::Region<'tcx>>,
param_env: ty::ParamEnv<'tcx>,
locations: Locations,
@ -139,43 +140,15 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> {
&self,
generic_kind: GenericKind<'tcx>,
region: ty::Region<'tcx>,
bound: VerifyBound<'tcx>,
verify_bound: VerifyBound<'tcx>,
) -> TypeTest<'tcx> {
let lower_bound = self.to_region_vid(region);
let test = self.verify_bound_to_region_test(&bound);
TypeTest {
generic_kind,
lower_bound,
locations: self.locations,
test,
}
}
fn verify_bound_to_region_test(&self, verify_bound: &VerifyBound<'tcx>) -> RegionTest {
match verify_bound {
VerifyBound::AnyRegion(regions) => RegionTest::IsOutlivedByAnyRegionIn(
regions.iter().map(|r| self.to_region_vid(r)).collect(),
),
VerifyBound::AllRegions(regions) => RegionTest::IsOutlivedByAllRegionsIn(
regions.iter().map(|r| self.to_region_vid(r)).collect(),
),
VerifyBound::AnyBound(bounds) => RegionTest::Any(
bounds
.iter()
.map(|b| self.verify_bound_to_region_test(b))
.collect(),
),
VerifyBound::AllBounds(bounds) => RegionTest::All(
bounds
.iter()
.map(|b| self.verify_bound_to_region_test(b))
.collect(),
),
verify_bound,
}
}

View file

@ -17,31 +17,31 @@ use borrow_check::nll::constraints::{ConstraintCategory, ConstraintSet, Outlives
use borrow_check::nll::facts::AllFacts;
use borrow_check::nll::region_infer::values::{LivenessValues, RegionValueElements};
use borrow_check::nll::region_infer::{ClosureRegionRequirementsExt, TypeTest};
use borrow_check::nll::renumber;
use borrow_check::nll::type_check::free_region_relations::{
CreateResult, UniversalRegionRelations,
};
use borrow_check::nll::universal_regions::UniversalRegions;
use borrow_check::nll::ToRegionVid;
use borrow_check::nll::renumber;
use dataflow::move_paths::MoveData;
use dataflow::FlowAtLocation;
use dataflow::MaybeInitializedPlaces;
use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::infer::canonical::QueryRegionConstraint;
use rustc::infer::region_constraints::GenericKind;
use rustc::infer::outlives::env::RegionBoundPairs;
use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime};
use rustc::mir::interpret::EvalErrorKind::BoundsCheck;
use rustc::mir::tcx::PlaceTy;
use rustc::mir::visit::{PlaceContext, Visitor};
use rustc::mir::*;
use rustc::traits::{ObligationCause, PredicateObligations};
use rustc::traits::query::type_op;
use rustc::traits::query::type_op::custom::CustomTypeOp;
use rustc::traits::query::{Fallible, NoSolution};
use rustc::traits::{ObligationCause, PredicateObligations};
use rustc::ty::fold::TypeFoldable;
use rustc::ty::{self, CanonicalTy, RegionVid, ToPolyTraitRef, Ty, TyCtxt, TyKind};
use rustc::ty::subst::Subst;
use rustc::ty::{self, CanonicalTy, RegionVid, ToPolyTraitRef, Ty, TyCtxt, TyKind};
use std::fmt;
use std::rc::Rc;
use syntax_pos::{Span, DUMMY_SP};
@ -161,11 +161,7 @@ pub(crate) fn type_check<'gcx, 'tcx>(
Some(&mut borrowck_context),
Some(&universal_region_relations),
|cx| {
cx.equate_inputs_and_outputs(
mir,
universal_regions,
&normalized_inputs_and_output,
);
cx.equate_inputs_and_outputs(mir, universal_regions, &normalized_inputs_and_output);
liveness::generate(cx, mir, elements, flow_inits, move_data);
},
);
@ -182,7 +178,7 @@ fn type_check_internal<'a, 'gcx, 'tcx, R>(
mir_def_id: DefId,
param_env: ty::ParamEnv<'gcx>,
mir: &'a Mir<'tcx>,
region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)],
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
implicit_region_bound: Option<ty::Region<'tcx>>,
borrowck_context: Option<&'a mut BorrowCheckContext<'a, 'tcx>>,
universal_region_relations: Option<&'a UniversalRegionRelations<'tcx>>,
@ -377,14 +373,12 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
debug!("sanitize_constant: expected_ty={:?}", constant.literal.ty);
if let Err(terr) = self.cx
.eq_types(
constant.literal.ty,
constant.ty,
location.to_locations(),
ConstraintCategory::Boring,
)
{
if let Err(terr) = self.cx.eq_types(
constant.literal.ty,
constant.ty,
location.to_locations(),
ConstraintCategory::Boring,
) {
span_mirbug!(
self,
constant,
@ -429,12 +423,10 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
let sty = self.sanitize_type(place, sty);
let ty = self.tcx().type_of(def_id);
let ty = self.cx.normalize(ty, location);
if let Err(terr) = self.cx.eq_types(
ty,
sty,
location.to_locations(),
ConstraintCategory::Boring,
) {
if let Err(terr) =
self.cx
.eq_types(ty, sty, location.to_locations(), ConstraintCategory::Boring)
{
span_mirbug!(
self,
place,
@ -693,7 +685,7 @@ struct TypeChecker<'a, 'gcx: 'tcx, 'tcx: 'a> {
last_span: Span,
mir: &'a Mir<'tcx>,
mir_def_id: DefId,
region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)],
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
implicit_region_bound: Option<ty::Region<'tcx>>,
reported_errors: FxHashSet<(Ty<'tcx>, Span)>,
borrowck_context: Option<&'a mut BorrowCheckContext<'a, 'tcx>>,
@ -802,7 +794,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
mir: &'a Mir<'tcx>,
mir_def_id: DefId,
param_env: ty::ParamEnv<'gcx>,
region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)],
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
implicit_region_bound: Option<ty::Region<'tcx>>,
borrowck_context: Option<&'a mut BorrowCheckContext<'a, 'tcx>>,
universal_region_relations: Option<&'a UniversalRegionRelations<'tcx>>,
@ -955,66 +947,55 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
let tcx = infcx.tcx;
let param_env = self.param_env;
let parent_def_id = infcx.tcx.closure_base_def_id(self.mir_def_id);
let opaque_type_map =
self.fully_perform_op(
locations,
category,
CustomTypeOp::new(
|infcx| {
let mut obligations = ObligationAccumulator::default();
let opaque_type_map = self.fully_perform_op(
locations,
category,
CustomTypeOp::new(
|infcx| {
let mut obligations = ObligationAccumulator::default();
let dummy_body_id = ObligationCause::dummy().body_id;
let (output_ty, opaque_type_map) =
obligations.add(infcx.instantiate_opaque_types(
parent_def_id,
dummy_body_id,
param_env,
&anon_ty,
));
let dummy_body_id = ObligationCause::dummy().body_id;
let (output_ty, opaque_type_map) =
obligations.add(infcx.instantiate_opaque_types(
parent_def_id,
dummy_body_id,
param_env,
&anon_ty,
));
debug!(
"eq_opaque_type_and_type: \
instantiated output_ty={:?} \
opaque_type_map={:#?} \
revealed_ty={:?}",
output_ty, opaque_type_map, revealed_ty
);
obligations.add(infcx
.at(&ObligationCause::dummy(), param_env)
.eq(output_ty, revealed_ty)?);
for (&opaque_def_id, opaque_decl) in &opaque_type_map {
let opaque_defn_ty = tcx.type_of(opaque_def_id);
let opaque_defn_ty = opaque_defn_ty.subst(tcx, opaque_decl.substs);
let opaque_defn_ty = renumber::renumber_regions(infcx, &opaque_defn_ty);
debug!(
"eq_opaque_type_and_type: \
instantiated output_ty={:?} \
opaque_type_map={:#?} \
revealed_ty={:?}",
output_ty,
opaque_type_map,
revealed_ty
);
obligations.add(
infcx
.at(&ObligationCause::dummy(), param_env)
.eq(output_ty, revealed_ty)?,
"eq_opaque_type_and_type: concrete_ty={:?} opaque_defn_ty={:?}",
opaque_decl.concrete_ty, opaque_defn_ty
);
obligations.add(infcx
.at(&ObligationCause::dummy(), param_env)
.eq(opaque_decl.concrete_ty, opaque_defn_ty)?);
}
for (&opaque_def_id, opaque_decl) in &opaque_type_map {
let opaque_defn_ty = tcx.type_of(opaque_def_id);
let opaque_defn_ty = opaque_defn_ty.subst(tcx, opaque_decl.substs);
let opaque_defn_ty = renumber::renumber_regions(
infcx,
&opaque_defn_ty,
);
debug!(
"eq_opaque_type_and_type: concrete_ty={:?} opaque_defn_ty={:?}",
opaque_decl.concrete_ty,
opaque_defn_ty
);
obligations.add(
infcx
.at(&ObligationCause::dummy(), param_env)
.eq(opaque_decl.concrete_ty, opaque_defn_ty)?,
);
}
debug!("eq_opaque_type_and_type: equated");
debug!("eq_opaque_type_and_type: equated");
Ok(InferOk {
value: Some(opaque_type_map),
obligations: obligations.into_vec(),
})
},
|| "input_output".to_string(),
),
)?;
Ok(InferOk {
value: Some(opaque_type_map),
obligations: obligations.into_vec(),
})
},
|| "input_output".to_string(),
),
)?;
let universal_region_relations = match self.universal_region_relations {
Some(rel) => rel,
@ -1035,7 +1016,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
infcx.constrain_opaque_type(
opaque_def_id,
&opaque_decl,
universal_region_relations
universal_region_relations,
);
Ok(InferOk {
value: (),
@ -1073,12 +1054,9 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
let place_ty = place.ty(mir, tcx).to_ty(tcx);
let rv_ty = rv.ty(mir, tcx);
if let Err(terr) = self.sub_types_or_anon(
rv_ty,
place_ty,
location.to_locations(),
category,
) {
if let Err(terr) =
self.sub_types_or_anon(rv_ty, place_ty, location.to_locations(), category)
{
span_mirbug!(
self,
stmt,
@ -1117,7 +1095,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
self.prove_trait_ref(
trait_ref,
location.to_locations(),
ConstraintCategory::SizedBound,
ConstraintCategory::SizedBound,
);
}
}
@ -1148,15 +1126,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
}
StatementKind::AscribeUserType(ref place, variance, c_ty) => {
let place_ty = place.ty(mir, tcx).to_ty(tcx);
if let Err(terr) =
self.relate_type_and_user_type(
place_ty,
variance,
c_ty,
Locations::All(stmt.source_info.span),
ConstraintCategory::TypeAnnotation,
)
{
if let Err(terr) = self.relate_type_and_user_type(
place_ty,
variance,
c_ty,
Locations::All(stmt.source_info.span),
ConstraintCategory::TypeAnnotation,
) {
span_mirbug!(
self,
stmt,
@ -1208,12 +1184,9 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
let rv_ty = value.ty(mir, tcx);
let locations = term_location.to_locations();
if let Err(terr) = self.sub_types(
rv_ty,
place_ty,
locations,
ConstraintCategory::Assignment,
) {
if let Err(terr) =
self.sub_types(rv_ty, place_ty, locations, ConstraintCategory::Assignment)
{
span_mirbug!(
self,
term,
@ -1327,8 +1300,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
ty,
term_location.to_locations(),
ConstraintCategory::Return,
)
{
) {
span_mirbug!(
self,
term,
@ -1366,12 +1338,9 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
let locations = term_location.to_locations();
if let Err(terr) = self.sub_types_or_anon(
sig.output(),
dest_ty,
locations,
category,
) {
if let Err(terr) =
self.sub_types_or_anon(sig.output(), dest_ty, locations, category)
{
span_mirbug!(
self,
term,
@ -1539,12 +1508,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
}
}
fn check_local(
&mut self,
mir: &Mir<'tcx>,
local: Local,
local_decl: &LocalDecl<'tcx>,
) {
fn check_local(&mut self, mir: &Mir<'tcx>, local: Local, local_decl: &LocalDecl<'tcx>) {
match mir.local_kind(local) {
LocalKind::ReturnPointer | LocalKind::Arg => {
// return values of normal functions are required to be
@ -1713,13 +1677,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
ConstraintCategory::Cast,
) {
span_mirbug!(
self,
rvalue,
"equating {:?} with {:?} yields {:?}",
ty_fn_ptr_from,
ty,
terr
);
self,
rvalue,
"equating {:?} with {:?} yields {:?}",
ty_fn_ptr_from,
ty,
terr
);
}
}
@ -1739,13 +1703,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
ConstraintCategory::Cast,
) {
span_mirbug!(
self,
rvalue,
"equating {:?} with {:?} yields {:?}",
ty_fn_ptr_from,
ty,
terr
);
self,
rvalue,
"equating {:?} with {:?} yields {:?}",
ty_fn_ptr_from,
ty,
terr
);
}
}
@ -1768,13 +1732,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
ConstraintCategory::Cast,
) {
span_mirbug!(
self,
rvalue,
"equating {:?} with {:?} yields {:?}",
ty_fn_ptr_from,
ty,
terr
);
self,
rvalue,
"equating {:?} with {:?} yields {:?}",
ty_fn_ptr_from,
ty,
terr
);
}
}
@ -2232,7 +2196,7 @@ impl MirPass for TypeckMir {
def_id,
param_env,
mir,
&[],
&vec![],
None,
None,
None,
@ -2277,4 +2241,3 @@ impl<'tcx> ObligationAccumulator<'tcx> {
self.obligations
}
}

View file

@ -10,15 +10,13 @@
use borrow_check::nll::constraints::{ConstraintCategory, OutlivesConstraint};
use borrow_check::nll::type_check::{BorrowCheckContext, Locations};
use borrow_check::nll::universal_regions::UniversalRegions;
use borrow_check::nll::ToRegionVid;
use rustc::infer::canonical::{Canonical, CanonicalVarInfos};
use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
use rustc::traits::query::Fallible;
use rustc::ty::fold::{TypeFoldable, TypeVisitor};
use rustc::ty::relate::{self, Relate, RelateResult, TypeRelation};
use rustc::ty::subst::Kind;
use rustc::ty::{self, CanonicalTy, CanonicalVar, RegionVid, Ty, TyCtxt};
use rustc::ty::{self, CanonicalTy, CanonicalVar, Ty, TyCtxt};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::indexed_vec::IndexVec;
@ -33,11 +31,9 @@ pub(super) fn sub_types<'tcx>(
) -> Fallible<()> {
debug!("sub_types(a={:?}, b={:?}, locations={:?})", a, b, locations);
TypeRelating::new(
infcx,
infcx.tcx,
NllTypeRelatingDelegate::new(infcx, borrowck_context, locations, category),
ty::Variance::Covariant,
locations,
category,
borrowck_context,
ty::List::empty(),
).relate(&a, &b)?;
Ok(())
@ -54,11 +50,9 @@ pub(super) fn eq_types<'tcx>(
) -> Fallible<()> {
debug!("eq_types(a={:?}, b={:?}, locations={:?})", a, b, locations);
TypeRelating::new(
infcx,
infcx.tcx,
NllTypeRelatingDelegate::new(infcx, borrowck_context, locations, category),
ty::Variance::Invariant,
locations,
category,
borrowck_context,
ty::List::empty(),
).relate(&a, &b)?;
Ok(())
@ -91,18 +85,22 @@ pub(super) fn relate_type_and_user_type<'tcx>(
let v1 = ty::Contravariant.xform(v);
TypeRelating::new(
infcx,
infcx.tcx,
NllTypeRelatingDelegate::new(infcx, borrowck_context, locations, category),
v1,
locations,
category,
borrowck_context,
b_variables,
).relate(&b_value, &a)?;
Ok(())
}
struct TypeRelating<'cx, 'bccx: 'cx, 'gcx: 'tcx, 'tcx: 'bccx> {
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
struct TypeRelating<'me, 'gcx: 'tcx, 'tcx: 'me, D>
where
D: TypeRelatingDelegate<'tcx>,
{
tcx: TyCtxt<'me, 'gcx, 'tcx>,
/// Callback to use when we deduce an outlives relationship
delegate: D,
/// How are we relating `a` and `b`?
///
@ -122,19 +120,10 @@ struct TypeRelating<'cx, 'bccx: 'cx, 'gcx: 'tcx, 'tcx: 'bccx> {
///
/// This field stores the instantiations for late-bound regions in
/// the `a` type.
a_scopes: Vec<BoundRegionScope>,
a_scopes: Vec<BoundRegionScope<'tcx>>,
/// Same as `a_scopes`, but for the `b` type.
b_scopes: Vec<BoundRegionScope>,
/// Where (and why) is this relation taking place?
locations: Locations,
category: ConstraintCategory,
/// This will be `Some` when we are running the type check as part
/// of NLL, and `None` if we are running a "sanity check".
borrowck_context: Option<&'cx mut BorrowCheckContext<'bccx, 'tcx>>,
b_scopes: Vec<BoundRegionScope<'tcx>>,
/// As we execute, the type on the LHS *may* come from a canonical
/// source. In that case, we will sometimes find a constraint like
@ -150,37 +139,130 @@ struct TypeRelating<'cx, 'bccx: 'cx, 'gcx: 'tcx, 'tcx: 'bccx> {
canonical_var_values: IndexVec<CanonicalVar, Option<Kind<'tcx>>>,
}
trait TypeRelatingDelegate<'tcx> {
/// Push a constraint `sup: sub` -- this constraint must be
/// satisfied for the two types to be related. `sub` and `sup` may
/// be regions from the type or new variables created through the
/// delegate.
fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>);
/// Creates a new region variable representing an instantiated
/// higher-ranked region; this will be either existential or
/// universal depending on the context. So e.g. if you have
/// `for<'a> fn(..) <: for<'b> fn(..)`, then we will first
/// instantiate `'b` with a universally quantitifed region and
/// then `'a` with an existentially quantified region (the order
/// is important so that the existential region `'a` can see the
/// universal one).
fn next_region_var(
&mut self,
universally_quantified: UniversallyQuantified,
) -> ty::Region<'tcx>;
/// Creates a new existential region in the given universe. This
/// is used when handling subtyping and type variables -- if we
/// have that `?X <: Foo<'a>`, for example, we would instantiate
/// `?X` with a type like `Foo<'?0>` where `'?0` is a fresh
/// existential variable created by this function. We would then
/// relate `Foo<'?0>` with `Foo<'a>` (and probably add an outlives
/// relation stating that `'?0: 'a`).
fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>;
}
struct NllTypeRelatingDelegate<'me, 'bccx: 'me, 'gcx: 'tcx, 'tcx: 'bccx> {
infcx: &'me InferCtxt<'me, 'gcx, 'tcx>,
borrowck_context: Option<&'me mut BorrowCheckContext<'bccx, 'tcx>>,
/// Where (and why) is this relation taking place?
locations: Locations,
/// What category do we assign the resulting `'a: 'b` relationships?
category: ConstraintCategory,
}
impl NllTypeRelatingDelegate<'me, 'bccx, 'gcx, 'tcx> {
fn new(
infcx: &'me InferCtxt<'me, 'gcx, 'tcx>,
borrowck_context: Option<&'me mut BorrowCheckContext<'bccx, 'tcx>>,
locations: Locations,
category: ConstraintCategory,
) -> Self {
Self {
infcx,
borrowck_context,
locations,
category,
}
}
}
impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, '_, 'tcx> {
fn next_region_var(
&mut self,
universally_quantified: UniversallyQuantified,
) -> ty::Region<'tcx> {
let origin = if universally_quantified.0 {
NLLRegionVariableOrigin::BoundRegion(self.infcx.create_subuniverse())
} else {
NLLRegionVariableOrigin::Existential
};
self.infcx.next_nll_region_var(origin)
}
fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> {
self.infcx
.next_nll_region_var_in_universe(NLLRegionVariableOrigin::Existential, universe)
}
fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>) {
if let Some(borrowck_context) = &mut self.borrowck_context {
let sub = borrowck_context.universal_regions.to_region_vid(sub);
let sup = borrowck_context.universal_regions.to_region_vid(sup);
borrowck_context
.constraints
.outlives_constraints
.push(OutlivesConstraint {
sup,
sub,
locations: self.locations,
category: self.category,
});
// FIXME all facts!
}
}
}
#[derive(Clone, Debug)]
struct ScopesAndKind<'tcx> {
scopes: Vec<BoundRegionScope>,
scopes: Vec<BoundRegionScope<'tcx>>,
kind: Kind<'tcx>,
}
#[derive(Clone, Debug, Default)]
struct BoundRegionScope {
map: FxHashMap<ty::BoundRegion, RegionVid>,
struct BoundRegionScope<'tcx> {
map: FxHashMap<ty::BoundRegion, ty::Region<'tcx>>,
}
#[derive(Copy, Clone)]
struct UniversallyQuantified(bool);
impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> {
impl<'me, 'gcx, 'tcx, D> TypeRelating<'me, 'gcx, 'tcx, D>
where
D: TypeRelatingDelegate<'tcx>,
{
fn new(
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
tcx: TyCtxt<'me, 'gcx, 'tcx>,
delegate: D,
ambient_variance: ty::Variance,
locations: Locations,
category: ConstraintCategory,
borrowck_context: Option<&'cx mut BorrowCheckContext<'bccx, 'tcx>>,
canonical_var_infos: CanonicalVarInfos<'tcx>,
) -> Self {
let canonical_var_values = IndexVec::from_elem_n(None, canonical_var_infos.len());
Self {
infcx,
tcx,
delegate,
ambient_variance,
borrowck_context,
locations,
canonical_var_values,
category,
a_scopes: vec![],
b_scopes: vec![],
}
@ -204,10 +286,10 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> {
&mut self,
value: &ty::Binder<impl TypeFoldable<'tcx>>,
universally_quantified: UniversallyQuantified,
) -> BoundRegionScope {
) -> BoundRegionScope<'tcx> {
let mut scope = BoundRegionScope::default();
value.skip_binder().visit_with(&mut ScopeInstantiator {
infcx: self.infcx,
delegate: &mut self.delegate,
target_index: ty::INNERMOST,
universally_quantified,
bound_region_scope: &mut scope,
@ -227,8 +309,8 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> {
debruijn: ty::DebruijnIndex,
br: &ty::BoundRegion,
first_free_index: ty::DebruijnIndex,
scopes: &[BoundRegionScope],
) -> RegionVid {
scopes: &[BoundRegionScope<'tcx>],
) -> ty::Region<'tcx> {
// The debruijn index is a "reverse index" into the
// scopes listing. So when we have INNERMOST (0), we
// want the *last* scope pushed, and so forth.
@ -245,40 +327,23 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> {
/// with. Otherwise just return `r`.
fn replace_bound_region(
&self,
universal_regions: &UniversalRegions<'tcx>,
r: ty::Region<'tcx>,
first_free_index: ty::DebruijnIndex,
scopes: &[BoundRegionScope],
) -> RegionVid {
match r {
ty::ReLateBound(debruijn, br) => {
Self::lookup_bound_region(*debruijn, br, first_free_index, scopes)
}
ty::ReVar(v) => *v,
_ => universal_regions.to_region_vid(r),
scopes: &[BoundRegionScope<'tcx>],
) -> ty::Region<'tcx> {
if let ty::ReLateBound(debruijn, br) = r {
Self::lookup_bound_region(*debruijn, br, first_free_index, scopes)
} else {
r
}
}
/// Push a new outlives requirement into our output set of
/// constraints.
fn push_outlives(&mut self, sup: RegionVid, sub: RegionVid) {
fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>) {
debug!("push_outlives({:?}: {:?})", sup, sub);
if let Some(borrowck_context) = &mut self.borrowck_context {
borrowck_context
.constraints
.outlives_constraints
.push(OutlivesConstraint {
sup,
sub,
locations: self.locations,
category: self.category,
});
// FIXME all facts!
}
self.delegate.push_outlives(sup, sub);
}
/// When we encounter a canonical variable `var` in the output,
@ -316,12 +381,10 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> {
return result;
}
fn generalize_value(
&self,
kind: Kind<'tcx>,
) -> Kind<'tcx> {
fn generalize_value(&mut self, kind: Kind<'tcx>) -> Kind<'tcx> {
TypeGeneralizer {
type_rel: self,
tcx: self.tcx,
delegate: &mut self.delegate,
first_free_index: ty::INNERMOST,
ambient_variance: self.ambient_variance,
@ -333,11 +396,12 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> {
}
}
impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx>
for TypeRelating<'cx, 'bccx, 'gcx, 'tcx>
impl<D> TypeRelation<'me, 'gcx, 'tcx> for TypeRelating<'me, 'gcx, 'tcx, D>
where
D: TypeRelatingDelegate<'tcx>,
{
fn tcx(&self) -> TyCtxt<'cx, 'gcx, 'tcx> {
self.infcx.tcx
fn tcx(&self) -> TyCtxt<'me, 'gcx, 'tcx> {
self.tcx
}
fn tag(&self) -> &'static str {
@ -397,37 +461,30 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx>
a: ty::Region<'tcx>,
b: ty::Region<'tcx>,
) -> RelateResult<'tcx, ty::Region<'tcx>> {
if let Some(&mut BorrowCheckContext {
universal_regions, ..
}) = self.borrowck_context
{
if let ty::ReCanonical(var) = a {
self.relate_var(*var, b.into())?;
return Ok(a);
}
if let ty::ReCanonical(var) = a {
self.relate_var(*var, b.into())?;
return Ok(a);
}
debug!(
"regions(a={:?}, b={:?}, variance={:?})",
a, b, self.ambient_variance
);
debug!(
"regions(a={:?}, b={:?}, variance={:?})",
a, b, self.ambient_variance
);
let v_a =
self.replace_bound_region(universal_regions, a, ty::INNERMOST, &self.a_scopes);
let v_b =
self.replace_bound_region(universal_regions, b, ty::INNERMOST, &self.b_scopes);
let v_a = self.replace_bound_region(a, ty::INNERMOST, &self.a_scopes);
let v_b = self.replace_bound_region(b, ty::INNERMOST, &self.b_scopes);
debug!("regions: v_a = {:?}", v_a);
debug!("regions: v_b = {:?}", v_b);
debug!("regions: v_a = {:?}", v_a);
debug!("regions: v_b = {:?}", v_b);
if self.ambient_covariance() {
// Covariance: a <= b. Hence, `b: a`.
self.push_outlives(v_b, v_a);
}
if self.ambient_covariance() {
// Covariance: a <= b. Hence, `b: a`.
self.push_outlives(v_b, v_a);
}
if self.ambient_contravariance() {
// Contravariant: b <= a. Hence, `a: b`.
self.push_outlives(v_a, v_b);
}
if self.ambient_contravariance() {
// Contravariant: b <= a. Hence, `a: b`.
self.push_outlives(v_a, v_b);
}
Ok(a)
@ -527,10 +584,8 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx>
// Reset ambient variance to contravariance. See the
// covariant case above for an explanation.
let variance = ::std::mem::replace(
&mut self.ambient_variance,
ty::Variance::Contravariant,
);
let variance =
::std::mem::replace(&mut self.ambient_variance, ty::Variance::Contravariant);
self.relate(a.skip_binder(), b.skip_binder())?;
@ -551,15 +606,21 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx>
/// binder depth, and finds late-bound regions targeting the
/// `for<..`>. For each of those, it creates an entry in
/// `bound_region_scope`.
struct ScopeInstantiator<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
struct ScopeInstantiator<'me, 'tcx: 'me, D>
where
D: TypeRelatingDelegate<'tcx> + 'me,
{
delegate: &'me mut D,
// The debruijn index of the scope we are instantiating.
target_index: ty::DebruijnIndex,
universally_quantified: UniversallyQuantified,
bound_region_scope: &'cx mut BoundRegionScope,
bound_region_scope: &'me mut BoundRegionScope<'tcx>,
}
impl<'cx, 'gcx, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'cx, 'gcx, 'tcx> {
impl<'me, 'tcx, D> TypeVisitor<'tcx> for ScopeInstantiator<'me, 'tcx, D>
where
D: TypeRelatingDelegate<'tcx>,
{
fn visit_binder<T: TypeFoldable<'tcx>>(&mut self, t: &ty::Binder<T>) -> bool {
self.target_index.shift_in(1);
t.super_visit_with(self);
@ -570,21 +631,18 @@ impl<'cx, 'gcx, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'cx, 'gcx, 'tcx> {
fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool {
let ScopeInstantiator {
infcx,
universally_quantified,
bound_region_scope,
delegate,
..
} = *self;
} = self;
match r {
ty::ReLateBound(debruijn, br) if *debruijn == self.target_index => {
self.bound_region_scope.map.entry(*br).or_insert_with(|| {
let origin = if universally_quantified.0 {
NLLRegionVariableOrigin::BoundRegion(infcx.create_subuniverse())
} else {
NLLRegionVariableOrigin::Existential
};
infcx.next_nll_region_var(origin).to_region_vid()
});
bound_region_scope
.map
.entry(*br)
.or_insert_with(|| delegate.next_region_var(*universally_quantified));
}
_ => {}
@ -613,8 +671,13 @@ impl<'cx, 'gcx, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'cx, 'gcx, 'tcx> {
/// scopes.
///
/// [blog post]: https://is.gd/0hKvIr
struct TypeGeneralizer<'me, 'bccx: 'me, 'gcx: 'tcx, 'tcx: 'bccx> {
type_rel: &'me TypeRelating<'me, 'bccx, 'gcx, 'tcx>,
struct TypeGeneralizer<'me, 'gcx: 'tcx, 'tcx: 'me, D>
where
D: TypeRelatingDelegate<'tcx> + 'me,
{
tcx: TyCtxt<'me, 'gcx, 'tcx>,
delegate: &'me mut D,
/// After we generalize this type, we are going to relative it to
/// some other type. What will be the variance at this point?
@ -625,9 +688,12 @@ struct TypeGeneralizer<'me, 'bccx: 'me, 'gcx: 'tcx, 'tcx: 'bccx> {
universe: ty::UniverseIndex,
}
impl TypeRelation<'me, 'gcx, 'tcx> for TypeGeneralizer<'me, 'bbcx, 'gcx, 'tcx> {
impl<D> TypeRelation<'me, 'gcx, 'tcx> for TypeGeneralizer<'me, 'gcx, 'tcx, D>
where
D: TypeRelatingDelegate<'tcx>,
{
fn tcx(&self) -> TyCtxt<'me, 'gcx, 'tcx> {
self.type_rel.infcx.tcx
self.tcx
}
fn tag(&self) -> &'static str {
@ -710,9 +776,7 @@ impl TypeRelation<'me, 'gcx, 'tcx> for TypeGeneralizer<'me, 'bbcx, 'gcx, 'tcx> {
// though, we may however need to check well-formedness or
// risk a problem like #41677 again.
let replacement_region_vid = self.type_rel
.infcx
.next_nll_region_var_in_universe(NLLRegionVariableOrigin::Existential, self.universe);
let replacement_region_vid = self.delegate.generalize_existential(self.universe);
Ok(replacement_region_vid)
}

View file

@ -11,12 +11,12 @@
use check::regionck::RegionCtxt;
use hir::def_id::DefId;
use rustc::infer::{self, InferOk};
use rustc::infer::outlives::env::OutlivesEnvironment;
use rustc::infer::{self, InferOk, SuppressRegionErrors};
use rustc::middle::region;
use rustc::traits::{ObligationCause, TraitEngine, TraitEngineExt};
use rustc::ty::subst::{Subst, Substs, UnpackedKind};
use rustc::ty::{self, Ty, TyCtxt};
use rustc::traits::{ObligationCause, TraitEngine, TraitEngineExt};
use util::common::ErrorReported;
use syntax::ast;
@ -39,32 +39,41 @@ use syntax_pos::Span;
/// struct/enum definition for the nominal type itself (i.e.
/// cannot do `struct S<T>; impl<T:Clone> Drop for S<T> { ... }`).
///
pub fn check_drop_impl<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
drop_impl_did: DefId)
-> Result<(), ErrorReported> {
pub fn check_drop_impl<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
drop_impl_did: DefId,
) -> Result<(), ErrorReported> {
let dtor_self_type = tcx.type_of(drop_impl_did);
let dtor_predicates = tcx.predicates_of(drop_impl_did);
match dtor_self_type.sty {
ty::Adt(adt_def, self_to_impl_substs) => {
ensure_drop_params_and_item_params_correspond(tcx,
drop_impl_did,
dtor_self_type,
adt_def.did)?;
ensure_drop_params_and_item_params_correspond(
tcx,
drop_impl_did,
dtor_self_type,
adt_def.did,
)?;
ensure_drop_predicates_are_implied_by_item_defn(tcx,
drop_impl_did,
&dtor_predicates,
adt_def.did,
self_to_impl_substs)
ensure_drop_predicates_are_implied_by_item_defn(
tcx,
drop_impl_did,
&dtor_predicates,
adt_def.did,
self_to_impl_substs,
)
}
_ => {
// Destructors only work on nominal types. This was
// already checked by coherence, but compilation may
// not have been terminated.
let span = tcx.def_span(drop_impl_did);
tcx.sess.delay_span_bug(span,
&format!("should have been rejected by coherence check: {}",
dtor_self_type));
tcx.sess.delay_span_bug(
span,
&format!(
"should have been rejected by coherence check: {}",
dtor_self_type
),
);
Err(ErrorReported)
}
}
@ -74,9 +83,8 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
drop_impl_did: DefId,
drop_impl_ty: Ty<'tcx>,
self_type_did: DefId)
-> Result<(), ErrorReported>
{
self_type_did: DefId,
) -> Result<(), ErrorReported> {
let drop_impl_node_id = tcx.hir.as_local_node_id(drop_impl_did).unwrap();
// check that the impl type can be made to match the trait type.
@ -89,22 +97,29 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>(
let named_type = tcx.type_of(self_type_did);
let drop_impl_span = tcx.def_span(drop_impl_did);
let fresh_impl_substs =
infcx.fresh_substs_for_item(drop_impl_span, drop_impl_did);
let fresh_impl_substs = infcx.fresh_substs_for_item(drop_impl_span, drop_impl_did);
let fresh_impl_self_ty = drop_impl_ty.subst(tcx, fresh_impl_substs);
let cause = &ObligationCause::misc(drop_impl_span, drop_impl_node_id);
match infcx.at(cause, impl_param_env).eq(named_type, fresh_impl_self_ty) {
match infcx
.at(cause, impl_param_env)
.eq(named_type, fresh_impl_self_ty)
{
Ok(InferOk { obligations, .. }) => {
fulfillment_cx.register_predicate_obligations(infcx, obligations);
}
Err(_) => {
let item_span = tcx.def_span(self_type_did);
struct_span_err!(tcx.sess, drop_impl_span, E0366,
"Implementations of Drop cannot be specialized")
.span_note(item_span,
"Use same sequence of generic type and region \
parameters that is on the struct/enum definition")
struct_span_err!(
tcx.sess,
drop_impl_span,
E0366,
"Implementations of Drop cannot be specialized"
).span_note(
item_span,
"Use same sequence of generic type and region \
parameters that is on the struct/enum definition",
)
.emit();
return Err(ErrorReported);
}
@ -128,7 +143,12 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>(
// conservative. -nmatsakis
let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty());
infcx.resolve_regions_and_report_errors(drop_impl_did, &region_scope_tree, &outlives_env);
infcx.resolve_regions_and_report_errors(
drop_impl_did,
&region_scope_tree,
&outlives_env,
SuppressRegionErrors::default(),
);
Ok(())
})
}
@ -140,9 +160,8 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'a, 'tcx>(
drop_impl_did: DefId,
dtor_predicates: &ty::GenericPredicates<'tcx>,
self_type_did: DefId,
self_to_impl_substs: &Substs<'tcx>)
-> Result<(), ErrorReported>
{
self_to_impl_substs: &Substs<'tcx>,
) -> Result<(), ErrorReported> {
let mut result = Ok(());
// Here is an example, analogous to that from
@ -213,11 +232,17 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'a, 'tcx>(
if !assumptions_in_impl_context.contains(&predicate) {
let item_span = tcx.hir.span(self_type_node_id);
struct_span_err!(tcx.sess, drop_impl_span, E0367,
"The requirement `{}` is added only by the Drop impl.", predicate)
.span_note(item_span,
"The same requirement must be part of \
the struct/enum definition")
struct_span_err!(
tcx.sess,
drop_impl_span,
E0367,
"The requirement `{}` is added only by the Drop impl.",
predicate
).span_note(
item_span,
"The same requirement must be part of \
the struct/enum definition",
)
.emit();
result = Err(ErrorReported);
}
@ -283,18 +308,18 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'gcx, 'tcx>(
ty: Ty<'tcx>,
span: Span,
body_id: ast::NodeId,
scope: region::Scope)
-> Result<(), ErrorReported>
{
debug!("check_safety_of_destructor_if_necessary typ: {:?} scope: {:?}",
ty, scope);
scope: region::Scope,
) -> Result<(), ErrorReported> {
debug!(
"check_safety_of_destructor_if_necessary typ: {:?} scope: {:?}",
ty, scope
);
let parent_scope = match rcx.region_scope_tree.opt_encl_scope(scope) {
Some(parent_scope) => parent_scope,
// If no enclosing scope, then it must be the root scope
// which cannot be outlived.
None => return Ok(())
None => return Ok(()),
};
let parent_scope = rcx.tcx.mk_region(ty::ReScope(parent_scope));
let origin = || infer::SubregionOrigin::SafeDestructor(span);

File diff suppressed because it is too large Load diff

View file

@ -11,6 +11,7 @@
//! Check properties that are required by built-in traits and set
//! up data structures required by type-checking/codegen.
use rustc::infer::SuppressRegionErrors;
use rustc::infer::outlives::env::OutlivesEnvironment;
use rustc::middle::region;
use rustc::middle::lang_items::UnsizeTraitLangItem;
@ -396,6 +397,7 @@ pub fn coerce_unsized_info<'a, 'gcx>(gcx: TyCtxt<'a, 'gcx, 'gcx>,
impl_did,
&region_scope_tree,
&outlives_env,
SuppressRegionErrors::default(),
);
CoerceUnsizedInfo {

View file

@ -1,11 +0,0 @@
error[E0621]: explicit lifetime required in the type of `value`
--> $DIR/issue-16922.rs:14:5
|
LL | fn foo<T: Any>(value: &T) -> Box<Any> {
| -- help: add explicit lifetime `'static` to the type of `value`: `&'static T`
LL | Box::new(value) as Box<Any>
| ^^^^^^^^^^^^^^^ lifetime `'static` required
error: aborting due to previous error
For more information about this error, try `rustc --explain E0621`.

View file

@ -4,7 +4,7 @@ error[E0621]: explicit lifetime required in the type of `value`
LL | fn foo<T: Any>(value: &T) -> Box<Any> {
| -- help: add explicit lifetime `'static` to the type of `value`: `&'static T`
LL | Box::new(value) as Box<Any>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime `'static` required
| ^^^^^^^^^^^^^^^ lifetime `'static` required
error: aborting due to previous error

View file

@ -0,0 +1,91 @@
// Regression test for #53789.
//
// compile-pass
#![feature(nll)]
#![allow(unused_variables)]
use std::collections::BTreeMap;
trait ValueTree {
type Value;
}
trait Strategy {
type Value: ValueTree;
}
type StrategyFor<A> = StrategyType<'static, A>;
type StrategyType<'a, A> = <A as Arbitrary<'a>>::Strategy;
impl<K: ValueTree, V: ValueTree> Strategy for (K, V) {
type Value = TupleValueTree<(K, V)>;
}
impl<K: ValueTree, V: ValueTree> ValueTree for TupleValueTree<(K, V)> {
type Value = BTreeMapValueTree<K, V>;
}
struct TupleValueTree<T> {
tree: T,
}
struct BTreeMapStrategy<K, V>(std::marker::PhantomData<(K, V)>)
where
K: Strategy,
V: Strategy;
struct BTreeMapValueTree<K, V>(std::marker::PhantomData<(K, V)>)
where
K: ValueTree,
V: ValueTree;
impl<K, V> Strategy for BTreeMapStrategy<K, V>
where
K: Strategy,
V: Strategy,
{
type Value = BTreeMapValueTree<K::Value, V::Value>;
}
impl<K, V> ValueTree for BTreeMapValueTree<K, V>
where
K: ValueTree,
V: ValueTree,
{
type Value = BTreeMap<K::Value, V::Value>;
}
trait Arbitrary<'a>: Sized {
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy;
type Parameters;
type Strategy: Strategy<Value = Self::ValueTree>;
type ValueTree: ValueTree<Value = Self>;
}
impl<'a, A, B> Arbitrary<'a> for BTreeMap<A, B>
where
A: Arbitrary<'static>,
B: Arbitrary<'static>,
StrategyFor<A>: 'static,
StrategyFor<B>: 'static,
{
type ValueTree = <Self::Strategy as Strategy>::Value;
type Parameters = (A::Parameters, B::Parameters);
type Strategy = BTreeMapStrategy<A::Strategy, B::Strategy>;
fn arbitrary_with(args: Self::Parameters) -> BTreeMapStrategy<A::Strategy, B::Strategy> {
let (a, b) = args;
btree_map(any_with::<A>(a), any_with::<B>(b))
}
}
fn btree_map<K: Strategy + 'static, V: Strategy>(key: K, value: V) -> BTreeMapStrategy<K, V> {
unimplemented!()
}
fn any_with<'a, A: Arbitrary<'a>>(args: A::Parameters) -> StrategyType<'a, A> {
unimplemented!()
}
fn main() { }

View file

@ -0,0 +1,251 @@
// Regression test for #53789.
//
// compile-pass
#![feature(nll)]
#![allow(unused_variables)]
use std::collections::BTreeMap;
use std::ops::Range;
use std::cmp::Ord;
macro_rules! valuetree {
() => {
type ValueTree =
<Self::Strategy as $crate::Strategy>::Value;
};
}
macro_rules! product_unpack {
($factor: pat) => {
($factor,)
};
($($factor: pat),*) => {
( $( $factor ),* )
};
($($factor: pat),*,) => {
( $( $factor ),* )
};
}
macro_rules! product_type {
($factor: ty) => {
($factor,)
};
($($factor: ty),*) => {
( $( $factor, )* )
};
($($factor: ty),*,) => {
( $( $factor, )* )
};
}
macro_rules! default {
($type: ty, $val: expr) => {
impl Default for $type {
fn default() -> Self { $val.into() }
}
};
}
// Pervasive internal sugar
macro_rules! mapfn {
($(#[$meta:meta])* [$($vis:tt)*]
fn $name:ident[$($gen:tt)*]($parm:ident: $input:ty) -> $output:ty {
$($body:tt)*
}) => {
$(#[$meta])*
#[derive(Clone, Copy)]
$($vis)* struct $name;
impl $($gen)* statics::MapFn<$input> for $name {
type Output = $output;
}
}
}
macro_rules! opaque_strategy_wrapper {
($(#[$smeta:meta])* pub struct $stratname:ident
[$($sgen:tt)*][$($swhere:tt)*]
($innerstrat:ty) -> $stratvtty:ty;
$(#[$vmeta:meta])* pub struct $vtname:ident
[$($vgen:tt)*][$($vwhere:tt)*]
($innervt:ty) -> $actualty:ty;
) => {
$(#[$smeta])* struct $stratname $($sgen)* (std::marker::PhantomData<(K, V)>)
$($swhere)*;
$(#[$vmeta])* struct $vtname $($vgen)* ($innervt) $($vwhere)*;
impl $($sgen)* Strategy for $stratname $($sgen)* $($swhere)* {
type Value = $stratvtty;
}
impl $($vgen)* ValueTree for $vtname $($vgen)* $($vwhere)* {
type Value = $actualty;
}
}
}
trait ValueTree {
type Value;
}
trait Strategy {
type Value : ValueTree;
}
#[derive(Clone)]
struct VecStrategy<T : Strategy> {
element: T,
size: Range<usize>,
}
fn vec<T : Strategy>(element: T, size: Range<usize>)
-> VecStrategy<T> {
VecStrategy {
element: element,
size: size,
}
}
type ValueFor<S> = <<S as Strategy>::Value as ValueTree>::Value;
trait Arbitrary<'a>: Sized {
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy;
type Parameters: Default;
type Strategy: Strategy<Value = Self::ValueTree>;
type ValueTree: ValueTree<Value = Self>;
}
type StrategyFor<A> = StrategyType<'static, A>;
type StrategyType<'a, A> = <A as Arbitrary<'a>>::Strategy;
//#[derive(Clone, PartialEq, Eq, Hash, Debug, From, Into)]
struct SizeBounds(Range<usize>);
default!(SizeBounds, 0..100);
impl From<Range<usize>> for SizeBounds {
fn from(high: Range<usize>) -> Self {
unimplemented!()
}
}
impl From<SizeBounds> for Range<usize> {
fn from(high: SizeBounds) -> Self {
unimplemented!()
}
}
fn any_with<'a, A: Arbitrary<'a>>(args: A::Parameters)
-> StrategyType<'a, A> {
unimplemented!()
}
impl<K: ValueTree, V: ValueTree> Strategy for (K, V) where
<K as ValueTree>::Value: Ord {
type Value = TupleValueTree<(K, V)>;
}
impl<K: ValueTree, V: ValueTree> ValueTree for TupleValueTree<(K, V)> where
<K as ValueTree>::Value: Ord {
type Value = BTreeMapValueTree<K, V>;
}
#[derive(Clone)]
struct VecValueTree<T : ValueTree> {
elements: Vec<T>,
}
#[derive(Clone, Copy)]
struct TupleValueTree<T> {
tree: T,
}
opaque_strategy_wrapper! {
#[derive(Clone)]
pub struct BTreeMapStrategy[<K, V>]
[where K : Strategy, V : Strategy, ValueFor<K> : Ord](
statics::Filter<statics::Map<VecStrategy<(K,V)>,
VecToBTreeMap>, MinSize>)
-> BTreeMapValueTree<K::Value, V::Value>;
#[derive(Clone)]
pub struct BTreeMapValueTree[<K, V>]
[where K : ValueTree, V : ValueTree, K::Value : Ord](
statics::Filter<statics::Map<VecValueTree<TupleValueTree<(K, V)>>,
VecToBTreeMap>, MinSize>)
-> BTreeMap<K::Value, V::Value>;
}
type RangedParams2<A, B> = product_type![SizeBounds, A, B];
impl<'a, A, B> Arbitrary<'a> for BTreeMap<A, B>
where
A: Arbitrary<'static> + Ord,
B: Arbitrary<'static>,
StrategyFor<A>: 'static,
StrategyFor<B>: 'static,
{
valuetree!();
type Parameters = RangedParams2<A::Parameters, B::Parameters>;
type Strategy = BTreeMapStrategy<A::Strategy, B::Strategy>;
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
let product_unpack![range, a, b] = args;
btree_map(any_with::<A>(a), any_with::<B>(b), range.into())
}
}
#[derive(Clone, Copy)]
struct MinSize(usize);
mapfn! {
[] fn VecToBTreeMap[<K : Ord, V>]
(vec: Vec<(K, V)>) -> BTreeMap<K, V>
{
vec.into_iter().collect()
}
}
fn btree_map<K : Strategy + 'static, V : Strategy + 'static>
(key: K, value: V, size: Range<usize>)
-> BTreeMapStrategy<K, V>
where ValueFor<K> : Ord {
unimplemented!()
}
mod statics {
pub(super) trait MapFn<T> {
type Output;
}
#[derive(Clone)]
pub struct Filter<S, F> {
source: S,
fun: F,
}
impl<S, F> Filter<S, F> {
pub fn new(source: S, whence: String, filter: F) -> Self {
unimplemented!()
}
}
#[derive(Clone)]
pub struct Map<S, F> {
source: S,
fun: F,
}
impl<S, F> Map<S, F> {
pub fn new(source: S, fun: F) -> Self {
unimplemented!()
}
}
}
fn main() { }

View file

@ -0,0 +1,27 @@
// Test that when we infer the lifetime to a subset of the fn body, it
// works out.
//
// compile-pass
trait MyTrait<'a> {
type Output;
}
fn foo1<T>()
where
for<'x> T: MyTrait<'x>,
{
// Here the region `'c` in `<T as MyTrait<'c>>::Output` will be
// inferred to a subset of the fn body.
let x = bar::<T::Output>();
drop(x);
}
fn bar<'a, T>() -> &'a ()
where
T: 'a,
{
&()
}
fn main() {}

View file

@ -74,19 +74,10 @@ where
T: Anything<'b>,
T::AssocType: 'a,
{
// This error is unfortunate. This code ought to type-check: we
// are projecting `<T as Anything<'b>>::AssocType`, and we know
// that this outlives `'a` because of the where-clause. However,
// the way the region checker works, we don't register this
// outlives obligation, and hence we get an error: this is because
// what we see is a projection like `<T as
// Anything<'?0>>::AssocType`, and we don't yet know if `?0` will
// equal `'b` or not, so we ignore the where-clause. Obviously we
// can do better here with a more involved verification step.
// We are projecting `<T as Anything<'b>>::AssocType`, and we know
// that this outlives `'a` because of the where-clause.
with_signature(cell, t, |cell, t| require(cell, t));
//~^ ERROR the parameter type `T` may not live long enough
//~| ERROR
}
#[rustc_regions]

View file

@ -106,7 +106,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ closure body requires that `'b` must outlive `'a`
note: External requirements
--> $DIR/projection-one-region-closure.rs:87:29
--> $DIR/projection-one-region-closure.rs:80:29
|
LL | with_signature(cell, t, |cell, t| require(cell, t));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -119,8 +119,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t));
extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T))
]
= note: number of external vids: 4
= note: where T: '_#3r
= note: where '_#2r: '_#3r
= note: where <T as Anything<ReClosureBound('_#2r)>>::AssocType: '_#3r
note: No external requirements
--> $DIR/projection-one-region-closure.rs:72:1
@ -130,7 +129,7 @@ LL | | where
LL | | T: Anything<'b>,
LL | | T::AssocType: 'a,
... |
LL | | //~| ERROR
LL | | with_signature(cell, t, |cell, t| require(cell, t));
LL | | }
| |_^
|
@ -140,27 +139,8 @@ LL | | }
T
]
error[E0309]: the parameter type `T` may not live long enough
--> $DIR/projection-one-region-closure.rs:87:29
|
LL | with_signature(cell, t, |cell, t| require(cell, t));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `T: ReEarlyBound(0, 'a)`...
error: unsatisfied lifetime constraints
--> $DIR/projection-one-region-closure.rs:87:29
|
LL | fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
...
LL | with_signature(cell, t, |cell, t| require(cell, t));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ closure body requires that `'b` must outlive `'a`
note: External requirements
--> $DIR/projection-one-region-closure.rs:99:29
--> $DIR/projection-one-region-closure.rs:90:29
|
LL | with_signature(cell, t, |cell, t| require(cell, t));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -177,7 +157,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t));
= note: where '_#2r: '_#3r
note: No external requirements
--> $DIR/projection-one-region-closure.rs:93:1
--> $DIR/projection-one-region-closure.rs:84:1
|
LL | / fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
LL | | where
@ -194,6 +174,6 @@ LL | | }
T
]
error: aborting due to 6 previous errors
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0309`.

View file

@ -64,18 +64,10 @@ where
T: Anything<'b>,
T::AssocType: 'a,
{
// This error is unfortunate. This code ought to type-check: we
// are projecting `<T as Anything<'b>>::AssocType`, and we know
// that this outlives `'a` because of the where-clause. However,
// the way the region checker works, we don't register this
// outlives obligation, and hence we get an error: this is because
// what we see is a projection like `<T as
// Anything<'?0>>::AssocType`, and we don't yet know if `?0` will
// equal `'b` or not, so we ignore the where-clause. Obviously we
// can do better here with a more involved verification step.
// We are projecting `<T as Anything<'b>>::AssocType`, and we know
// that this outlives `'a` because of the where-clause.
with_signature(cell, t, |cell, t| require(cell, t));
//~^ ERROR
}
#[rustc_regions]

View file

@ -88,7 +88,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ closure body requires that `'b` must outlive `'a`
note: External requirements
--> $DIR/projection-one-region-trait-bound-closure.rs:77:29
--> $DIR/projection-one-region-trait-bound-closure.rs:70:29
|
LL | with_signature(cell, t, |cell, t| require(cell, t));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -101,7 +101,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t));
extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T))
]
= note: number of external vids: 4
= note: where '_#2r: '_#3r
= note: where <T as Anything<ReClosureBound('_#2r)>>::AssocType: '_#3r
note: No external requirements
--> $DIR/projection-one-region-trait-bound-closure.rs:62:1
@ -111,7 +111,7 @@ LL | | where
LL | | T: Anything<'b>,
LL | | T::AssocType: 'a,
... |
LL | | //~^ ERROR
LL | | with_signature(cell, t, |cell, t| require(cell, t));
LL | | }
| |_^
|
@ -121,19 +121,8 @@ LL | | }
T
]
error: unsatisfied lifetime constraints
--> $DIR/projection-one-region-trait-bound-closure.rs:77:29
|
LL | fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
...
LL | with_signature(cell, t, |cell, t| require(cell, t));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ closure body requires that `'b` must outlive `'a`
note: External requirements
--> $DIR/projection-one-region-trait-bound-closure.rs:87:29
--> $DIR/projection-one-region-trait-bound-closure.rs:79:29
|
LL | with_signature(cell, t, |cell, t| require(cell, t));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -149,7 +138,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t));
= note: where '_#2r: '_#3r
note: No external requirements
--> $DIR/projection-one-region-trait-bound-closure.rs:82:1
--> $DIR/projection-one-region-trait-bound-closure.rs:74:1
|
LL | / fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
LL | | where
@ -167,7 +156,7 @@ LL | | }
]
note: External requirements
--> $DIR/projection-one-region-trait-bound-closure.rs:99:29
--> $DIR/projection-one-region-trait-bound-closure.rs:91:29
|
LL | with_signature(cell, t, |cell, t| require(cell, t));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -182,7 +171,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t));
= note: where '_#1r: '_#2r
note: No external requirements
--> $DIR/projection-one-region-trait-bound-closure.rs:91:1
--> $DIR/projection-one-region-trait-bound-closure.rs:83:1
|
LL | / fn one_region<'a, T>(cell: Cell<&'a ()>, t: T)
LL | | where
@ -198,5 +187,5 @@ LL | | }
T
]
error: aborting due to 3 previous errors
error: aborting due to 2 previous errors

View file

@ -65,18 +65,10 @@ where
T: Anything<'b, 'c>,
T::AssocType: 'a,
{
// This error is unfortunate. This code ought to type-check: we
// are projecting `<T as Anything<'b>>::AssocType`, and we know
// that this outlives `'a` because of the where-clause. However,
// the way the region checker works, we don't register this
// outlives obligation, and hence we get an error: this is because
// what we see is a projection like `<T as
// Anything<'?0>>::AssocType`, and we don't yet know if `?0` will
// equal `'b` or not, so we ignore the where-clause. Obviously we
// can do better here with a more involved verification step.
// We are projecting `<T as Anything<'b>>::AssocType`, and we know
// that this outlives `'a` because of the where-clause.
with_signature(cell, t, |cell, t| require(cell, t));
//~^ ERROR associated type `<T as Anything<'_#6r, '_#7r>>::AssocType` may not live long enough
}
#[rustc_regions]

View file

@ -86,7 +86,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t));
= help: consider adding an explicit lifetime bound `<T as Anything<'_#6r, '_#7r>>::AssocType: ReEarlyBound(0, 'a)`...
note: External requirements
--> $DIR/projection-two-region-trait-bound-closure.rs:78:29
--> $DIR/projection-two-region-trait-bound-closure.rs:71:29
|
LL | with_signature(cell, t, |cell, t| require(cell, t));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -110,7 +110,7 @@ LL | | where
LL | | T: Anything<'b, 'c>,
LL | | T::AssocType: 'a,
... |
LL | | //~^ ERROR associated type `<T as Anything<'_#6r, '_#7r>>::AssocType` may not live long enough
LL | | with_signature(cell, t, |cell, t| require(cell, t));
LL | | }
| |_^
|
@ -121,16 +121,8 @@ LL | | }
T
]
error[E0309]: the associated type `<T as Anything<'_#6r, '_#7r>>::AssocType` may not live long enough
--> $DIR/projection-two-region-trait-bound-closure.rs:78:29
|
LL | with_signature(cell, t, |cell, t| require(cell, t));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `<T as Anything<'_#6r, '_#7r>>::AssocType: ReEarlyBound(0, 'a)`...
note: External requirements
--> $DIR/projection-two-region-trait-bound-closure.rs:88:29
--> $DIR/projection-two-region-trait-bound-closure.rs:80:29
|
LL | with_signature(cell, t, |cell, t| require(cell, t));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -147,7 +139,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t));
= note: where <T as Anything<ReClosureBound('_#2r), ReClosureBound('_#3r)>>::AssocType: '_#4r
note: No external requirements
--> $DIR/projection-two-region-trait-bound-closure.rs:83:1
--> $DIR/projection-two-region-trait-bound-closure.rs:75:1
|
LL | / fn elements_outlive1<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T)
LL | | where
@ -166,7 +158,7 @@ LL | | }
]
note: External requirements
--> $DIR/projection-two-region-trait-bound-closure.rs:97:29
--> $DIR/projection-two-region-trait-bound-closure.rs:89:29
|
LL | with_signature(cell, t, |cell, t| require(cell, t));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -183,7 +175,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t));
= note: where <T as Anything<ReClosureBound('_#2r), ReClosureBound('_#3r)>>::AssocType: '_#4r
note: No external requirements
--> $DIR/projection-two-region-trait-bound-closure.rs:92:1
--> $DIR/projection-two-region-trait-bound-closure.rs:84:1
|
LL | / fn elements_outlive2<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T)
LL | | where
@ -202,7 +194,7 @@ LL | | }
]
note: External requirements
--> $DIR/projection-two-region-trait-bound-closure.rs:105:29
--> $DIR/projection-two-region-trait-bound-closure.rs:97:29
|
LL | with_signature(cell, t, |cell, t| require(cell, t));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -218,7 +210,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t));
= note: where <T as Anything<ReClosureBound('_#1r), ReClosureBound('_#1r)>>::AssocType: '_#2r
note: No external requirements
--> $DIR/projection-two-region-trait-bound-closure.rs:101:1
--> $DIR/projection-two-region-trait-bound-closure.rs:93:1
|
LL | / fn two_regions<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
LL | | where
@ -235,7 +227,7 @@ LL | | }
]
error: unsatisfied lifetime constraints
--> $DIR/projection-two-region-trait-bound-closure.rs:105:29
--> $DIR/projection-two-region-trait-bound-closure.rs:97:29
|
LL | fn two_regions<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
| -- -- lifetime `'b` defined here
@ -246,7 +238,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ closure body requires that `'b` must outlive `'a`
note: External requirements
--> $DIR/projection-two-region-trait-bound-closure.rs:115:29
--> $DIR/projection-two-region-trait-bound-closure.rs:107:29
|
LL | with_signature(cell, t, |cell, t| require(cell, t));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -262,7 +254,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t));
= note: where <T as Anything<ReClosureBound('_#2r), ReClosureBound('_#2r)>>::AssocType: '_#3r
note: No external requirements
--> $DIR/projection-two-region-trait-bound-closure.rs:110:1
--> $DIR/projection-two-region-trait-bound-closure.rs:102:1
|
LL | / fn two_regions_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T)
LL | | where
@ -280,7 +272,7 @@ LL | | }
]
note: External requirements
--> $DIR/projection-two-region-trait-bound-closure.rs:127:29
--> $DIR/projection-two-region-trait-bound-closure.rs:119:29
|
LL | with_signature(cell, t, |cell, t| require(cell, t));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -295,7 +287,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t));
= note: where <T as Anything<ReClosureBound('_#1r), ReClosureBound('_#1r)>>::AssocType: '_#2r
note: No external requirements
--> $DIR/projection-two-region-trait-bound-closure.rs:119:1
--> $DIR/projection-two-region-trait-bound-closure.rs:111:1
|
LL | / fn one_region<'a, T>(cell: Cell<&'a ()>, t: T)
LL | | where
@ -311,6 +303,6 @@ LL | | }
T
]
error: aborting due to 4 previous errors
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0309`.

View file

@ -0,0 +1,36 @@
#![feature(nll)]
// Test that we are able to establish that `<T as
// MyTrait<'a>>::Output` outlives `'b` here. We need to prove however
// that `<T as MyTrait<'a>>::Output` outlives `'a`, so we also have to
// prove that `'b: 'a`.
trait MyTrait<'a> {
type Output;
}
fn foo1<'a, 'b, T>() -> &'a ()
where
T: MyTrait<'a>,
<T as MyTrait<'a>>::Output: 'b,
{
bar::<T::Output>() //~ ERROR may not live long enough
}
fn foo2<'a, 'b, T>() -> &'a ()
where
T: MyTrait<'a>,
<T as MyTrait<'a>>::Output: 'b,
'b: 'a,
{
bar::<T::Output>() // OK
}
fn bar<'a, T>() -> &'a ()
where
T: 'a,
{
&()
}
fn main() {}

View file

@ -0,0 +1,11 @@
error[E0309]: the associated type `<T as MyTrait<'_>>::Output` may not live long enough
--> $DIR/projection-where-clause-env-wrong-bound.rs:17:5
|
LL | bar::<T::Output>() //~ ERROR may not live long enough
| ^^^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `<T as MyTrait<'_>>::Output: 'a`...
error: aborting due to previous error
For more information about this error, try `rustc --explain E0309`.

View file

@ -0,0 +1,11 @@
error[E0309]: the associated type `<T as MyTrait<'_>>::Output` may not live long enough
--> $DIR/projection-where-clause-env-wrong-lifetime.rs:14:5
|
LL | bar::<<T as MyTrait<'a>>::Output>()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `<T as MyTrait<'_>>::Output: 'a`...
error: aborting due to previous error
For more information about this error, try `rustc --explain E0309`.

View file

@ -0,0 +1,25 @@
// Test that if we need to prove that `<T as MyTrait<'a>>::Output:
// 'a`, but we only know that `<T as MyTrait<'b>>::Output: 'a`, that
// doesn't suffice.
trait MyTrait<'a> {
type Output;
}
fn foo1<'a, 'b, T>() -> &'a ()
where
for<'x> T: MyTrait<'x>,
<T as MyTrait<'b>>::Output: 'a,
{
bar::<<T as MyTrait<'a>>::Output>()
//~^ ERROR the associated type `<T as MyTrait<'a>>::Output` may not live long enough
}
fn bar<'a, T>() -> &'a ()
where
T: 'a,
{
&()
}
fn main() {}

View file

@ -0,0 +1,16 @@
error[E0309]: the associated type `<T as MyTrait<'a>>::Output` may not live long enough
--> $DIR/projection-where-clause-env-wrong-lifetime.rs:14:5
|
LL | bar::<<T as MyTrait<'a>>::Output>()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `<T as MyTrait<'a>>::Output: 'a`...
note: ...so that the type `<T as MyTrait<'a>>::Output` will meet its required lifetime bounds
--> $DIR/projection-where-clause-env-wrong-lifetime.rs:14:5
|
LL | bar::<<T as MyTrait<'a>>::Output>()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0309`.

View file

@ -0,0 +1,30 @@
#![feature(nll)]
// Test that when we have a `<T as MyTrait<'a>>::Output: 'a`
// relationship in the environment we take advantage of it. In this
// case, that means we **don't** have to prove that `T: 'a`.
//
// Regression test for #53121.
//
// compile-pass
trait MyTrait<'a> {
type Output;
}
fn foo<'a, T>() -> &'a ()
where
T: MyTrait<'a>,
<T as MyTrait<'a>>::Output: 'a,
{
bar::<T::Output>()
}
fn bar<'a, T>() -> &'a ()
where
T: 'a,
{
&()
}
fn main() {}

View file

@ -0,0 +1,26 @@
#![feature(nll)]
// Test that we are NOT able to establish that `<T as
// MyTrait<'a>>::Output: 'a` outlives `'a` here -- we have only one
// recourse, which is to prove that `T: 'a` and `'a: 'a`, but we don't
// know that `T: 'a`.
trait MyTrait<'a> {
type Output;
}
fn foo<'a, T>() -> &'a ()
where
T: MyTrait<'a>,
{
bar::<T::Output>() //~ ERROR the parameter type `T` may not live long enough
}
fn bar<'a, T>() -> &'a ()
where
T: 'a,
{
&()
}
fn main() {}

View file

@ -0,0 +1,11 @@
error[E0309]: the parameter type `T` may not live long enough
--> $DIR/projection-where-clause-none.rs:16:5
|
LL | bar::<T::Output>() //~ ERROR the parameter type `T` may not live long enough
| ^^^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `T: 'a`...
error: aborting due to previous error
For more information about this error, try `rustc --explain E0309`.

View file

@ -0,0 +1,27 @@
#![feature(nll)]
// Test that we are able to establish that `<T as
// MyTrait<'a>>::Output: 'a` outlives `'a` (because the trait says
// so).
//
// compile-pass
trait MyTrait<'a> {
type Output: 'a;
}
fn foo<'a, T>() -> &'a ()
where
T: MyTrait<'a>,
{
bar::<T::Output>()
}
fn bar<'a, T>() -> &'a ()
where
T: 'a,
{
&()
}
fn main() {}

View file

@ -9,7 +9,7 @@ note: first, the lifetime cannot outlive the lifetime 'a as defined on the funct
|
LL | fn load3<'a,'b>(ss: &'a SomeTrait) -> &'b SomeTrait {
| ^^
note: ...so that the type `(dyn SomeTrait + 'a)` is not borrowed for too long
note: ...so that reference does not outlive borrowed content
--> $DIR/object-lifetime-default-elision.rs:81:5
|
LL | ss

View file

@ -9,7 +9,7 @@ note: first, the lifetime cannot outlive the lifetime 'a as defined on the funct
|
LL | fn borrowed_receiver_different_lifetimes<'a,'b>(x: &'a Foo) -> &'b () {
| ^^
note: ...so that the type `(dyn Foo + 'a)` is not borrowed for too long
note: ...so that reference does not outlive borrowed content
--> $DIR/region-object-lifetime-2.rs:20:5
|
LL | x.borrowed() //~ ERROR cannot infer

View file

@ -26,7 +26,7 @@ note: first, the lifetime cannot outlive the lifetime 'a as defined on the funct
|
LL | fn foo3<'a,'b>(x: &'a mut Dummy) -> &'b mut Dummy {
| ^^
note: ...so that the type `(dyn Dummy + 'a)` is not borrowed for too long
note: ...so that reference does not outlive borrowed content
--> $DIR/regions-trait-object-subtyping.rs:25:5
|
LL | x //~ ERROR lifetime bound not satisfied