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<ty::t>,
- parameter_environments: Vec<ParameterEnvironment>,
}
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();
.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());
}
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));
}
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)
sp: Span,
fn_id: NodeId) {
- // Check kinds on free variables:
- with_appropriate_checker(cx, fn_id, |chk| {
+ // <Check kinds on free variables:
+ with_appropriate_checker(cx, fn_id, sp, |chk| {
freevars::with_freevars(cx.tcx, fn_id, |freevars| {
for fv in freevars.iter() {
chk(cx, fv);
match fk {
visit::FkFnBlock(..) => {
- 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());
}
}
}
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 {
_ => {}
}
- // 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) => {
});
}
-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<ty::t>)
{
check_builtin_bounds(cx, ty, bounds, |missing| {
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));
});
* 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.
*/
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,
use middle::subst;
use middle::subst::Subst;
+use middle::traits;
use middle::ty::*;
use middle::ty;
use middle::typeck::astconv::AstConv;
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;
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);
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),
});
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);
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
rcvr_ty,
param_ty.space,
param_ty.idx,
- restrict_to,
- param_index { space: param_ty.space, index: param_ty.idx });
+ restrict_to);
}
self_ty: ty::t,
space: subst::ParamSpace,
index: uint,
- restrict_to: Option<DefId>,
- param: param_index) {
+ restrict_to: Option<DefId>) {
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 {
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,
})
})
})
mk_cand: |this: &mut LookupContext,
tr: Rc<TraitRef>,
m: Rc<ty::Method>,
- method_num: uint,
- bound_num: uint|
- -> Option<Candidate>) {
+ method_num: uint|
+ -> Option<Candidate>)
+ {
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| {
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()));
// check next trait or bound
}
}
- true
- });
+ }
}
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(),
// 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 =>
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 => {}
}
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());
}
Some(self_expr_id) => {
self.fcx.write_adjustment(
self_expr_id,
+ self.span,
ty::AutoDerefRef(ty::AutoDerefRef {
autoderefs: autoderefs,
autoref: Some(kind(region, *mutbl))
// 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()
});
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
}
}
- 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
}
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);
}
}
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)
}
}
}
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};
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;
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:
item_substs: RefCell<NodeMap<ty::ItemSubsts>>,
adjustments: RefCell<NodeMap<ty::AutoAdjustment>>,
method_map: MethodMap,
- vtable_map: vtable_map,
upvar_borrow_map: RefCell<ty::UpvarBorrowMap>,
unboxed_closures: RefCell<DefIdMap<ty::UnboxedClosure>>,
+ 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
// 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.)
//
// obligations (otherwise, it's easy to fail to walk to a
// particular node-id).
region_obligations: RefCell<NodeMap<Vec<RegionObligation>>>,
+
+ // Tracks trait obligations incurred during this function body.
+ fulfillment_cx: RefCell<traits::FulfillmentContext>,
}
struct RegionObligation {
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()),
}
}
}
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) {
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
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")
}
impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
- fn assign(&mut self, 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);
- }
- 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>) -> 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
+ }
+ }
}
}
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(
// 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) {
// 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 {
}
}
-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<ty::t>)
- {
- 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,
ast_trait_ref,
&*impl_trait_ref,
impl_items.as_slice());
- vtable::resolve_impl(ccx.tcx, it, &impl_pty.generics, &*impl_trait_ref);
}
None => { }
}
}
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);
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
}
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> {
self.inh.node_types.borrow_mut().insert(node_id, ty);
}
+ pub fn write_object_cast(&self,
+ key: ast::NodeId,
+ trait_ref: Rc<ty::TraitRef>) {
+ 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 {}",
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 })
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());
}
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 {
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(())
}
}
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`)
* 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
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(),
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);
- }
-
- 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);
- }
+ self.add_region_obligations_for_region_parameter(
+ cause.span, bounds.as_slice(), region_param);
}
+ }
- 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_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(&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);
}
}
}
fn check_argument_types(fcx: &FnCtxt,
sp: Span,
fn_inputs: &[ty::t],
- callee_expr: &ast::Expr,
+ _callee_expr: &ast::Expr,
args: &[P<ast::Expr>],
deref_args: DerefArgs,
variadic: bool,
// 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
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<A,B> 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) =
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);
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 => {}
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 => {}
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);
span,
class_id,
id,
- substitutions,
+ struct_substs,
class_fields.as_slice(),
fields,
base_expr.is_none());
// 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);
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
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) {
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,
}
_ => {}
}
- check_cast(fcx, &**e, &**t, id, expr.span);
+ check_cast(fcx, expr, &**e, &**t);
}
ast::ExprVec(ref args) => {
let uty = match expected {
}
};
+ 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) {
}
}
}
+
+ 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());
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.
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
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,
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();
}
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
+++ /dev/null
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-
-use 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<A> is
-// instantiated with Option<int>, 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<DefIdMap<ty::UnboxedClosure>>,
-}
-
-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<ty::TypeParameterDef>,
- substs: &subst::Substs,
- is_early: bool)
- -> VecPerParamSpace<vtable_param_res> {
- 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<ty::TraitRef>,
- exp_trait_ref: Rc<ty::TraitRef>) {
- /*!
- *
- * 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<ty::TraitRef>,
- is_early: bool)
- -> Option<vtable_origin>
-{
- 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<ty::TraitRef>],
- param: param_index,
- trait_ref: Rc<ty::TraitRef>)
- -> Option<vtable_origin> {
- 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<ty::TraitRef>)
- -> Option<vtable_origin> {
- 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<ty::TraitRef>,
- is_early: bool)
- -> Option<vtable_origin> {
- 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<T> some_trait<T> for self_ty<T> { ... }
- //
- // we check whether self_ty<T> 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<T>, so we would be unifying trait_ref<U> (for
- // some value of U) with some_trait<T>. 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<subst::Substs> {
- 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<ty::t> {
- 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<ty::TraitRef>,
- 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<Trait>`
- // 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<A:Clone> : Bar {
- * fn method() { ... }
- * }
- *
- * and an impl like:
- *
- * impl<B:Clone> Foo<B> 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<B> 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<T:B> : C { fn x(&self) { ... } }
- //
- // and
- //
- // impl A<int> for uint { ... }
- //
- // In that case, the trait ref will be `A<int> 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<T:B, Self:A>(...) { .. .})
-
- 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<ty::TypeParameterDef>,
- 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(..) => {}
- }
- }
- }
-}
-
--- /dev/null
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use 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<Trait>`
+ 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<ty::TraitRef>
+{
+ // 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<FulfillmentError>) {
+ 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");
+ }
+ }
+}
--- /dev/null
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use 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<ty::t>
+}
+
+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<ty::t>)
+ {
+ /*!
+ * 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<ty::t>>,
+}
+
+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<ty::t>>)
+ -> 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<B>`, where `Trait` is
+ * defined as (say):
+ *
+ * trait Trait<B:OtherTrait> : Copy { ... }
+ *
+ * This routine will check that `B : OtherTrait` and `A :
+ * Trait<B>`. 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<B>` 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
+ }
+}
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,
}
wbcx.visit_upvar_borrow_map();
wbcx.visit_unboxed_closures();
+ wbcx.visit_object_cast_map();
}
pub fn resolve_impl_res(infcx: &infer::InferCtxt,
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, _) |
}
}
+ 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);
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 {
}
}
- 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<T:ResolveIn>(&self, t: &T, reason: ResolveReason) -> T {
t.resolve_in(&mut Resolver::new(self.fcx, reason))
}
}
}
}
+
+///////////////////////////////////////////////////////////////////////////
+// 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.
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,
}
})
}
- (&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)));
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 {
})))
}
}
+
+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,
+ }
+}