Remove subtyping for object types and replace with an *upcast* coercion.

This upcast coercion currently preserves the vtable for the object, but
eventually it can be used to create a derived vtable. The upcast
coercion is not introduced into method dispatch; see comment on #18737
for information about why. Fixes #18737.
This commit is contained in:
Niko Matsakis 2015-02-17 05:17:19 -05:00
parent bde09eea35
commit 5f5ed62298
18 changed files with 187 additions and 101 deletions

View file

@ -241,7 +241,7 @@ pub trait BoxAny {
/// Returns the boxed value if it is of type `T`, or
/// `Err(Self)` if it isn't.
#[stable(feature = "rust1", since = "1.0.0")]
fn downcast<T: 'static>(self) -> Result<Box<T>, Self>;
fn downcast<T: 'static>(self) -> Result<Box<T>, Box<Any>>;
}
#[stable(feature = "rust1", since = "1.0.0")]
@ -264,6 +264,15 @@ fn downcast<T: 'static>(self) -> Result<Box<T>, Box<Any>> {
}
}
#[cfg(not(stage0))]
#[stable(feature = "rust1", since = "1.0.0")]
impl BoxAny for Box<Any+Send> {
#[inline]
fn downcast<T: 'static>(self) -> Result<Box<T>, Box<Any>> {
<Box<Any>>::downcast(self)
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: fmt::Display + ?Sized> fmt::Display for Box<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

View file

@ -71,6 +71,7 @@
#![stable(feature = "rust1", since = "1.0.0")]
use marker::Send;
use mem::transmute;
use option::Option::{self, Some, None};
use raw::TraitObject;
@ -154,6 +155,31 @@ pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
}
}
#[cfg(not(stage0))]
impl Any+Send {
/// Forwards to the method defined on the type `Any`.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn is<T: 'static>(&self) -> bool {
Any::is::<T>(self)
}
/// Forwards to the method defined on the type `Any`.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
Any::downcast_ref::<T>(self)
}
/// Forwards to the method defined on the type `Any`.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
Any::downcast_mut::<T>(self)
}
}
///////////////////////////////////////////////////////////////////////////////
// TypeID and its methods
///////////////////////////////////////////////////////////////////////////////

View file

