Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduce "type must be valid for" into lexical region solver #55988

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/librustc_typeck/check/dropck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,16 +314,16 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'gcx, 'tcx>(
// which cannot be outlived.
None => return Ok(()),
};
let parent_scope = rcx.tcx.mk_region(ty::ReScope(parent_scope));
let parent_scope_region = rcx.tcx.mk_region(ty::ReScope(parent_scope));
let origin = || infer::SubregionOrigin::SafeDestructor(span);
let cause = &ObligationCause::misc(span, body_id);
let infer_ok = rcx.infcx.at(cause, rcx.fcx.param_env).dropck_outlives(ty);
debug!("dropck_outlives = {:#?}", infer_ok);
let kinds = rcx.fcx.register_infer_ok_obligations(infer_ok);
for kind in kinds {
match kind.unpack() {
UnpackedKind::Lifetime(r) => rcx.sub_regions(origin(), parent_scope, r),
UnpackedKind::Type(ty) => rcx.type_must_outlive(origin(), ty, parent_scope),
UnpackedKind::Lifetime(r) => rcx.sub_regions(origin(), parent_scope_region, r),
UnpackedKind::Type(ty) => rcx.type_must_be_valid_for_scope(origin(), ty, parent_scope),
}
}
Ok(())
Expand Down
110 changes: 72 additions & 38 deletions src/librustc_typeck/check/regionck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,10 +373,13 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
body.id(),
call_site_scope
);
let call_site_region = self.tcx.mk_region(ty::ReScope(call_site_scope));

let body_hir_id = self.tcx.hir.node_to_hir_id(body_id.node_id);
self.type_of_node_must_outlive(infer::CallReturn(span), body_hir_id, call_site_region);
self.type_of_node_must_be_valid_for_scope(
infer::CallReturn(span),
body_hir_id,
call_site_scope,
);

