diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs index 33f7680d873c..aeb0c155a3f4 100644 --- a/src/librustc/middle/kind.rs +++ b/src/librustc/middle/kind.rs @@ -10,50 +10,30 @@ use middle::freevars::freevar_entry; use middle::freevars; +use middle::mem_categorization::Typer; use middle::subst; -use middle::ty::ParameterEnvironment; use middle::ty; use middle::ty_fold::TypeFoldable; use middle::ty_fold; -use middle::typeck::check::vtable; -use middle::typeck::{MethodCall, NoAdjustment}; -use middle::typeck; -use util::ppaux::{Repr, ty_to_string}; +use util::ppaux::{ty_to_string}; use util::ppaux::UserString; -use std::collections::HashSet; use syntax::ast::*; -use syntax::ast_util; use syntax::attr; use syntax::codemap::Span; use syntax::print::pprust::{expr_to_string, ident_to_string}; use syntax::visit::Visitor; use syntax::visit; -// Kind analysis pass. -// -// There are several kinds defined by various operations. The most restrictive -// kind is noncopyable. The noncopyable kind can be extended with any number -// of the following attributes. -// -// Send: Things that can be sent on channels or included in spawned closures. It -// includes scalar types as well as classes and unique types containing only -// sendable types. -// 'static: Things that do not contain references. -// -// This pass ensures that type parameters are only instantiated with types -// whose kinds are equal or less general than the way the type parameter was -// annotated (with the `Send` bound). -// -// It also verifies that noncopyable kinds are not copied. Sendability is not -// applied, since none of our language primitives send. Instead, the sending -// primitives in the stdlib are explicitly annotated to only take sendable -// types. +// Kind analysis pass. This pass does some ad-hoc checks that are more +// convenient to do after type checking is complete and all checks are +// known. These are generally related to the builtin bounds `Copy` and +// `Sized`. Note that many of the builtin bound properties that used +// to be checked here are actually checked by trait checking these +// days. -pub struct Context<'a, 'tcx: 'a> { +pub struct Context<'a,'tcx:'a> { tcx: &'a ty::ctxt<'tcx>, - struct_and_enum_bounds_checked: HashSet, - parameter_environments: Vec, } impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> { @@ -77,17 +57,11 @@ impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> { fn visit_pat(&mut self, p: &Pat) { check_pat(self, p); } - - fn visit_local(&mut self, l: &Local) { - check_local(self, l); - } } pub fn check_crate(tcx: &ty::ctxt) { let mut ctx = Context { tcx: tcx, - struct_and_enum_bounds_checked: HashSet::new(), - parameter_environments: Vec::new(), }; visit::walk_crate(&mut ctx, tcx.map.krate()); tcx.sess.abort_if_errors(); @@ -135,27 +109,11 @@ fn check_impl_of_trait(cx: &mut Context, it: &Item, trait_ref: &TraitRef, self_t .find(&trait_ref.ref_id) .expect("trait ref not in def map!"); let trait_def_id = ast_trait_def.def_id(); - let trait_def = cx.tcx.trait_defs.borrow() - .find_copy(&trait_def_id) - .expect("trait def not in trait-defs map!"); - - // If this trait has builtin-kind supertraits, meet them. - let self_ty: ty::t = ty::node_id_to_type(cx.tcx, it.id); - debug!("checking impl with self type {}", ty::get(self_ty).sty); - check_builtin_bounds( - cx, self_ty, trait_def.bounds.builtin_bounds, - |missing| { - span_err!(cx.tcx.sess, self_type.span, E0142, - "the type `{}', which does not fulfill `{}`, \ - cannot implement this trait", - ty_to_string(cx.tcx, self_ty), missing.user_string(cx.tcx)); - span_note!(cx.tcx.sess, self_type.span, - "types implementing this trait must fulfill `{}`", - trait_def.bounds.user_string(cx.tcx)); - }); // If this is a destructor, check kinds. - if cx.tcx.lang_items.drop_trait() == Some(trait_def_id) { + if cx.tcx.lang_items.drop_trait() == Some(trait_def_id) && + !attr::contains_name(it.attrs.as_slice(), "unsafe_destructor") + { match self_type.node { TyPath(_, ref bounds, path_node_id) => { assert!(bounds.is_none()); @@ -172,133 +130,50 @@ fn check_impl_of_trait(cx: &mut Context, it: &Item, trait_ref: &TraitRef, self_t } fn check_item(cx: &mut Context, item: &Item) { - if !attr::contains_name(item.attrs.as_slice(), "unsafe_destructor") { - match item.node { - ItemImpl(_, ref trait_ref, ref self_type, _) => { - let parameter_environment = - ParameterEnvironment::for_item(cx.tcx, item.id); - cx.parameter_environments.push(parameter_environment); - - // Check bounds on the `self` type. - check_bounds_on_structs_or_enums_in_type_if_possible( - cx, - item.span, - ty::node_id_to_type(cx.tcx, item.id)); - - match trait_ref { - &Some(ref trait_ref) => { - check_impl_of_trait(cx, item, trait_ref, &**self_type); - - // Check bounds on the trait ref. - match ty::impl_trait_ref(cx.tcx, - ast_util::local_def(item.id)) { - None => {} - Some(trait_ref) => { - check_bounds_on_structs_or_enums_in_trait_ref( - cx, - item.span, - &*trait_ref); - - let trait_def = ty::lookup_trait_def(cx.tcx, trait_ref.def_id); - for (ty, type_param_def) in trait_ref.substs.types - .iter() - .zip(trait_def.generics - .types - .iter()) { - check_typaram_bounds(cx, item.span, *ty, type_param_def); - } - } - } - } - &None => {} - } - - drop(cx.parameter_environments.pop()); - } - ItemEnum(..) => { - let parameter_environment = - ParameterEnvironment::for_item(cx.tcx, item.id); - cx.parameter_environments.push(parameter_environment); - - let def_id = ast_util::local_def(item.id); - for variant in ty::enum_variants(cx.tcx, def_id).iter() { - for arg in variant.args.iter() { - check_bounds_on_structs_or_enums_in_type_if_possible( - cx, - item.span, - *arg) - } - } - - drop(cx.parameter_environments.pop()); - } - ItemStruct(..) => { - let parameter_environment = - ParameterEnvironment::for_item(cx.tcx, item.id); - cx.parameter_environments.push(parameter_environment); - - let def_id = ast_util::local_def(item.id); - for field in ty::lookup_struct_fields(cx.tcx, def_id).iter() { - check_bounds_on_structs_or_enums_in_type_if_possible( - cx, - item.span, - ty::node_id_to_type(cx.tcx, field.id.node)) - } - - drop(cx.parameter_environments.pop()); - - } - ItemStatic(..) => { - let parameter_environment = - ParameterEnvironment::for_item(cx.tcx, item.id); - cx.parameter_environments.push(parameter_environment); - - check_bounds_on_structs_or_enums_in_type_if_possible( - cx, - item.span, - ty::node_id_to_type(cx.tcx, item.id)); - - drop(cx.parameter_environments.pop()); - } - _ => {} + match item.node { + ItemImpl(_, Some(ref trait_ref), ref self_type, _) => { + check_impl_of_trait(cx, item, trait_ref, &**self_type); } + _ => {} } visit::walk_item(cx, item) } -fn check_local(cx: &mut Context, local: &Local) { - check_bounds_on_structs_or_enums_in_type_if_possible( - cx, - local.span, - ty::node_id_to_type(cx.tcx, local.id)); - - visit::walk_local(cx, local) -} - // 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, &freevar_entry||) { - fn check_for_uniq(cx: &Context, fv: &freevar_entry, bounds: ty::BuiltinBounds) { + fn check_for_uniq(cx: &Context, + fn_span: Span, + fv: &freevar_entry, + 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, fv.span, var_t, bounds, None); + check_freevar_bounds(cx, fn_span, fv.span, var_t, bounds, None); } - fn check_for_block(cx: &Context, fv: &freevar_entry, - bounds: ty::BuiltinBounds, region: ty::Region) { + fn check_for_block(cx: &Context, + fn_span: Span, + fn_id: NodeId, + fv: &freevar_entry, + bounds: ty::BuiltinBounds) { let id = fv.def.def_id().node; let var_t = ty::node_id_to_type(cx.tcx, id); - // FIXME(#3569): Figure out whether the implicit borrow is actually - // mutable. Currently we assume all upvars are referenced mutably. - let implicit_borrowed_type = ty::mk_mut_rptr(cx.tcx, region, var_t); - check_freevar_bounds(cx, fv.span, implicit_borrowed_type, + 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)); } @@ -315,12 +190,16 @@ fn with_appropriate_checker(cx: &Context, bounds: bounds, .. }) => { - b(|cx, fv| check_for_uniq(cx, fv, bounds.builtin_bounds)) + b(|cx, fv| check_for_uniq(cx, fn_span, fv, + bounds.builtin_bounds)) } ty::ty_closure(box ty::ClosureTy { - store: ty::RegionTraitStore(region, _), bounds, .. - }) => b(|cx, fv| check_for_block(cx, fv, bounds.builtin_bounds, region)), + 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) @@ -346,8 +225,8 @@ fn check_fn( sp: Span, fn_id: NodeId) { - // Check kinds on free variables: - with_appropriate_checker(cx, fn_id, |chk| { + // { - let ty = ty::node_id_to_type(cx.tcx, fn_id); - check_bounds_on_structs_or_enums_in_type_if_possible(cx, sp, ty); - visit::walk_fn(cx, fk, decl, body, sp) } visit::FkItemFn(..) | visit::FkMethod(..) => { - let parameter_environment = ParameterEnvironment::for_item(cx.tcx, - fn_id); - cx.parameter_environments.push(parameter_environment); - - let ty = ty::node_id_to_type(cx.tcx, fn_id); - check_bounds_on_structs_or_enums_in_type_if_possible(cx, sp, ty); - visit::walk_fn(cx, fk, decl, body, sp); - drop(cx.parameter_environments.pop()); } } } @@ -379,30 +247,7 @@ fn check_fn( pub fn check_expr(cx: &mut Context, e: &Expr) { debug!("kind::check_expr({})", expr_to_string(e)); - // Handle any kind bounds on type parameters - check_bounds_on_type_parameters(cx, e); - - // Check bounds on structures or enumerations in the type of the - // expression. - let expression_type = ty::expr_ty(cx.tcx, e); - check_bounds_on_structs_or_enums_in_type_if_possible(cx, - e.span, - expression_type); - match e.node { - ExprCast(ref source, _) => { - let source_ty = ty::expr_ty(cx.tcx, &**source); - let target_ty = ty::expr_ty(cx.tcx, e); - let method_call = MethodCall { - expr_id: e.id, - adjustment: NoAdjustment, - }; - check_trait_cast(cx, - source_ty, - target_ty, - source.span, - method_call); - } ExprRepeat(ref element, ref count_expr) => { let count = ty::eval_repeat_count(cx.tcx, &**count_expr); if count > 1 { @@ -427,174 +272,8 @@ pub fn check_expr(cx: &mut Context, e: &Expr) { _ => {} } - // Search for auto-adjustments to find trait coercions. - match cx.tcx.adjustments.borrow().find(&e.id) { - Some(adjustment) => { - match adjustment { - adj if ty::adjust_is_object(adj) => { - let source_ty = ty::expr_ty(cx.tcx, e); - let target_ty = ty::expr_ty_adjusted(cx.tcx, e); - let method_call = MethodCall { - expr_id: e.id, - adjustment: typeck::AutoObject, - }; - check_trait_cast(cx, - source_ty, - target_ty, - e.span, - method_call); - } - _ => {} - } - } - None => {} - } - visit::walk_expr(cx, e); } - -fn check_bounds_on_type_parameters(cx: &mut Context, e: &Expr) { - let method_map = cx.tcx.method_map.borrow(); - let method_call = typeck::MethodCall::expr(e.id); - let method = method_map.find(&method_call); - - // Find the values that were provided (if any) - let item_substs = cx.tcx.item_substs.borrow(); - let (types, is_object_call) = match method { - Some(method) => { - let is_object_call = match method.origin { - typeck::MethodObject(..) => true, - typeck::MethodStatic(..) | - typeck::MethodStaticUnboxedClosure(..) | - typeck::MethodParam(..) => false - }; - (&method.substs.types, is_object_call) - } - None => { - match item_substs.find(&e.id) { - None => { return; } - Some(s) => { (&s.substs.types, false) } - } - } - }; - - // Find the relevant type parameter definitions - let def_map = cx.tcx.def_map.borrow(); - let type_param_defs = match e.node { - ExprPath(_) => { - let did = def_map.get_copy(&e.id).def_id(); - ty::lookup_item_type(cx.tcx, did).generics.types.clone() - } - _ => { - // Type substitutions should only occur on paths and - // method calls, so this needs to be a method call. - - // Even though the callee_id may have been the id with - // node_type_substs, e.id is correct here. - match method { - Some(method) => { - ty::method_call_type_param_defs(cx.tcx, method.origin) - } - None => { - cx.tcx.sess.span_bug(e.span, - "non path/method call expr has type substs??"); - } - } - } - }; - - // Check that the value provided for each definition meets the - // kind requirements - for type_param_def in type_param_defs.iter() { - let ty = *types.get(type_param_def.space, type_param_def.index); - - // If this is a call to an object method (`foo.bar()` where - // `foo` has a type like `Trait`), then the self type is - // unknown (after all, this is a virtual call). In that case, - // we will have put a ty_err in the substitutions, and we can - // just skip over validating the bounds (because the bounds - // would have been enforced when the object instance was - // created). - if is_object_call && type_param_def.space == subst::SelfSpace { - assert_eq!(type_param_def.index, 0); - assert!(ty::type_is_error(ty)); - continue; - } - - debug!("type_param_def space={} index={} ty={}", - type_param_def.space, type_param_def.index, ty.repr(cx.tcx)); - check_typaram_bounds(cx, e.span, ty, type_param_def) - } - - // Check the vtable. - let vtable_map = cx.tcx.vtable_map.borrow(); - let vtable_res = match vtable_map.find(&method_call) { - None => return, - Some(vtable_res) => vtable_res, - }; - check_type_parameter_bounds_in_vtable_result(cx, e.span, vtable_res); -} - -fn check_type_parameter_bounds_in_vtable_result( - cx: &mut Context, - span: Span, - vtable_res: &typeck::vtable_res) { - for origins in vtable_res.iter() { - for origin in origins.iter() { - let (type_param_defs, substs) = match *origin { - typeck::vtable_static(def_id, ref tys, _) => { - let type_param_defs = - ty::lookup_item_type(cx.tcx, def_id).generics - .types - .clone(); - (type_param_defs, (*tys).clone()) - } - _ => { - // Nothing to do here. - continue - } - }; - for type_param_def in type_param_defs.iter() { - let typ = substs.types.get(type_param_def.space, - type_param_def.index); - check_typaram_bounds(cx, span, *typ, type_param_def) - } - } - } -} - -fn check_trait_cast(cx: &mut Context, - source_ty: ty::t, - target_ty: ty::t, - span: Span, - method_call: MethodCall) { - match ty::get(target_ty).sty { - ty::ty_uniq(ty) | ty::ty_rptr(_, ty::mt{ ty, .. }) => { - match ty::get(ty).sty { - ty::ty_trait(box ty::TyTrait { bounds, .. }) => { - match cx.tcx.vtable_map.borrow().find(&method_call) { - None => { - cx.tcx.sess.span_bug(span, - "trait cast not in vtable \ - map?!") - } - Some(vtable_res) => { - check_type_parameter_bounds_in_vtable_result( - cx, - span, - vtable_res) - } - }; - check_trait_cast_bounds(cx, span, source_ty, - bounds.builtin_bounds); - } - _ => {} - } - } - _ => {} - } -} - fn check_ty(cx: &mut Context, aty: &Ty) { match aty.node { TyPath(_, _, id) => { @@ -651,77 +330,7 @@ pub fn check_typaram_bounds(cx: &Context, }); } -fn check_bounds_on_structs_or_enums_in_type_if_possible(cx: &mut Context, - span: Span, - ty: ty::t) { - // If we aren't in a function, structure, or enumeration context, we don't - // have enough information to ensure that bounds on structures or - // enumerations are satisfied. So we don't perform the check. - if cx.parameter_environments.len() == 0 { - return - } - - // If we've already checked for this type, don't do it again. This - // massively speeds up kind checking. - if cx.struct_and_enum_bounds_checked.contains(&ty) { - return - } - cx.struct_and_enum_bounds_checked.insert(ty); - - ty::walk_ty(ty, |ty| { - match ty::get(ty).sty { - ty::ty_struct(type_id, ref substs) | - ty::ty_enum(type_id, ref substs) => { - let polytype = ty::lookup_item_type(cx.tcx, type_id); - - // Check builtin bounds. - for (ty, type_param_def) in substs.types - .iter() - .zip(polytype.generics - .types - .iter()) { - check_typaram_bounds(cx, span, *ty, type_param_def); - } - - // Check trait bounds. - let parameter_environment = - cx.parameter_environments.get(cx.parameter_environments - .len() - 1); - debug!( - "check_bounds_on_structs_or_enums_in_type_if_possible(): \ - checking {}", - ty.repr(cx.tcx)); - vtable::check_param_bounds(cx.tcx, - span, - parameter_environment, - &polytype.generics.types, - substs, - |missing| { - cx.tcx - .sess - .span_err(span, - format!("instantiating a type parameter with \ - an incompatible type `{}`, which \ - does not fulfill `{}`", - ty_to_string(cx.tcx, ty), - missing.user_string( - cx.tcx)).as_slice()); - }) - } - _ => {} - } - }); -} - -fn check_bounds_on_structs_or_enums_in_trait_ref(cx: &mut Context, - span: Span, - trait_ref: &ty::TraitRef) { - for ty in trait_ref.substs.types.iter() { - check_bounds_on_structs_or_enums_in_type_if_possible(cx, span, *ty) - } -} - -pub fn check_freevar_bounds(cx: &Context, sp: Span, ty: ty::t, +pub fn check_freevar_bounds(cx: &Context, fn_span: Span, sp: Span, ty: ty::t, bounds: ty::BuiltinBounds, referenced_ty: Option) { check_builtin_bounds(cx, ty, bounds, |missing| { @@ -741,7 +350,7 @@ pub fn check_freevar_bounds(cx: &Context, sp: Span, ty: ty::t, ty_to_string(cx.tcx, ty), missing.user_string(cx.tcx)); } } - span_note!(cx.tcx.sess, sp, + span_note!(cx.tcx.sess, fn_span, "this closure's environment must satisfy `{}`", bounds.user_string(cx.tcx)); }); diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index c26505fdc657..d0b94cb3abb8 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -5423,7 +5423,7 @@ impl BorrowKind { * Returns a mutability `m` such that an `&m T` pointer could * be used to obtain this borrow kind. Because borrow kinds * are richer than mutabilities, we sometimes have to pick a - * mutability that is stornger than necessary so that it at + * mutability that is stronger than necessary so that it at * least *would permit* the borrow in question. */ diff --git a/src/librustc/middle/typeck/check/_match.rs b/src/librustc/middle/typeck/check/_match.rs index 1602dfeaa280..46aba94a5f12 100644 --- a/src/librustc/middle/typeck/check/_match.rs +++ b/src/librustc/middle/typeck/check/_match.rs @@ -555,11 +555,9 @@ pub fn check_pat(pcx: &pat_ctxt, pat: &ast::Pat, expected: ty::t) { None); match tcx.def_map.borrow().find(&pat.id) { Some(def) => { - let item_type = ty::lookup_item_type(tcx, def.def_id()); - let substitutions = fcx.infcx().fresh_substs_for_type( - pat.span, &item_type.generics); + let struct_ty = fcx.instantiate_item_type(pat.span, def.def_id()); check_struct_pat(pcx, pat.span, fields.as_slice(), - etc, def.def_id(), &substitutions); + etc, def.def_id(), &struct_ty.substs); } None => { tcx.sess.span_bug(pat.span, diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index ecda5890fc5b..074074c13ae9 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -82,6 +82,7 @@ obtained the type `Foo`, we would never match this method. use middle::subst; use middle::subst::Subst; +use middle::traits; use middle::ty::*; use middle::ty; use middle::typeck::astconv::AstConv; @@ -91,7 +92,6 @@ use middle::typeck::infer; use middle::typeck::MethodCallee; use middle::typeck::{MethodOrigin, MethodParam}; use middle::typeck::{MethodStatic, MethodStaticUnboxedClosure, MethodObject}; -use middle::typeck::{param_index}; use middle::typeck::check::regionmanip::replace_late_bound_regions_in_fn_sig; use middle::typeck::TypeAndSubsts; use util::common::indenter; @@ -538,14 +538,12 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { return } - let vcx = self.fcx.vtable_context(); - // Get the tupled type of the arguments. let arguments_type = *closure_function_type.sig.inputs.get(0); let return_type = closure_function_type.sig.output; let closure_region = - vcx.infcx.next_region_var(infer::MiscVariable(self.span)); + self.fcx.infcx().next_region_var(infer::MiscVariable(self.span)); let unboxed_closure_type = ty::mk_unboxed_closure(self.tcx(), closure_did, closure_region); @@ -555,7 +553,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { rcvr_substs: subst::Substs::new_trait( vec![arguments_type, return_type], vec![], - *vcx.infcx.next_ty_vars(1).get(0)), + *self.fcx.infcx().next_ty_vars(1).get(0)), method_ty: method, origin: MethodStaticUnboxedClosure(closure_did), }); @@ -618,7 +616,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { self.push_inherent_candidates_from_bounds_inner( &[trait_ref.clone()], - |_this, new_trait_ref, m, method_num, _bound_num| { + |_this, new_trait_ref, m, method_num| { let vtable_index = get_method_index(tcx, &*new_trait_ref, trait_ref.clone(), method_num); @@ -633,7 +631,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { rcvr_substs: new_trait_ref.substs.clone(), method_ty: Rc::new(m), origin: MethodObject(MethodObject { - trait_id: new_trait_ref.def_id, + trait_ref: new_trait_ref, object_trait_id: did, method_num: method_num, real_index: vtable_index @@ -652,8 +650,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { rcvr_ty, param_ty.space, param_ty.idx, - restrict_to, - param_index { space: param_ty.space, index: param_ty.idx }); + restrict_to); } @@ -661,13 +658,12 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { self_ty: ty::t, space: subst::ParamSpace, index: uint, - restrict_to: Option, - param: param_index) { + restrict_to: Option) { let bounds = self.fcx.inh.param_env.bounds.get(space, index).trait_bounds .as_slice(); self.push_inherent_candidates_from_bounds_inner(bounds, - |this, trait_ref, m, method_num, bound_num| { + |this, trait_ref, m, method_num| { match restrict_to { Some(trait_did) => { if trait_did != trait_ref.def_id { @@ -701,10 +697,8 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { rcvr_substs: trait_ref.substs.clone(), method_ty: m, origin: MethodParam(MethodParam { - trait_id: trait_ref.def_id, + trait_ref: trait_ref, method_num: method_num, - param_num: param, - bound_num: bound_num, }) }) }) @@ -718,15 +712,16 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { mk_cand: |this: &mut LookupContext, tr: Rc, m: Rc, - method_num: uint, - bound_num: uint| - -> Option) { + method_num: uint| + -> Option) + { let tcx = self.tcx(); - let mut next_bound_idx = 0; // count only trait bounds - - ty::each_bound_trait_and_supertraits(tcx, bounds, |bound_trait_ref| { - let this_bound_idx = next_bound_idx; - next_bound_idx += 1; + let mut cache = HashSet::new(); + for bound_trait_ref in traits::transitive_bounds(tcx, bounds) { + // Already visited this trait, skip it. + if !cache.insert(bound_trait_ref.def_id) { + continue; + } let trait_items = ty::trait_items(tcx, bound_trait_ref.def_id); match trait_items.iter().position(|ti| { @@ -745,8 +740,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { match mk_cand(self, bound_trait_ref, method, - pos, - this_bound_idx) { + pos) { Some(cand) => { debug!("pushing inherent candidate for param: {}", cand.repr(self.tcx())); @@ -761,8 +755,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { // check next trait or bound } } - true - }); + } } @@ -773,7 +766,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { let impl_items = self.tcx().impl_items.borrow(); for impl_infos in self.tcx().inherent_impls.borrow().find(&did).iter() { - for impl_did in impl_infos.borrow().iter() { + for impl_did in impl_infos.iter() { let items = impl_items.get(impl_did); self.push_candidates_from_impl(*impl_did, items.as_slice(), @@ -825,11 +818,10 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { // determine the `self` of the impl with fresh // variables for each parameter: let span = self.self_expr.map_or(self.span, |e| e.span); - let vcx = self.fcx.vtable_context(); let TypeAndSubsts { substs: impl_substs, ty: impl_ty - } = impl_self_ty(&vcx, span, impl_did); + } = impl_self_ty(self.fcx, span, impl_did); let condition = match method.explicit_self { ByReferenceExplicitSelfCategory(_, mt) if mt == MutMutable => @@ -877,7 +869,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { adjustment {:?} for {}", adjustment, self.ty_to_string(self_ty)); match adjustment { Some((self_expr_id, adj)) => { - self.fcx.write_adjustment(self_expr_id, adj); + self.fcx.write_adjustment(self_expr_id, self.span, adj); } None => {} } @@ -1109,7 +1101,9 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { ty_err => None, - ty_infer(TyVar(_)) => { + ty_infer(TyVar(_)) | + ty_infer(SkolemizedTy(_)) | + ty_infer(SkolemizedIntTy(_)) => { self.bug(format!("unexpected type: {}", self.ty_to_string(self_ty)).as_slice()); } @@ -1150,6 +1144,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { Some(self_expr_id) => { self.fcx.write_adjustment( self_expr_id, + self.span, ty::AutoDerefRef(ty::AutoDerefRef { autoderefs: autoderefs, autoref: Some(kind(region, *mutbl)) @@ -1209,7 +1204,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { // return something so we don't get errors for every mutability return Some(MethodCallee { - origin: relevant_candidates.get(0).origin, + origin: relevant_candidates.get(0).origin.clone(), ty: ty::mk_err(), substs: subst::Substs::empty() }); @@ -1237,12 +1232,14 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { candidate_b.repr(self.tcx())); match (&candidate_a.origin, &candidate_b.origin) { (&MethodParam(ref p1), &MethodParam(ref p2)) => { - let same_trait = p1.trait_id == p2.trait_id; - let same_method = p1.method_num == p2.method_num; - let same_param = p1.param_num == p2.param_num; - // The bound number may be different because - // multiple bounds may lead to the same trait - // impl + let same_trait = + p1.trait_ref.def_id == p2.trait_ref.def_id; + let same_method = + p1.method_num == p2.method_num; + // it's ok to compare self-ty with `==` here because + // they are always a TyParam + let same_param = + p1.trait_ref.self_ty() == p2.trait_ref.self_ty(); same_trait && same_method && same_param } _ => false @@ -1369,13 +1366,13 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { } } - self.fcx.add_region_obligations_for_parameters( - self.span, + self.fcx.add_obligations_for_parameters( + traits::ObligationCause::misc(self.span), &all_substs, &candidate.method_ty.generics); MethodCallee { - origin: candidate.origin, + origin: candidate.origin.clone(), ty: fty, substs: all_substs } @@ -1452,10 +1449,10 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { MethodStaticUnboxedClosure(_) => bad = false, // FIXME: does this properly enforce this on everything now // that self has been merged in? -sully - MethodParam(MethodParam { trait_id: trait_id, .. }) | - MethodObject(MethodObject { trait_id: trait_id, .. }) => { + MethodParam(MethodParam { trait_ref: ref trait_ref, .. }) | + MethodObject(MethodObject { trait_ref: ref trait_ref, .. }) => { bad = self.tcx().destructor_for_type.borrow() - .contains_key(&trait_id); + .contains_key(&trait_ref.def_id); } } @@ -1602,10 +1599,10 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { self.report_static_candidate(idx, did) } MethodParam(ref mp) => { - self.report_param_candidate(idx, (*mp).trait_id) + self.report_param_candidate(idx, mp.trait_ref.def_id) } MethodObject(ref mo) => { - self.report_trait_candidate(idx, mo.trait_id) + self.report_trait_candidate(idx, mo.trait_ref.def_id) } } } diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 375fc75bf62f..20fe8186adf4 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -87,6 +87,7 @@ use middle::pat_util::pat_id_map; use middle::pat_util; use middle::subst; use middle::subst::{Subst, Substs, VecPerParamSpace, ParamSpace}; +use middle::traits; use middle::ty::{FnSig, VariantInfo}; use middle::ty::{Polytype}; use middle::ty::{Disr, ParamTy, ParameterEnvironment}; @@ -102,15 +103,14 @@ use middle::typeck::check::method::{CheckTraitsAndInherentMethods}; use middle::typeck::check::method::{DontAutoderefReceiver}; use middle::typeck::check::method::{IgnoreStaticMethods, ReportStaticMethods}; use middle::typeck::check::regionmanip::replace_late_bound_regions_in_fn_sig; -use middle::typeck::check::vtable::VtableContext; use middle::typeck::CrateCtxt; use middle::typeck::infer::{resolve_type, force_tvar}; use middle::typeck::infer; use middle::typeck::rscope::RegionScope; use middle::typeck::{lookup_def_ccx}; use middle::typeck::no_params; -use middle::typeck::{require_same_types, vtable_map}; -use middle::typeck::{MethodCall, MethodMap}; +use middle::typeck::{require_same_types}; +use middle::typeck::{MethodCall, MethodMap, ObjectCastMap}; use middle::typeck::{TypeAndSubsts}; use middle::typeck; use middle::lang_items::TypeIdLangItem; @@ -143,12 +143,13 @@ use syntax::visit::Visitor; use syntax; pub mod _match; -pub mod vtable; +pub mod vtable2; // New trait code pub mod writeback; pub mod regionmanip; pub mod regionck; pub mod demand; pub mod method; +pub mod wf; /// Fields that are part of a `FnCtxt` which are inherited by /// closures defined within the function. For example: @@ -170,9 +171,9 @@ pub struct Inherited<'a, 'tcx: 'a> { item_substs: RefCell>, adjustments: RefCell>, method_map: MethodMap, - vtable_map: vtable_map, upvar_borrow_map: RefCell, unboxed_closures: RefCell>, + object_cast_map: ObjectCastMap, // A mapping from each fn's id to its signature, with all bound // regions replaced with free ones. Unlike the other tables, this @@ -190,7 +191,7 @@ pub struct Inherited<'a, 'tcx: 'a> { // then in some expression `let x = Foo { ... }` it will // instantiate the type parameter `T` with a fresh type `$0`. At // the same time, it will record a region obligation of - // `$0:'static`. This will get checked later by regionck. (We + // `$0:'static`. This will get checked later by regionck. (We // can't generally check these things right away because we have // to wait until types are resolved.) // @@ -204,6 +205,9 @@ pub struct Inherited<'a, 'tcx: 'a> { // obligations (otherwise, it's easy to fail to walk to a // particular node-id). region_obligations: RefCell>>, + + // Tracks trait obligations incurred during this function body. + fulfillment_cx: RefCell, } struct RegionObligation { @@ -335,11 +339,12 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> { item_substs: RefCell::new(NodeMap::new()), adjustments: RefCell::new(NodeMap::new()), method_map: RefCell::new(FnvHashMap::new()), - vtable_map: RefCell::new(FnvHashMap::new()), + object_cast_map: RefCell::new(NodeMap::new()), upvar_borrow_map: RefCell::new(HashMap::new()), unboxed_closures: RefCell::new(DefIdMap::new()), fn_sig_map: RefCell::new(NodeMap::new()), region_obligations: RefCell::new(NodeMap::new()), + fulfillment_cx: RefCell::new(traits::FulfillmentContext::new()), } } } @@ -369,20 +374,12 @@ fn static_inherited_fields<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>) free_substs: subst::Substs::empty(), bounds: subst::VecPerParamSpace::empty(), implicit_region_bound: ty::ReStatic, + caller_obligations: subst::VecPerParamSpace::empty(), }; Inherited::new(ccx.tcx, param_env) } struct CheckItemTypesVisitor<'a, 'tcx: 'a> { ccx: &'a CrateCtxt<'a, 'tcx> } -struct CheckTypeWellFormedVisitor<'a, 'tcx: 'a> { ccx: &'a CrateCtxt<'a, 'tcx> } - -impl<'a, 'tcx, 'v> Visitor<'v> for CheckTypeWellFormedVisitor<'a, 'tcx> { - fn visit_item(&mut self, i: &ast::Item) { - check_type_well_formed(self.ccx, i); - visit::walk_item(self, i); - } -} - impl<'a, 'tcx, 'v> Visitor<'v> for CheckItemTypesVisitor<'a, 'tcx> { fn visit_item(&mut self, i: &ast::Item) { @@ -404,8 +401,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckItemSizedTypesVisitor<'a, 'tcx> { pub fn check_item_types(ccx: &CrateCtxt) { let krate = ccx.tcx.map.krate(); - - let mut visit = CheckTypeWellFormedVisitor { ccx: ccx }; + let mut visit = wf::CheckTypeWellFormedVisitor::new(ccx); visit::walk_crate(&mut visit, krate); // If types are not well-formed, it leads to all manner of errors @@ -437,9 +433,10 @@ fn check_bare_fn(ccx: &CrateCtxt, let fcx = check_fn(ccx, fn_ty.fn_style, id, &fn_ty.sig, decl, id, body, &inh); - vtable::resolve_in_block(&fcx, body); + vtable2::select_all_fcx_obligations_or_error(&fcx); regionck::regionck_fn(&fcx, id, body); writeback::resolve_type_vars_in_fn(&fcx, decl, body); + vtable2::check_builtin_bound_obligations(&fcx); // must happen after writeback } _ => ccx.tcx.sess.impossible_case(body.span, "check_bare_fn: function type expected") @@ -451,19 +448,21 @@ struct GatherLocalsVisitor<'a, 'tcx: 'a> { } impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { - fn assign(&mut self, nid: ast::NodeId, ty_opt: Option) { - match ty_opt { - None => { - // infer the variable's type - let var_id = self.fcx.infcx().next_ty_var_id(); - let var_ty = ty::mk_var(self.fcx.tcx(), var_id); - self.fcx.inh.locals.borrow_mut().insert(nid, var_ty); - } - Some(typ) => { - // take type that the user specified - self.fcx.inh.locals.borrow_mut().insert(nid, typ); - } + fn assign(&mut self, _span: Span, nid: ast::NodeId, ty_opt: Option) -> ty::t { + match ty_opt { + None => { + // infer the variable's type + let var_id = self.fcx.infcx().next_ty_var_id(); + let var_ty = ty::mk_var(self.fcx.tcx(), var_id); + self.fcx.inh.locals.borrow_mut().insert(nid, var_ty); + var_ty } + Some(typ) => { + // take type that the user specified + self.fcx.inh.locals.borrow_mut().insert(nid, typ); + typ + } + } } } @@ -474,7 +473,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for GatherLocalsVisitor<'a, 'tcx> { ast::TyInfer => None, _ => Some(self.fcx.to_ty(&*local.ty)) }; - self.assign(local.id, o_ty); + self.assign(local.span, local.id, o_ty); debug!("Local variable {} is assigned type {}", self.fcx.pat_to_string(&*local.pat), self.fcx.infcx().ty_to_string( @@ -484,19 +483,23 @@ impl<'a, 'tcx, 'v> Visitor<'v> for GatherLocalsVisitor<'a, 'tcx> { // Add pattern bindings. fn visit_pat(&mut self, p: &ast::Pat) { - match p.node { - ast::PatIdent(_, ref path1, _) - if pat_util::pat_is_binding(&self.fcx.ccx.tcx.def_map, p) => { - self.assign(p.id, None); - debug!("Pattern binding {} is assigned to {}", - token::get_ident(path1.node), - self.fcx.infcx().ty_to_string( - self.fcx.inh.locals.borrow().get_copy(&p.id))); - } - _ => {} - } - visit::walk_pat(self, p); + match p.node { + ast::PatIdent(_, ref path1, _) + if pat_util::pat_is_binding(&self.fcx.ccx.tcx.def_map, p) => { + let var_ty = self.assign(p.span, p.id, None); + self.fcx.require_type_is_sized(var_ty, p.span, + traits::VariableType(p.id)); + + debug!("Pattern binding {} is assigned to {} with type {}", + token::get_ident(path1.node), + self.fcx.infcx().ty_to_string( + self.fcx.inh.locals.borrow().get_copy(&p.id)), + var_ty.repr(self.fcx.tcx())); + } + _ => {} + } + visit::walk_pat(self, p); } fn visit_block(&mut self, b: &ast::Block) { @@ -592,11 +595,14 @@ fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>, // Add formal parameters. for (arg_ty, input) in arg_tys.iter().zip(decl.inputs.iter()) { // Create type variables for each argument. - pat_util::pat_bindings(&tcx.def_map, - &*input.pat, - |_bm, pat_id, _sp, _path| { - visit.assign(pat_id, None); - }); + pat_util::pat_bindings( + &tcx.def_map, + &*input.pat, + |_bm, pat_id, sp, _path| { + let var_ty = visit.assign(sp, pat_id, None); + fcx.require_type_is_sized(var_ty, sp, + traits::VariableType(pat_id)); + }); // Check the pattern. let pcx = pat_ctxt { @@ -713,71 +719,6 @@ pub fn check_struct(ccx: &CrateCtxt, id: ast::NodeId, span: Span) { } } -fn check_type_well_formed(ccx: &CrateCtxt, item: &ast::Item) { - /*! - * Checks that the field types (in a struct def'n) or - * argument types (in an enum def'n) are well-formed, - * meaning that they do not require any constraints not - * declared in the struct definition itself. - * For example, this definition would be illegal: - * - * struct Ref<'a, T> { x: &'a T } - * - * because the type did not declare that `T:'a`. - * - * We do this check as a pre-pass before checking fn bodies - * because if these constraints are not included it frequently - * leads to confusing errors in fn bodies. So it's better to check - * the types first. - */ - - debug!("check_type_well_formed(it.id={}, it.ident={})", - item.id, - ty::item_path_str(ccx.tcx, local_def(item.id))); - - match item.node { - ast::ItemStruct(..) => { - check_type_defn(ccx, item, |fcx| { - ty::struct_fields(ccx.tcx, local_def(item.id), - &fcx.inh.param_env.free_substs) - .iter() - .map(|f| f.mt.ty) - .collect() - }); - } - ast::ItemEnum(..) => { - check_type_defn(ccx, item, |fcx| { - ty::substd_enum_variants(ccx.tcx, local_def(item.id), - &fcx.inh.param_env.free_substs) - .iter() - .flat_map(|variant| { - variant.args - .iter() - .map(|&arg_ty| arg_ty) - }) - .collect() - }); - } - _ => {} - } - - fn check_type_defn(ccx: &CrateCtxt, - item: &ast::Item, - lookup_fields: |&FnCtxt| -> Vec) - { - let item_def_id = local_def(item.id); - let polytype = ty::lookup_item_type(ccx.tcx, item_def_id); - let param_env = - ty::construct_parameter_environment(ccx.tcx, - &polytype.generics, - item.id); - let inh = Inherited::new(ccx.tcx, param_env); - let fcx = blank_fn_ctxt(ccx, &inh, polytype.ty, item.id); - let field_tys = lookup_fields(&fcx); - regionck::regionck_type_defn(&fcx, item.span, field_tys.as_slice()); - } -} - pub fn check_item_sized(ccx: &CrateCtxt, it: &ast::Item) { debug!("check_item(it.id={}, it.ident={})", it.id, @@ -836,7 +777,6 @@ pub fn check_item(ccx: &CrateCtxt, it: &ast::Item) { ast_trait_ref, &*impl_trait_ref, impl_items.as_slice()); - vtable::resolve_impl(ccx.tcx, it, &impl_pty.generics, &*impl_trait_ref); } None => { } } @@ -1409,10 +1349,12 @@ fn compare_impl_method(tcx: &ty::ctxt, } fn check_cast(fcx: &FnCtxt, + cast_expr: &ast::Expr, e: &ast::Expr, - t: &ast::Ty, - id: ast::NodeId, - span: Span) { + t: &ast::Ty) { + let id = cast_expr.id; + let span = cast_expr.span; + // Find the type of `e`. Supply hints based on the type we are casting to, // if appropriate. let t_1 = fcx.to_ty(t); @@ -1443,6 +1385,7 @@ fn check_cast(fcx: &FnCtxt, if ty::type_is_trait(t_1) { // This will be looked up later on. + vtable2::check_object_cast(fcx, cast_expr, e, t_1); fcx.write_ty(id, t_1); return } @@ -1582,14 +1525,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn err_count_since_creation(&self) -> uint { self.ccx.tcx.sess.err_count() - self.err_count_on_creation } - - pub fn vtable_context<'a>(&'a self) -> VtableContext<'a, 'tcx> { - VtableContext { - infcx: self.infcx(), - param_env: &self.inh.param_env, - unboxed_closures: &self.inh.unboxed_closures, - } - } } impl<'a, 'tcx> RegionScope for infer::InferCtxt<'a, 'tcx> { @@ -1629,6 +1564,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.inh.node_types.borrow_mut().insert(node_id, ty); } + pub fn write_object_cast(&self, + key: ast::NodeId, + trait_ref: Rc) { + debug!("write_object_cast key={} trait_ref={}", + key, trait_ref.repr(self.tcx())); + self.inh.object_cast_map.borrow_mut().insert(key, trait_ref); + } + pub fn write_substs(&self, node_id: ast::NodeId, substs: ty::ItemSubsts) { if !substs.substs.is_noop() { debug!("write_substs({}, {}) in fcx {}", @@ -1651,10 +1594,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn write_autoderef_adjustment(&self, node_id: ast::NodeId, + span: Span, derefs: uint) { if derefs == 0 { return; } self.write_adjustment( node_id, + span, ty::AutoDerefRef(ty::AutoDerefRef { autoderefs: derefs, autoref: None }) @@ -1663,11 +1608,109 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn write_adjustment(&self, node_id: ast::NodeId, + span: Span, adj: ty::AutoAdjustment) { debug!("write_adjustment(node_id={:?}, adj={:?})", node_id, adj); + + // Careful: adjustments can imply trait obligations if we are + // casting from a concrete type to an object type. I think + // it'd probably be nicer to move the logic that creates the + // obligation into the code that creates the adjustment, but + // that's a bit awkward, so instead we go digging and pull the + // obligation out here. + self.register_adjustment_obligations(span, &adj); self.inh.adjustments.borrow_mut().insert(node_id, adj); } + fn register_adjustment_obligations(&self, + span: Span, + adj: &ty::AutoAdjustment) { + match *adj { + ty::AutoAddEnv(..) => { } + ty::AutoDerefRef(ref d_r) => { + match d_r.autoref { + Some(ref a_r) => { + self.register_autoref_obligations(span, a_r); + } + None => {} + } + } + } + } + + fn register_autoref_obligations(&self, + span: Span, + autoref: &ty::AutoRef) { + match *autoref { + ty::AutoUnsize(ref unsize) => { + self.register_unsize_obligations(span, unsize); + } + ty::AutoPtr(_, _, None) | + ty::AutoUnsafe(_, None) => { + } + ty::AutoPtr(_, _, Some(ref a_r)) | + ty::AutoUnsafe(_, Some(ref a_r)) => { + self.register_autoref_obligations(span, &**a_r) + } + ty::AutoUnsizeUniq(ref unsize) => { + self.register_unsize_obligations(span, unsize); + } + } + } + + fn register_unsize_obligations(&self, + span: Span, + unsize: &ty::UnsizeKind) { + debug!("register_unsize_obligations: unsize={:?}", unsize); + + match *unsize { + ty::UnsizeLength(..) => {} + ty::UnsizeStruct(ref u, _) => { + self.register_unsize_obligations(span, &**u) + } + ty::UnsizeVtable(ref ty_trait, self_ty) => { + vtable2::register_object_cast_obligations(self, + span, + ty_trait, + self_ty); + } + } + } + + pub fn instantiate_item_type(&self, + span: Span, + def_id: ast::DefId) + -> TypeAndSubsts + { + /*! + * Returns the type of `def_id` with all generics replaced by + * by fresh type/region variables. Also returns the + * substitution from the type parameters on `def_id` to the + * fresh variables. Registers any trait obligations specified + * on `def_id` at the same time. + */ + + let polytype = + ty::lookup_item_type(self.tcx(), def_id); + let substs = + self.infcx().fresh_substs_for_generics( + span, + &polytype.generics); + self.add_obligations_for_parameters( + traits::ObligationCause::new( + span, + traits::ItemObligation(def_id)), + &substs, + &polytype.generics); + let monotype = + polytype.ty.subst(self.tcx(), &substs); + + TypeAndSubsts { + ty: monotype, + substs: substs + } + } + pub fn write_nil(&self, node_id: ast::NodeId) { self.write_ty(node_id, ty::mk_nil()); } @@ -1678,8 +1721,56 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.write_ty(node_id, ty::mk_err()); } + pub fn require_type_meets(&self, + ty: ty::t, + span: Span, + code: traits::ObligationCauseCode, + bound: ty::BuiltinBound) + { + self.register_obligation( + traits::obligation_for_builtin_bound( + self.tcx(), + traits::ObligationCause::new(span, code), + ty, + bound)); + } + + pub fn require_type_is_sized(&self, + ty: ty::t, + span: Span, + code: traits::ObligationCauseCode) + { + self.require_type_meets(ty, span, code, ty::BoundSized); + } + + pub fn require_expr_have_sized_type(&self, + expr: &ast::Expr, + code: traits::ObligationCauseCode) + { + self.require_type_is_sized(self.expr_ty(expr), expr.span, code); + } + + pub fn register_obligation(&self, + obligation: traits::Obligation) + { + debug!("register_obligation({})", + obligation.repr(self.tcx())); + + self.inh.fulfillment_cx + .borrow_mut() + .register_obligation(self.tcx(), obligation); + } + pub fn to_ty(&self, ast_t: &ast::Ty) -> ty::t { - ast_ty_to_ty(self, self.infcx(), ast_t) + let t = ast_ty_to_ty(self, self.infcx(), ast_t); + + let mut bounds_checker = wf::BoundsChecker::new(self, + ast_t.span, + self.body_id, + None); + bounds_checker.check_ty(t); + + t } pub fn pat_to_string(&self, pat: &ast::Pat) -> String { @@ -1761,7 +1852,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(None) => Ok(()), Err(ref e) => Err((*e)), Ok(Some(adjustment)) => { - self.write_adjustment(expr.id, adjustment); + self.write_adjustment(expr.id, expr.span, adjustment); Ok(()) } } @@ -1829,10 +1920,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { origin: origin }); } - pub fn add_region_obligations_for_parameters(&self, - span: Span, - substs: &Substs, - generics: &ty::Generics) + pub fn add_obligations_for_parameters(&self, + cause: traits::ObligationCause, + substs: &Substs, + generics: &ty::Generics) { /*! * Given a set of generic parameter definitions (`generics`) @@ -1853,10 +1944,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { * locally. */ - debug!("add_region_obligations_for_parameters(substs={}, generics={})", + debug!("add_obligations_for_parameters(substs={}, generics={})", substs.repr(self.tcx()), generics.repr(self.tcx())); + self.add_trait_obligations_for_generics(cause, substs, generics); + self.add_region_obligations_for_generics(cause, substs, generics); + } + + fn add_trait_obligations_for_generics(&self, + cause: traits::ObligationCause, + substs: &Substs, + generics: &ty::Generics) { + let obligations = + traits::obligations_for_generics(self.tcx(), + cause, + generics, + substs); + obligations.map_move(|o| self.register_obligation(o)); + } + + fn add_region_obligations_for_generics(&self, + cause: traits::ObligationCause, + substs: &Substs, + generics: &ty::Generics) + { assert_eq!(generics.types.iter().len(), substs.types.iter().len()); for (type_def, &type_param) in @@ -1867,8 +1979,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { idx: type_def.index, def_id: type_def.def_id }; let bounds = type_def.bounds.subst(self.tcx(), substs); - add_region_obligations_for_type_parameter( - self, span, param_ty, &bounds, type_param); + self.add_region_obligations_for_type_parameter( + cause.span, param_ty, &bounds, type_param); } assert_eq!(generics.regions.iter().len(), @@ -1878,42 +1990,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { substs.regions().iter()) { let bounds = region_def.bounds.subst(self.tcx(), substs); - add_region_obligations_for_region_parameter( - self, span, bounds.as_slice(), region_param); + self.add_region_obligations_for_region_parameter( + cause.span, bounds.as_slice(), region_param); } + } - fn add_region_obligations_for_type_parameter( - fcx: &FnCtxt, - span: Span, - param_ty: ty::ParamTy, - param_bound: &ty::ParamBounds, - ty: ty::t) - { - // For each declared region bound `T:r`, `T` must outlive `r`. - let region_bounds = - ty::required_region_bounds( - fcx.tcx(), - param_bound.opt_region_bound.as_slice(), - param_bound.builtin_bounds, - param_bound.trait_bounds.as_slice()); - for &r in region_bounds.iter() { - let origin = infer::RelateParamBound(span, param_ty, ty); - fcx.register_region_obligation(origin, ty, r); - } + fn add_region_obligations_for_type_parameter(&self, + span: Span, + param_ty: ty::ParamTy, + param_bound: &ty::ParamBounds, + ty: ty::t) + { + // For each declared region bound `T:r`, `T` must outlive `r`. + let region_bounds = + ty::required_region_bounds( + self.tcx(), + param_bound.opt_region_bound.as_slice(), + param_bound.builtin_bounds, + param_bound.trait_bounds.as_slice()); + for &r in region_bounds.iter() { + let origin = infer::RelateParamBound(span, param_ty, ty); + self.register_region_obligation(origin, ty, r); } + } - fn add_region_obligations_for_region_parameter( - fcx: &FnCtxt, - span: Span, - region_bounds: &[ty::Region], - region_param: ty::Region) - { - for &b in region_bounds.iter() { - // For each bound `region:b`, `b <= region` must hold - // (i.e., `region` must outlive `b`). - let origin = infer::RelateRegionParamBound(span); - fcx.mk_subr(origin, b, region_param); - } + fn add_region_obligations_for_region_parameter(&self, + span: Span, + region_bounds: &[ty::Region], + region_param: ty::Region) + { + for &b in region_bounds.iter() { + // For each bound `region:b`, `b <= region` must hold + // (i.e., `region` must outlive `b`). + let origin = infer::RelateRegionParamBound(span); + self.mk_subr(origin, b, region_param); } } } @@ -2272,7 +2382,7 @@ fn check_method_argument_types(fcx: &FnCtxt, fn check_argument_types(fcx: &FnCtxt, sp: Span, fn_inputs: &[ty::t], - callee_expr: &ast::Expr, + _callee_expr: &ast::Expr, args: &[P], deref_args: DerefArgs, variadic: bool, @@ -2369,7 +2479,7 @@ fn check_argument_types(fcx: &FnCtxt, // an "opportunistic" vtable resolution of any trait // bounds on the call. if check_blocks { - vtable::early_resolve_expr(callee_expr, fcx, true); + vtable2::select_fcx_obligations_where_possible(fcx); } // For variadic functions, we don't have a declared type for all of @@ -2568,16 +2678,15 @@ fn check_expr_with_lvalue_pref(fcx: &FnCtxt, expr: &ast::Expr, check_expr_with_unifier(fcx, expr, NoExpectation, lvalue_pref, || ()) } - // determine the `self` type, using fresh variables for all variables // declared on the impl declaration e.g., `impl for ~[(A,B)]` // would return ($0, $1) where $0 and $1 are freshly instantiated type // variables. -pub fn impl_self_ty(vcx: &VtableContext, +pub fn impl_self_ty(fcx: &FnCtxt, span: Span, // (potential) receiver for this impl did: ast::DefId) -> TypeAndSubsts { - let tcx = vcx.tcx(); + let tcx = fcx.tcx(); let ity = ty::lookup_item_type(tcx, did); let (n_tps, rps, raw_ty) = @@ -2585,8 +2694,8 @@ pub fn impl_self_ty(vcx: &VtableContext, ity.generics.regions.get_slice(subst::TypeSpace), ity.ty); - let rps = vcx.infcx.region_vars_for_defs(span, rps); - let tps = vcx.infcx.next_ty_vars(n_tps); + let rps = fcx.inh.infcx.region_vars_for_defs(span, rps); + let tps = fcx.inh.infcx.next_ty_vars(n_tps); let substs = subst::Substs::new_type(tps, rps); let substd_ty = raw_ty.subst(tcx, &substs); @@ -3260,7 +3369,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, match field_ty { Some(field_ty) => { fcx.write_ty(expr.id, field_ty); - fcx.write_autoderef_adjustment(base.id, autoderefs); + fcx.write_autoderef_adjustment(base.id, base.span, autoderefs); return; } None => {} @@ -3344,7 +3453,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, match field_ty { Some(field_ty) => { fcx.write_ty(expr.id, field_ty); - fcx.write_autoderef_adjustment(base.id, autoderefs); + fcx.write_autoderef_adjustment(base.id, base.span, autoderefs); return; } None => {} @@ -3467,15 +3576,11 @@ fn check_expr_with_unifier(fcx: &FnCtxt, base_expr: Option<&ast::Expr>) { let tcx = fcx.ccx.tcx; - // Look up the number of type parameters and the raw type, and - // determine whether the class is region-parameterized. - let item_type = ty::lookup_item_type(tcx, class_id); - let raw_type = item_type.ty; - // Generate the struct type. - let substitutions = fcx.infcx().fresh_substs_for_type( - span, &item_type.generics); - let mut struct_type = raw_type.subst(tcx, &substitutions); + let TypeAndSubsts { + ty: mut struct_type, + substs: struct_substs + } = fcx.instantiate_item_type(span, class_id); // Look up and check the fields. let class_fields = ty::lookup_struct_fields(tcx, class_id); @@ -3484,7 +3589,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, span, class_id, id, - substitutions, + struct_substs, class_fields.as_slice(), fields, base_expr.is_none()); @@ -3517,9 +3622,10 @@ fn check_expr_with_unifier(fcx: &FnCtxt, // Look up the number of type parameters and the raw type, and // determine whether the enum is region-parameterized. - let item_type = ty::lookup_item_type(tcx, enum_id); - let substitutions = fcx.infcx().fresh_substs_for_type(span, &item_type.generics); - let enum_type = item_type.ty.subst(tcx, &substitutions); + let TypeAndSubsts { + ty: enum_type, + substs: substitutions + } = fcx.instantiate_item_type(span, enum_id); // Look up and check the enum variant fields. let variant_fields = ty::lookup_struct_fields(tcx, variant_id); @@ -3619,6 +3725,8 @@ fn check_expr_with_unifier(fcx: &FnCtxt, span_err!(tcx.sess, lhs.span, E0067, "illegal left-hand side expression"); } + fcx.require_expr_have_sized_type(&**lhs, traits::AssignmentLhsSized); + // Overwrite result of check_binop...this preserves existing behavior // but seems quite dubious with regard to user-defined methods // and so forth. - Niko @@ -3833,6 +3941,8 @@ fn check_expr_with_unifier(fcx: &FnCtxt, check_expr_has_type(fcx, &**rhs, lhs_ty); let rhs_ty = fcx.expr_ty(&**rhs); + fcx.require_expr_have_sized_type(&**lhs, traits::AssignmentLhsSized); + if ty::type_is_error(lhs_ty) || ty::type_is_error(rhs_ty) { fcx.write_error(id); } else if ty::type_is_bot(lhs_ty) || ty::type_is_bot(rhs_ty) { @@ -3863,7 +3973,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, ast::ExprForLoop(ref pat, ref head, ref block, _) => { check_expr(fcx, &**head); let typ = lookup_method_for_for_loop(fcx, &**head, expr.id); - vtable::early_resolve_expr(expr, fcx, true); + vtable2::select_fcx_obligations_where_possible(fcx); let pcx = pat_ctxt { fcx: fcx, @@ -3958,7 +4068,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, } _ => {} } - check_cast(fcx, &**e, &**t, id, expr.span); + check_cast(fcx, expr, &**e, &**t); } ast::ExprVec(ref args) => { let uty = match expected { @@ -4015,6 +4125,16 @@ fn check_expr_with_unifier(fcx: &FnCtxt, } }; + if count > 1 { + // For [foo, ..n] where n > 1, `foo` must have + // Copy type: + fcx.require_type_meets( + t, + expr.span, + traits::RepeatVec, + ty::BoundCopy); + } + if ty::type_is_error(element_ty) { fcx.write_error(id); } else if ty::type_is_bot(element_ty) { @@ -4143,6 +4263,8 @@ fn check_expr_with_unifier(fcx: &FnCtxt, } } } + + fcx.require_expr_have_sized_type(expr, traits::StructInitializerSized); } ast::ExprField(ref base, ref field, ref tys) => { check_field(fcx, expr, lvalue_pref, &**base, field, tys.as_slice()); @@ -4167,7 +4289,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, Some(ty) => { check_expr_has_type(fcx, &**idx, ty::mk_uint()); fcx.write_ty(id, ty); - fcx.write_autoderef_adjustment(base.id, autoderefs); + fcx.write_autoderef_adjustment(base.id, base.span, autoderefs); } None => { // This is an overloaded method. @@ -4520,8 +4642,10 @@ pub fn check_const_with_ty(fcx: &FnCtxt, check_expr_with_hint(fcx, e, declty); demand::coerce(fcx, e.span, declty, e); + vtable2::select_all_fcx_obligations_or_error(fcx); regionck::regionck_expr(fcx, e); writeback::resolve_type_vars_in_expr(fcx, e); + vtable2::check_builtin_bound_obligations(fcx); } /// Checks whether a type can be represented in memory. In particular, it @@ -5009,8 +5133,11 @@ pub fn instantiate_path(fcx: &FnCtxt, assert_eq!(substs.regions().len(space), region_defs.len(space)); } - fcx.add_region_obligations_for_parameters( - span, &substs, &polytype.generics); + fcx.add_obligations_for_parameters( + traits::ObligationCause::new(span, + traits::ItemObligation(def.def_id())), + &substs, + &polytype.generics); fcx.write_ty_substs(node_id, polytype.ty, ty::ItemSubsts { substs: substs, diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 95b7e03e6d9a..45ffddf3fe80 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -155,18 +155,9 @@ pub fn regionck_expr(fcx: &FnCtxt, e: &ast::Expr) { fcx.infcx().resolve_regions_and_report_errors(); } -pub fn regionck_type_defn(fcx: &FnCtxt, - span: Span, - component_tys: &[ty::t]) { - let mut rcx = Rcx::new(fcx, 0); - for &component_ty in component_tys.iter() { - // Check that each type outlives the empty region. Since the - // empty region is a subregion of all others, this can't fail - // unless the type does not meet the well-formedness - // requirements. - type_must_outlive(&mut rcx, infer::RelateRegionParamBound(span), - component_ty, ty::ReEmpty); - } +pub fn regionck_item(fcx: &FnCtxt, item: &ast::Item) { + let mut rcx = Rcx::new(fcx, item.id); + rcx.visit_region_obligations(item.id); fcx.infcx().resolve_regions_and_report_errors(); } @@ -179,6 +170,26 @@ pub fn regionck_fn(fcx: &FnCtxt, id: ast::NodeId, blk: &ast::Block) { fcx.infcx().resolve_regions_and_report_errors(); } +pub fn regionck_ensure_component_tys_wf(fcx: &FnCtxt, + span: Span, + component_tys: &[ty::t]) { + /*! + * Checks that the types in `component_tys` are well-formed. + * This will add constraints into the region graph. + * Does *not* run `resolve_regions_and_report_errors` and so forth. + */ + + let mut rcx = Rcx::new(fcx, 0); + for &component_ty in component_tys.iter() { + // Check that each type outlives the empty region. Since the + // empty region is a subregion of all others, this can't fail + // unless the type does not meet the well-formedness + // requirements. + type_must_outlive(&mut rcx, infer::RelateRegionParamBound(span), + component_ty, ty::ReEmpty); + } +} + /////////////////////////////////////////////////////////////////////////// // INTERNALS diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs deleted file mode 100644 index 9a70cf574fc4..000000000000 --- a/src/librustc/middle/typeck/check/vtable.rs +++ /dev/null @@ -1,1077 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - - -use middle::ty; -use middle::ty::{AutoDerefRef, ParamTy}; -use middle::ty_fold::TypeFolder; -use middle::typeck::astconv::AstConv; -use middle::typeck::check::{FnCtxt, impl_self_ty}; -use middle::typeck::check::{structurally_resolved_type}; -use middle::typeck::check::regionmanip; -use middle::typeck::check::writeback; -use middle::typeck::infer::fixup_err_to_string; -use middle::typeck::infer::{resolve_and_force_all_but_regions, resolve_type}; -use middle::typeck::infer; -use middle::typeck::{MethodCall, TypeAndSubsts}; -use middle::typeck::{param_index, vtable_error, vtable_origin, vtable_param}; -use middle::typeck::{vtable_param_res, vtable_res, vtable_static}; -use middle::typeck::{vtable_unboxed_closure}; -use middle::subst; -use middle::subst::{Subst, VecPerParamSpace}; -use util::common::indenter; -use util::nodemap::DefIdMap; -use util::ppaux; -use util::ppaux::Repr; - -use std::cell::RefCell; -use std::rc::Rc; -use std::collections::HashSet; -use syntax::ast; -use syntax::ast_util; -use syntax::codemap::Span; -use syntax::print::pprust::expr_to_string; -use syntax::visit; -use syntax::visit::Visitor; - -// vtable resolution looks for places where trait bounds are -// substituted in and figures out which vtable is used. There is some -// extra complication thrown in to support early "opportunistic" -// vtable resolution. This is a hacky mechanism that is invoked while -// typechecking function calls (after typechecking non-closure -// arguments and before typechecking closure arguments) in the hope of -// solving for the trait parameters from the impl. (For example, -// determining that if a parameter bounded by BaseIter is -// instantiated with Option, that A = int.) -// -// In early resolution mode, no vtables are recorded, and a number of -// errors are ignored. Early resolution only works if a type is -// *fully* resolved. (We could be less restrictive than that, but it -// would require much more care, and this seems to work decently in -// practice.) -// -// While resolution on a single type requires the type to be fully -// resolved, when resolving a substitution against a list of bounds, -// we do not require all of the types to be resolved in advance. -// Furthermore, we process substitutions in reverse order, which -// allows resolution on later parameters to give information on -// earlier params referenced by the typeclass bounds. -// It may be better to do something more clever, like processing fully -// resolved types first. - -/// A vtable context includes an inference context, a parameter environment, -/// and a list of unboxed closure types. -pub struct VtableContext<'a, 'tcx: 'a> { - pub infcx: &'a infer::InferCtxt<'a, 'tcx>, - pub param_env: &'a ty::ParameterEnvironment, - pub unboxed_closures: &'a RefCell>, -} - -impl<'a, 'tcx> VtableContext<'a, 'tcx> { - pub fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.infcx.tcx } -} - -fn lookup_vtables(vcx: &VtableContext, - span: Span, - type_param_defs: &VecPerParamSpace, - substs: &subst::Substs, - is_early: bool) - -> VecPerParamSpace { - debug!("lookup_vtables(\ - type_param_defs={}, \ - substs={}", - type_param_defs.repr(vcx.tcx()), - substs.repr(vcx.tcx())); - - // We do this backwards for reasons discussed above. - let result = type_param_defs.map_rev(|def| { - let ty = *substs.types.get(def.space, def.index); - lookup_vtables_for_param(vcx, span, Some(substs), - &def.bounds, ty, is_early) - }); - - debug!("lookup_vtables result(\ - type_param_defs={}, \ - substs={}, \ - result={})", - type_param_defs.repr(vcx.tcx()), - substs.repr(vcx.tcx()), - result.repr(vcx.tcx())); - - result -} - -fn lookup_vtables_for_param(vcx: &VtableContext, - span: Span, - // None for substs means the identity - substs: Option<&subst::Substs>, - type_param_bounds: &ty::ParamBounds, - ty: ty::t, - is_early: bool) - -> vtable_param_res { - let tcx = vcx.tcx(); - - debug!("lookup_vtables_for_param(ty={}, type_param_bounds={}, is_early={})", - ty.repr(vcx.tcx()), - type_param_bounds.repr(vcx.tcx()), - is_early); - - // ty is the value supplied for the type parameter A... - let mut param_result = Vec::new(); - - ty::each_bound_trait_and_supertraits(tcx, - type_param_bounds.trait_bounds - .as_slice(), - |trait_ref| { - // ...and here trait_ref is each bound that was declared on A, - // expressed in terms of the type parameters. - - debug!("matching ty={} trait_ref={}", - ty.repr(vcx.tcx()), - trait_ref.repr(vcx.tcx())); - - ty::populate_implementations_for_trait_if_necessary(tcx, - trait_ref.def_id); - - // Substitute the values of the type parameters that may - // appear in the bound. - let trait_ref = substs.as_ref().map_or(trait_ref.clone(), |substs| { - debug!("about to subst: {}, {}", - trait_ref.repr(tcx), substs.repr(tcx)); - trait_ref.subst(tcx, *substs) - }); - - debug!("after subst: {}", trait_ref.repr(tcx)); - - match lookup_vtable(vcx, span, ty, trait_ref.clone(), is_early) { - Some(vtable) => param_result.push(vtable), - None => { - vcx.tcx().sess.span_err(span, - format!("failed to find an implementation of \ - trait {} for {}", - vcx.infcx.trait_ref_to_string(&*trait_ref), - vcx.infcx.ty_to_string(ty)).as_slice()); - param_result.push(vtable_error) - } - } - true - }); - - debug!("lookup_vtables_for_param result(\ - type_param_bounds={}, \ - ty={}, \ - result={})", - type_param_bounds.repr(vcx.tcx()), - ty.repr(vcx.tcx()), - param_result.repr(vcx.tcx())); - - param_result -} - -fn relate_trait_refs(vcx: &VtableContext, - span: Span, - act_trait_ref: Rc, - exp_trait_ref: Rc) { - /*! - * - * Checks that an implementation of `act_trait_ref` is suitable - * for use where `exp_trait_ref` is required and reports an - * error otherwise. - */ - - match infer::mk_sub_trait_refs(vcx.infcx, - false, - infer::RelateTraitRefs(span), - act_trait_ref.clone(), - exp_trait_ref.clone()) { - Ok(()) => {} // Ok. - Err(ref err) => { - // There is an error, but we need to do some work to make - // the message good. - // Resolve any type vars in the trait refs - let r_act_trait_ref = - vcx.infcx.resolve_type_vars_in_trait_ref_if_possible(&*act_trait_ref); - let r_exp_trait_ref = - vcx.infcx.resolve_type_vars_in_trait_ref_if_possible(&*exp_trait_ref); - // Only print the message if there aren't any previous type errors - // inside the types. - if !ty::trait_ref_contains_error(&r_act_trait_ref) && - !ty::trait_ref_contains_error(&r_exp_trait_ref) - { - let tcx = vcx.tcx(); - span_err!(tcx.sess, span, E0095, "expected {}, found {} ({})", - ppaux::trait_ref_to_string(tcx, &r_exp_trait_ref), - ppaux::trait_ref_to_string(tcx, &r_act_trait_ref), - ty::type_err_to_str(tcx, err)); - } - } - } -} - -// Look up the vtable implementing the trait `trait_ref` at type `t` -fn lookup_vtable(vcx: &VtableContext, - span: Span, - ty: ty::t, - trait_ref: Rc, - is_early: bool) - -> Option -{ - debug!("lookup_vtable(ty={}, trait_ref={})", - ty.repr(vcx.tcx()), - trait_ref.repr(vcx.tcx())); - let _i = indenter(); - - let ty = match fixup_ty(vcx, span, ty, is_early) { - Some(ty) => ty, - None => { - // fixup_ty can only fail if this is early resolution - assert!(is_early); - // The type has unconstrained type variables in it, so we can't - // do early resolution on it. Return some completely bogus vtable - // information: we aren't storing it anyways. - return Some(vtable_error); - } - }; - - if ty::type_is_error(ty) { - return Some(vtable_error); - } - - // If the type is self or a param, we look at the trait/supertrait - // bounds to see if they include the trait we are looking for. - let vtable_opt = match ty::get(ty).sty { - ty::ty_param(ParamTy {space, idx: n, ..}) => { - let env_bounds = &vcx.param_env.bounds; - let type_param_bounds = &env_bounds.get(space, n).trait_bounds; - lookup_vtable_from_bounds(vcx, - span, - type_param_bounds.as_slice(), - param_index { - space: space, - index: n, - }, - trait_ref.clone()) - } - - // Default case just falls through - _ => None - }; - - if vtable_opt.is_some() { return vtable_opt; } - - // If we aren't a self type or param, or it was, but we didn't find it, - // do a search. - search_for_vtable(vcx, span, ty, trait_ref, is_early) -} - -// Given a list of bounds on a type, search those bounds to see if any -// of them are the vtable we are looking for. -fn lookup_vtable_from_bounds(vcx: &VtableContext, - span: Span, - bounds: &[Rc], - param: param_index, - trait_ref: Rc) - -> Option { - let tcx = vcx.tcx(); - - let mut n_bound = 0; - let mut ret = None; - ty::each_bound_trait_and_supertraits(tcx, bounds, |bound_trait_ref| { - debug!("checking bounds trait {}", - bound_trait_ref.repr(vcx.tcx())); - - if bound_trait_ref.def_id == trait_ref.def_id { - relate_trait_refs(vcx, span, bound_trait_ref, trait_ref.clone()); - let vtable = vtable_param(param, n_bound); - debug!("found param vtable: {:?}", - vtable); - ret = Some(vtable); - false - } else { - n_bound += 1; - true - } - }); - ret -} - -fn search_for_unboxed_closure_vtable(vcx: &VtableContext, - span: Span, - ty: ty::t, - trait_ref: Rc) - -> Option { - let tcx = vcx.tcx(); - let closure_def_id = match ty::get(ty).sty { - ty::ty_unboxed_closure(closure_def_id, _) => closure_def_id, - _ => return None, - }; - - let fn_traits = [ - (ty::FnUnboxedClosureKind, tcx.lang_items.fn_trait()), - (ty::FnMutUnboxedClosureKind, tcx.lang_items.fn_mut_trait()), - (ty::FnOnceUnboxedClosureKind, tcx.lang_items.fn_once_trait()), - ]; - for tuple in fn_traits.iter() { - let kind = match tuple { - &(kind, Some(ref fn_trait)) if *fn_trait == trait_ref.def_id => { - kind - } - _ => continue, - }; - - // Check to see whether the argument and return types match. - let unboxed_closures = tcx.unboxed_closures.borrow(); - let closure_type = match unboxed_closures.find(&closure_def_id) { - Some(closure) => { - if closure.kind != kind { - continue - } - closure.closure_type.clone() - } - None => { - // Try the inherited unboxed closure type map. - let unboxed_closures = vcx.unboxed_closures.borrow(); - match unboxed_closures.find(&closure_def_id) { - Some(closure) => { - if closure.kind != kind { - continue - } - closure.closure_type.clone() - } - None => { - tcx.sess.span_bug(span, - "didn't find unboxed closure type \ - in tcx map or inh map") - } - } - } - }; - - // FIXME(pcwalton): This is a bogus thing to do, but - // it'll do for now until we get the new trait-bound - // region skolemization working. - let (_, new_signature) = - regionmanip::replace_late_bound_regions_in_fn_sig( - tcx, - &closure_type.sig, - |br| { - vcx.infcx.next_region_var(infer::LateBoundRegion(span, - br)) - }); - - let arguments_tuple = *new_signature.inputs.get(0); - let corresponding_trait_ref = Rc::new(ty::TraitRef { - def_id: trait_ref.def_id, - substs: subst::Substs::new_trait( - vec![arguments_tuple, new_signature.output], - Vec::new(), - ty) - }); - - relate_trait_refs(vcx, span, corresponding_trait_ref, trait_ref); - return Some(vtable_unboxed_closure(closure_def_id)) - } - - None -} - -fn search_for_vtable(vcx: &VtableContext, - span: Span, - ty: ty::t, - trait_ref: Rc, - is_early: bool) - -> Option { - let tcx = vcx.tcx(); - - // First, check to see whether this is a call to the `call` method of an - // unboxed closure. If so, and the arguments match, we're done. - match search_for_unboxed_closure_vtable(vcx, - span, - ty, - trait_ref.clone()) { - Some(vtable_origin) => return Some(vtable_origin), - None => {} - } - - // Nope. Continue. - - let mut found = Vec::new(); - let mut impls_seen = HashSet::new(); - - // Load the implementations from external metadata if necessary. - ty::populate_implementations_for_trait_if_necessary(tcx, - trait_ref.def_id); - - let impls = match tcx.trait_impls.borrow().find_copy(&trait_ref.def_id) { - Some(impls) => impls, - None => { - return None; - } - }; - // impls is the list of all impls in scope for trait_ref. - for &impl_did in impls.borrow().iter() { - // im is one specific impl of trait_ref. - - // First, ensure we haven't processed this impl yet. - if impls_seen.contains(&impl_did) { - continue; - } - impls_seen.insert(impl_did); - - // ty::impl_traits gives us the trait im implements. - // - // If foo implements a trait t, and if t is the same trait as - // trait_ref, we need to unify it with trait_ref in order to - // get all the ty vars sorted out. - let r = ty::impl_trait_ref(tcx, impl_did); - let of_trait_ref = r.expect("trait_ref missing on trait impl"); - if of_trait_ref.def_id != trait_ref.def_id { continue; } - - // At this point, we know that of_trait_ref is the same trait - // as trait_ref, but possibly applied to different substs. - // - // Next, we check whether the "for" ty in the impl is - // compatible with the type that we're casting to a - // trait. That is, if im is: - // - // impl some_trait for self_ty { ... } - // - // we check whether self_ty is the type of the thing that - // we're trying to cast to some_trait. If not, then we try - // the next impl. - // - // FIXME: document a bit more what this means - let TypeAndSubsts { - substs: substs, - ty: for_ty - } = impl_self_ty(vcx, span, impl_did); - match infer::mk_eqty(vcx.infcx, - false, - infer::RelateSelfType(span), - ty, - for_ty) { - Err(_) => continue, - Ok(()) => () - } - - // Now, in the previous example, for_ty is bound to - // the type self_ty, and substs is bound to [T]. - debug!("The self ty is {} and its substs are {}", - for_ty.repr(tcx), - substs.types.repr(tcx)); - - // Next, we unify trait_ref -- the type that we want to cast - // to -- with of_trait_ref -- the trait that im implements. At - // this point, we require that they be unifiable with each - // other -- that's what relate_trait_refs does. - // - // For example, in the above example, of_trait_ref would be - // some_trait, so we would be unifying trait_ref (for - // some value of U) with some_trait. This would fail if T - // and U weren't compatible. - - let of_trait_ref = of_trait_ref.subst(tcx, &substs); - - debug!("(checking vtable) num 2 relating trait \ - ty {} to of_trait_ref {}", - vcx.infcx.trait_ref_to_string(&*trait_ref), - vcx.infcx.trait_ref_to_string(&*of_trait_ref)); - - relate_trait_refs(vcx, span, of_trait_ref, trait_ref.clone()); - - - // Recall that trait_ref -- the trait type we're casting to -- - // is the trait with id trait_ref.def_id applied to the substs - // trait_ref.substs. - - // Resolve any sub bounds. Note that there still may be free - // type variables in substs. This might still be OK: the - // process of looking up bounds might constrain some of them. - // - // This does not check built-in traits because those are handled - // later in the kind checking pass. - let im_generics = - ty::lookup_item_type(tcx, impl_did).generics; - let subres = lookup_vtables(vcx, - span, - &im_generics.types, - &substs, - is_early); - - // substs might contain type variables, so we call - // fixup_substs to resolve them. - let substs_f = match fixup_substs(vcx, span, - trait_ref.def_id, - substs, - is_early) { - Some(ref substs) => (*substs).clone(), - None => { - assert!(is_early); - // Bail out with a bogus answer - return Some(vtable_error); - } - }; - - debug!("The fixed-up substs are {} - \ - they will be unified with the bounds for \ - the target ty, {}", - substs_f.types.repr(tcx), - trait_ref.repr(tcx)); - - // Next, we unify the fixed-up substitutions for the impl self - // ty with the substitutions from the trait type that we're - // trying to cast to. connect_trait_tps requires these lists - // of types to unify pairwise. - // I am a little confused about this, since it seems to be - // very similar to the relate_trait_refs we already do, - // but problems crop up if it is removed, so... -sully - connect_trait_tps(vcx, span, &substs_f, trait_ref.clone(), impl_did); - - // Finally, we register that we found a matching impl, and - // record the def ID of the impl as well as the resolved list - // of type substitutions for the target trait. - found.push(vtable_static(impl_did, substs_f, subres)); - } - - match found.len() { - 0 => { return None } - 1 => return Some(found.get(0).clone()), - _ => { - if !is_early { - span_err!(vcx.tcx().sess, span, E0096, - "multiple applicable methods in scope"); - } - return Some(found.get(0).clone()); - } - } -} - - -fn fixup_substs(vcx: &VtableContext, - span: Span, - id: ast::DefId, - substs: subst::Substs, - is_early: bool) - -> Option { - let tcx = vcx.tcx(); - // use a dummy type just to package up the substs that need fixing up - let t = ty::mk_trait(tcx, - id, substs, - ty::region_existential_bound(ty::ReStatic)); - fixup_ty(vcx, span, t, is_early).map(|t_f| { - match ty::get(t_f).sty { - ty::ty_trait(ref inner) => inner.substs.clone(), - _ => fail!("t_f should be a trait") - } - }) -} - -fn fixup_ty(vcx: &VtableContext, - span: Span, - ty: ty::t, - is_early: bool) - -> Option { - let tcx = vcx.tcx(); - match resolve_type(vcx.infcx, Some(span), ty, resolve_and_force_all_but_regions) { - Ok(new_type) => Some(new_type), - Err(e) if !is_early => { - tcx.sess.span_err(span, - format!("cannot determine a type for this bounded type \ - parameter: {}", - fixup_err_to_string(e)).as_slice()); - Some(ty::mk_err()) - } - Err(_) => { - None - } - } -} - -fn connect_trait_tps(vcx: &VtableContext, - span: Span, - impl_substs: &subst::Substs, - trait_ref: Rc, - impl_did: ast::DefId) { - let tcx = vcx.tcx(); - - let impl_trait_ref = match ty::impl_trait_ref(tcx, impl_did) { - Some(t) => t, - None => vcx.tcx().sess.span_bug(span, - "connect_trait_tps invoked on a type impl") - }; - - let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs); - relate_trait_refs(vcx, span, impl_trait_ref, trait_ref); -} - -fn insert_vtables(fcx: &FnCtxt, vtable_key: MethodCall, vtables: vtable_res) { - debug!("insert_vtables(vtable_key={}, vtables={})", - vtable_key, vtables.repr(fcx.tcx())); - fcx.inh.vtable_map.borrow_mut().insert(vtable_key, vtables); -} - -pub fn early_resolve_expr(ex: &ast::Expr, fcx: &FnCtxt, is_early: bool) { - fn mutability_allowed(a_mutbl: ast::Mutability, - b_mutbl: ast::Mutability) -> bool { - a_mutbl == b_mutbl || - (a_mutbl == ast::MutMutable && b_mutbl == ast::MutImmutable) - } - - debug!("vtable: early_resolve_expr() ex with id {:?} (early: {}): {}", - ex.id, is_early, expr_to_string(ex)); - let _indent = indenter(); - - let cx = fcx.ccx; - let check_object_cast = |src_ty: ty::t, target_ty: ty::t| { - debug!("check_object_cast {} to {}", - fcx.infcx().ty_to_string(src_ty), - fcx.infcx().ty_to_string(target_ty)); - // Check that a cast is of correct types. - match (&ty::get(target_ty).sty, &ty::get(src_ty).sty) { - (&ty::ty_rptr(_, ty::mt{ty, mutbl}), &ty::ty_rptr(_, mt)) - | (&ty::ty_ptr(ty::mt{ty, mutbl}), &ty::ty_rptr(_, mt)) - if !mutability_allowed(mt.mutbl, mutbl) => { - match ty::get(ty).sty { - ty::ty_trait(..) => { - span_err!(fcx.tcx().sess, ex.span, E0097, "types differ in mutability"); - } - _ => {} - } - } - (&ty::ty_uniq(..), &ty::ty_uniq(..) ) - | (&ty::ty_ptr(..), &ty::ty_ptr(..) ) - | (&ty::ty_ptr(..), &ty::ty_rptr(..)) => {} - (&ty::ty_rptr(r_t, _), &ty::ty_rptr(r_s, _)) => { - infer::mk_subr(fcx.infcx(), - infer::RelateObjectBound(ex.span), - r_t, - r_s); - } - (&ty::ty_uniq(ty), _) => { - match ty::get(ty).sty { - ty::ty_trait(..) => { - span_err!(fcx.ccx.tcx.sess, ex.span, E0098, - "can only cast an boxed pointer to a boxed object, not a {}", - ty::ty_sort_string(fcx.tcx(), src_ty)); - } - _ => {} - } - - } - (&ty::ty_rptr(_, ty::mt{ty, ..}), _) => { - match ty::get(ty).sty { - ty::ty_trait(..) => { - span_err!(fcx.ccx.tcx.sess, ex.span, E0099, - "can only cast an &-pointer to an &-object, not a {}", - ty::ty_sort_string(fcx.tcx(), src_ty)); - } - _ => {} - } - } - (&ty::ty_ptr(ty::mt{ty, ..}), _) => { - match ty::get(ty).sty { - ty::ty_trait(..) => { - span_err!(fcx.ccx.tcx.sess, ex.span, E0160, - "can only cast an *-pointer or &-pointer to an *-object, not a {}", - ty::ty_sort_string(fcx.tcx(), src_ty)); - } - _ => {} - } - } - _ => {} - } - }; - let resolve_object_cast = |src_ty: ty::t, target_ty: ty::t, key: MethodCall| { - // Look up vtables for the type we're casting to, - // passing in the source and target type. The source - // must be a pointer type suitable to the object sigil, - // e.g.: `&x as &Trait` or `box x as Box` - // Bounds of type's contents are not checked here, but in kind.rs. - match ty::get(target_ty).sty { - ty::ty_trait(box ty::TyTrait { - def_id: target_def_id, substs: ref target_substs, .. - }) => { - let vcx = fcx.vtable_context(); - - // Take the type parameters from the object - // type, but set the Self type (which is - // unknown, for the object type) to be the type - // we are casting from. - let mut target_types = target_substs.types.clone(); - assert!(target_types.get_self().is_none()); - target_types.push(subst::SelfSpace, src_ty); - - let target_trait_ref = Rc::new(ty::TraitRef { - def_id: target_def_id, - substs: subst::Substs { - regions: target_substs.regions.clone(), - types: target_types - } - }); - - let param_bounds = ty::ParamBounds { - opt_region_bound: None, - builtin_bounds: ty::empty_builtin_bounds(), - trait_bounds: vec!(target_trait_ref) - }; - - let vtables = - lookup_vtables_for_param(&vcx, - ex.span, - None, - ¶m_bounds, - src_ty, - is_early); - - if !is_early { - let mut r = VecPerParamSpace::empty(); - r.push(subst::SelfSpace, vtables); - insert_vtables(fcx, key, r); - } - } - _ => {} - } - }; - match ex.node { - ast::ExprPath(..) => { - fcx.opt_node_ty_substs(ex.id, |item_substs| { - debug!("vtable resolution on parameter bounds for expr {}", - ex.repr(fcx.tcx())); - let def = cx.tcx.def_map.borrow().get_copy(&ex.id); - let did = def.def_id(); - let item_ty = ty::lookup_item_type(cx.tcx, did); - debug!("early resolve expr: def {:?} {:?}, {:?}, {}", ex.id, did, def, - fcx.infcx().ty_to_string(item_ty.ty)); - debug!("early_resolve_expr: looking up vtables for type params {}", - item_ty.generics.types.repr(fcx.tcx())); - let vcx = fcx.vtable_context(); - let vtbls = lookup_vtables(&vcx, ex.span, - &item_ty.generics.types, - &item_substs.substs, is_early); - if !is_early { - insert_vtables(fcx, MethodCall::expr(ex.id), vtbls); - } - }); - } - - // Must resolve bounds on methods with bounded params - ast::ExprBinary(_, _, _) | - ast::ExprUnary(_, _) | - ast::ExprAssignOp(_, _, _) | - ast::ExprIndex(_, _) | - ast::ExprMethodCall(_, _, _) | - ast::ExprForLoop(..) | - ast::ExprCall(..) => { - match fcx.inh.method_map.borrow().find(&MethodCall::expr(ex.id)) { - Some(method) => { - debug!("vtable resolution on parameter bounds for method call {}", - ex.repr(fcx.tcx())); - let type_param_defs = - ty::method_call_type_param_defs(fcx, method.origin); - let substs = fcx.method_ty_substs(ex.id); - let vcx = fcx.vtable_context(); - let vtbls = lookup_vtables(&vcx, ex.span, - &type_param_defs, - &substs, is_early); - if !is_early { - insert_vtables(fcx, MethodCall::expr(ex.id), vtbls); - } - } - None => {} - } - } - ast::ExprCast(ref src, _) => { - debug!("vtable resolution on expr {}", ex.repr(fcx.tcx())); - let target_ty = fcx.expr_ty(ex); - let src_ty = structurally_resolved_type(fcx, ex.span, - fcx.expr_ty(&**src)); - check_object_cast(src_ty, target_ty); - match (ty::deref(src_ty, false), ty::deref(target_ty, false)) { - (Some(s), Some(t)) => { - let key = MethodCall::expr(ex.id); - resolve_object_cast(s.ty, t.ty, key) - } - _ => {} - } - } - _ => () - } - - // Search for auto-adjustments to find trait coercions - match fcx.inh.adjustments.borrow().find(&ex.id) { - Some(adjustment) => { - match *adjustment { - _ if ty::adjust_is_object(adjustment) => { - let src_ty = structurally_resolved_type(fcx, ex.span, - fcx.expr_ty(ex)); - match ty::type_of_adjust(fcx.tcx(), adjustment) { - Some(target_ty) => { - check_object_cast(src_ty, target_ty) - } - None => {} - } - - match trait_cast_types(fcx, adjustment, src_ty, ex.span) { - Some((s, t)) => { - let key = MethodCall::autoobject(ex.id); - resolve_object_cast(s, t, key) - } - None => fail!("Couldn't extract types from adjustment") - } - } - AutoDerefRef(ref adj) => { - for autoderef in range(0, adj.autoderefs) { - let method_call = MethodCall::autoderef(ex.id, autoderef); - match fcx.inh.method_map.borrow().find(&method_call) { - Some(method) => { - debug!("vtable resolution on parameter bounds for autoderef {}", - ex.repr(fcx.tcx())); - let type_param_defs = - ty::method_call_type_param_defs(cx.tcx, method.origin); - let vcx = fcx.vtable_context(); - let vtbls = lookup_vtables(&vcx, ex.span, - &type_param_defs, - &method.substs, is_early); - if !is_early { - insert_vtables(fcx, method_call, vtbls); - } - } - None => {} - } - } - } - _ => {} - } - } - None => {} - } -} - -// When we coerce (possibly implicitly) from a concrete type to a trait type, this -// function returns the concrete type and trait. This might happen arbitrarily -// deep in the adjustment. This function will fail if the adjustment does not -// match the source type. -// This function will always return types if ty::adjust_is_object is true for the -// adjustment -fn trait_cast_types(fcx: &FnCtxt, - adj: &ty::AutoAdjustment, - src_ty: ty::t, - sp: Span) - -> Option<(ty::t, ty::t)> { - fn trait_cast_types_autoref(fcx: &FnCtxt, - autoref: &ty::AutoRef, - src_ty: ty::t, - sp: Span) - -> Option<(ty::t, ty::t)> { - fn trait_cast_types_unsize(fcx: &FnCtxt, - k: &ty::UnsizeKind, - src_ty: ty::t, - sp: Span) - -> Option<(ty::t, ty::t)> { - match k { - &ty::UnsizeVtable(bounds, def_id, ref substs) => { - Some((src_ty, ty::mk_trait(fcx.tcx(), def_id, substs.clone(), bounds))) - } - &ty::UnsizeStruct(box ref k, tp_index) => match ty::get(src_ty).sty { - ty::ty_struct(_, ref substs) => { - let ty_substs = substs.types.get_slice(subst::TypeSpace); - let field_ty = structurally_resolved_type(fcx, sp, ty_substs[tp_index]); - trait_cast_types_unsize(fcx, k, field_ty, sp) - } - _ => fail!("Failed to find a ty_struct to correspond with \ - UnsizeStruct whilst walking adjustment. Found {}", - ppaux::ty_to_string(fcx.tcx(), src_ty)) - }, - _ => None - } - } - - match autoref { - &ty::AutoUnsize(ref k) | - &ty::AutoUnsizeUniq(ref k) => trait_cast_types_unsize(fcx, k, src_ty, sp), - &ty::AutoPtr(_, _, Some(box ref autoref)) | - &ty::AutoUnsafe(_, Some(box ref autoref)) => { - trait_cast_types_autoref(fcx, autoref, src_ty, sp) - } - _ => None - } - } - - match adj { - &ty::AutoDerefRef(AutoDerefRef{autoref: Some(ref autoref), autoderefs}) => { - let mut derefed_type = src_ty; - for _ in range(0, autoderefs) { - derefed_type = ty::deref(derefed_type, true).unwrap().ty; - derefed_type = structurally_resolved_type(fcx, sp, derefed_type) - } - trait_cast_types_autoref(fcx, autoref, derefed_type, sp) - } - _ => None - } -} - -pub fn resolve_impl(tcx: &ty::ctxt, - impl_item: &ast::Item, - impl_generics: &ty::Generics, - impl_trait_ref: &ty::TraitRef) { - /*! - * The situation is as follows. We have some trait like: - * - * trait Foo : Bar { - * fn method() { ... } - * } - * - * and an impl like: - * - * impl Foo for int { ... } - * - * We want to validate that the various requirements of the trait - * are met: - * - * A:Clone, Self:Bar - * - * But of course after substituting the types from the impl: - * - * B:Clone, int:Bar - * - * We store these results away as the "impl_res" for use by the - * default methods. - */ - - debug!("resolve_impl(impl_item.id={})", - impl_item.id); - - let param_env = ty::construct_parameter_environment(tcx, - impl_generics, - impl_item.id); - - // The impl_trait_ref in our example above would be - // `Foo for int` - let impl_trait_ref = impl_trait_ref.subst(tcx, ¶m_env.free_substs); - debug!("impl_trait_ref={}", impl_trait_ref.repr(tcx)); - - let infcx = &infer::new_infer_ctxt(tcx); - let unboxed_closures = RefCell::new(DefIdMap::new()); - let vcx = VtableContext { - infcx: infcx, - param_env: ¶m_env, - unboxed_closures: &unboxed_closures, - }; - - // Resolve the vtables for the trait reference on the impl. This - // serves many purposes, best explained by example. Imagine we have: - // - // trait A : C { fn x(&self) { ... } } - // - // and - // - // impl A for uint { ... } - // - // In that case, the trait ref will be `A for uint`. Resolving - // this will first check that the various types meet their requirements: - // - // 1. Because of T:B, int must implement the trait B - // 2. Because of the supertrait C, uint must implement the trait C. - // - // Simultaneously, the result of this resolution (`vtbls`), is precisely - // the set of vtable information needed to compile the default method - // `x()` adapted to the impl. (After all, a default method is basically - // the same as: - // - // fn default_x(...) { .. .}) - - let trait_def = ty::lookup_trait_def(tcx, impl_trait_ref.def_id); - let vtbls = lookup_vtables(&vcx, - impl_item.span, - &trait_def.generics.types, - &impl_trait_ref.substs, - false); - - infcx.resolve_regions_and_report_errors(); - - let vtbls = writeback::resolve_impl_res(infcx, impl_item.span, &vtbls); - let impl_def_id = ast_util::local_def(impl_item.id); - - debug!("impl_vtables for {} are {}", - impl_def_id.repr(tcx), - vtbls.repr(tcx)); - - tcx.impl_vtables.borrow_mut().insert(impl_def_id, vtbls); -} - -/// Resolve vtables for a method call after typeck has finished. -/// Used by trans to monomorphize artificial method callees (e.g. drop). -pub fn trans_resolve_method(tcx: &ty::ctxt, id: ast::NodeId, - substs: &subst::Substs) -> vtable_res { - let generics = ty::lookup_item_type(tcx, ast_util::local_def(id)).generics; - let unboxed_closures = RefCell::new(DefIdMap::new()); - let vcx = VtableContext { - infcx: &infer::new_infer_ctxt(tcx), - param_env: &ty::construct_parameter_environment(tcx, &ty::Generics::empty(), id), - unboxed_closures: &unboxed_closures, - }; - - lookup_vtables(&vcx, - tcx.map.span(id), - &generics.types, - substs, - false) -} - -impl<'a, 'b, 'tcx, 'v> Visitor<'v> for &'a FnCtxt<'b, 'tcx> { - fn visit_expr(&mut self, ex: &ast::Expr) { - early_resolve_expr(ex, *self, false); - visit::walk_expr(self, ex); - } - fn visit_item(&mut self, _: &ast::Item) { - // no-op - } -} - -// Detect points where a trait-bounded type parameter is -// instantiated, resolve the impls for the parameters. -pub fn resolve_in_block(mut fcx: &FnCtxt, bl: &ast::Block) { - visit::walk_block(&mut fcx, bl); -} - -/// Used in the kind checker after typechecking has finished. Calls -/// `any_missing` if any bounds were missing. -pub fn check_param_bounds(tcx: &ty::ctxt, - span: Span, - parameter_environment: &ty::ParameterEnvironment, - type_param_defs: - &VecPerParamSpace, - substs: &subst::Substs, - any_missing: |&ty::TraitRef|) { - let unboxed_closures = RefCell::new(DefIdMap::new()); - let vcx = VtableContext { - infcx: &infer::new_infer_ctxt(tcx), - param_env: parameter_environment, - unboxed_closures: &unboxed_closures, - }; - let vtable_param_results = - lookup_vtables(&vcx, span, type_param_defs, substs, false); - for (vtable_param_result, type_param_def) in - vtable_param_results.iter().zip(type_param_defs.iter()) { - for (vtable_result, trait_ref) in - vtable_param_result.iter() - .zip(type_param_def.bounds - .trait_bounds - .iter()) { - match *vtable_result { - vtable_error => any_missing(&**trait_ref), - vtable_static(..) | - vtable_param(..) | - vtable_unboxed_closure(..) => {} - } - } - } -} - diff --git a/src/librustc/middle/typeck/check/vtable2.rs b/src/librustc/middle/typeck/check/vtable2.rs new file mode 100644 index 000000000000..f75d2622fdb8 --- /dev/null +++ b/src/librustc/middle/typeck/check/vtable2.rs @@ -0,0 +1,407 @@ +// Copyright 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use middle::subst::{SelfSpace}; +use middle::traits; +use middle::traits::{SelectionError, Overflow, + OutputTypeParameterMismatch, Unimplemented}; +use middle::traits::{Obligation, obligation_for_builtin_bound}; +use middle::traits::{FulfillmentError, Ambiguity}; +use middle::traits::{ObligationCause}; +use middle::ty; +use middle::typeck::check::{FnCtxt, + structurally_resolved_type}; +use middle::typeck::infer; +use std::rc::Rc; +use syntax::ast; +use syntax::codemap::Span; +use util::ppaux::UserString; +use util::ppaux::Repr; + +/// When reporting an error about a failed trait obligation, it's nice +/// to include some context indicating why we were checking that +/// obligation in the first place. The span is often enough but +/// sometimes it's not. Currently this enum is a bit of a hack and I +/// suspect it should be carried in the obligation or more deeply +/// integrated somehow. +pub enum ErrorReportingContext { + GenericContext, + ImplSupertraitCheck, +} + +pub fn check_object_cast(fcx: &FnCtxt, + cast_expr: &ast::Expr, + source_expr: &ast::Expr, + target_object_ty: ty::t) +{ + debug!("check_object_cast(cast_expr={}, target_object_ty={})", + cast_expr.repr(fcx.tcx()), + target_object_ty.repr(fcx.tcx())); + + // Look up vtables for the type we're casting to, + // passing in the source and target type. The source + // must be a pointer type suitable to the object sigil, + // e.g.: `&x as &Trait` or `box x as Box` + let source_ty = fcx.expr_ty(source_expr); + let source_ty = structurally_resolved_type(fcx, source_expr.span, source_ty); + debug!("source_ty={}", source_ty.repr(fcx.tcx())); + match (&ty::get(source_ty).sty, &ty::get(target_object_ty).sty) { + (&ty::ty_uniq(referent_ty), &ty::ty_uniq(object_trait_ty)) => { + let object_trait = object_trait(&object_trait_ty); + + // Ensure that if ~T is cast to ~Trait, then T : Trait + push_cast_obligation(fcx, cast_expr, object_trait, referent_ty); + } + + (&ty::ty_rptr(referent_region, ty::mt { ty: referent_ty, + mutbl: referent_mutbl }), + &ty::ty_rptr(target_region, ty::mt { ty: object_trait_ty, + mutbl: target_mutbl })) => + { + let object_trait = object_trait(&object_trait_ty); + if !mutability_allowed(referent_mutbl, target_mutbl) { + fcx.tcx().sess.span_err(source_expr.span, + "types differ in mutability"); + } else { + // Ensure that if &'a T is cast to &'b Trait, then T : Trait + push_cast_obligation(fcx, cast_expr, + object_trait, + referent_ty); + + // Ensure that if &'a T is cast to &'b Trait, then 'b <= 'a + infer::mk_subr(fcx.infcx(), + infer::RelateObjectBound(source_expr.span), + target_region, + referent_region); + } + } + + (_, &ty::ty_uniq(..)) => { + fcx.ccx.tcx.sess.span_err( + source_expr.span, + format!("can only cast an boxed pointer \ + to a boxed object, not a {}", + ty::ty_sort_string(fcx.tcx(), source_ty)).as_slice()); + } + + (_, &ty::ty_rptr(..)) => { + fcx.ccx.tcx.sess.span_err( + source_expr.span, + format!("can only cast a &-pointer \ + to an &-object, not a {}", + ty::ty_sort_string(fcx.tcx(), source_ty)).as_slice()); + } + + _ => { + fcx.tcx().sess.span_bug( + source_expr.span, + "expected object type"); + } + } + + // Because we currently give unsound lifetimes to the "ty_box", I + // could have written &'static ty::TyTrait here, but it seems + // gratuitously unsafe. + fn object_trait<'a>(t: &'a ty::t) -> &'a ty::TyTrait { + match ty::get(*t).sty { + ty::ty_trait(ref ty_trait) => &**ty_trait, + _ => fail!("expected ty_trait") + } + } + + fn mutability_allowed(a_mutbl: ast::Mutability, + b_mutbl: ast::Mutability) + -> bool { + a_mutbl == b_mutbl || + (a_mutbl == ast::MutMutable && b_mutbl == ast::MutImmutable) + } + + fn push_cast_obligation(fcx: &FnCtxt, + cast_expr: &ast::Expr, + object_trait: &ty::TyTrait, + referent_ty: ty::t) { + let object_trait_ref = + register_object_cast_obligations(fcx, + cast_expr.span, + object_trait, + referent_ty); + + // Finally record the object_trait_ref for use during trans + // (it would prob be better not to do this, but it's just kind + // of a pain to have to reconstruct it). + fcx.write_object_cast(cast_expr.id, object_trait_ref); + } +} + +pub fn register_object_cast_obligations(fcx: &FnCtxt, + span: Span, + object_trait: &ty::TyTrait, + referent_ty: ty::t) + -> Rc +{ + // This is just for better error reporting. Kinda goofy. The object type stuff + // needs some refactoring so there is a more convenient type to pass around. + let object_trait_ty = + ty::mk_trait(fcx.tcx(), + object_trait.def_id, + object_trait.substs.clone(), + object_trait.bounds); + + debug!("register_object_cast_obligations: referent_ty={} object_trait_ty={}", + referent_ty.repr(fcx.tcx()), + object_trait_ty.repr(fcx.tcx())); + + // Take the type parameters from the object type, but set + // the Self type (which is unknown, for the object type) + // to be the type we are casting from. + let mut object_substs = object_trait.substs.clone(); + assert!(object_substs.self_ty().is_none()); + object_substs.types.push(SelfSpace, referent_ty); + + // Create the obligation for casting from T to Trait. + let object_trait_ref = + Rc::new(ty::TraitRef { def_id: object_trait.def_id, + substs: object_substs }); + let object_obligation = + Obligation::new( + ObligationCause::new(span, + traits::ObjectCastObligation(object_trait_ty)), + object_trait_ref.clone()); + fcx.register_obligation(object_obligation); + + // Create additional obligations for all the various builtin + // bounds attached to the object cast. (In other words, if the + // object type is Foo+Send, this would create an obligation + // for the Send check.) + for builtin_bound in object_trait.bounds.builtin_bounds.iter() { + fcx.register_obligation( + obligation_for_builtin_bound( + fcx.tcx(), + ObligationCause::new(span, + traits::ObjectCastObligation(object_trait_ty)), + referent_ty, + builtin_bound)); + } + + object_trait_ref +} + +pub fn select_all_fcx_obligations_or_error(fcx: &FnCtxt) { + debug!("select_all_fcx_obligations_or_error"); + + let mut fulfillment_cx = fcx.inh.fulfillment_cx.borrow_mut(); + let r = + fulfillment_cx.select_all_or_error( + fcx.infcx(), + &fcx.inh.param_env, + &*fcx.inh.unboxed_closures.borrow()); + match r { + Ok(()) => { } + Err(errors) => { report_fulfillment_errors(fcx, &errors); } + } +} + +pub fn check_builtin_bound_obligations(fcx: &FnCtxt) { + /*! + * Hacky second pass to check builtin-bounds obligations *after* + * writeback occurs. + */ + + match + fcx.inh.fulfillment_cx.borrow() + .check_builtin_bound_obligations(fcx.infcx()) + { + Ok(()) => { } + Err(errors) => { report_fulfillment_errors(fcx, &errors); } + } +} + +fn resolve_trait_ref(fcx: &FnCtxt, obligation: &Obligation) + -> (ty::TraitRef, ty::t) +{ + let trait_ref = + fcx.infcx().resolve_type_vars_in_trait_ref_if_possible( + &*obligation.trait_ref); + let self_ty = + trait_ref.substs.self_ty().unwrap(); + (trait_ref, self_ty) +} + +pub fn report_fulfillment_errors(fcx: &FnCtxt, + errors: &Vec) { + for error in errors.iter() { + report_fulfillment_error(fcx, error); + } +} + +pub fn report_fulfillment_error(fcx: &FnCtxt, + error: &FulfillmentError) { + match error.code { + SelectionError(ref e) => { + report_selection_error(fcx, &error.obligation, e); + } + Ambiguity => { + maybe_report_ambiguity(fcx, &error.obligation); + } + } +} + +pub fn report_selection_error(fcx: &FnCtxt, + obligation: &Obligation, + error: &SelectionError) { + match *error { + Unimplemented => { + let (trait_ref, self_ty) = resolve_trait_ref(fcx, obligation); + if !ty::type_is_error(self_ty) { + fcx.tcx().sess.span_err( + obligation.cause.span, + format!( + "the trait `{}` is not implemented for the type `{}`", + trait_ref.user_string(fcx.tcx()), + self_ty.user_string(fcx.tcx())).as_slice()); + note_obligation_cause(fcx, obligation); + } + } + Overflow => { + report_overflow(fcx, obligation); + } + OutputTypeParameterMismatch(ref expected_trait_ref, ref e) => { + let expected_trait_ref = + fcx.infcx().resolve_type_vars_in_trait_ref_if_possible( + &**expected_trait_ref); + let (trait_ref, self_ty) = resolve_trait_ref(fcx, obligation); + if !ty::type_is_error(self_ty) { + fcx.tcx().sess.span_err( + obligation.cause.span, + format!( + "type mismatch: the type `{}` implements the trait `{}`, \ + but the trait `{}` is required ({})", + self_ty.user_string(fcx.tcx()), + expected_trait_ref.user_string(fcx.tcx()), + trait_ref.user_string(fcx.tcx()), + ty::type_err_to_str(fcx.tcx(), e)).as_slice()); + note_obligation_cause(fcx, obligation); + } + } + } +} + +pub fn report_overflow(fcx: &FnCtxt, obligation: &Obligation) { + let (trait_ref, self_ty) = resolve_trait_ref(fcx, obligation); + if ty::type_is_error(self_ty) { + fcx.tcx().sess.span_err( + obligation.cause.span, + format!( + "could not locate an impl of the trait `{}` for \ + the type `{}` due to overflow; possible cyclic \ + dependency between impls", + trait_ref.user_string(fcx.tcx()), + self_ty.user_string(fcx.tcx())).as_slice()); + note_obligation_cause(fcx, obligation); + } +} + +pub fn maybe_report_ambiguity(fcx: &FnCtxt, obligation: &Obligation) { + // Unable to successfully determine, probably means + // insufficient type information, but could mean + // ambiguous impls. The latter *ought* to be a + // coherence violation, so we don't report it here. + let (trait_ref, self_ty) = resolve_trait_ref(fcx, obligation); + debug!("maybe_report_ambiguity(trait_ref={}, self_ty={}, obligation={})", + trait_ref.repr(fcx.tcx()), + self_ty.repr(fcx.tcx()), + obligation.repr(fcx.tcx())); + if ty::type_is_error(self_ty) { + } else if ty::type_needs_infer(self_ty) { + fcx.tcx().sess.span_err( + obligation.cause.span, + format!( + "unable to infer enough type information to \ + locate the impl of the trait `{}` for \ + the type `{}`; type annotations required", + trait_ref.user_string(fcx.tcx()), + self_ty.user_string(fcx.tcx())).as_slice()); + note_obligation_cause(fcx, obligation); + } else if fcx.tcx().sess.err_count() == 0 { + // Ambiguity. Coherence should have reported an error. + fcx.tcx().sess.span_bug( + obligation.cause.span, + format!( + "coherence failed to report ambiguity: \ + cannot locate the impl of the trait `{}` for \ + the type `{}`", + trait_ref.user_string(fcx.tcx()), + self_ty.user_string(fcx.tcx())).as_slice()); + } +} + +pub fn select_fcx_obligations_where_possible(fcx: &FnCtxt) { + /*! Select as many obligations as we can at present. */ + + match + fcx.inh.fulfillment_cx + .borrow_mut() + .select_where_possible(fcx.infcx(), + &fcx.inh.param_env, + &*fcx.inh.unboxed_closures.borrow()) + { + Ok(()) => { } + Err(errors) => { report_fulfillment_errors(fcx, &errors); } + } +} + +fn note_obligation_cause(fcx: &FnCtxt, + obligation: &Obligation) { + let tcx = fcx.tcx(); + let trait_name = ty::item_path_str(tcx, obligation.trait_ref.def_id); + match obligation.cause.code { + traits::MiscObligation => { } + traits::ItemObligation(item_def_id) => { + let item_name = ty::item_path_str(tcx, item_def_id); + tcx.sess.span_note( + obligation.cause.span, + format!( + "the trait `{}` must be implemented because it is required by `{}`", + trait_name, + item_name).as_slice()); + } + traits::ObjectCastObligation(object_ty) => { + tcx.sess.span_note( + obligation.cause.span, + format!( + "the trait `{}` must be implemented for the cast \ + to the object type `{}`", + trait_name, + fcx.infcx().ty_to_string(object_ty)).as_slice()); + } + traits::RepeatVec => { + tcx.sess.span_note( + obligation.cause.span, + format!( + "the `Copy` trait is required because the \ + repeated element will be copied").as_slice()); + } + traits::VariableType(_) => { + tcx.sess.span_note( + obligation.cause.span, + "all local variables must have a statically known size"); + } + traits::AssignmentLhsSized => { + tcx.sess.span_note( + obligation.cause.span, + "the left-hand-side of an assignment must have a statically known size"); + } + traits::StructInitializerSized => { + tcx.sess.span_note( + obligation.cause.span, + "structs must have a statically known size to be initialized"); + } + } +} diff --git a/src/librustc/middle/typeck/check/wf.rs b/src/librustc/middle/typeck/check/wf.rs new file mode 100644 index 000000000000..73c0a4e10fc2 --- /dev/null +++ b/src/librustc/middle/typeck/check/wf.rs @@ -0,0 +1,365 @@ +// Copyright 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use middle::subst::{Subst}; +use middle::traits; +use middle::ty; +use middle::ty_fold::{TypeFolder, TypeFoldable}; +use middle::typeck::astconv::AstConv; +use middle::typeck::check::{FnCtxt, Inherited, blank_fn_ctxt, vtable2, regionck}; +use middle::typeck::check::regionmanip::replace_late_bound_regions_in_fn_sig; +use middle::typeck::CrateCtxt; +use util::ppaux::Repr; + +use std::collections::HashSet; +use syntax::ast; +use syntax::ast_util::{local_def}; +use syntax::codemap::Span; +use syntax::visit; +use syntax::visit::Visitor; + +pub struct CheckTypeWellFormedVisitor<'ccx, 'tcx:'ccx> { + ccx: &'ccx CrateCtxt<'ccx, 'tcx>, + cache: HashSet +} + +impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> { + pub fn new(ccx: &'ccx CrateCtxt<'ccx, 'tcx>) -> CheckTypeWellFormedVisitor<'ccx, 'tcx> { + CheckTypeWellFormedVisitor { ccx: ccx, cache: HashSet::new() } + } + + fn check_item_well_formed(&mut self, ccx: &CrateCtxt, item: &ast::Item) { + /*! + * Checks that the field types (in a struct def'n) or + * argument types (in an enum def'n) are well-formed, + * meaning that they do not require any constraints not + * declared in the struct definition itself. + * For example, this definition would be illegal: + * + * struct Ref<'a, T> { x: &'a T } + * + * because the type did not declare that `T:'a`. + * + * We do this check as a pre-pass before checking fn bodies + * because if these constraints are not included it frequently + * leads to confusing errors in fn bodies. So it's better to check + * the types first. + */ + + debug!("check_item_well_formed(it.id={}, it.ident={})", + item.id, + ty::item_path_str(ccx.tcx, local_def(item.id))); + + let ccx = self.ccx; + match item.node { + ast::ItemImpl(..) => { + self.check_impl(item); + } + ast::ItemFn(..) => { + self.check_item_type(item); + } + ast::ItemStatic(..) => { + self.check_item_type(item); + } + ast::ItemStruct(..) => { + self.check_type_defn(item, |fcx| { + ty::struct_fields(ccx.tcx, local_def(item.id), + &fcx.inh.param_env.free_substs) + .iter() + .map(|f| f.mt.ty) + .collect() + }); + } + ast::ItemEnum(..) => { + self.check_type_defn(item, |fcx| { + ty::substd_enum_variants(ccx.tcx, local_def(item.id), + &fcx.inh.param_env.free_substs) + .iter() + .flat_map(|variant| { + variant.args + .iter() + .map(|&arg_ty| arg_ty) + }) + .collect() + }); + } + _ => {} + } + } + + fn with_fcx(&mut self, + ccx: &CrateCtxt, + item: &ast::Item, + f: |&mut CheckTypeWellFormedVisitor, &FnCtxt|) { + let item_def_id = local_def(item.id); + let polytype = ty::lookup_item_type(ccx.tcx, item_def_id); + let param_env = + ty::construct_parameter_environment(ccx.tcx, + item.span, + &polytype.generics, + item.id); + let inh = Inherited::new(ccx.tcx, param_env); + let fcx = blank_fn_ctxt(ccx, &inh, polytype.ty, item.id); + f(self, &fcx); + vtable2::select_all_fcx_obligations_or_error(&fcx); + regionck::regionck_item(&fcx, item); + vtable2::check_builtin_bound_obligations(&fcx); + } + + fn check_type_defn(&mut self, + item: &ast::Item, + lookup_fields: |&FnCtxt| -> Vec) + { + /*! + * In a type definition, we check that to ensure that the types of the fields are + * well-formed. + */ + + self.with_fcx(self.ccx, item, |this, fcx| { + let field_tys = lookup_fields(fcx); + let mut bounds_checker = BoundsChecker::new(fcx, item.span, + item.id, Some(&mut this.cache)); + for &ty in field_tys.iter() { + // Regions are checked below. + bounds_checker.check_traits_in_ty(ty); + } + + regionck::regionck_ensure_component_tys_wf( + fcx, item.span, field_tys.as_slice()); + }); + } + + fn check_item_type(&mut self, + item: &ast::Item) + { + self.with_fcx(self.ccx, item, |this, fcx| { + let mut bounds_checker = BoundsChecker::new(fcx, item.span, + item.id, Some(&mut this.cache)); + let polytype = ty::lookup_item_type(fcx.tcx(), local_def(item.id)); + let item_ty = polytype.ty.subst(fcx.tcx(), &fcx.inh.param_env.free_substs); + bounds_checker.check_traits_in_ty(item_ty); + }); + } + + fn check_impl(&mut self, + item: &ast::Item) + { + self.with_fcx(self.ccx, item, |this, fcx| { + let mut bounds_checker = BoundsChecker::new(fcx, item.span, + item.id, Some(&mut this.cache)); + + let self_ty = ty::node_id_to_type(fcx.tcx(), item.id); + let self_ty = self_ty.subst(fcx.tcx(), &fcx.inh.param_env.free_substs); + + bounds_checker.check_traits_in_ty(self_ty); + + let trait_ref = match ty::impl_trait_ref(fcx.tcx(), local_def(item.id)) { + None => { return; } + Some(t) => { t } + }; + let trait_ref = (*trait_ref).subst(fcx.tcx(), &fcx.inh.param_env.free_substs); + + // We are stricter on the trait-ref in an impl than the + // self-type. In particular, we enforce region + // relationships. The reason for this is that (at least + // presently) "appyling" an impl does not require that the + // application site check the well-formedness constraints on the + // trait reference. Instead, this is done at the impl site. + // Arguably this is wrong and we should treat the trait-reference + // the same way as we treat the self-type. + bounds_checker.check_trait_ref(&trait_ref); + + let trait_def = ty::lookup_trait_def(fcx.tcx(), trait_ref.def_id); + + let cause = + traits::ObligationCause::new( + item.span, + traits::ItemObligation(trait_ref.def_id)); + + // Find the supertrait bounds. This will add `int:Bar`. + // + // FIXME -- This is a bit ill-factored. There is very similar + // code in traits::util::obligations_for_generics. + fcx.add_region_obligations_for_type_parameter(item.span, + ty::ParamTy::for_self(trait_ref.def_id), + &trait_def.bounds, + trait_ref.self_ty()); + for builtin_bound in trait_def.bounds.builtin_bounds.iter() { + fcx.register_obligation( + traits::obligation_for_builtin_bound(fcx.tcx(), + cause, + trait_ref.self_ty(), + builtin_bound)); + } + for trait_bound in trait_def.bounds.trait_bounds.iter() { + let trait_bound = trait_bound.subst(fcx.tcx(), &trait_ref.substs); + fcx.register_obligation( + traits::Obligation::new(cause, trait_bound)); + } + }); + } +} + +impl<'ccx, 'tcx, 'v> Visitor<'v> for CheckTypeWellFormedVisitor<'ccx, 'tcx> { + fn visit_item(&mut self, i: &'v ast::Item) { + self.check_item_well_formed(self.ccx, i); + visit::walk_item(self, i); + } +} + +pub struct BoundsChecker<'cx,'tcx:'cx> { + fcx: &'cx FnCtxt<'cx,'tcx>, + span: Span, + scope_id: ast::NodeId, + binding_count: uint, + cache: Option<&'cx mut HashSet>, +} + +impl<'cx,'tcx> BoundsChecker<'cx,'tcx> { + pub fn new(fcx: &'cx FnCtxt<'cx,'tcx>, + span: Span, + scope_id: ast::NodeId, + cache: Option<&'cx mut HashSet>) + -> BoundsChecker<'cx,'tcx> { + BoundsChecker { fcx: fcx, span: span, scope_id: scope_id, + cache: cache, binding_count: 0 } + } + + pub fn check_trait_ref(&mut self, trait_ref: &ty::TraitRef) { + /*! + * Given a trait ref like `A : Trait`, where `Trait` is + * defined as (say): + * + * trait Trait : Copy { ... } + * + * This routine will check that `B : OtherTrait` and `A : + * Trait`. It will also recursively check that the types + * `A` and `B` are well-formed. + * + * Note that it does not (currently, at least) + * check that `A : Copy` (that check is delegated to the point + * where impl `A : Trait` is implemented). + */ + + let trait_def = ty::lookup_trait_def(self.fcx.tcx(), trait_ref.def_id); + + self.fcx.add_obligations_for_parameters( + traits::ObligationCause::new( + self.span, + traits::ItemObligation(trait_ref.def_id)), + &trait_ref.substs, + &trait_def.generics); + + for &ty in trait_ref.substs.types.iter() { + self.check_traits_in_ty(ty); + } + } + + pub fn check_ty(&mut self, ty: ty::t) { + ty.fold_with(self); + } + + fn check_traits_in_ty(&mut self, ty: ty::t) { + // When checking types outside of a type def'n, we ignore + // region obligations. See discussion below in fold_ty(). + self.binding_count += 1; + ty.fold_with(self); + self.binding_count -= 1; + } +} + +impl<'cx,'tcx> TypeFolder<'tcx> for BoundsChecker<'cx,'tcx> { + fn tcx(&self) -> &ty::ctxt<'tcx> { + self.fcx.tcx() + } + + fn fold_ty(&mut self, t: ty::t) -> ty::t { + debug!("BoundsChecker t={}", + t.repr(self.tcx())); + + match self.cache { + Some(ref mut cache) => { + if !cache.insert(t) { + // Already checked this type! Don't check again. + debug!("cached"); + return t; + } + } + None => { } + } + + match ty::get(t).sty{ + ty::ty_struct(type_id, ref substs) | + ty::ty_enum(type_id, ref substs) => { + let polytype = ty::lookup_item_type(self.fcx.tcx(), type_id); + + if self.binding_count == 0 { + self.fcx.add_obligations_for_parameters( + traits::ObligationCause::new(self.span, + traits::ItemObligation(type_id)), + substs, + &polytype.generics); + } else { + // There are two circumstances in which we ignore + // region obligations. + // + // The first is when we are inside of a closure + // type. This is because in that case the region + // obligations for the parameter types are things + // that the closure body gets to assume and the + // caller must prove at the time of call. In other + // words, if there is a type like `<'a, 'b> | &'a + // &'b int |`, it is well-formed, and caller will + // have to show that `'b : 'a` at the time of + // call. + // + // The second is when we are checking for + // well-formedness outside of a type def'n or fn + // body. This is for a similar reason: in general, + // we only do WF checking for regions in the + // result of expressions and type definitions, so + // to as allow for implicit where clauses. + // + // (I believe we should do the same for traits, but + // that will require an RFC. -nmatsakis) + self.fcx.add_trait_obligations_for_generics( + traits::ObligationCause::new(self.span, + traits::ItemObligation(type_id)), + substs, + &polytype.generics); + } + + self.fold_substs(substs); + } + ty::ty_bare_fn(ty::BareFnTy{sig: ref fn_sig, ..}) | + ty::ty_closure(box ty::ClosureTy{sig: ref fn_sig, ..}) => { + self.binding_count += 1; + + let (_, fn_sig) = + replace_late_bound_regions_in_fn_sig( + self.fcx.tcx(), fn_sig, + |br| ty::ReFree(ty::FreeRegion{scope_id: self.scope_id, + bound_region: br})); + + debug!("late-bound regions replaced: {}", + fn_sig.repr(self.tcx())); + + self.fold_sig(&fn_sig); + + self.binding_count -= 1; + } + ref sty => { + self.fold_sty(sty); + } + } + + t // we're not folding to produce a new type, so just return `t` here + } +} diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs index 4716ffe700b5..ffe019b314a8 100644 --- a/src/librustc/middle/typeck/check/writeback.rs +++ b/src/librustc/middle/typeck/check/writeback.rs @@ -44,6 +44,7 @@ pub fn resolve_type_vars_in_expr(fcx: &FnCtxt, e: &ast::Expr) { wbcx.visit_expr(e); wbcx.visit_upvar_borrow_map(); wbcx.visit_unboxed_closures(); + wbcx.visit_object_cast_map(); } pub fn resolve_type_vars_in_fn(fcx: &FnCtxt, @@ -63,6 +64,7 @@ pub fn resolve_type_vars_in_fn(fcx: &FnCtxt, } wbcx.visit_upvar_borrow_map(); wbcx.visit_unboxed_closures(); + wbcx.visit_object_cast_map(); } pub fn resolve_impl_res(infcx: &infer::InferCtxt, @@ -128,8 +130,6 @@ impl<'cx, 'tcx, 'v> Visitor<'v> for WritebackCx<'cx, 'tcx> { self.visit_node_id(ResolvingExpr(e.span), e.id); self.visit_method_map_entry(ResolvingExpr(e.span), MethodCall::expr(e.id)); - self.visit_vtable_map_entry(ResolvingExpr(e.span), - MethodCall::expr(e.id)); match e.node { ast::ExprFnBlock(_, ref decl, _) | @@ -235,6 +235,27 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } + fn visit_object_cast_map(&self) { + if self.fcx.writeback_errors.get() { + return + } + + for (&node_id, trait_ref) in self.fcx + .inh + .object_cast_map + .borrow() + .iter() + { + let span = ty::expr_span(self.tcx(), node_id); + let reason = ResolvingExpr(span); + let closure_ty = self.resolve(trait_ref, reason); + self.tcx() + .object_cast_map + .borrow_mut() + .insert(node_id, closure_ty); + } + } + fn visit_node_id(&self, reason: ResolveReason, id: ast::NodeId) { // Resolve any borrowings for the node with id `id` self.visit_adjustments(reason, id); @@ -284,13 +305,11 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { for autoderef in range(0, adj.autoderefs) { let method_call = MethodCall::autoderef(id, autoderef); self.visit_method_map_entry(reason, method_call); - self.visit_vtable_map_entry(reason, method_call); } if adj_object { let method_call = MethodCall::autoobject(id); self.visit_method_map_entry(reason, method_call); - self.visit_vtable_map_entry(reason, method_call); } ty::AutoDerefRef(ty::AutoDerefRef { @@ -329,22 +348,6 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } - fn visit_vtable_map_entry(&self, - reason: ResolveReason, - vtable_key: MethodCall) { - // Resolve any vtable map entry - match self.fcx.inh.vtable_map.borrow_mut().pop(&vtable_key) { - Some(origins) => { - let r_origins = self.resolve(&origins, reason); - debug!("writeback::resolve_vtable_map_entry(\ - vtable_key={}, vtables={:?})", - vtable_key, r_origins.repr(self.tcx())); - self.tcx().vtable_map.borrow_mut().insert(vtable_key, r_origins); - } - None => {} - } - } - fn resolve(&self, t: &T, reason: ResolveReason) -> T { t.resolve_in(&mut Resolver::new(self.fcx, reason)) } @@ -504,3 +507,11 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { } } } + +/////////////////////////////////////////////////////////////////////////// +// During type check, we store promises with the result of trait +// lookup rather than the actual results (because the results are not +// necessarily available immediately). These routines unwind the +// promises. It is expected that we will have already reported any +// errors that may be encountered, so if the promises store an error, +// a dummy result is returned. diff --git a/src/librustc/middle/typeck/infer/coercion.rs b/src/librustc/middle/typeck/infer/coercion.rs index c66d10138d8b..e0a35dc72a39 100644 --- a/src/librustc/middle/typeck/infer/coercion.rs +++ b/src/librustc/middle/typeck/infer/coercion.rs @@ -327,10 +327,14 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let sty_b = &ty::get(b).sty; match (sty_a, sty_b) { - (&ty::ty_rptr(_, ty::mt{ty: t_a, ..}), &ty::ty_rptr(_, mt_b)) => { + (&ty::ty_rptr(_, ty::mt{ty: t_a, mutbl: mutbl_a}), &ty::ty_rptr(_, mt_b)) => { self.unpack_actual_value(t_a, |sty_a| { - match self.unsize_ty(sty_a, mt_b.ty) { + match self.unsize_ty(t_a, sty_a, mt_b.ty) { Some((ty, kind)) => { + if !can_coerce_mutbls(mutbl_a, mt_b.mutbl) { + return Err(ty::terr_mutability); + } + let coercion = Coercion(self.get_ref().trace.clone()); let r_borrow = self.get_ref().infcx.next_region_var(coercion); let ty = ty::mk_rptr(self.get_ref().infcx.tcx, @@ -349,10 +353,14 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } }) } - (&ty::ty_rptr(_, ty::mt{ty: t_a, ..}), &ty::ty_ptr(mt_b)) => { + (&ty::ty_rptr(_, ty::mt{ty: t_a, mutbl: mutbl_a}), &ty::ty_ptr(mt_b)) => { self.unpack_actual_value(t_a, |sty_a| { - match self.unsize_ty(sty_a, mt_b.ty) { + match self.unsize_ty(t_a, sty_a, mt_b.ty) { Some((ty, kind)) => { + if !can_coerce_mutbls(mutbl_a, mt_b.mutbl) { + return Err(ty::terr_mutability); + } + let ty = ty::mk_ptr(self.get_ref().infcx.tcx, ty::mt{ty: ty, mutbl: mt_b.mutbl}); try!(self.get_ref().infcx.try(|| sub.tys(ty, b))); @@ -510,19 +518,23 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { a: ty::t, sty_a: &ty::sty, b: ty::t, + b_mutbl: ast::Mutability, mk_ty: |ty::t| -> ty::t, mk_adjust: || -> ty::AutoRef) -> CoerceResult { let tcx = self.get_ref().infcx.tcx; match *sty_a { - ty::ty_rptr(_, ty::mt{ty, ..}) => match ty::get(ty).sty { + ty::ty_rptr(_, ty::mt{ty, mutbl}) => match ty::get(ty).sty { ty::ty_trait(box ty::TyTrait { def_id, ref substs, bounds, .. - }) => { + }) => + { + debug!("mutbl={} b_mutbl={}", mutbl, b_mutbl); + let tr = ty::mk_trait(tcx, def_id, substs.clone(), bounds); try!(self.subtype(mk_ty(tr), b)); Ok(Some(AutoDerefRef(AutoDerefRef { @@ -624,3 +636,14 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { }))) } } + +fn can_coerce_mutbls(from_mutbl: ast::Mutability, + to_mutbl: ast::Mutability) + -> bool { + match (from_mutbl, to_mutbl) { + (ast::MutMutable, ast::MutMutable) => true, + (ast::MutImmutable, ast::MutImmutable) => true, + (ast::MutMutable, ast::MutImmutable) => true, + (ast::MutImmutable, ast::MutMutable) => false, + } +}