@ -1102,6 +1102,11 @@ fn emit_unsize_kind<'b>(&mut self, ecx: &e::EncodeContext<'b, 'tcx>,
this.emit_enum_variant_arg(1, |this| Ok(this.emit_ty(ecx, self_ty)))
})
}
ty::UnsizeUpcast(target_ty) => {
this.emit_enum_variant("UnsizeUpcast", 3, 1, |this| {
this.emit_enum_variant_arg(0, |this| Ok(this.emit_ty(ecx, target_ty)))
})
}
}
});
}
@ -1707,7 +1712,7 @@ fn read_autoref<'b, 'c>(&mut self, dcx: &DecodeContext<'b, 'c, 'tcx>) -> ty::Aut
fn read_unsize_kind<'b, 'c>(&mut self, dcx: &DecodeContext<'b, 'c, 'tcx>)
-> ty::UnsizeKind<'tcx> {
self.read_enum("UnsizeKind", |this| {
let variants = &["UnsizeLength", "UnsizeStruct", "UnsizeVtable"];
let variants = &["UnsizeLength", "UnsizeStruct", "UnsizeVtable", "UnsizeUpcast"];
this.read_enum_variant(variants, |this, i| {
Ok(match i {
0 => {
@ -1741,6 +1746,11 @@ fn read_unsize_kind<'b, 'c>(&mut self, dcx: &DecodeContext<'b, 'c, 'tcx>)
this.read_enum_variant_arg(1, |this| Ok(this.read_ty(dcx))).unwrap();
ty::UnsizeVtable(ty_trait, self_ty)
}
3 => {
let target_ty =
this.read_enum_variant_arg(0, |this| Ok(this.read_ty(dcx))).unwrap();
ty::UnsizeUpcast(target_ty)
}
_ => panic!("bad enum variant for ty::UnsizeKind")
})
})

View file

@ -314,9 +314,18 @@ fn existential_bounds(&self,
}
fn builtin_bounds(&self,
a: ty::BuiltinBounds,
b: ty::BuiltinBounds)
-> cres<'tcx, ty::BuiltinBounds>;
a: BuiltinBounds,
b: BuiltinBounds)
-> cres<'tcx, BuiltinBounds>
{
// Two sets of builtin bounds are only relatable if they are
// precisely the same (but see the coercion code).
if a != b {
Err(ty::terr_builtin_bounds(expected_found(self, a, b)))
} else {
Ok(a)
}
}
fn trait_refs(&self,
a: &ty::TraitRef<'tcx>,

View file

@ -8,7 +8,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use middle::ty::{BuiltinBounds};
use middle::ty::{self, Ty};
use middle::ty::TyVar;
use middle::infer::combine::*;
@ -73,23 +72,6 @@ fn unsafeties(&self, a: Unsafety, b: Unsafety) -> cres<'tcx, Unsafety> {
}
}
fn builtin_bounds(&self,
a: BuiltinBounds,
b: BuiltinBounds)
-> cres<'tcx, BuiltinBounds>
{
// More bounds is a subtype of fewer bounds.
//
// e.g., fn:Copy() <: fn(), because the former is a function
// that only closes over copyable things, but the latter is
// any function at all.
if a != b {
Err(ty::terr_builtin_bounds(expected_found(self, a, b)))
} else {
Ok(a)
}
}
fn tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> cres<'tcx, Ty<'tcx>> {
debug!("{}.tys({}, {})", self.tag(),
a.repr(self.fields.infcx.tcx), b.repr(self.fields.infcx.tcx));

View file

@ -14,7 +14,6 @@
use super::{cres};
use super::Subtype;
use middle::ty::{BuiltinBounds};
use middle::ty::{self, Ty};
use syntax::ast::{MutImmutable, MutMutable, Unsafety};
use util::ppaux::mt_to_string;
@ -94,15 +93,6 @@ fn unsafeties(&self, a: Unsafety, b: Unsafety) -> cres<'tcx, Unsafety> {
}
}
fn builtin_bounds(&self,
a: ty::BuiltinBounds,
b: ty::BuiltinBounds)
-> cres<'tcx, ty::BuiltinBounds> {
// More bounds is a subtype of fewer bounds, so
// the GLB (mutual subtype) is the union.
Ok(a.union(b))
}
fn regions(&self, a: ty::Region, b: ty::Region) -> cres<'tcx, ty::Region> {
debug!("{}.regions({}, {})",
self.tag(),

View file

@ -14,7 +14,6 @@
use super::{cres};
use super::{Subtype};
use middle::ty::{BuiltinBounds};
use middle::ty::{self, Ty};
use syntax::ast::{MutMutable, MutImmutable, Unsafety};
use util::ppaux::mt_to_string;
@ -89,15 +88,6 @@ fn unsafeties(&self, a: Unsafety, b: Unsafety) -> cres<'tcx, Unsafety> {
}
}
fn builtin_bounds(&self,
a: ty::BuiltinBounds,
b: ty::BuiltinBounds)
-> cres<'tcx, ty::BuiltinBounds> {
// More bounds is a subtype of fewer bounds, so
// the LUB (mutual supertype) is the intersection.
Ok(a.intersection(b))
}
fn regions(&self, a: ty::Region, b: ty::Region) -> cres<'tcx, ty::Region> {
debug!("{}.regions({}, {})",
self.tag(),

View file

@ -14,7 +14,6 @@
use super::{Subtype};
use super::type_variable::{SubtypeOf, SupertypeOf};
use middle::ty::{BuiltinBounds};
use middle::ty::{self, Ty};
use middle::ty::TyVar;
use util::ppaux::{Repr};
@ -97,20 +96,6 @@ fn unsafeties(&self, a: Unsafety, b: Unsafety) -> cres<'tcx, Unsafety> {
})
}
fn builtin_bounds(&self, a: BuiltinBounds, b: BuiltinBounds)
-> cres<'tcx, BuiltinBounds> {
// More bounds is a subtype of fewer bounds.
//
// e.g., fn:Copy() <: fn(), because the former is a function
// that only closes over copyable things, but the latter is
// any function at all.
if a.is_superset(&b) {
Ok(a)
} else {
Err(ty::terr_builtin_bounds(expected_found(self, a, b)))
}
}
fn tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> cres<'tcx, Ty<'tcx>> {
debug!("{}.tys({}, {})", self.tag(),
a.repr(self.tcx()), b.repr(self.tcx()));

View file

@ -292,7 +292,8 @@ pub enum UnsizeKind<'tcx> {
// An unsize coercion applied to the tail field of a struct.
// The uint is the index of the type parameter which is unsized.
UnsizeStruct(Box<UnsizeKind<'tcx>>, uint),
UnsizeVtable(TyTrait<'tcx>, /* the self type of the trait */ Ty<'tcx>)
UnsizeVtable(TyTrait<'tcx>, /* the self type of the trait */ Ty<'tcx>),
UnsizeUpcast(Ty<'tcx>),
}
#[derive(Clone, Debug)]
@ -4627,6 +4628,9 @@ pub fn unsize_ty<'tcx>(cx: &ctxt<'tcx>,
&UnsizeVtable(TyTrait { ref principal, ref bounds }, _) => {
mk_trait(cx, principal.clone(), bounds.clone())
}
&UnsizeUpcast(target_ty) => {
target_ty
}
}
}
@ -6830,6 +6834,7 @@ fn repr(&self, tcx: &ctxt<'tcx>) -> String {
UnsizeLength(n) => format!("UnsizeLength({})", n),
UnsizeStruct(ref k, n) => format!("UnsizeStruct({},{})", k.repr(tcx), n),
UnsizeVtable(ref a, ref b) => format!("UnsizeVtable({},{})", a.repr(tcx), b.repr(tcx)),
UnsizeUpcast(ref a) => format!("UnsizeUpcast({})", a.repr(tcx)),
}
}
}

