librustc: Have the kind checker check sub-bounds in trait casts.

This can break code that looked like:

    struct S<T> {
        val: T,
    }
    trait Gettable<T> {
        ...
    }
    impl<T: Copy> Gettable<T> for S<T> {
        ...
    }
    let t: Box<S<String>> = box S {
        val: "one".to_string(),
    };
    let a = t as Box<Gettable<String>>;
    //                        ^ note no `Copy` bound

Change this code to:

    impl<T> Gettable<T> for S<T> {
    //   ^ remove `Copy` bound
        ...
    }

Closes #14061.

[breaking-change]
This commit is contained in:
Patrick Walton 2014-07-02 13:27:14 -07:00 committed by Alex Crichton
parent 704f11d3d8
commit 8297edd549
4 changed files with 116 additions and 10 deletions

View file

@ -13,6 +13,7 @@ use middle::freevars::freevar_entry;
use middle::freevars;
use middle::subst;
use middle::ty;
use middle::typeck::{MethodCall, NoAdjustment};
use middle::typeck;
use util::ppaux::{Repr, ty_to_str};
use util::ppaux::UserString;
@ -261,7 +262,15 @@ pub fn check_expr(cx: &mut Context, e: &Expr) {
ExprCast(ref source, _) => {
let source_ty = ty::expr_ty(cx.tcx, &**source);
let target_ty = ty::expr_ty(cx.tcx, e);
check_trait_cast(cx, source_ty, target_ty, source.span);
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);
@ -281,7 +290,15 @@ pub fn check_expr(cx: &mut Context, e: &Expr) {
ty::AutoObject(..) => {
let source_ty = ty::expr_ty(cx.tcx, e);
let target_ty = ty::expr_ty_adjusted(cx.tcx, e);
check_trait_cast(cx, source_ty, target_ty, e.span);
let method_call = MethodCall {
expr_id: e.id,
adjustment: typeck::AutoObject,
};
check_trait_cast(cx,
source_ty,
target_ty,
e.span,
method_call);
}
ty::AutoAddEnv(..) |
ty::AutoDerefRef(..) => {}
@ -364,15 +381,62 @@ fn check_bounds_on_type_parameters(cx: &mut Context, e: &Expr) {
}
}
fn check_trait_cast(cx: &mut Context, source_ty: ty::t, target_ty: ty::t, span: Span) {
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) {
check_cast_for_escaping_regions(cx, source_ty, target_ty, span);
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, .. }) => {
check_trait_cast_bounds(cx, span, source_ty, bounds);
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);
}
_ => {}
}
_ => {}
},
}
_ => {}
}
}

View file

@ -398,6 +398,9 @@ fn search_for_vtable(vcx: &VtableContext,
// 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,

View file

@ -216,9 +216,9 @@ pub type vtable_res = VecPerParamSpace<vtable_param_res>;
#[deriving(Clone)]
pub enum vtable_origin {
/*
Statically known vtable. def_id gives the class or impl item
Statically known vtable. def_id gives the impl item
from whence comes the vtable, and tys are the type substs.
vtable_res is the vtable itself
vtable_res is the vtable itself.
*/
vtable_static(ast::DefId, subst::Substs, vtable_res),

View file

@ -0,0 +1,39 @@
// Copyright 2012 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.
// Issue #14061: tests the interaction between generic implementation
// parameter bounds and trait objects.
struct S<T>;
trait Gettable<T> {}
impl<T: Send + Copy> Gettable<T> for S<T> {}
fn f<T>(val: T) {
let t: S<T> = S;
let a = &t as &Gettable<T>;
//~^ ERROR instantiating a type parameter with an incompatible type `T`
let a: &Gettable<T> = &t;
//~^ ERROR instantiating a type parameter with an incompatible type `T`
}
fn main() {
let t: S<&int> = S;
let a = &t as &Gettable<&int>;
//~^ ERROR instantiating a type parameter with an incompatible type `&int`
let t: Box<S<String>> = box S;
let a = t as Box<Gettable<String>>;
//~^ ERROR instantiating a type parameter with an incompatible type
let t: Box<S<String>> = box S;
let a: Box<Gettable<String>> = t;
//~^ ERROR instantiating a type parameter with an incompatible type
}