From f9f3ba5920ddb2b8bae5047eea556081e0da159d Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Fri, 16 Jan 2015 05:48:15 +0200 Subject: [PATCH 1/4] rustc: move infer::coercion to rustc_typeck. --- src/librustc/middle/infer/mod.rs | 43 ++++++------------- .../check}/coercion.rs | 42 +++++++++--------- src/librustc_typeck/check/mod.rs | 18 ++++---- 3 files changed, 43 insertions(+), 60 deletions(-) rename src/{librustc/middle/infer => librustc_typeck/check}/coercion.rs (96%) diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs index cfcead51f7844..1665966a5e5f5 100644 --- a/src/librustc/middle/infer/mod.rs +++ b/src/librustc/middle/infer/mod.rs @@ -33,12 +33,10 @@ use std::rc::Rc; use syntax::ast; use syntax::codemap; use syntax::codemap::Span; -use util::common::indent; use util::nodemap::FnvHashMap; use util::ppaux::{ty_to_string}; use util::ppaux::{Repr, UserString}; -use self::coercion::Coerce; use self::combine::{Combine, Combineable, CombineFields}; use self::region_inference::{RegionVarBindings, RegionSnapshot}; use self::equate::Equate; @@ -47,7 +45,6 @@ use self::lub::Lub; use self::unify::{UnificationTable, InferCtxtMethodsForSimplyUnifiableTypes}; use self::error_reporting::ErrorReporting; -pub mod coercion; pub mod combine; pub mod doc; pub mod equate; @@ -68,7 +65,6 @@ pub type Bound = Option; pub type cres<'tcx, T> = Result>; // "combine result" pub type ures<'tcx> = cres<'tcx, ()>; // "unify result" pub type fres = Result; // "fixup result" -pub type CoerceResult<'tcx> = cres<'tcx, Option>>; pub struct InferCtxt<'a, 'tcx: 'a> { pub tcx: &'a ty::ctxt<'tcx>, @@ -409,24 +405,6 @@ fn expected_found(a_is_expected: bool, } } -pub fn mk_coercety<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>, - a_is_expected: bool, - origin: TypeOrigin, - a: Ty<'tcx>, - b: Ty<'tcx>) - -> CoerceResult<'tcx> { - debug!("mk_coercety({} -> {})", a.repr(cx.tcx), b.repr(cx.tcx)); - indent(|| { - cx.commit_if_ok(|| { - let trace = TypeTrace { - origin: origin, - values: Types(expected_found(a_is_expected, a, b)) - }; - Coerce(cx.combine_fields(a_is_expected, trace)).tys(a, b) - }) - }) -} - trait then<'tcx> { fn then(&self, f: F) -> Result> where T: Clone, @@ -689,10 +667,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { { debug!("sub_types({} <: {})", a.repr(self.tcx), b.repr(self.tcx)); self.commit_if_ok(|| { - let trace = TypeTrace { - origin: origin, - values: Types(expected_found(a_is_expected, a, b)) - }; + let trace = TypeTrace::types(origin, a_is_expected, a, b); self.sub(a_is_expected, trace).tys(a, b).to_ures() }) } @@ -705,10 +680,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { -> ures<'tcx> { self.commit_if_ok(|| { - let trace = TypeTrace { - origin: origin, - values: Types(expected_found(a_is_expected, a, b)) - }; + let trace = TypeTrace::types(origin, a_is_expected, a, b); self.equate(a_is_expected, trace).tys(a, b).to_ures() }) } @@ -1118,6 +1090,17 @@ impl<'tcx> TypeTrace<'tcx> { self.origin.span() } + pub fn types(origin: TypeOrigin, + a_is_expected: bool, + a: Ty<'tcx>, + b: Ty<'tcx>) + -> TypeTrace<'tcx> { + TypeTrace { + origin: origin, + values: Types(expected_found(a_is_expected, a, b)) + } + } + pub fn dummy(tcx: &ty::ctxt<'tcx>) -> TypeTrace<'tcx> { TypeTrace { origin: Misc(codemap::DUMMY_SP), diff --git a/src/librustc/middle/infer/coercion.rs b/src/librustc_typeck/check/coercion.rs similarity index 96% rename from src/librustc/middle/infer/coercion.rs rename to src/librustc_typeck/check/coercion.rs index e8b8ecc701f91..e375647bc2839 100644 --- a/src/librustc/middle/infer/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -60,14 +60,15 @@ //! sort of a minor point so I've opted to leave it for later---after all //! we may want to adjust precisely when coercions occur. -use super::{CoerceResult, Coercion}; -use super::combine::{CombineFields, Combine}; -use super::sub::Sub; +use middle::infer::{cres, Coercion, InferCtxt, TypeOrigin, TypeTrace}; +use middle::infer::combine::{CombineFields, Combine}; +use middle::infer::sub::Sub; use middle::subst; use middle::ty::{AutoPtr, AutoDerefRef, AdjustDerefRef, AutoUnsize, AutoUnsafe}; use middle::ty::{mt}; use middle::ty::{self, Ty}; +use util::common::indent; use util::ppaux; use util::ppaux::Repr; @@ -472,24 +473,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } } - pub fn coerce_borrowed_fn(&self, - a: Ty<'tcx>, - b: Ty<'tcx>) - -> CoerceResult<'tcx> { - debug!("coerce_borrowed_fn(a={}, b={})", - a.repr(self.tcx()), - b.repr(self.tcx())); - - match a.sty { - ty::ty_bare_fn(Some(a_def_id), f) => { - self.coerce_from_fn_item(a, a_def_id, f, b) - } - _ => { - self.subtype(a, b) - } - } - } - fn coerce_from_fn_item(&self, a: Ty<'tcx>, fn_def_id_a: ast::DefId, @@ -551,6 +534,23 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } } +pub type CoerceResult<'tcx> = cres<'tcx, Option>>; + +pub fn mk_coercety<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>, + a_is_expected: bool, + origin: TypeOrigin, + a: Ty<'tcx>, + b: Ty<'tcx>) + -> CoerceResult<'tcx> { + debug!("mk_coercety({} -> {})", a.repr(cx.tcx), b.repr(cx.tcx)); + indent(|| { + cx.commit_if_ok(|| { + let trace = TypeTrace::types(origin, a_is_expected, a, b); + Coerce(cx.combine_fields(a_is_expected, trace)).tys(a, b) + }) + }) +} + fn can_coerce_mutbls(from_mutbl: ast::Mutability, to_mutbl: ast::Mutability) -> bool { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 6dabec31e2c6f..799c7a355b786 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -132,6 +132,7 @@ pub mod vtable; pub mod writeback; pub mod regionmanip; pub mod regionck; +pub mod coercion; pub mod demand; pub mod method; mod upvar; @@ -1730,18 +1731,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sub: Ty<'tcx>, sup: Ty<'tcx>) -> Result<(), ty::type_err<'tcx>> { - match infer::mk_coercety(self.infcx(), - false, - infer::ExprAssignable(expr.span), - sub, - sup) { - Ok(None) => Ok(()), - Err(ref e) => Err((*e)), - Ok(Some(adjustment)) => { + match try!(coercion::mk_coercety(self.infcx(), + false, + infer::ExprAssignable(expr.span), + sub, + sup)) { + None => {} + Some(adjustment) => { self.write_adjustment(expr.id, expr.span, adjustment); - Ok(()) } } + Ok(()) } pub fn mk_eqty(&self, From 6a478bdfd24ccc4df99a23d275a82e71a990115d Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Sat, 17 Jan 2015 22:44:42 +0200 Subject: [PATCH 2/4] rustc_typeck: use FnCtxt in coercion, instead of mimicking a combiner. --- src/librustc_typeck/check/coercion.rs | 130 +++++++++++++------------- src/librustc_typeck/check/demand.rs | 4 +- src/librustc_typeck/check/mod.rs | 18 ---- 3 files changed, 65 insertions(+), 87 deletions(-) diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index e375647bc2839..79d57d957abcc 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -60,10 +60,11 @@ //! sort of a minor point so I've opted to leave it for later---after all //! we may want to adjust precisely when coercions occur. -use middle::infer::{cres, Coercion, InferCtxt, TypeOrigin, TypeTrace}; -use middle::infer::combine::{CombineFields, Combine}; -use middle::infer::sub::Sub; +use check::FnCtxt; +use middle::infer::{self, cres, Coercion, TypeTrace}; +use middle::infer::combine::Combine; +use middle::infer::sub::Sub; use middle::subst; use middle::ty::{AutoPtr, AutoDerefRef, AdjustDerefRef, AutoUnsize, AutoUnsafe}; use middle::ty::{mt}; @@ -74,21 +75,31 @@ use util::ppaux::Repr; use syntax::ast; -// Note: Coerce is not actually a combiner, in that it does not -// conform to the same interface, though it performs a similar -// function. -pub struct Coerce<'f, 'tcx: 'f>(pub CombineFields<'f, 'tcx>); +struct Coerce<'a, 'tcx: 'a> { + fcx: &'a FnCtxt<'a, 'tcx>, + trace: TypeTrace<'tcx> +} + +type CoerceResult<'tcx> = cres<'tcx, Option>>; impl<'f, 'tcx> Coerce<'f, 'tcx> { - pub fn get_ref<'a>(&'a self) -> &'a CombineFields<'f, 'tcx> { - let Coerce(ref v) = *self; v + fn tcx(&self) -> &ty::ctxt<'tcx> { + self.fcx.tcx() } - fn tcx(&self) -> &ty::ctxt<'tcx> { - self.get_ref().infcx.tcx + fn subtype(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { + let sub = Sub(self.fcx.infcx().combine_fields(false, self.trace.clone())); + try!(sub.tys(a, b)); + Ok(None) // No coercion required. } - pub fn tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { + fn unpack_actual_value(&self, a: Ty<'tcx>, f: F) -> T where + F: FnOnce(Ty<'tcx>) -> T, + { + f(self.fcx.infcx().shallow_resolve(a)) + } + + fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { debug!("Coerce.tys({} => {})", a.repr(self.tcx()), b.repr(self.tcx())); @@ -179,25 +190,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { }) } - pub fn subtype(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { - match Sub(self.get_ref().clone()).tys(a, b) { - Ok(_) => Ok(None), // No coercion required. - Err(ref e) => Err(*e) - } - } - - pub fn unpack_actual_value(&self, a: Ty<'tcx>, f: F) -> T where - F: FnOnce(Ty<'tcx>) -> T, - { - f(self.get_ref().infcx.shallow_resolve(a)) - } - // ~T -> &T or &mut T -> &T (including where T = [U] or str) - pub fn coerce_borrowed_pointer(&self, - a: Ty<'tcx>, - b: Ty<'tcx>, - mutbl_b: ast::Mutability) - -> CoerceResult<'tcx> { + fn coerce_borrowed_pointer(&self, + a: Ty<'tcx>, + b: Ty<'tcx>, + mutbl_b: ast::Mutability) + -> CoerceResult<'tcx> { debug!("coerce_borrowed_pointer(a={}, b={})", a.repr(self.tcx()), b.repr(self.tcx())); @@ -208,9 +206,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // to type check, we will construct the type that `&M*expr` would // yield. - let sub = Sub(self.get_ref().clone()); - let coercion = Coercion(self.get_ref().trace.clone()); - let r_borrow = self.get_ref().infcx.next_region_var(coercion); + let coercion = Coercion(self.trace.clone()); + let r_borrow = self.fcx.infcx().next_region_var(coercion); let inner_ty = match a.sty { ty::ty_uniq(_) => return Err(ty::terr_mismatch), @@ -220,15 +217,14 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } mt_a.ty } - _ => { - return self.subtype(a, b); - } + _ => return self.subtype(a, b) }; let a_borrowed = ty::mk_rptr(self.tcx(), self.tcx().mk_region(r_borrow), mt {ty: inner_ty, mutbl: mutbl_b}); - try!(sub.tys(a_borrowed, b)); + try!(self.subtype(a_borrowed, b)); + if let Err(original_err) = self.subtype(a_borrowed, b) { Ok(Some(AdjustDerefRef(AutoDerefRef { autoderefs: 1, @@ -253,8 +249,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // we can't unify [T] with U. But to properly support DST, we need to allow // that, at which point we will need extra checks on b here. - let sub = Sub(self.get_ref().clone()); - match (&a.sty, &b.sty) { (&ty::ty_rptr(_, ty::mt{ty: t_a, mutbl: mutbl_a}), &ty::ty_rptr(_, mt_b)) => { self.unpack_actual_value(t_a, |a| { @@ -264,12 +258,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { 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 coercion = Coercion(self.trace.clone()); + let r_borrow = self.fcx.infcx().next_region_var(coercion); let ty = ty::mk_rptr(self.tcx(), self.tcx().mk_region(r_borrow), ty::mt{ty: ty, mutbl: mt_b.mutbl}); - try!(self.get_ref().infcx.try(|_| sub.tys(ty, b))); + try!(self.fcx.infcx().try(|_| self.subtype(ty, b))); debug!("Success, coerced with AutoDerefRef(1, \ AutoPtr(AutoUnsize({:?})))", kind); Ok(Some(AdjustDerefRef(AutoDerefRef { @@ -292,7 +286,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let ty = ty::mk_ptr(self.tcx(), ty::mt{ty: ty, mutbl: mt_b.mutbl}); - try!(self.get_ref().infcx.try(|_| sub.tys(ty, b))); + try!(self.fcx.infcx().try(|_| self.subtype(ty, b))); debug!("Success, coerced with AutoDerefRef(1, \ AutoPtr(AutoUnsize({:?})))", kind); Ok(Some(AdjustDerefRef(AutoDerefRef { @@ -310,7 +304,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { match self.unsize_ty(t_a, a, t_b) { Some((ty, kind)) => { let ty = ty::mk_uniq(self.tcx(), ty); - try!(self.get_ref().infcx.try(|_| sub.tys(ty, b))); + try!(self.fcx.infcx().try(|_| self.subtype(ty, b))); debug!("Success, coerced with AutoDerefRef(1, \ AutoUnsizeUniq({:?}))", kind); Ok(Some(AdjustDerefRef(AutoDerefRef { @@ -362,12 +356,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let ty_substs_b = substs_b.types.get_slice(subst::TypeSpace); assert!(ty_substs_a.len() == ty_substs_b.len()); - let sub = Sub(self.get_ref().clone()); - let mut result = None; let mut tps = ty_substs_a.iter().zip(ty_substs_b.iter()).enumerate(); for (i, (tp_a, tp_b)) in tps { - if self.get_ref().infcx.try(|_| sub.tys(*tp_a, *tp_b)).is_ok() { + if self.fcx.infcx().try(|_| self.subtype(*tp_a, *tp_b)).is_ok() { continue; } match @@ -380,7 +372,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let mut new_substs = substs_a.clone(); new_substs.types.get_mut_slice(subst::TypeSpace)[i] = new_tp; let ty = ty::mk_struct(tcx, did_a, tcx.mk_substs(new_substs)); - if self.get_ref().infcx.try(|_| sub.tys(ty, ty_b)).is_err() { + if self.fcx.infcx().try(|_| self.subtype(ty, ty_b)).is_err() { debug!("Unsized type parameter '{}', but still \ could not match types {} and {}", ppaux::ty_to_string(tcx, *tp_a), @@ -416,8 +408,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { a.repr(tcx), b.repr(tcx), b_mutbl); - let coercion = Coercion(self.get_ref().trace.clone()); - let r_a = self.get_ref().infcx.next_region_var(coercion); + let coercion = Coercion(self.trace.clone()); + let r_a = self.fcx.infcx().next_region_var(coercion); self.coerce_object(a, b, b_mutbl, |tr| ty::mk_rptr(tcx, tcx.mk_region(r_a), @@ -501,11 +493,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { }) } - pub fn coerce_unsafe_ptr(&self, - a: Ty<'tcx>, - b: Ty<'tcx>, - mutbl_b: ast::Mutability) - -> CoerceResult<'tcx> { + fn coerce_unsafe_ptr(&self, + a: Ty<'tcx>, + b: Ty<'tcx>, + mutbl_b: ast::Mutability) + -> CoerceResult<'tcx> { debug!("coerce_unsafe_ptr(a={}, b={})", a.repr(self.tcx()), b.repr(self.tcx())); @@ -534,21 +526,25 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } } -pub type CoerceResult<'tcx> = cres<'tcx, Option>>; - -pub fn mk_coercety<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>, - a_is_expected: bool, - origin: TypeOrigin, +pub fn mk_assignty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, + expr: &ast::Expr, a: Ty<'tcx>, b: Ty<'tcx>) - -> CoerceResult<'tcx> { - debug!("mk_coercety({} -> {})", a.repr(cx.tcx), b.repr(cx.tcx)); - indent(|| { - cx.commit_if_ok(|| { - let trace = TypeTrace::types(origin, a_is_expected, a, b); - Coerce(cx.combine_fields(a_is_expected, trace)).tys(a, b) + -> cres<'tcx, ()> { + debug!("mk_assignty({} -> {})", a.repr(fcx.tcx()), b.repr(fcx.tcx())); + let adjustment = try!(indent(|| { + fcx.infcx().commit_if_ok(|| { + let origin = infer::ExprAssignable(expr.span); + Coerce { + fcx: fcx, + trace: infer::TypeTrace::types(origin, false, a, b) + }.coerce(a, b) }) - }) + })); + if let Some(adjustment) = adjustment { + fcx.write_adjustment(expr.id, expr.span, adjustment); + } + Ok(()) } fn can_coerce_mutbls(from_mutbl: ast::Mutability, diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 8188835718cd6..6f2d0cb366741 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -9,7 +9,7 @@ // except according to those terms. -use check::FnCtxt; +use check::{coercion, FnCtxt}; use middle::ty::{self, Ty}; use middle::infer; @@ -62,7 +62,7 @@ pub fn coerce<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span, expr_ty.repr(fcx.ccx.tcx)); let expr_ty = fcx.resolve_type_vars_if_possible(expr_ty); let expected = fcx.resolve_type_vars_if_possible(expected); - match fcx.mk_assignty(expr, expr_ty, expected) { + match coercion::mk_assignty(fcx, expr, expr_ty, expected) { Ok(()) => { /* ok */ } Err(ref err) => { fcx.report_mismatched_types(sp, expected, expr_ty, err); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 799c7a355b786..8d1a43cb08194 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1726,24 +1726,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { infer::mk_subty(self.infcx(), a_is_expected, origin, sub, sup) } - pub fn mk_assignty(&self, - expr: &ast::Expr, - sub: Ty<'tcx>, - sup: Ty<'tcx>) - -> Result<(), ty::type_err<'tcx>> { - match try!(coercion::mk_coercety(self.infcx(), - false, - infer::ExprAssignable(expr.span), - sub, - sup)) { - None => {} - Some(adjustment) => { - self.write_adjustment(expr.id, expr.span, adjustment); - } - } - Ok(()) - } - pub fn mk_eqty(&self, a_is_expected: bool, origin: infer::TypeOrigin, From b48c4c8cf4f6366fd88693d2b359afc2b94bc575 Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Sun, 18 Jan 2015 11:20:29 +0200 Subject: [PATCH 3/4] rustc_typeck: cleanup coercion logic that has been obsolete/unused for a while now. --- src/librustc_typeck/check/coercion.rs | 126 ++------------------------ 1 file changed, 7 insertions(+), 119 deletions(-) diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 79d57d957abcc..c2ea8277fc868 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -67,8 +67,7 @@ use middle::infer::combine::Combine; use middle::infer::sub::Sub; use middle::subst; use middle::ty::{AutoPtr, AutoDerefRef, AdjustDerefRef, AutoUnsize, AutoUnsafe}; -use middle::ty::{mt}; -use middle::ty::{self, Ty}; +use middle::ty::{self, mt, Ty}; use util::common::indent; use util::ppaux; use util::ppaux::Repr; @@ -118,57 +117,15 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // See above for details. match b.sty { ty::ty_ptr(mt_b) => { - match mt_b.ty.sty { - ty::ty_str => { - return self.unpack_actual_value(a, |a| { - self.coerce_unsafe_ptr(a, b, ast::MutImmutable) - }); - } - - ty::ty_trait(..) => { - let result = self.unpack_actual_value(a, |a| { - self.coerce_unsafe_object(a, b, mt_b.mutbl) - }); - - match result { - Ok(t) => return Ok(t), - Err(..) => {} - } - } - - _ => { - return self.unpack_actual_value(a, |a| { - self.coerce_unsafe_ptr(a, b, mt_b.mutbl) - }); - } - }; + return self.unpack_actual_value(a, |a| { + self.coerce_unsafe_ptr(a, b, mt_b.mutbl) + }); } ty::ty_rptr(_, mt_b) => { - match mt_b.ty.sty { - ty::ty_str => { - return self.unpack_actual_value(a, |a| { - self.coerce_borrowed_pointer(a, b, ast::MutImmutable) - }); - } - - ty::ty_trait(..) => { - let result = self.unpack_actual_value(a, |a| { - self.coerce_borrowed_object(a, b, mt_b.mutbl) - }); - - match result { - Ok(t) => return Ok(t), - Err(..) => {} - } - } - - _ => { - return self.unpack_actual_value(a, |a| { - self.coerce_borrowed_pointer(a, b, mt_b.mutbl) - }); - } - }; + return self.unpack_actual_value(a, |a| { + self.coerce_borrowed_pointer(a, b, mt_b.mutbl) + }); } _ => {} @@ -210,7 +167,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let r_borrow = self.fcx.infcx().next_region_var(coercion); let inner_ty = match a.sty { - ty::ty_uniq(_) => return Err(ty::terr_mismatch), ty::ty_rptr(_, mt_a) => { if !can_coerce_mutbls(mt_a.mutbl, mutbl_b) { return Err(ty::terr_mutability); @@ -397,74 +353,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ) } - fn coerce_borrowed_object(&self, - a: Ty<'tcx>, - b: Ty<'tcx>, - b_mutbl: ast::Mutability) -> CoerceResult<'tcx> - { - let tcx = self.tcx(); - - debug!("coerce_borrowed_object(a={}, b={}, b_mutbl={:?})", - a.repr(tcx), - b.repr(tcx), b_mutbl); - - let coercion = Coercion(self.trace.clone()); - let r_a = self.fcx.infcx().next_region_var(coercion); - - self.coerce_object(a, b, b_mutbl, - |tr| ty::mk_rptr(tcx, tcx.mk_region(r_a), - ty::mt{ mutbl: b_mutbl, ty: tr }), - || AutoPtr(r_a, b_mutbl, None)) - } - - fn coerce_unsafe_object(&self, - a: Ty<'tcx>, - b: Ty<'tcx>, - b_mutbl: ast::Mutability) -> CoerceResult<'tcx> - { - let tcx = self.tcx(); - - debug!("coerce_unsafe_object(a={}, b={}, b_mutbl={:?})", - a.repr(tcx), - b.repr(tcx), b_mutbl); - - self.coerce_object(a, b, b_mutbl, - |tr| ty::mk_ptr(tcx, ty::mt{ mutbl: b_mutbl, ty: tr }), - || AutoUnsafe(b_mutbl, None)) - } - - fn coerce_object(&self, - a: Ty<'tcx>, - b: Ty<'tcx>, - b_mutbl: ast::Mutability, - mk_ty: F, - mk_adjust: G) -> CoerceResult<'tcx> where - F: FnOnce(Ty<'tcx>) -> Ty<'tcx>, - G: FnOnce() -> ty::AutoRef<'tcx>, - { - let tcx = self.tcx(); - - match a.sty { - ty::ty_rptr(_, ty::mt{ty, mutbl}) => match ty.sty { - ty::ty_trait(box ty::TyTrait { ref principal, ref bounds }) => { - debug!("mutbl={:?} b_mutbl={:?}", mutbl, b_mutbl); - let tr = ty::mk_trait(tcx, principal.clone(), bounds.clone()); - try!(self.subtype(mk_ty(tr), b)); - Ok(Some(AdjustDerefRef(AutoDerefRef { - autoderefs: 1, - autoref: Some(mk_adjust()) - }))) - } - _ => { - self.subtype(a, b) - } - }, - _ => { - self.subtype(a, b) - } - } - } - fn coerce_from_fn_item(&self, a: Ty<'tcx>, fn_def_id_a: ast::DefId, From ae076e1e3b037e557b613d97698a9f28a1683d8b Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Thu, 29 Jan 2015 12:17:51 +0200 Subject: [PATCH 4/4] Implement deref coercions (rust-lang/rfcs#241). --- src/librustc_typeck/check/callee.rs | 2 + src/librustc_typeck/check/coercion.rs | 80 ++++++++++++++----- src/librustc_typeck/check/method/confirm.rs | 40 +++++++--- src/librustc_typeck/check/method/probe.rs | 25 +++--- src/librustc_typeck/check/mod.rs | 60 +++++++++++--- .../coerce-overloaded-autoderef.rs | 40 ++++++++++ src/test/compile-fail/method-self-arg-1.rs | 5 -- .../run-pass/coerce-overloaded-autoderef.rs | 69 ++++++++++++++++ 8 files changed, 261 insertions(+), 60 deletions(-) create mode 100644 src/test/compile-fail/coerce-overloaded-autoderef.rs create mode 100644 src/test/run-pass/coerce-overloaded-autoderef.rs diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index d851206f384e3..e37c1ab7f0cb7 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -21,6 +21,7 @@ use super::LvaluePreference; use super::method; use super::structurally_resolved_type; use super::TupleArgumentsFlag; +use super::UnresolvedTypeAction; use super::write_call; use middle::infer; @@ -77,6 +78,7 @@ pub fn check_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, callee_expr.span, original_callee_ty, Some(callee_expr), + UnresolvedTypeAction::Error, LvaluePreference::NoPreference, |adj_ty, idx| { let autoderefref = ty::AutoDerefRef { autoderefs: idx, autoref: None }; diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index c2ea8277fc868..8bac89ac1843f 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -60,7 +60,7 @@ //! sort of a minor point so I've opted to leave it for later---after all //! we may want to adjust precisely when coercions occur. -use check::FnCtxt; +use check::{autoderef, FnCtxt, NoPreference, PreferMutLvalue, UnresolvedTypeAction}; use middle::infer::{self, cres, Coercion, TypeTrace}; use middle::infer::combine::Combine; @@ -98,7 +98,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { f(self.fcx.infcx().shallow_resolve(a)) } - fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { + fn coerce(&self, + expr_a: &ast::Expr, + a: Ty<'tcx>, + b: Ty<'tcx>) + -> CoerceResult<'tcx> { debug!("Coerce.tys({} => {})", a.repr(self.tcx()), b.repr(self.tcx())); @@ -124,7 +128,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ty::ty_rptr(_, mt_b) => { return self.unpack_actual_value(a, |a| { - self.coerce_borrowed_pointer(a, b, mt_b.mutbl) + self.coerce_borrowed_pointer(expr_a, a, b, mt_b.mutbl) }); } @@ -147,8 +151,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { }) } - // ~T -> &T or &mut T -> &T (including where T = [U] or str) + /// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`. + /// To match `A` with `B`, autoderef will be performed, + /// calling `deref`/`deref_mut` where necessary. fn coerce_borrowed_pointer(&self, + expr_a: &ast::Expr, a: Ty<'tcx>, b: Ty<'tcx>, mutbl_b: ast::Mutability) @@ -163,29 +170,62 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // to type check, we will construct the type that `&M*expr` would // yield. - let coercion = Coercion(self.trace.clone()); - let r_borrow = self.fcx.infcx().next_region_var(coercion); - - let inner_ty = match a.sty { + match a.sty { ty::ty_rptr(_, mt_a) => { if !can_coerce_mutbls(mt_a.mutbl, mutbl_b) { return Err(ty::terr_mutability); } - mt_a.ty } _ => return self.subtype(a, b) - }; + } - let a_borrowed = ty::mk_rptr(self.tcx(), - self.tcx().mk_region(r_borrow), - mt {ty: inner_ty, mutbl: mutbl_b}); - try!(self.subtype(a_borrowed, b)); - if let Err(original_err) = self.subtype(a_borrowed, b) { + let coercion = Coercion(self.trace.clone()); + let r_borrow = self.fcx.infcx().next_region_var(coercion); + let autoref = Some(AutoPtr(r_borrow, mutbl_b, None)); - Ok(Some(AdjustDerefRef(AutoDerefRef { - autoderefs: 1, - autoref: Some(AutoPtr(r_borrow, mutbl_b, None)) - }))) + let r_borrow = self.tcx().mk_region(r_borrow); + let lvalue_pref = match mutbl_b { + ast::MutMutable => PreferMutLvalue, + ast::MutImmutable => NoPreference + }; + let mut first_error = None; + let (_, autoderefs, success) = autoderef(self.fcx, + expr_a.span, + a, + Some(expr_a), + UnresolvedTypeAction::Ignore, + lvalue_pref, + |inner_ty, autoderef| { + if autoderef == 0 { + // Don't let this pass, otherwise it would cause + // &T to autoref to &&T. + return None; + } + let ty = ty::mk_rptr(self.tcx(), r_borrow, + mt {ty: inner_ty, mutbl: mutbl_b}); + if let Err(err) = self.fcx.infcx().try(|_| self.subtype(ty, b)) { + if first_error.is_none() { + first_error = Some(err); + } + None + } else { + Some(()) + } + }); + + match success { + Some(_) => { + Ok(Some(AdjustDerefRef(AutoDerefRef { + autoderefs: autoderefs, + autoref: autoref + }))) + } + None => { + // Return original error as if overloaded deref was never + // attempted, to avoid irrelevant/confusing error messages. + Err(first_error.expect("coerce_borrowed_pointer failed with no error?")) + } + } } @@ -426,7 +466,7 @@ pub fn mk_assignty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, Coerce { fcx: fcx, trace: infer::TypeTrace::types(origin, false, a, b) - }.coerce(a, b) + }.coerce(expr, a, b) }) })); if let Some(adjustment) = adjustment { diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index 67b055ac946cc..56a32186c9eac 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -11,6 +11,7 @@ use super::probe; use check::{self, FnCtxt, NoPreference, PreferMutLvalue, callee, demand}; +use check::UnresolvedTypeAction; use middle::mem_categorization::Typer; use middle::subst::{self}; use middle::traits; @@ -141,10 +142,19 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { // Commit the autoderefs by calling `autoderef again, but this // time writing the results into the various tables. - let (autoderefd_ty, n, result) = - check::autoderef( - self.fcx, self.span, unadjusted_self_ty, Some(self.self_expr), NoPreference, - |_, n| if n == auto_deref_ref.autoderefs { Some(()) } else { None }); + let (autoderefd_ty, n, result) = check::autoderef(self.fcx, + self.span, + unadjusted_self_ty, + Some(self.self_expr), + UnresolvedTypeAction::Error, + NoPreference, + |_, n| { + if n == auto_deref_ref.autoderefs { + Some(()) + } else { + None + } + }); assert_eq!(n, auto_deref_ref.autoderefs); assert_eq!(result, Some(())); @@ -302,15 +312,18 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { // yield an object-type (e.g., `&Object` or `Box` // etc). - let (_, _, result) = - check::autoderef( - self.fcx, self.span, self_ty, None, NoPreference, - |ty, _| { - match ty.sty { - ty::ty_trait(ref data) => Some(closure(self, ty, &**data)), - _ => None, - } - }); + let (_, _, result) = check::autoderef(self.fcx, + self.span, + self_ty, + None, + UnresolvedTypeAction::Error, + NoPreference, + |ty, _| { + match ty.sty { + ty::ty_trait(ref data) => Some(closure(self, ty, &**data)), + _ => None, + } + }); match result { Some(r) => r, @@ -517,6 +530,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { expr.span, self.fcx.expr_ty(expr), Some(expr), + UnresolvedTypeAction::Error, PreferMutLvalue, |_, autoderefs| { if autoderefs == autoderef_count + 1 { diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 4980630a03593..2e366f4450744 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -14,7 +14,7 @@ use super::{CandidateSource,ImplSource,TraitSource}; use super::suggest; use check; -use check::{FnCtxt, NoPreference}; +use check::{FnCtxt, NoPreference, UnresolvedTypeAction}; use middle::fast_reject; use middle::subst; use middle::subst::Subst; @@ -169,16 +169,19 @@ fn create_steps<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, -> Option>> { let mut steps = Vec::new(); - let (fully_dereferenced_ty, dereferences, _) = - check::autoderef( - fcx, span, self_ty, None, NoPreference, - |t, d| { - let adjustment = AutoDeref(d); - steps.push(CandidateStep { self_ty: t, adjustment: adjustment }); - None::<()> // keep iterating until we can't anymore - }); - - match fully_dereferenced_ty.sty { + let (final_ty, dereferences, _) = check::autoderef(fcx, + span, + self_ty, + None, + UnresolvedTypeAction::Error, + NoPreference, + |t, d| { + let adjustment = AutoDeref(d); + steps.push(CandidateStep { self_ty: t, adjustment: adjustment }); + None::<()> // keep iterating until we can't anymore + }); + + match final_ty.sty { ty::ty_vec(elem_ty, Some(len)) => { steps.push(CandidateStep { self_ty: ty::mk_vec(fcx.tcx(), elem_ty, None), diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 8d1a43cb08194..1f04cab572a4b 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1865,6 +1865,17 @@ pub enum LvaluePreference { NoPreference } +/// Whether `autoderef` requires types to resolve. +#[derive(Copy, Show, PartialEq, Eq)] +pub enum UnresolvedTypeAction { + /// Produce an error and return `ty_err` whenever a type cannot + /// be resolved (i.e. it is `ty_infer`). + Error, + /// Go on without emitting any errors, and return the unresolved + /// type. Useful for probing, e.g. in coercions. + Ignore +} + /// Executes an autoderef loop for the type `t`. At each step, invokes `should_stop` to decide /// whether to terminate the loop. Returns the final type and number of derefs that it performed. /// @@ -1874,6 +1885,7 @@ pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>, sp: Span, base_ty: Ty<'tcx>, opt_expr: Option<&ast::Expr>, + unresolved_type_action: UnresolvedTypeAction, mut lvalue_pref: LvaluePreference, mut should_stop: F) -> (Ty<'tcx>, uint, Option) @@ -1886,11 +1898,22 @@ pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>, let mut t = base_ty; for autoderefs in 0..fcx.tcx().sess.recursion_limit.get() { - let resolved_t = structurally_resolved_type(fcx, sp, t); - - if ty::type_is_error(resolved_t) { - return (resolved_t, autoderefs, None); - } + let resolved_t = match unresolved_type_action { + UnresolvedTypeAction::Error => { + let resolved_t = structurally_resolved_type(fcx, sp, t); + if ty::type_is_error(resolved_t) { + return (resolved_t, autoderefs, None); + } + resolved_t + } + UnresolvedTypeAction::Ignore => { + // We can continue even when the type cannot be resolved + // (i.e. it is an inference variable) because `ty::deref` + // and `try_overloaded_deref` both simply return `None` + // in such a case without producing spurious errors. + fcx.resolve_type_vars_if_possible(t) + } + }; match should_stop(resolved_t, autoderefs) { Some(x) => return (resolved_t, autoderefs, Some(x)), @@ -2011,8 +2034,13 @@ fn autoderef_for_index<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>, // autoderef that normal method probing does. They could likely be // consolidated. - let (ty, autoderefs, final_mt) = - autoderef(fcx, base_expr.span, base_ty, Some(base_expr), lvalue_pref, |adj_ty, idx| { + let (ty, autoderefs, final_mt) = autoderef(fcx, + base_expr.span, + base_ty, + Some(base_expr), + UnresolvedTypeAction::Error, + lvalue_pref, + |adj_ty, idx| { let autoderefref = ty::AutoDerefRef { autoderefs: idx, autoref: None }; step(adj_ty, autoderefref) }); @@ -3053,8 +3081,13 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, let expr_t = structurally_resolved_type(fcx, expr.span, fcx.expr_ty(base)); // FIXME(eddyb) #12808 Integrate privacy into this auto-deref loop. - let (_, autoderefs, field_ty) = - autoderef(fcx, expr.span, expr_t, Some(base), lvalue_pref, |base_t, _| { + let (_, autoderefs, field_ty) = autoderef(fcx, + expr.span, + expr_t, + Some(base), + UnresolvedTypeAction::Error, + lvalue_pref, + |base_t, _| { match base_t.sty { ty::ty_struct(base_id, substs) => { debug!("struct named {}", ppaux::ty_to_string(tcx, base_t)); @@ -3146,8 +3179,13 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, fcx.expr_ty(base)); let mut tuple_like = false; // FIXME(eddyb) #12808 Integrate privacy into this auto-deref loop. - let (_, autoderefs, field_ty) = - autoderef(fcx, expr.span, expr_t, Some(base), lvalue_pref, |base_t, _| { + let (_, autoderefs, field_ty) = autoderef(fcx, + expr.span, + expr_t, + Some(base), + UnresolvedTypeAction::Error, + lvalue_pref, + |base_t, _| { match base_t.sty { ty::ty_struct(base_id, substs) => { tuple_like = ty::is_tuple_struct(tcx, base_id); diff --git a/src/test/compile-fail/coerce-overloaded-autoderef.rs b/src/test/compile-fail/coerce-overloaded-autoderef.rs new file mode 100644 index 0000000000000..14fbc34c43bb4 --- /dev/null +++ b/src/test/compile-fail/coerce-overloaded-autoderef.rs @@ -0,0 +1,40 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn borrow_mut(x: &mut T) -> &mut T { x } +fn borrow(x: &T) -> &T { x } + +fn borrow_mut2(_: &mut T, _: &mut T) {} +fn borrow2(_: &mut T, _: &T) {} + +fn double_mut_borrow(x: &mut Box) { + let y = borrow_mut(x); + let z = borrow_mut(x); + //~^ ERROR cannot borrow `*x` as mutable more than once at a time +} + +fn double_imm_borrow(x: &mut Box) { + let y = borrow(x); + let z = borrow(x); + **x += 1; + //~^ ERROR cannot assign to `**x` because it is borrowed +} + +fn double_mut_borrow2(x: &mut Box) { + borrow_mut2(x, x); + //~^ ERROR cannot borrow `*x` as mutable more than once at a time +} + +fn double_borrow2(x: &mut Box) { + borrow2(x, x); + //~^ ERROR cannot borrow `*x` as immutable because it is also borrowed as mutable +} + +pub fn main() {} diff --git a/src/test/compile-fail/method-self-arg-1.rs b/src/test/compile-fail/method-self-arg-1.rs index 4d416ed42debf..98b9e453889c1 100644 --- a/src/test/compile-fail/method-self-arg-1.rs +++ b/src/test/compile-fail/method-self-arg-1.rs @@ -23,11 +23,6 @@ fn main() { //~| found `Foo` //~| expected &-ptr //~| found struct `Foo` - Foo::bar(&&x); //~ ERROR mismatched types - //~| expected `&Foo` - //~| found `&&Foo` - //~| expected struct `Foo` - //~| found &-ptr Foo::bar(&42is); //~ ERROR mismatched types //~| expected `&Foo` //~| found `&isize` diff --git a/src/test/run-pass/coerce-overloaded-autoderef.rs b/src/test/run-pass/coerce-overloaded-autoderef.rs new file mode 100644 index 0000000000000..ae4db40411553 --- /dev/null +++ b/src/test/run-pass/coerce-overloaded-autoderef.rs @@ -0,0 +1,69 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::rc::Rc; + +// Examples from the "deref coercions" RFC, at rust-lang/rfcs#241. + +fn use_ref(_: &T) {} +fn use_mut(_: &mut T) {} + +fn use_rc(t: Rc) { + use_ref(&*t); // what you have to write today + use_ref(&t); // what you'd be able to write + use_ref(&&&&&&t); + use_ref(&mut &&&&&t); + use_ref(&&&mut &&&t); +} + +fn use_mut_box(mut t: &mut Box) { + use_mut(&mut *t); // what you have to write today + use_mut(t); // what you'd be able to write + use_mut(&mut &mut &mut t); + + use_ref(&*t); // what you have to write today + use_ref(t); // what you'd be able to write + use_ref(&&&&&&t); + use_ref(&mut &&&&&t); + use_ref(&&&mut &&&t); +} + +fn use_nested(t: &Box) { + use_ref(&**t); // what you have to write today + use_ref(t); // what you'd be able to write (note: recursive deref) + use_ref(&&&&&&t); + use_ref(&mut &&&&&t); + use_ref(&&&mut &&&t); +} + +fn use_slice(_: &[u8]) {} +fn use_slice_mut(_: &mut [u8]) {} + +fn use_vec(mut v: Vec) { + use_slice_mut(&mut v[]); // what you have to write today + use_slice_mut(&mut v); // what you'd be able to write + use_slice_mut(&mut &mut &mut v); + + use_slice(&v[]); // what you have to write today + use_slice(&v); // what you'd be able to write + use_slice(&&&&&&v); + use_slice(&mut &&&&&v); + use_slice(&&&mut &&&v); +} + +fn use_vec_ref(v: &Vec) { + use_slice(&v[]); // what you have to write today + use_slice(v); // what you'd be able to write + use_slice(&&&&&&v); + use_slice(&mut &&&&&v); + use_slice(&&&mut &&&v); +} + +pub fn main() {}