View file

@ -480,6 +480,7 @@ fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::UnsizeKind<'tcx>
},
self_ty.fold_with(folder))
}
ty::UnsizeUpcast(t) => ty::UnsizeUpcast(t.fold_with(folder)),
}
}
}

View file

@ -1214,17 +1214,17 @@ impl<'tcx> Repr<'tcx> for ty::ExistentialBounds<'tcx> {
fn repr(&self, tcx: &ctxt<'tcx>) -> String {
let mut res = Vec::new();
let region_str = self.region_bound.user_string(tcx);
let region_str = self.region_bound.repr(tcx);
if !region_str.is_empty() {
res.push(region_str);
}
for bound in &self.builtin_bounds {
res.push(bound.user_string(tcx));
res.push(bound.repr(tcx));
}
for projection_bound in &self.projection_bounds {
res.push(projection_bound.user_string(tcx));
res.push(projection_bound.repr(tcx));
}
res.connect("+")

View file

@ -311,12 +311,16 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
llconst = addr_of(cx, llconst, "autoref", e.id);
}
Some(box ty::AutoUnsize(ref k)) => {
let info = expr::unsized_info(cx, k, e.id, ty, param_substs,
|t| ty::mk_imm_rptr(cx.tcx(), cx.tcx().mk_region(ty::ReStatic), t));
let info =
expr::unsized_info(
cx, k, e.id, ty, param_substs,
|t| ty::mk_imm_rptr(cx.tcx(), cx.tcx().mk_region(ty::ReStatic), t),
|| const_get_elt(cx, llconst, &[abi::FAT_PTR_EXTRA as u32]));
let unsized_ty = ty::unsize_ty(cx.tcx(), ty, k, e.span);
let ptr_ty = type_of::in_memory_type_of(cx, unsized_ty).ptr_to();
let base = ptrcast(llconst, ptr_ty);
let prev_const = cx.const_unsized().borrow_mut()
.insert(base, llconst);
assert!(prev_const.is_none() || prev_const == Some(llconst));

View file

@ -287,18 +287,50 @@ pub fn get_dataptr(bcx: Block, fat_ptr: ValueRef) -> ValueRef {
// Retrieve the information we are losing (making dynamic) in an unsizing
// adjustment.
//
// When making a dtor, we need to do different things depending on the
// ownership of the object.. mk_ty is a function for turning `unadjusted_ty`
// into a type to be destructed. If we want to end up with a Box pointer,
// then mk_ty should make a Box pointer (T -> Box<T>), if we want a
// borrowed reference then it should be T -> &T.
pub fn unsized_info<'a, 'tcx, F>(ccx: &CrateContext<'a, 'tcx>,
kind: &ty::UnsizeKind<'tcx>,
id: ast::NodeId,
unadjusted_ty: Ty<'tcx>,
param_substs: &'tcx subst::Substs<'tcx>,
mk_ty: F) -> ValueRef where
F: FnOnce(Ty<'tcx>) -> Ty<'tcx>,
//
// The `unadjusted_val` argument is a bit funny. It is intended
// for use in an upcast, where the new vtable for an object will
// be drived from the old one. Hence it is a pointer to the fat
// pointer.
pub fn unsized_info_bcx<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
kind: &ty::UnsizeKind<'tcx>,
id: ast::NodeId,
unadjusted_ty: Ty<'tcx>,
unadjusted_val: ValueRef, // see above (*)
param_substs: &'tcx subst::Substs<'tcx>,
mk_ty: F)
-> ValueRef
where F: FnOnce(Ty<'tcx>) -> Ty<'tcx>
{
unsized_info(
bcx.ccx(),
kind,
id,
unadjusted_ty,
param_substs,
mk_ty,
|| Load(bcx, GEPi(bcx, unadjusted_val, &[0, abi::FAT_PTR_EXTRA])))
}
// Same as `unsize_info_bcx`, but does not require a bcx -- instead it
// takes an extra closure to compute the upcast vtable.
pub fn unsized_info<'ccx, 'tcx, MK_TY, MK_UPCAST_VTABLE>(
ccx: &CrateContext<'ccx, 'tcx>,
kind: &ty::UnsizeKind<'tcx>,
id: ast::NodeId,
unadjusted_ty: Ty<'tcx>,
param_substs: &'tcx subst::Substs<'tcx>,
mk_ty: MK_TY,
mk_upcast_vtable: MK_UPCAST_VTABLE) // see notes above
-> ValueRef
where MK_TY: FnOnce(Ty<'tcx>) -> Ty<'tcx>,
MK_UPCAST_VTABLE: FnOnce() -> ValueRef,
{
// FIXME(#19596) workaround: `|t| t` causes monomorphization recursion
fn identity<T>(t: T) -> T { t }
@ -312,7 +344,8 @@ fn identity<T>(t: T) -> T { t }
let ty_substs = substs.types.get_slice(subst::TypeSpace);
// The dtor for a field treats it like a value, so mk_ty
// should just be the identity function.
unsized_info(ccx, k, id, ty_substs[tp_index], param_substs, identity)
unsized_info(ccx, k, id, ty_substs[tp_index], param_substs,
identity, mk_upcast_vtable)
}
_ => ccx.sess().bug(&format!("UnsizeStruct with bad sty: {}",
unadjusted_ty.repr(ccx.tcx())))
@ -330,6 +363,12 @@ fn identity<T>(t: T) -> T { t }
consts::ptrcast(meth::get_vtable(ccx, box_ty, trait_ref, param_substs),
Type::vtable_ptr(ccx))
}
&ty::UnsizeUpcast(_) => {
// For now, upcasts are limited to changes in marker
// traits, and hence never actually require an actual
// change to the vtable.
mk_upcast_vtable()
}
}
}
@ -338,7 +377,8 @@ fn identity<T>(t: T) -> T { t }
fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
expr: &ast::Expr,
datum: Datum<'tcx, Expr>)
-> DatumBlock<'blk, 'tcx, Expr> {
-> DatumBlock<'blk, 'tcx, Expr>
{
let mut bcx = bcx;
let mut datum = datum;
let adjustment = match bcx.tcx().adjustments.borrow().get(&expr.id).cloned() {
@ -347,10 +387,10 @@ fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
}
Some(adj) => { adj }
};
debug!("unadjusted datum for expr {}: {}, adjustment={}",
debug!("unadjusted datum for expr {}: {} adjustment={:?}",
expr.repr(bcx.tcx()),
datum.to_string(bcx.ccx()),
adjustment.repr(bcx.tcx()));
adjustment);
match adjustment {
AdjustReifyFnPointer(_def_id) => {
// FIXME(#19925) once fn item types are
@ -434,7 +474,6 @@ fn apply_autoref<'blk, 'tcx>(autoref: &ty::AutoRef<'tcx>,
debug!(" AutoUnsize");
unpack_datum!(bcx, unsize_expr(bcx, expr, datum, k))
}
&ty::AutoUnsizeUniq(ty::UnsizeLength(len)) => {
debug!(" AutoUnsizeUniq(UnsizeLength)");
unpack_datum!(bcx, unsize_unique_vec(bcx, expr, datum, len))
@ -459,16 +498,27 @@ fn unsize_expr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
let unsized_ty = ty::unsize_ty(tcx, datum_ty, k, expr.span);
debug!("unsized_ty={}", unsized_ty.repr(bcx.tcx()));
let info = unsized_info(bcx.ccx(), k, expr.id, datum_ty, bcx.fcx.param_substs,
|t| ty::mk_imm_rptr(tcx, tcx.mk_region(ty::ReStatic), t));
let info = unsized_info_bcx(bcx, k, expr.id, datum_ty, datum.val, bcx.fcx.param_substs,
|t| ty::mk_imm_rptr(tcx, tcx.mk_region(ty::ReStatic), t));
// Arrange cleanup
let lval = unpack_datum!(bcx,
datum.to_lvalue_datum(bcx, "into_fat_ptr", expr.id));
let lval = unpack_datum!(bcx, datum.to_lvalue_datum(bcx, "into_fat_ptr", expr.id));
// Compute the base pointer. This doesn't change the pointer value,
// but merely its type.
let ptr_ty = type_of::in_memory_type_of(bcx.ccx(), unsized_ty).ptr_to();
let base = PointerCast(bcx, lval.val, ptr_ty);
let base = if !type_is_sized(bcx.tcx(), lval.ty) {
// Normally, the source is a thin pointer and we are
// adding extra info to make a fat pointer. The exception
// is when we are upcasting an existing object fat pointer
// to use a different vtable. In that case, we want to
// load out the original data pointer so we can repackage
// it.
Load(bcx, get_dataptr(bcx, lval.val))
} else {
lval.val
};
let base = PointerCast(bcx, base, ptr_ty);
let llty = type_of::type_of(bcx.ccx(), unsized_ty);
// HACK(eddyb) get around issues with lifetime intrinsics.
@ -540,8 +590,8 @@ fn unsize_unique_expr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
let base = PointerCast(bcx, get_dataptr(bcx, scratch.val), llbox_ty.ptr_to());
bcx = datum.store_to(bcx, base);
let info = unsized_info(bcx.ccx(), k, expr.id, unboxed_ty, bcx.fcx.param_substs,
|t| ty::mk_uniq(tcx, t));
let info = unsized_info_bcx(bcx, k, expr.id, unboxed_ty, base, bcx.fcx.param_substs,
|t| ty::mk_uniq(tcx, t));
Store(bcx, info, get_len(bcx, scratch.val));
DatumBlock::new(bcx, scratch.to_expr_datum())
@ -1373,8 +1423,7 @@ pub fn with_field_tys<'tcx, R, F>(tcx: &ty::ctxt<'tcx>,
let def = tcx.def_map.borrow()[node_id].full_def();
match def {
def::DefVariant(enum_id, variant_id, _) => {
let variant_info = ty::enum_variant_with_id(
tcx, enum_id, variant_id);
let variant_info = ty::enum_variant_with_id(tcx, enum_id, variant_id);
let fields = struct_fields(tcx, variant_id, substs);
let fields = monomorphize::normalize_associated_type(tcx, &fields);
op(variant_info.disr_val, &fields[..])

View file

@ -334,15 +334,33 @@ fn unsize_ty(&self,
let ty = ty::mk_vec(tcx, t_a, None);
Some((ty, ty::UnsizeLength(len)))
}
(&ty::ty_trait(..), &ty::ty_trait(..)) => {
None
(&ty::ty_trait(ref data_a), &ty::ty_trait(ref data_b)) => {
// For now, we only support upcasts from
// `Foo+Send` to `Foo` (really, any time there are
// fewer builtin bounds then before). These are
// convenient because they don't require any sort
// of change to the vtable at runtime.
if data_a.bounds.builtin_bounds != data_b.bounds.builtin_bounds &&
data_a.bounds.builtin_bounds.is_superset(&data_b.bounds.builtin_bounds)
{
let bounds_a1 = ty::ExistentialBounds {
region_bound: data_a.bounds.region_bound,
builtin_bounds: data_b.bounds.builtin_bounds,
projection_bounds: data_a.bounds.projection_bounds.clone(),
};
let ty_a1 = ty::mk_trait(tcx, data_a.principal.clone(), bounds_a1);
match self.fcx.infcx().try(|_| self.subtype(ty_a1, ty_b)) {
Ok(_) => Some((ty_b, ty::UnsizeUpcast(ty_b))),
Err(_) => None,
}
} else {
None
}
}
(_, &ty::ty_trait(box ty::TyTrait { ref principal, ref bounds })) => {
// FIXME what is the purpose of `ty`?
let ty = ty::mk_trait(tcx, principal.clone(), bounds.clone());
Some((ty, ty::UnsizeVtable(ty::TyTrait { principal: principal.clone(),
bounds: bounds.clone() },
ty_a)))
(_, &ty::ty_trait(ref data)) => {
Some((ty_b, ty::UnsizeVtable(ty::TyTrait { principal: data.principal.clone(),
bounds: data.bounds.clone() },
ty_a)))
}
(&ty::ty_struct(did_a, substs_a), &ty::ty_struct(did_b, substs_b))
if did_a == did_b => {

View file

@ -176,13 +176,13 @@ fn create_ty_adjustment(&mut self,
probe::AutoDeref(num) => {
ty::AutoDerefRef {
autoderefs: num,
autoref: None
autoref: None,
}
}
probe::AutoUnsizeLength(autoderefs, len) => {
ty::AutoDerefRef {
autoderefs: autoderefs,
autoref: Some(ty::AutoUnsize(ty::UnsizeLength(len)))
autoref: Some(ty::AutoUnsize(ty::UnsizeLength(len))),
}
}
probe::AutoRef(mutability, ref sub_adjustment) => {

View file

@ -857,7 +857,7 @@ fn pick_by_value_method(&mut self,
return self.pick_method(step.self_ty).map(|r| self.adjust(r, adjustment.clone()));
fn consider_reborrow(ty: Ty, d: uint) -> PickAdjustment {
fn consider_reborrow<'tcx>(ty: Ty<'tcx>, d: uint) -> PickAdjustment {
// Insert a `&*` or `&mut *` if this is a reference type:
match ty.sty {
ty::ty_rptr(_, ref mt) => AutoRef(mt.mutbl, box AutoDeref(d+1)),
@ -902,7 +902,8 @@ fn search_mutabilities<F, G>(&mut self,
fn adjust(&mut self,
result: PickResult<'tcx>,
adjustment: PickAdjustment)
-> PickResult<'tcx> {
-> PickResult<'tcx>
{
match result {
Err(e) => Err(e),
Ok(mut pick) => {

View file

@ -1592,6 +1592,7 @@ fn register_unsize_obligations(&self,
code: traits::ObjectCastObligation(self_ty) };
self.register_region_obligation(self_ty, ty_trait.bounds.region_bound, cause);
}
ty::UnsizeUpcast(_) => { }
}
}

View file

@ -12,9 +12,15 @@
#![allow(warnings)]
pub fn fail(x: Option<& (Iterator+Send)>) -> Option<&Iterator> {
// This call used to trigger an LLVM assertion because the return slot had type
// "Option<&Iterator>"* instead of "Option<&(Iterator+Send)>"*
inner(x)
// This call used to trigger an LLVM assertion because the return
// slot had type "Option<&Iterator>"* instead of
// "Option<&(Iterator+Send)>"* -- but this now yields a
// compilation error and I'm not sure how to create a comparable
// test. To ensure that this PARTICULAR failure doesn't occur
// again, though, I've left this test here, so if this ever starts
// to compile again, we can adjust the test appropriately (clearly
// it should never ICE...). -nmatsakis
inner(x) //~ ERROR mismatched types
}
pub fn inner(x: Option<& (Iterator+Send)>) -> Option<&(Iterator+Send)> {
@ -22,4 +28,4 @@ pub fn inner(x: Option<& (Iterator+Send)>) -> Option<&(Iterator+Send)> {
}
#[rustc_error]
fn main() {} //~ ERROR compilation successful
fn main() {}