diff --git a/src/libextra/enum_set.rs b/src/libextra/enum_set.rs index 75611f88b6384..6fd42f74a767f 100644 --- a/src/libextra/enum_set.rs +++ b/src/libextra/enum_set.rs @@ -13,7 +13,7 @@ //! This module defines a container which uses an efficient bit mask //! representation to hold C-like enum variants. -#[deriving(Clone, Eq, IterBytes, ToStr)] +#[deriving(Clone, Eq, IterBytes, ToStr, Encodable, Decodable)] /// A specialized Set implementation to use enum types. pub struct EnumSet { // We must maintain the invariant that no bits are set diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index 2e989f48b8002..22fc7f4b3ea6f 100644 --- a/src/librustc/metadata/tydecode.rs +++ b/src/librustc/metadata/tydecode.rs @@ -129,6 +129,12 @@ pub fn parse_trait_ref_data(data: &[u8], crate_num: ast::CrateNum, pos: uint, tc parse_trait_ref(&mut st, conv) } +pub fn parse_substs_data(data: &[u8], crate_num: ast::CrateNum, pos: uint, tcx: ty::ctxt, + conv: conv_did) -> ty::substs { + let mut st = parse_state_from_data(data, crate_num, pos, tcx); + parse_substs(&mut st, conv) +} + fn parse_sigil(st: &mut PState) -> ast::Sigil { match next(st) { '@' => ast::ManagedSigil, diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs index 06d74055af40c..1f9f1e1856537 100644 --- a/src/librustc/metadata/tyencode.rs +++ b/src/librustc/metadata/tyencode.rs @@ -140,7 +140,7 @@ fn enc_opt(w: &mut MemWriter, t: Option, enc_f: |&mut MemWriter, T|) { } } -fn enc_substs(w: &mut MemWriter, cx: @ctxt, substs: &ty::substs) { +pub fn enc_substs(w: &mut MemWriter, cx: @ctxt, substs: &ty::substs) { enc_region_substs(w, cx, &substs.regions); enc_opt(w, substs.self_ty, |w, t| enc_ty(w, cx, t)); mywrite!(w, "["); diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 38f23a900d890..3560a930237b9 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -450,15 +450,13 @@ impl tr for ast::Def { // ______________________________________________________________________ // Encoding and decoding of adjustment information -impl tr for ty::AutoAdjustment { - fn tr(&self, xcx: @ExtendedDecodeContext) -> ty::AutoAdjustment { - match *self { - ty::AutoAddEnv(r, s) => ty::AutoAddEnv(r.tr(xcx), s), - ty::AutoDerefRef(ref adr) => { - ty::AutoDerefRef(ty::AutoDerefRef { - autoderefs: adr.autoderefs, - autoref: adr.autoref.map(|ar| ar.tr(xcx)), - }) +impl tr for ty::AutoDerefRef { + fn tr(&self, xcx: @ExtendedDecodeContext) -> ty::AutoDerefRef { + ty::AutoDerefRef { + autoderefs: self.autoderefs, + autoref: match self.autoref { + Some(ref autoref) => Some(autoref.tr(xcx)), + None => None } } } @@ -786,6 +784,8 @@ trait ebml_writer_helpers { fn emit_tpbt(&mut self, ecx: &e::EncodeContext, tpbt: ty::ty_param_bounds_and_ty); + fn emit_substs(&mut self, ecx: &e::EncodeContext, substs: &ty::substs); + fn emit_auto_adjustment(&mut self, ecx: &e::EncodeContext, adj: &ty::AutoAdjustment); } impl<'a> ebml_writer_helpers for writer::Encoder<'a> { @@ -833,6 +833,40 @@ impl<'a> ebml_writer_helpers for writer::Encoder<'a> { }) }) } + + fn emit_substs(&mut self, ecx: &e::EncodeContext, substs: &ty::substs) { + self.emit_opaque(|this| tyencode::enc_substs(this.writer, ecx.ty_str_ctxt(), substs)) + } + + fn emit_auto_adjustment(&mut self, ecx: &e::EncodeContext, adj: &ty::AutoAdjustment) { + self.emit_enum("AutoAdjustment", |this| { + match *adj { + ty::AutoAddEnv(region, sigil) => { + this.emit_enum_variant("AutoAddEnv", 0, 2, |this| { + this.emit_enum_variant_arg(0, |this| region.encode(this)); + this.emit_enum_variant_arg(1, |this| sigil.encode(this)); + }); + } + + ty::AutoDerefRef(ref auto_deref_ref) => { + this.emit_enum_variant("AutoDerefRef", 1, 1, |this| { + this.emit_enum_variant_arg(0, |this| auto_deref_ref.encode(this)); + }); + } + + ty::AutoObject(sigil, region, m, b, def_id, ref substs) => { + this.emit_enum_variant("AutoObject", 2, 6, |this| { + this.emit_enum_variant_arg(0, |this| sigil.encode(this)); + this.emit_enum_variant_arg(1, |this| region.encode(this)); + this.emit_enum_variant_arg(2, |this| m.encode(this)); + this.emit_enum_variant_arg(3, |this| b.encode(this)); + this.emit_enum_variant_arg(4, |this| def_id.encode(this)); + this.emit_enum_variant_arg(5, |this| this.emit_substs(ecx, substs)); + }); + } + } + }); + } } trait write_tag_and_id { @@ -1023,7 +1057,7 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext, ebml_w.tag(c::tag_table_adjustments, |ebml_w| { ebml_w.id(id); ebml_w.tag(c::tag_table_val, |ebml_w| { - (**adj).encode(ebml_w) + ebml_w.emit_auto_adjustment(ecx, **adj); }) }) } @@ -1064,6 +1098,8 @@ trait ebml_decoder_decoder_helpers { -> ty::TypeParameterDef; fn read_ty_param_bounds_and_ty(&mut self, xcx: @ExtendedDecodeContext) -> ty::ty_param_bounds_and_ty; + fn read_substs(&mut self, xcx: @ExtendedDecodeContext) -> ty::substs; + fn read_auto_adjustment(&mut self, xcx: @ExtendedDecodeContext) -> ty::AutoAdjustment; fn convert_def_id(&mut self, xcx: @ExtendedDecodeContext, source: DefIdSource, @@ -1172,6 +1208,61 @@ impl<'a> ebml_decoder_decoder_helpers for reader::Decoder<'a> { }) } + fn read_substs(&mut self, xcx: @ExtendedDecodeContext) -> ty::substs { + self.read_opaque(|this, doc| { + tydecode::parse_substs_data(doc.data, + xcx.dcx.cdata.cnum, + doc.start, + xcx.dcx.tcx, + |s, a| this.convert_def_id(xcx, s, a)) + }) + } + + fn read_auto_adjustment(&mut self, xcx: @ExtendedDecodeContext) -> ty::AutoAdjustment { + self.read_enum("AutoAdjustment", |this| { + let variants = ["AutoAddEnv", "AutoDerefRef", "AutoObject"]; + this.read_enum_variant(variants, |this, i| { + match i { + 0 => { + let region: ty::Region = + this.read_enum_variant_arg(0, |this| Decodable::decode(this)); + let sigil: ast::Sigil = + this.read_enum_variant_arg(1, |this| Decodable::decode(this)); + + ty:: AutoAddEnv(region.tr(xcx), sigil) + } + 1 => { + let auto_deref_ref: ty::AutoDerefRef = + this.read_enum_variant_arg(0, |this| Decodable::decode(this)); + + ty::AutoDerefRef(auto_deref_ref.tr(xcx)) + } + 2 => { + let sigil: ast::Sigil = + this.read_enum_variant_arg(0, |this| Decodable::decode(this)); + let region: Option = + this.read_enum_variant_arg(1, |this| Decodable::decode(this)); + let m: ast::Mutability = + this.read_enum_variant_arg(2, |this| Decodable::decode(this)); + let b: ty::BuiltinBounds = + this.read_enum_variant_arg(3, |this| Decodable::decode(this)); + let def_id: ast::DefId = + this.read_enum_variant_arg(4, |this| Decodable::decode(this)); + let substs = this.read_enum_variant_arg(5, |this| this.read_substs(xcx)); + + let region = match region { + Some(r) => Some(r.tr(xcx)), + None => None + }; + + ty::AutoObject(sigil, region, m, b, def_id.tr(xcx), substs) + } + _ => fail!("bad enum variant for ty::AutoAdjustment") + } + }) + }) + } + fn convert_def_id(&mut self, xcx: @ExtendedDecodeContext, source: tydecode::DefIdSource, @@ -1289,8 +1380,7 @@ fn decode_side_tables(xcx: @ExtendedDecodeContext, vtable_map.get().insert(id, vtable_res); } c::tag_table_adjustments => { - let adj: @ty::AutoAdjustment = @Decodable::decode(val_dsr); - adj.tr(xcx); + let adj: @ty::AutoAdjustment = @val_dsr.read_auto_adjustment(xcx); let mut adjustments = dcx.tcx .adjustments .borrow_mut(); diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs index 36e35760400d0..86ccfda90c909 100644 --- a/src/librustc/middle/borrowck/gather_loans/mod.rs +++ b/src/librustc/middle/borrowck/gather_loans/mod.rs @@ -419,6 +419,10 @@ impl<'a> GatherLoanCtxt<'a> { ty::AutoUnsafe(_) => {} } } + + ty::AutoObject(..) => { + // XXX: Handle @Trait to &Trait casts here? + } } } diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index 61e5d1f25fe7c..ddc31598d6740 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -490,7 +490,7 @@ impl BorrowckCtxt { adj: @ty::AutoAdjustment) -> mc::cmt { match *adj { - ty::AutoAddEnv(..) => { + ty::AutoAddEnv(..) | ty::AutoObject(..) => { // no autoderefs mc::cat_expr_unadjusted(self.tcx, self.method_map, expr) } diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs index e426f92759fac..70abd94ea31fb 100644 --- a/src/librustc/middle/kind.rs +++ b/src/librustc/middle/kind.rs @@ -311,14 +311,9 @@ pub fn check_expr(cx: &mut Context, e: @Expr) { let _ = check_durable(cx.tcx, interior_type, interior.span); } ExprCast(source, _) => { - check_cast_for_escaping_regions(cx, source, e); - match ty::get(ty::expr_ty(cx.tcx, e)).sty { - ty::ty_trait(_, _, _, _, bounds) => { - let source_ty = ty::expr_ty(cx.tcx, source); - check_trait_cast_bounds(cx, e.span, source_ty, bounds) - } - _ => { } - } + 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); } ExprRepeat(element, count_expr, _) => { let count = ty::eval_repeat_count(&cx.tcx, count_expr); @@ -330,9 +325,31 @@ pub fn check_expr(cx: &mut Context, e: @Expr) { } _ => {} } + + // Search for auto-adjustments to find trait coercions. + let adjustments = cx.tcx.adjustments.borrow(); + match adjustments.get().find(&e.id) { + Some(&@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); + } + Some(&@ty::AutoAddEnv(..)) | Some(&@ty::AutoDerefRef(..)) | None => {} + } + visit::walk_expr(cx, e, ()); } +fn check_trait_cast(cx: &mut Context, source_ty: ty::t, target_ty: ty::t, span: Span) { + check_cast_for_escaping_regions(cx, source_ty, target_ty, span); + match ty::get(target_ty).sty { + ty::ty_trait(_, _, _, _, bounds) => { + check_trait_cast_bounds(cx, span, source_ty, bounds); + } + _ => {} + } +} + fn check_ty(cx: &mut Context, aty: &Ty) { match aty.node { ty_path(_, _, id) => { @@ -510,12 +527,12 @@ pub fn check_durable(tcx: ty::ctxt, ty: ty::t, sp: Span) -> bool { /// FIXME(#5723)---This code should probably move into regionck. pub fn check_cast_for_escaping_regions( cx: &Context, - source: &Expr, - target: &Expr) + source_ty: ty::t, + target_ty: ty::t, + source_span: Span) { // Determine what type we are casting to; if it is not an trait, then no // worries. - let target_ty = ty::expr_ty(cx.tcx, target); match ty::get(target_ty).sty { ty::ty_trait(..) => {} _ => { return; } @@ -545,7 +562,6 @@ pub fn check_cast_for_escaping_regions( // Assuming the trait instance can escape, then ensure that each parameter // either appears in the trait type or is sendable. let target_params = ty::param_tys_in_type(target_ty); - let source_ty = ty::expr_ty(cx.tcx, source); ty::walk_regions_and_ty( cx.tcx, source_ty, @@ -555,7 +571,7 @@ pub fn check_cast_for_escaping_regions( // // if !target_regions.iter().any(|t_r| is_subregion_of(cx, *t_r, r)) { // cx.tcx.sess.span_err( - // source.span, + // source_span, // format!("source contains borrowed pointer with lifetime \ // not found in the target type `{}`", // ty_to_str(cx.tcx, target_ty))); @@ -570,7 +586,7 @@ pub fn check_cast_for_escaping_regions( if target_params.iter().any(|x| x == &source_param) { /* case (2) */ } else { - check_durable(cx.tcx, ty, source.span); /* case (3) */ + check_durable(cx.tcx, ty, source_span); /* case (3) */ } } _ => {} diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index b39ddecf8c4f6..2349b5f4dd3ff 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -347,6 +347,13 @@ impl mem_categorization_ctxt { self.cat_expr_unadjusted(expr) } + Some(&@ty::AutoObject(..)) => { + // Implicity casts a concrete object to trait object + // Result is an rvalue + let expr_ty = ty::expr_ty_adjusted(self.tcx, expr); + self.cat_rvalue_node(expr, expr_ty) + } + Some(&@ty::AutoAddEnv(..)) => { // Convert a bare fn to a closure by adding NULL env. // Result is an rvalue. diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index 19dc19262c022..c9d30ec19945d 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -199,6 +199,9 @@ pub fn const_expr(cx: @CrateContext, e: &ast::Expr) -> (ValueRef, bool) { cx.sess.span_bug(e.span, format!("unexpected static function: \ region {:?} sigil {:?}", *r, *s)) } + Some(@ty::AutoObject(..)) => { + cx.sess.span_unimpl(e.span, "unimplemented const coercion to trait object"); + } Some(@ty::AutoDerefRef(ref adj)) => { let mut ty = ety; let mut maybe_ptr = None; diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 2990a27391fe6..bf7a0d6039bdc 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -139,7 +139,7 @@ use middle::trans::inline; use middle::trans::tvec; use middle::trans::type_of; use middle::ty::struct_fields; -use middle::ty::{AutoBorrowObj, AutoDerefRef, AutoAddEnv, AutoUnsafe}; +use middle::ty::{AutoBorrowObj, AutoDerefRef, AutoAddEnv, AutoObject, AutoUnsafe}; use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowVecRef, AutoBorrowFn}; use middle::ty; use util::common::indenter; @@ -228,6 +228,23 @@ pub fn trans_to_datum(bcx: @Block, expr: &ast::Expr) -> DatumBlock { } }; } + AutoObject(ref sigil, ref region, _, _, _, _) => { + + let adjusted_ty = ty::expr_ty_adjusted(bcx.tcx(), expr); + let scratch = scratch_datum(bcx, adjusted_ty, "__adjust", false); + + let trait_store = match *sigil { + ast::BorrowedSigil => ty::RegionTraitStore(region.expect("expected valid region")), + ast::OwnedSigil => ty::UniqTraitStore, + ast::ManagedSigil => ty::BoxTraitStore + }; + + bcx = meth::trans_trait_cast(bcx, expr, expr.id, SaveIn(scratch.val), + trait_store, false /* no adjustments */); + + datum = scratch.to_appropriate_datum(bcx); + datum.add_clean(bcx); + } } debug!("after adjustments, datum={}", datum.to_str(bcx.ccx())); return DatumBlock {bcx: bcx, datum: datum}; @@ -432,6 +449,10 @@ pub fn trans_into(bcx: @Block, expr: &ast::Expr, dest: Dest) -> @Block { }; } + trans_into_unadjusted(bcx, expr, dest) +} + +pub fn trans_into_unadjusted(bcx: @Block, expr: &ast::Expr, dest: Dest) -> @Block { let ty = expr_ty(bcx, expr); debug!("trans_into_unadjusted(expr={}, dest={})", @@ -778,8 +799,8 @@ fn trans_rvalue_dps_unadjusted(bcx: @Block, expr: &ast::Expr, ast::ExprCast(val, _) => { match ty::get(node_id_type(bcx, expr.id)).sty { ty::ty_trait(_, _, store, _, _) => { - return meth::trans_trait_cast(bcx, val, expr.id, dest, - store); + return meth::trans_trait_cast(bcx, val, expr.id, + dest, store, true /* adjustments */); } _ => { bcx.tcx().sess.span_bug(expr.span, diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index f4df98870bbdf..d16cf6f1c3b27 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -646,14 +646,22 @@ pub fn trans_trait_cast(bcx: @Block, val: &ast::Expr, id: ast::NodeId, dest: expr::Dest, - _store: ty::TraitStore) + _store: ty::TraitStore, + do_adjustments: bool) -> @Block { let mut bcx = bcx; let _icx = push_ctxt("impl::trans_cast"); + // Pick the right trans function + let trans_into = if do_adjustments { + expr::trans_into + } else { + expr::trans_into_unadjusted + }; + let lldest = match dest { Ignore => { - return expr::trans_into(bcx, val, Ignore); + return trans_into(bcx, val, Ignore); } SaveIn(dest) => dest }; @@ -668,7 +676,7 @@ pub fn trans_trait_cast(bcx: @Block, llboxdest = PointerCast(bcx, llboxdest, type_of(bcx.ccx(), v_ty).ptr_to()); - bcx = expr::trans_into(bcx, val, SaveIn(llboxdest)); + bcx = trans_into(bcx, val, SaveIn(llboxdest)); // Store the vtable into the pair or triple. // This is structured a bit funny because of dynamic borrow failures. diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 60bbca6cf358a..a3c2e2a4404af 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -224,10 +224,14 @@ pub enum Variance { Bivariant, // T <: T -- e.g., unused type parameter } -#[deriving(Decodable, Encodable)] pub enum AutoAdjustment { AutoAddEnv(ty::Region, ast::Sigil), - AutoDerefRef(AutoDerefRef) + AutoDerefRef(AutoDerefRef), + AutoObject(ast::Sigil, Option, + ast::Mutability, + ty::BuiltinBounds, + ast::DefId, /* Trait ID */ + ty::substs /* Trait substitutions */) } #[deriving(Decodable, Encodable)] @@ -730,7 +734,7 @@ pub struct ParamBounds { pub type BuiltinBounds = EnumSet; -#[deriving(Clone, Eq, IterBytes, ToStr)] +#[deriving(Clone, Encodable, Eq, Decodable, IterBytes, ToStr)] #[repr(uint)] pub enum BuiltinBound { BoundStatic, @@ -2955,6 +2959,10 @@ pub fn adjust_ty(cx: ctxt, } } } + + Some(@AutoObject(ref sigil, ref region, m, b, def_id, ref substs)) => { + trait_adjustment_to_ty(cx, sigil, region, def_id, substs, m, b) + } }; fn borrow_vec(cx: ctxt, span: Span, @@ -3014,6 +3022,19 @@ pub fn adjust_ty(cx: ctxt, } } +pub fn trait_adjustment_to_ty(cx: ctxt, sigil: &ast::Sigil, region: &Option, + def_id: ast::DefId, substs: &substs, m: ast::Mutability, + bounds: BuiltinBounds) -> t { + + let trait_store = match *sigil { + BorrowedSigil => RegionTraitStore(region.expect("expected valid region")), + OwnedSigil => UniqTraitStore, + ManagedSigil => BoxTraitStore + }; + + mk_trait(cx, def_id, substs.clone(), trait_store, m, bounds) +} + impl AutoRef { pub fn map_region(&self, f: |Region| -> Region) -> AutoRef { match *self { diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 87947a91ccd5c..03fe9dc41b8b7 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -327,6 +327,25 @@ fn visit_expr(rcx: &mut Rcx, expr: @ast::Expr) { infer::AutoBorrow(expr.span)); } } + @ty::AutoObject(ast::BorrowedSigil, Some(trait_region), _, _, _, _) => { + // Determine if we are casting `expr` to an trait + // instance. If so, we have to be sure that the type of + // the source obeys the trait's region bound. + // + // Note: there is a subtle point here concerning type + // parameters. It is possible that the type of `source` + // contains type parameters, which in turn may contain + // regions that are not visible to us (only the caller + // knows about them). The kind checker is ultimately + // responsible for guaranteeing region safety in that + // particular case. There is an extensive comment on the + // function check_cast_for_escaping_regions() in kind.rs + // explaining how it goes about doing that. + + let source_ty = rcx.fcx.expr_ty(expr); + constrain_regions_in_type(rcx, trait_region, + infer::RelateObjectBound(expr.span), source_ty); + } _ => {} } } @@ -1075,6 +1094,27 @@ pub mod guarantor { }; } + Some(&@ty::AutoObject(ast::BorrowedSigil, Some(region), _, _, _, _)) => { + expr_ct.cat = ExprCategorization { + guarantor: None, + pointer: BorrowedPointer(region) + }; + } + + Some(&@ty::AutoObject(ast::OwnedSigil, _, _, _, _, _)) => { + expr_ct.cat = ExprCategorization { + guarantor: None, + pointer: OwnedPointer + }; + } + + Some(&@ty::AutoObject(ast::ManagedSigil, _, _, _, _, _)) => { + expr_ct.cat = ExprCategorization { + guarantor: None, + pointer: OtherPointer + }; + } + Some(&@ty::AutoDerefRef(ref adjustment)) => { debug!("adjustment={:?}", adjustment); @@ -1103,6 +1143,8 @@ pub mod guarantor { } } + Some(..) => fail!("invalid or unhandled adjustment"), + None => {} } diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs index f2899cb634002..35501e5d5ebf2 100644 --- a/src/librustc/middle/typeck/check/vtable.rs +++ b/src/librustc/middle/typeck/check/vtable.rs @@ -9,8 +9,8 @@ // except according to those terms. -use middle::ty::param_ty; use middle::ty; +use middle::ty::{AutoAddEnv, AutoDerefRef, AutoObject, param_ty}; use middle::ty_fold::TypeFolder; use middle::typeck::check::{FnCtxt, impl_self_ty}; use middle::typeck::check::{structurally_resolved_type}; @@ -565,6 +565,106 @@ pub fn early_resolve_expr(ex: @ast::Expr, let _indent = indenter(); let cx = fcx.ccx; + let resolve_object_cast = |src: @ast::Expr, target_ty: ty::t| { + match ty::get(target_ty).sty { + // Bounds of type's contents are not checked here, but in kind.rs. + ty::ty_trait(target_def_id, ref target_substs, store, + target_mutbl, _bounds) => { + fn mutability_allowed(a_mutbl: ast::Mutability, + b_mutbl: ast::Mutability) -> bool { + a_mutbl == b_mutbl || + (a_mutbl == ast::MutMutable && b_mutbl == ast::MutImmutable) + } + // 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`, `&x as &Trait` or `~x as ~Trait` + let ty = structurally_resolved_type(fcx, ex.span, + fcx.expr_ty(src)); + match (&ty::get(ty).sty, store) { + (&ty::ty_box(mt), ty::BoxTraitStore) | + (&ty::ty_uniq(mt), ty::UniqTraitStore) | + (&ty::ty_rptr(_, mt), ty::RegionTraitStore(..)) + if !mutability_allowed(mt.mutbl, target_mutbl) => { + fcx.tcx().sess.span_err(ex.span, + format!("types differ in mutability")); + } + + (&ty::ty_box(mt), ty::BoxTraitStore) | + (&ty::ty_uniq(mt), ty::UniqTraitStore) | + (&ty::ty_rptr(_, mt), ty::RegionTraitStore(..)) => { + let location_info = + &location_info_for_expr(ex); + let vcx = fcx.vtable_context(); + let target_trait_ref = @ty::TraitRef { + def_id: target_def_id, + substs: ty::substs { + tps: target_substs.tps.clone(), + regions: target_substs.regions.clone(), + self_ty: Some(mt.ty) + } + }; + + let param_bounds = ty::ParamBounds { + builtin_bounds: ty::EmptyBuiltinBounds(), + trait_bounds: ~[target_trait_ref] + }; + let vtables = + lookup_vtables_for_param(&vcx, + location_info, + None, + ¶m_bounds, + mt.ty, + is_early); + + if !is_early { + insert_vtables(fcx, ex.id, @~[vtables]); + } + + // Now, if this is &trait, we need to link the + // regions. + match (&ty::get(ty).sty, store) { + (&ty::ty_rptr(ra, _), + ty::RegionTraitStore(rb)) => { + infer::mk_subr(fcx.infcx(), + false, + infer::RelateObjectBound( + ex.span), + rb, + ra); + } + _ => {} + } + } + + (_, ty::UniqTraitStore) => { + fcx.ccx.tcx.sess.span_err( + ex.span, + format!("can only cast an ~-pointer \ + to a ~-object, not a {}", + ty::ty_sort_str(fcx.tcx(), ty))); + } + + (_, ty::BoxTraitStore) => { + fcx.ccx.tcx.sess.span_err( + ex.span, + format!("can only cast an @-pointer \ + to an @-object, not a {}", + ty::ty_sort_str(fcx.tcx(), ty))); + } + + (_, ty::RegionTraitStore(_)) => { + fcx.ccx.tcx.sess.span_err( + ex.span, + format!("can only cast an &-pointer \ + to an &-object, not a {}", + ty::ty_sort_str(fcx.tcx(), ty))); + } + } + } + _ => { /* not a cast to a trait; ignore */ } + } + }; match ex.node { ast::ExprPath(..) => { fcx.opt_node_ty_substs(ex.id, |substs| { @@ -621,107 +721,24 @@ pub fn early_resolve_expr(ex: @ast::Expr, ast::ExprCast(src, _) => { debug!("vtable resolution on expr {}", ex.repr(fcx.tcx())); let target_ty = fcx.expr_ty(ex); - match ty::get(target_ty).sty { - // Bounds of type's contents are not checked here, but in kind.rs. - ty::ty_trait(target_def_id, ref target_substs, store, - target_mutbl, _bounds) => { - fn mutability_allowed(a_mutbl: ast::Mutability, - b_mutbl: ast::Mutability) -> bool { - a_mutbl == b_mutbl || - (a_mutbl == ast::MutMutable && b_mutbl == ast::MutImmutable) - } - // 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`, `&x as &Trait` or `~x as ~Trait` - let ty = structurally_resolved_type(fcx, ex.span, - fcx.expr_ty(src)); - match (&ty::get(ty).sty, store) { - (&ty::ty_box(mt), ty::BoxTraitStore) | - (&ty::ty_uniq(mt), ty::UniqTraitStore) | - (&ty::ty_rptr(_, mt), ty::RegionTraitStore(..)) - if !mutability_allowed(mt.mutbl, target_mutbl) => { - fcx.tcx().sess.span_err(ex.span, - format!("types differ in mutability")); - } - - (&ty::ty_box(mt), ty::BoxTraitStore) | - (&ty::ty_uniq(mt), ty::UniqTraitStore) | - (&ty::ty_rptr(_, mt), ty::RegionTraitStore(..)) => { - let location_info = - &location_info_for_expr(ex); - let vcx = fcx.vtable_context(); - let target_trait_ref = @ty::TraitRef { - def_id: target_def_id, - substs: ty::substs { - tps: target_substs.tps.clone(), - regions: target_substs.regions.clone(), - self_ty: Some(mt.ty) - } - }; - - let param_bounds = ty::ParamBounds { - builtin_bounds: ty::EmptyBuiltinBounds(), - trait_bounds: ~[target_trait_ref] - }; - let vtables = - lookup_vtables_for_param(&vcx, - location_info, - None, - ¶m_bounds, - mt.ty, - is_early); - - if !is_early { - insert_vtables(fcx, ex.id, @~[vtables]); - } - - // Now, if this is &trait, we need to link the - // regions. - match (&ty::get(ty).sty, store) { - (&ty::ty_rptr(ra, _), - ty::RegionTraitStore(rb)) => { - infer::mk_subr(fcx.infcx(), - false, - infer::RelateObjectBound( - ex.span), - rb, - ra); - } - _ => {} - } - } - - (_, ty::UniqTraitStore) => { - fcx.ccx.tcx.sess.span_err( - ex.span, - format!("can only cast an ~-pointer \ - to a ~-object, not a {}", - ty::ty_sort_str(fcx.tcx(), ty))); - } - - (_, ty::BoxTraitStore) => { - fcx.ccx.tcx.sess.span_err( - ex.span, - format!("can only cast an @-pointer \ - to an @-object, not a {}", - ty::ty_sort_str(fcx.tcx(), ty))); - } - - (_, ty::RegionTraitStore(_)) => { - fcx.ccx.tcx.sess.span_err( - ex.span, - format!("can only cast an &-pointer \ - to an &-object, not a {}", - ty::ty_sort_str(fcx.tcx(), ty))); - } - } - } - _ => { /* not a cast to a trait; ignore */ } - } + resolve_object_cast(src, target_ty); } _ => () } + + // Search for auto-adjustments to find trait coercions + let adjustments = fcx.inh.adjustments.borrow(); + match adjustments.get().find(&ex.id) { + Some(&@AutoObject(ref sigil, ref region, m, b, def_id, ref substs)) => { + debug!("doing trait adjustment for expr {} {} (early? {})", + ex.id, ex.repr(fcx.tcx()), is_early); + + let object_ty = ty::trait_adjustment_to_ty(cx.tcx, sigil, region, + def_id, substs, m, b); + resolve_object_cast(ex, object_ty); + } + Some(&@AutoAddEnv(..)) | Some(&@AutoDerefRef(..)) | None => {} + } } fn resolve_expr(fcx: @FnCtxt, diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs index 3f371253e0553..f84220da8e75c 100644 --- a/src/librustc/middle/typeck/check/writeback.rs +++ b/src/librustc/middle/typeck/check/writeback.rs @@ -182,6 +182,12 @@ fn resolve_type_vars_for_node(wbcx: &mut WbCtxt, sp: Span, id: ast::NodeId) let mut adjustments = fcx.tcx().adjustments.borrow_mut(); adjustments.get().insert(id, resolved_adj); } + + Some(adjustment @ @ty::AutoObject(..)) => { + debug!("Adjustments for node {}: {:?}", id, adjustment); + let mut adjustments = fcx.tcx().adjustments.borrow_mut(); + adjustments.get().insert(id, adjustment); + } } // Resolve the type of the node with id `id` diff --git a/src/librustc/middle/typeck/infer/coercion.rs b/src/librustc/middle/typeck/infer/coercion.rs index f4c3c33dbb35d..0d4c3ef52d3d5 100644 --- a/src/librustc/middle/typeck/infer/coercion.rs +++ b/src/librustc/middle/typeck/infer/coercion.rs @@ -121,16 +121,61 @@ impl Coerce { }); } - ty::ty_trait(_, _, ty::RegionTraitStore(..), m, _) => { + ty::ty_ptr(mt_b) => { return self.unpack_actual_value(a, |sty_a| { - self.coerce_borrowed_object(a, sty_a, b, m) + self.coerce_unsafe_ptr(a, sty_a, b, mt_b) }); } - ty::ty_ptr(mt_b) => { - return self.unpack_actual_value(a, |sty_a| { - self.coerce_unsafe_ptr(a, sty_a, b, mt_b) + ty::ty_trait(def_id, ref substs, ty::BoxTraitStore, m, bounds) => { + let result = self.unpack_actual_value(a, |sty_a| { + match *sty_a { + ty::ty_box(..) => { + self.coerce_object(a, sty_a, b, def_id, substs, + ty::BoxTraitStore, m, bounds) + } + _ => Err(ty::terr_mismatch) + } }); + + match result { + Ok(t) => return Ok(t), + Err(..) => {} + } + } + + ty::ty_trait(def_id, ref substs, ty::UniqTraitStore, m, bounds) => { + let result = self.unpack_actual_value(a, |sty_a| { + match *sty_a { + ty::ty_uniq(..) => { + self.coerce_object(a, sty_a, b, def_id, substs, + ty::UniqTraitStore, m, bounds) + } + _ => Err(ty::terr_mismatch) + } + }); + + match result { + Ok(t) => return Ok(t), + Err(..) => {} + } + } + + ty::ty_trait(def_id, ref substs, ty::RegionTraitStore(region), m, bounds) => { + let result = self.unpack_actual_value(a, |sty_a| { + match *sty_a { + ty::ty_rptr(..) => { + self.coerce_object(a, sty_a, b, def_id, substs, + ty::RegionTraitStore(region), m, bounds) + } + _ => self.coerce_borrowed_object(a, sty_a, b, m) + } + }); + + match result { + Ok(t) => return Ok(t), + Err(..) => {} + } } _ => {} @@ -410,4 +455,30 @@ impl Coerce { autoref: Some(ty::AutoUnsafe(mt_b.mutbl)) }))) } + + pub fn coerce_object(&self, + a: ty::t, + sty_a: &ty::sty, + b: ty::t, + trait_def_id: ast::DefId, + trait_substs: &ty::substs, + trait_store: ty::TraitStore, + m: ast::Mutability, + bounds: ty::BuiltinBounds) -> CoerceResult { + + debug!("coerce_object(a={}, sty_a={:?}, b={})", + a.inf_str(self.infcx), sty_a, + b.inf_str(self.infcx)); + + let (sigil, region) = match trait_store { + ty::BoxTraitStore => (ast::ManagedSigil, None), + ty::UniqTraitStore => (ast::OwnedSigil, None), + ty::RegionTraitStore(region) => (ast::BorrowedSigil, Some(region)) + }; + + let adjustment = @ty::AutoObject(sigil, region, m, bounds, + trait_def_id, trait_substs.clone()); + + Ok(Some(adjustment)) + } } diff --git a/src/test/compile-fail/map-types.rs b/src/test/compile-fail/map-types.rs index c2c4a0b6908e6..0a1eab913be19 100644 --- a/src/test/compile-fail/map-types.rs +++ b/src/test/compile-fail/map-types.rs @@ -17,7 +17,7 @@ use std::hashmap::HashMap; fn main() { let x: @HashMap<~str, ~str> = @HashMap::new(); - let x: @Map<~str, ~str> = x as @Map<~str, ~str>; + let x: @Map<~str, ~str> = x; let y: @Map = @x; - //~^ ERROR expected trait std::container::Map but found @-ptr + //~^ ERROR failed to find an implementation of trait std::container::Map for @std::container::Map<~str,~str>:'static } diff --git a/src/test/compile-fail/trait-coercion-generic-bad.rs b/src/test/compile-fail/trait-coercion-generic-bad.rs new file mode 100644 index 0000000000000..2d73158add2e1 --- /dev/null +++ b/src/test/compile-fail/trait-coercion-generic-bad.rs @@ -0,0 +1,32 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[feature(managed_boxes)]; + +struct Struct { + person: &'static str +} + +trait Trait { + fn f(&self, x: T); +} + +impl Trait<&'static str> for Struct { + fn f(&self, x: &'static str) { + println!("Hello, {}!", x); + } +} + +fn main() { + let s: @Trait = @Struct { person: "Fred" }; //~ ERROR expected Trait, but found Trait<&'static str> + //~^ ERROR expected Trait, but found Trait<&'static str> + s.f(1); +} + diff --git a/src/test/compile-fail/trait-coercion-generic-regions.rs b/src/test/compile-fail/trait-coercion-generic-regions.rs new file mode 100644 index 0000000000000..1ea18a7c75b4d --- /dev/null +++ b/src/test/compile-fail/trait-coercion-generic-regions.rs @@ -0,0 +1,32 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[feature(managed_boxes)]; + +struct Struct { + person: &'static str +} + +trait Trait { + fn f(&self, x: T); +} + +impl Trait<&'static str> for Struct { + fn f(&self, x: &'static str) { + println!("Hello, {}!", x); + } +} + +fn main() { + let person = ~"Fred"; + let person: &str = person; //~ ERROR borrowed value does not live long enough + let s: @Trait<&'static str> = @Struct { person: person }; +} + diff --git a/src/test/run-pass/trait-coercion-generic.rs b/src/test/run-pass/trait-coercion-generic.rs new file mode 100644 index 0000000000000..415f7baf3d9cc --- /dev/null +++ b/src/test/run-pass/trait-coercion-generic.rs @@ -0,0 +1,42 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[feature(managed_boxes)]; + +trait Trait { + fn f(&self, x: T); +} + +struct Struct { + x: int, + y: int, +} + +impl Trait<&'static str> for Struct { + fn f(&self, x: &'static str) { + println(~"Hi, " + x + ~"!"); + } +} + +fn f(x: @Trait<&'static str>) { + x.f("Sue"); +} + +pub fn main() { + let a = Struct { x: 1, y: 2 }; + let b: @Trait<&'static str> = @a; + b.f("Fred"); + let c: ~Trait<&'static str> = ~a; + c.f("Mary"); + let d: &Trait<&'static str> = &a; + d.f("Joe"); + f(@a); +} + diff --git a/src/test/run-pass/trait-coercion.rs b/src/test/run-pass/trait-coercion.rs new file mode 100644 index 0000000000000..1c6968266e18d --- /dev/null +++ b/src/test/run-pass/trait-coercion.rs @@ -0,0 +1,42 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[feature(managed_boxes)]; + +trait Trait { + fn f(&self); +} + +struct Struct { + x: int, + y: int, +} + +impl Trait for Struct { + fn f(&self) { + println("Hi!"); + } +} + +fn f(x: @Trait) { + x.f(); +} + +pub fn main() { + let a = Struct { x: 1, y: 2 }; + let b: @Trait = @a; + b.f(); + let c: ~Trait = ~a; + c.f(); + let d: &Trait = &a; + d.f(); + f(@a); +} +