mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
Move checks for closure bounds out of kind.rs
This commit is contained in:
parent
034f69ec4b
commit
2ec305d1bc
@ -146,10 +146,7 @@ register_diagnostics!(
|
||||
E0139,
|
||||
E0140,
|
||||
E0141,
|
||||
E0143,
|
||||
E0144,
|
||||
E0145,
|
||||
E0146,
|
||||
E0152,
|
||||
E0153,
|
||||
E0154,
|
||||
|
@ -30,11 +30,6 @@ pub struct Context<'a,'tcx:'a> {
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> {
|
||||
fn visit_fn(&mut self, fk: visit::FnKind, fd: &'v FnDecl,
|
||||
b: &'v Block, s: Span, n: NodeId) {
|
||||
check_fn(self, fk, fd, b, s, n);
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, t: &Ty) {
|
||||
check_ty(self, t);
|
||||
}
|
||||
@ -48,110 +43,6 @@ pub fn check_crate(tcx: &ty::ctxt) {
|
||||
tcx.sess.abort_if_errors();
|
||||
}
|
||||
|
||||
// Yields the appropriate function to check the kind of closed over
|
||||
// variables. `id` is the NodeId for some expression that creates the
|
||||
// closure.
|
||||
fn with_appropriate_checker(cx: &Context,
|
||||
id: NodeId,
|
||||
fn_span: Span,
|
||||
b: |checker: |&Context, &ty::Freevar||) {
|
||||
fn check_for_uniq(cx: &Context,
|
||||
fn_span: Span,
|
||||
fv: &ty::Freevar,
|
||||
bounds: ty::BuiltinBounds) {
|
||||
// all captured data must be owned, regardless of whether it is
|
||||
// moved in or copied in.
|
||||
let id = fv.def.def_id().node;
|
||||
let var_t = ty::node_id_to_type(cx.tcx, id);
|
||||
|
||||
check_freevar_bounds(cx, fn_span, fv.span, var_t, bounds, None);
|
||||
}
|
||||
|
||||
fn check_for_block(cx: &Context,
|
||||
fn_span: Span,
|
||||
fn_id: NodeId,
|
||||
fv: &ty::Freevar,
|
||||
bounds: ty::BuiltinBounds) {
|
||||
let id = fv.def.def_id().node;
|
||||
let var_t = ty::node_id_to_type(cx.tcx, id);
|
||||
let upvar_id = ty::UpvarId { var_id: id, closure_expr_id: fn_id };
|
||||
let upvar_borrow = cx.tcx.upvar_borrow(upvar_id);
|
||||
let implicit_borrowed_type =
|
||||
ty::mk_rptr(cx.tcx,
|
||||
upvar_borrow.region,
|
||||
ty::mt { mutbl: upvar_borrow.kind.to_mutbl_lossy(),
|
||||
ty: var_t });
|
||||
check_freevar_bounds(cx, fn_span, fv.span, implicit_borrowed_type,
|
||||
bounds, Some(var_t));
|
||||
}
|
||||
|
||||
fn check_for_bare(cx: &Context, fv: &ty::Freevar) {
|
||||
span_err!(cx.tcx.sess, fv.span, E0143,
|
||||
"can't capture dynamic environment in a fn item; \
|
||||
use the || {} closure form instead", "{ ... }");
|
||||
} // same check is done in resolve.rs, but shouldn't be done
|
||||
|
||||
let fty = ty::node_id_to_type(cx.tcx, id);
|
||||
match ty::get(fty).sty {
|
||||
ty::ty_closure(box ty::ClosureTy {
|
||||
store: ty::UniqTraitStore,
|
||||
bounds: bounds,
|
||||
..
|
||||
}) => {
|
||||
b(|cx, fv| check_for_uniq(cx, fn_span, fv,
|
||||
bounds.builtin_bounds))
|
||||
}
|
||||
|
||||
ty::ty_closure(box ty::ClosureTy {
|
||||
store: ty::RegionTraitStore(..), bounds, ..
|
||||
}) => {
|
||||
b(|cx, fv| check_for_block(cx, fn_span, id, fv,
|
||||
bounds.builtin_bounds))
|
||||
}
|
||||
|
||||
ty::ty_bare_fn(_) => {
|
||||
b(check_for_bare)
|
||||
}
|
||||
|
||||
ty::ty_unboxed_closure(..) => {}
|
||||
|
||||
ref s => {
|
||||
cx.tcx.sess.bug(format!("expect fn type in kind checker, not \
|
||||
{:?}",
|
||||
s).as_slice());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the free variables used in a shared/sendable closure conform
|
||||
// to the copy/move kind bounds. Then recursively check the function body.
|
||||
fn check_fn(
|
||||
cx: &mut Context,
|
||||
fk: visit::FnKind,
|
||||
decl: &FnDecl,
|
||||
body: &Block,
|
||||
sp: Span,
|
||||
fn_id: NodeId) {
|
||||
|
||||
// <Check kinds on free variables:
|
||||
with_appropriate_checker(cx, fn_id, sp, |chk| {
|
||||
ty::with_freevars(cx.tcx, fn_id, |freevars| {
|
||||
for fv in freevars.iter() {
|
||||
chk(cx, fv);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
match fk {
|
||||
visit::FkFnBlock(..) => {
|
||||
visit::walk_fn(cx, fk, decl, body, sp)
|
||||
}
|
||||
visit::FkItemFn(..) | visit::FkMethod(..) => {
|
||||
visit::walk_fn(cx, fk, decl, body, sp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_ty(cx: &mut Context, aty: &Ty) {
|
||||
match aty.node {
|
||||
TyPath(_, _, id) => {
|
||||
@ -208,29 +99,3 @@ pub fn check_typaram_bounds(cx: &Context,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn check_freevar_bounds(cx: &Context, fn_span: Span, sp: Span, ty: ty::t,
|
||||
bounds: ty::BuiltinBounds, referenced_ty: Option<ty::t>)
|
||||
{
|
||||
check_builtin_bounds(cx, ty, bounds, |missing| {
|
||||
// Will be Some if the freevar is implicitly borrowed (stack closure).
|
||||
// Emit a less mysterious error message in this case.
|
||||
match referenced_ty {
|
||||
Some(rty) => {
|
||||
span_err!(cx.tcx.sess, sp, E0145,
|
||||
"cannot implicitly borrow variable of type `{}` in a \
|
||||
bounded stack closure (implicit reference does not fulfill `{}`)",
|
||||
ty_to_string(cx.tcx, rty), missing.user_string(cx.tcx));
|
||||
}
|
||||
None => {
|
||||
span_err!(cx.tcx.sess, sp, E0146,
|
||||
"cannot capture variable of type `{}`, which does \
|
||||
not fulfill `{}`, in a bounded closure",
|
||||
ty_to_string(cx.tcx, ty), missing.user_string(cx.tcx));
|
||||
}
|
||||
}
|
||||
span_note!(cx.tcx.sess, fn_span,
|
||||
"this closure's environment must satisfy `{}`",
|
||||
bounds.user_string(cx.tcx));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -10,9 +10,9 @@
|
||||
|
||||
/*! See `doc.rs` for high-level documentation */
|
||||
|
||||
use super::DUMMY_CAUSE;
|
||||
use super::{EvaluatedToMatch, EvaluatedToAmbiguity, EvaluatedToUnmatch};
|
||||
use super::{evaluate_impl};
|
||||
use super::ObligationCause;
|
||||
use super::util;
|
||||
|
||||
use middle::subst;
|
||||
@ -39,7 +39,7 @@ pub fn impl_can_satisfy(infcx: &InferCtxt,
|
||||
// Determine whether `impl2` can provide an implementation for those
|
||||
// same types.
|
||||
let param_env = ty::empty_parameter_environment();
|
||||
match evaluate_impl(infcx, ¶m_env, infcx.tcx, DUMMY_CAUSE,
|
||||
match evaluate_impl(infcx, ¶m_env, infcx.tcx, ObligationCause::dummy(),
|
||||
impl2_def_id, impl1_self_ty) {
|
||||
EvaluatedToMatch | EvaluatedToAmbiguity => true,
|
||||
EvaluatedToUnmatch => false,
|
||||
|
@ -77,11 +77,11 @@ pub enum ObligationCauseCode {
|
||||
StructInitializerSized, // S { ... } must be Sized
|
||||
VariableType(ast::NodeId), // Type of each variable must be Sized
|
||||
RepeatVec, // [T,..n] --> T must be Copy
|
||||
}
|
||||
|
||||
pub static DUMMY_CAUSE: ObligationCause =
|
||||
ObligationCause { span: DUMMY_SP,
|
||||
code: MiscObligation };
|
||||
// Captures of variable the given id by a closure (span is the
|
||||
// span of the closure)
|
||||
ClosureCapture(ast::NodeId, Span)
|
||||
}
|
||||
|
||||
pub type Obligations = subst::VecPerParamSpace<Obligation>;
|
||||
|
||||
@ -358,6 +358,10 @@ impl ObligationCause {
|
||||
pub fn misc(span: Span) -> ObligationCause {
|
||||
ObligationCause { span: span, code: MiscObligation }
|
||||
}
|
||||
|
||||
pub fn dummy() -> ObligationCause {
|
||||
ObligationCause { span: DUMMY_SP, code: MiscObligation }
|
||||
}
|
||||
}
|
||||
|
||||
impl<N> Vtable<N> {
|
||||
|
@ -120,11 +120,13 @@ and report an error, and it just seems like more mess in the end.)
|
||||
|
||||
use middle::def;
|
||||
use middle::mem_categorization as mc;
|
||||
use middle::traits;
|
||||
use middle::ty::{ReScope};
|
||||
use middle::ty;
|
||||
use middle::typeck::astconv::AstConv;
|
||||
use middle::typeck::check::FnCtxt;
|
||||
use middle::typeck::check::regionmanip;
|
||||
use middle::typeck::check::vtable2;
|
||||
use middle::typeck::infer::resolve_and_force_all_but_regions;
|
||||
use middle::typeck::infer::resolve_type;
|
||||
use middle::typeck::infer;
|
||||
@ -165,6 +167,11 @@ pub fn regionck_fn(fcx: &FnCtxt, id: ast::NodeId, blk: &ast::Block) {
|
||||
// regionck assumes typeck succeeded
|
||||
rcx.visit_fn_body(id, blk);
|
||||
}
|
||||
|
||||
// Region checking a fn can introduce new trait obligations,
|
||||
// particularly around closure bounds.
|
||||
vtable2::select_all_fcx_obligations_or_error(fcx);
|
||||
|
||||
fcx.infcx().resolve_regions_and_report_errors();
|
||||
}
|
||||
|
||||
@ -848,16 +855,6 @@ fn check_expr_fn_block(rcx: &mut Rcx,
|
||||
}
|
||||
});
|
||||
}
|
||||
ty::ty_closure(box ty::ClosureTy{store: ty::UniqTraitStore,
|
||||
bounds: ref bounds,
|
||||
..}) => {
|
||||
// For proc, ensure that the *types* of the variables
|
||||
// outlive region bound, since they are captured by value.
|
||||
ty::with_freevars(tcx, expr.id, |freevars| {
|
||||
ensure_free_variable_types_outlive_closure_bound(
|
||||
rcx, bounds.region_bound, expr, freevars);
|
||||
});
|
||||
}
|
||||
ty::ty_unboxed_closure(_, region) => {
|
||||
ty::with_freevars(tcx, expr.id, |freevars| {
|
||||
// No free variables means that there is no environment and
|
||||
@ -868,8 +865,9 @@ fn check_expr_fn_block(rcx: &mut Rcx,
|
||||
// NDM -- this seems wrong, discuss with pcwalton, should
|
||||
// be straightforward enough.
|
||||
if !freevars.is_empty() {
|
||||
let bounds = ty::region_existential_bound(region);
|
||||
ensure_free_variable_types_outlive_closure_bound(
|
||||
rcx, region, expr, freevars);
|
||||
rcx, bounds, expr, freevars);
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -881,20 +879,26 @@ fn check_expr_fn_block(rcx: &mut Rcx,
|
||||
rcx.set_repeating_scope(repeating_scope);
|
||||
|
||||
match ty::get(function_type).sty {
|
||||
ty::ty_closure(box ty::ClosureTy {
|
||||
store: ty::RegionTraitStore(..),
|
||||
..
|
||||
}) => {
|
||||
ty::ty_closure(box ty::ClosureTy { store: ty::RegionTraitStore(..), .. }) => {
|
||||
ty::with_freevars(tcx, expr.id, |freevars| {
|
||||
propagate_upupvar_borrow_kind(rcx, expr, freevars);
|
||||
})
|
||||
}
|
||||
_ => ()
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match ty::get(function_type).sty {
|
||||
ty::ty_closure(box ty::ClosureTy {bounds, ..}) => {
|
||||
ty::with_freevars(tcx, expr.id, |freevars| {
|
||||
ensure_free_variable_types_outlive_closure_bound(rcx, bounds, expr, freevars);
|
||||
})
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
fn ensure_free_variable_types_outlive_closure_bound(
|
||||
rcx: &mut Rcx,
|
||||
region_bound: ty::Region,
|
||||
bounds: ty::ExistentialBounds,
|
||||
expr: &ast::Expr,
|
||||
freevars: &[ty::Freevar])
|
||||
{
|
||||
@ -908,7 +912,7 @@ fn check_expr_fn_block(rcx: &mut Rcx,
|
||||
let tcx = rcx.fcx.ccx.tcx;
|
||||
|
||||
debug!("ensure_free_variable_types_outlive_closure_bound({}, {})",
|
||||
region_bound.repr(tcx), expr.repr(tcx));
|
||||
bounds.region_bound.repr(tcx), expr.repr(tcx));
|
||||
|
||||
for freevar in freevars.iter() {
|
||||
let var_node_id = {
|
||||
@ -917,11 +921,35 @@ fn check_expr_fn_block(rcx: &mut Rcx,
|
||||
def_id.node
|
||||
};
|
||||
|
||||
let var_ty = rcx.resolve_node_type(var_node_id);
|
||||
// Compute the type of the field in the environment that
|
||||
// represents `var_node_id`. For a by-value closure, this
|
||||
// will be the same as the type of the variable. For a
|
||||
// by-reference closure, this will be `&T` where `T` is
|
||||
// the type of the variable.
|
||||
let raw_var_ty = rcx.resolve_node_type(var_node_id);
|
||||
let upvar_id = ty::UpvarId { var_id: var_node_id,
|
||||
closure_expr_id: expr.id };
|
||||
let var_ty = match rcx.fcx.inh.upvar_borrow_map.borrow().find(&upvar_id) {
|
||||
Some(upvar_borrow) => {
|
||||
ty::mk_rptr(rcx.tcx(),
|
||||
upvar_borrow.region,
|
||||
ty::mt { mutbl: upvar_borrow.kind.to_mutbl_lossy(),
|
||||
ty: raw_var_ty })
|
||||
}
|
||||
None => raw_var_ty
|
||||
};
|
||||
|
||||
// Check that the type meets the criteria of the existential bounds:
|
||||
for builtin_bound in bounds.builtin_bounds.iter() {
|
||||
let code = traits::ClosureCapture(var_node_id, expr.span);
|
||||
let cause = traits::ObligationCause::new(freevar.span, code);
|
||||
let obligation = traits::obligation_for_builtin_bound(rcx.tcx(), cause,
|
||||
var_ty, builtin_bound);
|
||||
rcx.fcx.inh.fulfillment_cx.borrow_mut().register_obligation(rcx.tcx(), obligation);
|
||||
}
|
||||
type_must_outlive(
|
||||
rcx, infer::RelateProcBound(expr.span, var_node_id, var_ty),
|
||||
var_ty, region_bound);
|
||||
var_ty, bounds.region_bound);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -398,5 +398,13 @@ fn note_obligation_cause(fcx: &FnCtxt,
|
||||
"use \"#[unsafe_destructor]\" on the implementation \
|
||||
to force the compiler to allow this");
|
||||
}
|
||||
traits::ClosureCapture(var_id, closure_span) => {
|
||||
let name = ty::local_var_name_str(tcx, var_id);
|
||||
span_note!(tcx.sess, closure_span,
|
||||
"the closure that captures `{}` requires that all captured variables \"
|
||||
implement the trait `{}`",
|
||||
name,
|
||||
trait_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1560,7 +1560,7 @@ impl<'a, 'tcx> ErrorReportingHelpers for InferCtxt<'a, 'tcx> {
|
||||
"...so that it can be closed over into an object");
|
||||
}
|
||||
infer::RelateProcBound(span, var_node_id, _ty) => {
|
||||
self.tcx.sess.span_err(
|
||||
self.tcx.sess.span_note(
|
||||
span,
|
||||
format!(
|
||||
"...so that the variable `{}` can be captured \
|
||||
|
@ -12,8 +12,9 @@ fn bar(blk: ||:'static) {
|
||||
}
|
||||
|
||||
fn foo(x: &()) {
|
||||
bar(|| {
|
||||
let _ = x; //~ ERROR captured variable `x` does not outlive
|
||||
bar(|| { //~ ERROR cannot infer an appropriate lifetime
|
||||
let _ = x;
|
||||
//~^ ERROR captured variable `x` does not outlive
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,8 @@ fn foo(_x: Gc<uint>) {}
|
||||
|
||||
fn main() {
|
||||
let x = box(GC) 3u;
|
||||
let _: proc():Send = proc() foo(x); //~ ERROR does not fulfill `Send`
|
||||
let _: proc():Send = proc() foo(x); //~ ERROR does not fulfill `Send`
|
||||
let _: proc():Send = proc() foo(x); //~ ERROR does not fulfill `Send`
|
||||
let _: proc():Send = proc() foo(x); //~ ERROR `core::kinds::Send` is not implemented
|
||||
let _: proc():Send = proc() foo(x); //~ ERROR `core::kinds::Send` is not implemented
|
||||
let _: proc():Send = proc() foo(x); //~ ERROR `core::kinds::Send` is not implemented
|
||||
let _: proc() = proc() foo(x);
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ fn main() {
|
||||
|
||||
task::spawn(proc() {
|
||||
let y = x;
|
||||
//~^ ERROR does not fulfill `Send`
|
||||
//~^ ERROR `core::kinds::Send` is not implemented
|
||||
println!("{:?}", y);
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user