self.constrain_opaque_types(
&self.fcx.opaque_types.borrow(),
Expand Down Expand Up @@ -434,10 +437,9 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
// that the lifetime of any regions that appear in a
// variable's type enclose at least the variable's scope.
let var_scope = self.region_scope_tree.var_scope(hir_id.local_id);
let var_region = self.tcx.mk_region(ty::ReScope(var_scope));

let origin = infer::BindingTypeIsNotValidAtDecl(span);
self.type_of_node_must_outlive(origin, hir_id, var_region);
self.type_of_node_must_be_valid_for_scope(origin, hir_id, var_scope);

let typ = self.resolve_node_type(hir_id);
let body_id = self.body_id;
Expand Down Expand Up @@ -520,14 +522,15 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> {
// scope of that expression. This also guarantees basic WF.
let expr_ty = self.resolve_node_type(expr.hir_id);
// the region corresponding to this expression
let expr_region = self.tcx.mk_region(ty::ReScope(region::Scope {
let expr_scope = region::Scope {
id: expr.hir_id.local_id,
data: region::ScopeData::Node,
}));
self.type_must_outlive(
};
let expr_region = self.tcx.mk_region(ty::ReScope(expr_scope));
self.type_must_be_valid_for_scope(
infer::ExprTypeIsNotInScope(expr_ty, expr.span),
expr_ty,
expr_region,
expr_scope,
);

let is_method_call = self.tables.borrow().is_method_call(expr);
Expand All @@ -546,7 +549,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> {
};

let substs = self.tables.borrow().node_substs(expr.hir_id);
self.substs_wf_in_scope(origin, substs, expr.span, expr_region);
self.substs_wf_in_scope(origin, substs, expr.span, expr_scope);
// Arguments (sub-expressions) are checked via `constrain_call`, below.
}

Expand All @@ -572,7 +575,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> {
hir::ExprKind::Path(_) => {
let substs = self.tables.borrow().node_substs(expr.hir_id);
let origin = infer::ParameterOrigin::Path;
self.substs_wf_in_scope(origin, substs, expr.span, expr_region);
self.substs_wf_in_scope(origin, substs, expr.span, expr_scope);
}

hir::ExprKind::Call(ref callee, ref args) => {
Expand Down Expand Up @@ -619,7 +622,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> {
let lhs_ty = self.resolve_expr_type_adjusted(&lhs);
let rhs_ty = self.resolve_expr_type_adjusted(&rhs);
for &ty in &[lhs_ty, rhs_ty] {
self.type_must_outlive(infer::Operand(expr.span), ty, expr_region);
self.type_must_be_valid_for_scope(infer::Operand(expr.span), ty, expr_scope);
}
intravisit::walk_expr(self, expr);
}
Expand Down Expand Up @@ -674,7 +677,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> {
// FIXME(https://github.com/rust-lang/rfcs/issues/811)
// nested method calls requires that this rule change
let ty0 = self.resolve_node_type(expr.hir_id);
self.type_must_outlive(infer::AddrOf(expr.span), ty0, expr_region);
self.type_must_be_valid_for_scope(infer::AddrOf(expr.span), ty0, expr_scope);
intravisit::walk_expr(self, expr);
}

Expand Down Expand Up @@ -705,16 +708,15 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> {
}

hir::ExprKind::Ret(Some(ref ret_expr)) => {
let call_site_scope = self.call_site_scope;
let call_site_scope = self.call_site_scope.unwrap();
debug!(
"visit_expr ExprKind::Ret ret_expr.id {} call_site_scope: {:?}",
ret_expr.id, call_site_scope
);
let call_site_region = self.tcx.mk_region(ty::ReScope(call_site_scope.unwrap()));
self.type_of_node_must_outlive(
self.type_of_node_must_be_valid_for_scope(
infer::CallReturn(ret_expr.span),
ret_expr.hir_id,
call_site_region,
call_site_scope,
);
intravisit::walk_expr(self, expr);
}
Expand Down Expand Up @@ -813,26 +815,29 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
id: call_expr.hir_id.local_id,
data: region::ScopeData::Node,
};
let callee_region = self.tcx.mk_region(ty::ReScope(callee_scope));

debug!("callee_region={:?}", callee_region);
debug!("callee_scope={:?}", callee_scope);

for arg_expr in arg_exprs {
debug!("Argument: {:?}", arg_expr);

// ensure that any regions appearing in the argument type are
// valid for at least the lifetime of the function:
self.type_of_node_must_outlive(
self.type_of_node_must_be_valid_for_scope(
infer::CallArg(arg_expr.span),
arg_expr.hir_id,
callee_region,
callee_scope,
);
}

// as loop above, but for receiver
if let Some(r) = receiver {
debug!("receiver: {:?}", r);
self.type_of_node_must_outlive(infer::CallRcvr(r.span), r.hir_id, callee_region);
self.type_of_node_must_be_valid_for_scope(
infer::CallRcvr(r.span),
r.hir_id,
callee_scope,
);
}
}

Expand Down Expand Up @@ -867,10 +872,11 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
// expression.
self.check_safety_of_rvalue_destructor_if_necessary(&cmt, expr.span);

let expr_region = self.tcx.mk_region(ty::ReScope(region::Scope {
let expr_scope = region::Scope {
id: expr.hir_id.local_id,
data: region::ScopeData::Node,
}));
};
let expr_region = self.tcx.mk_region(ty::ReScope(expr_scope));
for adjustment in adjustments {
debug!(
"constrain_adjustments: adjustment={:?}, cmt={:?}",
Expand Down Expand Up @@ -905,8 +911,8 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
);

// Specialized version of constrain_call.
self.type_must_outlive(infer::CallRcvr(expr.span), input, expr_region);
self.type_must_outlive(infer::CallReturn(expr.span), output, expr_region);
self.type_must_be_valid_for_scope(infer::CallRcvr(expr.span), input, expr_scope);
self.type_must_be_valid_for_scope(infer::CallReturn(expr.span), output, expr_scope);
}

if let adjustment::Adjust::Borrow(ref autoref) = adjustment.kind {
Expand All @@ -916,10 +922,10 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
// the current node.
//
// FIXME(#6268) remove to support nested method calls
self.type_of_node_must_outlive(
self.type_of_node_must_be_valid_for_scope(
infer::AutoBorrow(expr.span),
expr.hir_id,
expr_region,
expr_scope,
);
}

Expand Down Expand Up @@ -999,13 +1005,16 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
}
}

/// Guarantees that any lifetimes which appear in the type of the node `id` (after applying
/// adjustments) are valid for at least `minimum_lifetime`
fn type_of_node_must_outlive(
/// Requires that all regions in the (fully adjusted) type of the
/// node `hir_id` are **valid** during the scope. This means that
/// they have to outlive `scope`. This implies `T: scope` but is
/// actually stronger than that in the case of projections. This
/// helps overcome some weakenesses in our inference (see #55756).
fn type_of_node_must_be_valid_for_scope(
&mut self,
origin: infer::SubregionOrigin<'tcx>,
hir_id: hir::HirId,
minimum_lifetime: ty::Region<'tcx>,
scope: region::Scope,
) {
// Try to resolve the type. If we encounter an error, then typeck
// is going to fail anyway, so just stop here and let typeck
Expand All @@ -1021,10 +1030,34 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
let ty = self.resolve_type(ty);
debug!(
"constrain_regions_in_type_of_node(\
ty={}, ty0={}, id={:?}, minimum_lifetime={:?})",
ty, ty0, hir_id, minimum_lifetime
ty={}, ty0={}, id={:?}, scope={:?})",
ty, ty0, hir_id, scope
);

self.type_must_be_valid_for_scope(origin, ty, scope)
}

/// Requires that all regions in the type `T` are **valid** during
/// the scope. This means that they have to outlive `scope`. This
/// implies `T: scope` but is actually stronger than that in the
/// case of projections. This helps overcome some weakenesses in
/// our inference (see #55756).
///
/// (This is the lexical equivalent of NLL's "liveness rules",
/// which require that any region which appears at the point P
/// must contain the point P.)
pub fn type_must_be_valid_for_scope(
&self,
origin: infer::SubregionOrigin<'tcx>,
ty: Ty<'tcx>,
scope: region::Scope,
) {
let scope_region = self.tcx.mk_region(ty::ReScope(scope));
let ty = self.resolve_type_vars_if_possible(&ty);
self.tcx.for_each_free_region(
&ty,
|ty_region| self.sub_regions(origin.clone(), scope_region, ty_region),
);
self.type_must_outlive(origin, ty, minimum_lifetime);
}

/// Adds constraints to inference such that `T: 'a` holds (or
Expand Down Expand Up @@ -1408,25 +1441,26 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> {
origin: infer::ParameterOrigin,
substs: &Substs<'tcx>,
expr_span: Span,
expr_region: ty::Region<'tcx>,
expr_scope: region::Scope,
) {
debug!(
"substs_wf_in_scope(substs={:?}, \
expr_region={:?}, \
expr_scope={:?}, \
origin={:?}, \
expr_span={:?})",
substs, expr_region, origin, expr_span
substs, expr_scope, origin, expr_span
);

let origin = infer::ParameterInScope(origin, expr_span);

let expr_region = self.tcx.mk_region(ty::ReScope(expr_scope));
for region in substs.regions() {
self.sub_regions(origin.clone(), expr_region, region);
}

for ty in substs.types() {
let ty = self.resolve_type(ty);
self.type_must_outlive(origin.clone(), ty, expr_region);
self.type_must_be_valid_for_scope(origin.clone(), ty, expr_scope);
}
}
}
6 changes: 3 additions & 3 deletions src/test/incremental/hashes/closure_expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,14 @@ pub fn add_parameter() {
// Change parameter pattern ----------------------------------------------------
#[cfg(cfail1)]
pub fn change_parameter_pattern() {
let _ = |x: &u32| x;
let _ = |x: &u32| { println!("{}", x); };
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2", except="HirBody, MirValidated, TypeckTables")]
#[rustc_clean(cfg="cfail2", except="HirBody, TypeckTables")]
#[rustc_clean(cfg="cfail3")]
pub fn change_parameter_pattern() {
let _ = |&x: &u32| x;
let _ = |&x: &u32| { println!("{}", x); };
}


Expand Down
8 changes: 4 additions & 4 deletions src/test/ui/borrowck/borrowck-describe-lvalue.ast.nll.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ LL | *y = 1;
| ------ first borrow later used here

error[E0499]: cannot borrow `x` as mutable more than once at a time
--> $DIR/borrowck-describe-lvalue.rs:307:20
--> $DIR/borrowck-describe-lvalue.rs:308:20
|
LL | let y = &mut x;
| ------ first mutable borrow occurs here
Expand All @@ -26,10 +26,10 @@ error: captured variable cannot escape `FnMut` closure body
LL | || {
| - inferred to be a `FnMut` closure
LL | / || { //[mir]~ ERROR captured variable cannot escape `FnMut` closure body
LL | | //[ast]~^ ERROR lifetime of `x` is too short
LL | | let y = &mut x;
LL | | &mut x; //[ast]~ ERROR cannot borrow `**x` as mutable more than once at a time
LL | | //[mir]~^ ERROR cannot borrow `x` as mutable more than once at a time
LL | | *y = 1;
... |
LL | | drop(y);
LL | | }
| |_________________^ returns a closure that contains a reference to a captured variable, which then escapes the closure body
Expand Down Expand Up @@ -370,7 +370,7 @@ LL | drop(x);
This warning will become a hard error in the future.

error[E0382]: use of moved value: `x`
--> $DIR/borrowck-describe-lvalue.rs:318:22
--> $DIR/borrowck-describe-lvalue.rs:319:22
|
LL | drop(x);
| - value moved here
Expand Down
31 changes: 27 additions & 4 deletions src/test/ui/borrowck/borrowck-describe-lvalue.ast.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,31 @@ LL | &mut x; //[ast]~ ERROR cannot borrow `**x` as mutable more than
LL | };
| - first borrow ends here

error[E0598]: lifetime of `x` is too short to guarantee its contents can be safely reborrowed
--> $DIR/borrowck-describe-lvalue.rs:305:16
|
LL | || { //[mir]~ ERROR captured variable cannot escape `FnMut` closure body
| ^^
|
note: `x` would have to be valid for the expression at 304:12...
--> $DIR/borrowck-describe-lvalue.rs:304:12
|
LL | / || {
LL | | || { //[mir]~ ERROR captured variable cannot escape `FnMut` closure body
LL | | //[ast]~^ ERROR lifetime of `x` is too short
LL | | let y = &mut x;
... |
LL | | }
LL | | };
| |____________^
note: ...but `x` is only valid for the lifetime as defined on the body at 304:12
--> $DIR/borrowck-describe-lvalue.rs:304:12
|
LL | || {
| ^^

error[E0499]: cannot borrow `**x` as mutable more than once at a time
--> $DIR/borrowck-describe-lvalue.rs:307:25
--> $DIR/borrowck-describe-lvalue.rs:308:25
|
LL | let y = &mut x;
| - first mutable borrow occurs here
Expand All @@ -231,7 +254,7 @@ LL | }
| - first borrow ends here

error[E0382]: use of moved value: `x`
--> $DIR/borrowck-describe-lvalue.rs:318:22
--> $DIR/borrowck-describe-lvalue.rs:319:22
|
LL | drop(x);
| - value moved here
Expand All @@ -240,7 +263,7 @@ LL | drop(x); //[ast]~ ERROR use of moved value: `x`
|
= note: move occurs because `x` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait

error: aborting due to 26 previous errors
error: aborting due to 27 previous errors

Some errors occurred: E0382, E0499, E0502, E0503.
Some errors occurred: E0382, E0499, E0502, E0503, E0598.
For more information about an error, try `rustc --explain E0382`.
Loading