Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Fixes for various shortcomings of regionck #16319

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/librustc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ pub mod util {
pub mod common;
pub mod ppaux;
pub mod nodemap;
pub mod snapshot_vec;
}

pub mod lib {
Expand Down
7 changes: 4 additions & 3 deletions src/librustc/middle/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -692,11 +692,12 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> {
* `x.deref()`. Since `deref()` is declared with `&self`, this
* is an autoref of `x`.
*/
debug!("walk_autoderefs expr={} autoderefs={}", expr.repr(self.tcx()), autoderefs);
debug!("walk_autoderefs expr={} autoderefs={}",
expr.repr(self.tcx()), autoderefs);

for i in range(0, autoderefs) {
let deref_id = typeck::MethodCall::autoderef(expr.id, i);
match self.typer.node_method_ty(deref_id) {
match self.typer.node_method_return_ty(deref_id) {
None => {}
Some(method_ty) => {
let cmt = return_if_err!(self.mc.cat_expr_autoderefd(expr, i));
Expand All @@ -705,7 +706,7 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> {
ty::ty_rptr(r, ref m) => (m.mutbl, r),
_ => self.tcx().sess.span_bug(expr.span,
format!("bad overloaded deref type {}",
method_ty.repr(self.tcx())).as_slice())
method_ty.repr(self.tcx())).as_slice())
};
let bk = ty::BorrowKind::from_mutbl(m);
self.delegate.borrow(expr.id, expr.span, cmt,
Expand Down
118 changes: 28 additions & 90 deletions src/librustc/middle/kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@

use middle::freevars::freevar_entry;
use middle::freevars;
use mc = middle::mem_categorization;
use middle::subst;
use middle::ty;
use middle::ty_fold;
use middle::ty_fold::TypeFoldable;
use middle::typeck;
use middle::typeck::{MethodCall, NoAdjustment};
use util::ppaux::{Repr, ty_to_string};
use util::ppaux::{ty_to_string};
use util::ppaux::UserString;

use syntax::ast::*;
Expand All @@ -26,6 +27,7 @@ use syntax::codemap::Span;
use syntax::print::pprust::{expr_to_string, ident_to_string};
use syntax::{visit};
use syntax::visit::Visitor;
use std::rc::Rc;

// Kind analysis pass.
//
Expand Down Expand Up @@ -134,10 +136,11 @@ fn check_impl_of_trait(cx: &mut Context, it: &Item, trait_ref: &TraitRef, self_t
// 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, |missing| {
check_builtin_bounds(cx, self_ty, trait_def.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));
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));
Expand Down Expand Up @@ -261,7 +264,20 @@ 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);
mc::each_type_parameters_and_def(cx.tcx, e, |type_param_ty, type_param_def| {
check_typaram_bounds(cx, e.span, type_param_ty, type_param_def)
});

// If this node is a method call (or overloaded op), check the
// vtable.
{
let vtable_map = cx.tcx.vtable_map.borrow();
let vtable_res = match vtable_map.find(&MethodCall::expr(e.id)) {
None => return,
Some(vtable_res) => vtable_res,
};
check_type_parameter_bounds_in_vtable_result(cx, e.span, vtable_res);
}

match e.node {
ExprBox(ref loc, ref interior) => {
Expand Down Expand Up @@ -322,88 +338,6 @@ pub fn check_expr(cx: &mut Context, e: &Expr) {
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,
Expand Down Expand Up @@ -490,14 +424,15 @@ fn check_ty(cx: &mut Context, aty: &Ty) {
pub fn check_builtin_bounds(cx: &Context,
ty: ty::t,
bounds: ty::BuiltinBounds,
traits: &[Rc<ty::TraitRef>],
any_missing: |ty::BuiltinBounds|) {
let kind = ty::type_contents(cx.tcx, ty);
let mut missing = ty::empty_builtin_bounds();
for bound in bounds.iter() {
ty::each_inherited_builtin_bound(cx.tcx, bounds, traits, |bound| {
if !kind.meets_bound(cx.tcx, bound) {
missing.add(bound);
}
}
});
if !missing.is_empty() {
any_missing(missing);
}
Expand All @@ -507,9 +442,12 @@ pub fn check_typaram_bounds(cx: &Context,
sp: Span,
ty: ty::t,
type_param_def: &ty::TypeParameterDef) {
debug!("check_typaram_bounds(ty={}, type_param_def={}, sp={})",
ty.repr(cx.tcx), type_param_def.repr(cx.tcx), sp.repr(cx.tcx));
check_builtin_bounds(cx,
ty,
type_param_def.bounds.builtin_bounds,
type_param_def.bounds.trait_bounds.as_slice(),
|missing| {
span_err!(cx.tcx.sess, sp, E0144,
"instantiating a type parameter with an incompatible type \
Expand All @@ -522,7 +460,7 @@ pub fn check_typaram_bounds(cx: &Context,
pub fn check_freevar_bounds(cx: &Context, sp: Span, ty: ty::t,
bounds: ty::BuiltinBounds, referenced_ty: Option<ty::t>)
{
check_builtin_bounds(cx, ty, bounds, |missing| {
check_builtin_bounds(cx, ty, bounds, [], |missing| {
// Will be Some if the freevar is implicitly borrowed (stack closure).
// Emit a less mysterious error message in this case.
match referenced_ty {
Expand All @@ -547,7 +485,7 @@ pub fn check_freevar_bounds(cx: &Context, sp: Span, ty: ty::t,

pub fn check_trait_cast_bounds(cx: &Context, sp: Span, ty: ty::t,
bounds: ty::BuiltinBounds) {
check_builtin_bounds(cx, ty, bounds, |missing| {
check_builtin_bounds(cx, ty, bounds, [], |missing| {
span_err!(cx.tcx.sess, sp, E0147,
"cannot pack type `{}`, which does not fulfill `{}`, as a trait bounded by {}",
ty_to_string(cx.tcx, ty),
Expand Down
114 changes: 106 additions & 8 deletions src/librustc/middle/mem_categorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
#![allow(non_camel_case_types)]

use middle::def;
use middle::subst::Substs;
use middle::ty;
use middle::typeck;
use util::nodemap::NodeMap;
Expand Down Expand Up @@ -265,7 +266,10 @@ pub type McResult<T> = Result<T, ()>;
pub trait Typer {
fn tcx<'a>(&'a self) -> &'a ty::ctxt;
fn node_ty(&self, id: ast::NodeId) -> McResult<ty::t>;
fn node_method_ty(&self, method_call: typeck::MethodCall) -> Option<ty::t>;
fn node_method_return_ty(&self, method_call: typeck::MethodCall) -> Option<ty::t>;
fn node_method_callee(&self, method_call: typeck::MethodCall)
-> Option<typeck::MethodCallee>;
fn node_substs(&self, expr_id: ast::NodeId) -> Option<Substs>;
fn adjustments<'a>(&'a self) -> &'a RefCell<NodeMap<ty::AutoAdjustment>>;
fn is_method_call(&self, id: ast::NodeId) -> bool;
fn temporary_scope(&self, rvalue_id: ast::NodeId) -> Option<ast::NodeId>;
Expand Down Expand Up @@ -361,9 +365,10 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> {

fn expr_ty_adjusted(&self, expr: &ast::Expr) -> McResult<ty::t> {
let unadjusted_ty = if_ok!(self.expr_ty(expr));
Ok(ty::adjust_ty(self.tcx(), expr.span, expr.id, unadjusted_ty,
self.typer.adjustments().borrow().find(&expr.id),
|method_call| self.typer.node_method_ty(method_call)))
Ok(ty::adjust_ty(
self.tcx(), expr.span, expr.id, unadjusted_ty,
self.typer.adjustments().borrow().find(&expr.id),
|method_call| self.typer.node_method_return_ty(method_call)))
}

fn node_ty(&self, id: ast::NodeId) -> McResult<ty::t> {
Expand Down Expand Up @@ -445,7 +450,7 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> {

ast::ExprIndex(ref base, _) => {
let method_call = typeck::MethodCall::expr(expr.id());
match self.typer.node_method_ty(method_call) {
match self.typer.node_method_return_ty(method_call) {
Some(method_ty) => {
// If this is an index implemented by a method call, then it will
// include an implicit deref of the result.
Expand Down Expand Up @@ -734,10 +739,10 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> {
expr_id: node.id(),
adjustment: adjustment
};
let method_ty = self.typer.node_method_ty(method_call);
let method_ty = self.typer.node_method_return_ty(method_call);

debug!("cat_deref: method_call={:?} method_ty={}",
method_call, method_ty.map(|ty| ty.repr(self.tcx())));
method_call, method_ty.repr(self.tcx()));

let base_cmt = match method_ty {
Some(method_ty) => {
Expand Down Expand Up @@ -830,7 +835,7 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> {
//! the implicit index deref, if any (see above)

let method_call = typeck::MethodCall::expr(elt.id());
let method_ty = self.typer.node_method_ty(method_call);
let method_ty = self.typer.node_method_return_ty(method_call);

let element_ty = match method_ty {
Some(method_ty) => {
Expand Down Expand Up @@ -1380,3 +1385,96 @@ fn element_kind(t: ty::t) -> ElementKind {
_ => OtherElement
}
}

///////////////////////////////////////////////////////////////////////////

pub fn each_type_parameters_and_def<TYPER:Typer>(typer: &TYPER,
expr: &ast::Expr,
op: |ty::t, &ty::TypeParameterDef|)
{
/*!
* Given an expression `expr`, identifies any type parameters whose
* values are supplied as part of this expression and walks over them,
* passing a reference to the type which was used to instantiate the
* parameter as well as the definition of the parameter itself.
*
* For example:
*
* fn foo<T:Bound1>(t: T) { }
* foo(22_u)
* ^~~ <-- expr
*
* If invoked on the path node referencing `foo` as part of the
* call, it would call back with `(uint, T:Bound1)`.
*/

debug!("each_type_parameters_and_def(expr={})",
expr.repr(typer.tcx()));

// If this is a path, it (could be) a reference to a generic item.
// We must check extract the types provided and match them against
// the type parameter definitions from the item.
//
// Otherwise, check if this node is a method call (either
// overloaded or explicit). If so, extract type parameters defined
// on method (including those inherited from impl) and match them
// to those values provided in the method call.
let tcx = typer.tcx();
let method_callee =
typer.node_method_callee(typeck::MethodCall::expr(expr.id));
let (substs, type_param_defs) = match method_callee {
Some(typeck::MethodCallee { substs, origin, ty: _ }) => {
// Method call:
let defs = ty::method_call_type_param_defs(tcx, origin);
(substs, defs)
}

None => {
debug!("each_type_parameters_and_def(expr.id={}) -- not method call",
expr.id);

// Reference to generic item?
match expr.node {
ast::ExprPath(_) => { } // Yes, continue.
_ => { return; } // No, done.
}

debug!("each_type_parameters_and_def(expr.id={}) -- is path",
expr.id);

let substs = match typer.node_substs(expr.id) {
Some(s) => s,
None => { return; } // No parameters supplied, not generic item.
};

let did = tcx.def_map.borrow().get_copy(&expr.id).def_id();
let defs = ty::lookup_item_type(tcx, did).generics.types.clone();
(substs, defs)
}
};

debug!("each_type_parameters_and_def(expr={},substs={})",
expr.repr(typer.tcx()),
substs.repr(typer.tcx()));

// Check that the value provided for each definition meets the
// kind requirements
for type_param_def in type_param_defs.iter() {
let ty = *substs.types.get(type_param_def.space, type_param_def.index);

// Note: during type checking, error types can normally occur.
// Even afterwards, 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). So either way, ignore
// ty_err.
if !ty::type_is_error(ty) {
debug!("type_param_def space={} index={} ty={}",
type_param_def.space, type_param_def.index, ty.repr(tcx));
op(ty, type_param_def)
}
}
}
Loading