From b8dad48435ebf8d85bffe4d65a247b96854b14ef Mon Sep 17 00:00:00 2001 From: Andrew Paseltiner Date: Thu, 3 Sep 2015 13:10:34 -0400 Subject: [PATCH] Fix multiple mutable autoderefs with `Box` Closes #26205. --- src/librustc/middle/check_match.rs | 2 +- src/librustc/middle/mem_categorization.rs | 6 ++-- src/librustc/middle/ty.rs | 25 +++++++++++-- src/librustc_trans/trans/consts.rs | 9 ++--- src/librustc_trans/trans/expr.rs | 5 +-- src/librustc_typeck/check/_match.rs | 10 +++--- src/librustc_typeck/check/callee.rs | 3 +- src/librustc_typeck/check/coercion.rs | 4 +-- src/librustc_typeck/check/method/confirm.rs | 6 ++-- src/librustc_typeck/check/method/probe.rs | 4 +-- src/librustc_typeck/check/method/suggest.rs | 2 +- src/librustc_typeck/check/mod.rs | 23 +++--------- src/librustc_typeck/check/op.rs | 3 +- src/librustc_typeck/check/regionck.rs | 2 +- src/test/run-pass/issue-26205.rs | 39 +++++++++++++++++++++ 15 files changed, 93 insertions(+), 50 deletions(-) create mode 100644 src/test/run-pass/issue-26205.rs diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index d0111860b44..40025aa0b7b 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -664,7 +664,7 @@ fn is_useful(cx: &MatchCheckCtxt, match real_pat.node { hir::PatIdent(hir::BindByRef(..), _, _) => { - left_ty.builtin_deref(false).unwrap().ty + left_ty.builtin_deref(false, NoPreference).unwrap().ty } _ => left_ty, } diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index a8c80d52209..ba7c675817f 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -400,7 +400,7 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> { // a bind-by-ref means that the base_ty will be the type of the ident itself, // but what we want here is the type of the underlying value being borrowed. // So peel off one-level, turning the &T into T. - match base_ty.builtin_deref(false) { + match base_ty.builtin_deref(false, ty::NoPreference) { Some(t) => t.ty, None => { return Err(()); } } @@ -897,7 +897,7 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> { None => base_cmt }; let base_cmt_ty = base_cmt.ty; - match base_cmt_ty.builtin_deref(true) { + match base_cmt_ty.builtin_deref(true, ty::NoPreference) { Some(mt) => { let ret = self.cat_deref_common(node, base_cmt, deref_cnt, mt.ty, @@ -1044,7 +1044,7 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> { span:elt.span(), cat:cat_deref(base_cmt.clone(), 0, ptr), mutbl:m, - ty: match base_cmt.ty.builtin_deref(false) { + ty: match base_cmt.ty.builtin_deref(false, ty::NoPreference) { Some(mt) => mt.ty, None => self.tcx().sess.bug("Found non-derefable type") }, diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 541bc8896aa..2e17a063775 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -29,6 +29,7 @@ pub use self::BoundRegion::*; pub use self::TypeVariants::*; pub use self::IntVarValue::*; pub use self::CopyImplementationError::*; +pub use self::LvaluePreference::*; pub use self::BuiltinBound::Send as BoundSend; pub use self::BuiltinBound::Sized as BoundSized; @@ -4828,6 +4829,21 @@ impl<'tcx> TyS<'tcx> { } } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum LvaluePreference { + PreferMutLvalue, + NoPreference +} + +impl LvaluePreference { + pub fn from_mutbl(m: hir::Mutability) -> Self { + match m { + hir::MutMutable => PreferMutLvalue, + hir::MutImmutable => NoPreference, + } + } +} + /// Describes whether a type is representable. For types that are not /// representable, 'SelfRecursive' and 'ContainsRecursive' are used to /// distinguish between types that are recursive with themselves and types that @@ -5073,12 +5089,15 @@ impl<'tcx> TyS<'tcx> { // // The parameter `explicit` indicates if this is an *explicit* dereference. // Some types---notably unsafe ptrs---can only be dereferenced explicitly. - pub fn builtin_deref(&self, explicit: bool) -> Option> { + pub fn builtin_deref(&self, explicit: bool, pref: LvaluePreference) + -> Option> + { match self.sty { TyBox(ty) => { Some(TypeAndMut { ty: ty, - mutbl: hir::MutImmutable, + mutbl: + if pref == PreferMutLvalue { hir::MutMutable } else { hir::MutImmutable }, }) }, TyRef(_, mt) => Some(mt), @@ -5183,7 +5202,7 @@ impl<'tcx> TyS<'tcx> { } None => {} } - match adjusted_ty.builtin_deref(true) { + match adjusted_ty.builtin_deref(true, NoPreference) { Some(mt) => { adjusted_ty = mt.ty; } None => { cx.sess.span_bug( diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index b707c48adc8..c5b6bf73b53 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -155,7 +155,7 @@ fn const_deref<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, v: ValueRef, ty: Ty<'tcx>) -> (ValueRef, Ty<'tcx>) { - match ty.builtin_deref(true) { + match ty.builtin_deref(true, ty::NoPreference) { Some(mt) => { if type_is_sized(cx.tcx(), mt.ty) { (const_deref_ptr(cx, v), mt.ty) @@ -329,7 +329,7 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, param_substs, &target); - let pointee_ty = ty.builtin_deref(true) + let pointee_ty = ty.builtin_deref(true, ty::NoPreference) .expect("consts: unsizing got non-pointer type").ty; let (base, old_info) = if !type_is_sized(cx.tcx(), pointee_ty) { // Normally, the source is a thin pointer and we are @@ -344,7 +344,7 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, (llconst, None) }; - let unsized_ty = target.builtin_deref(true) + let unsized_ty = target.builtin_deref(true, ty::NoPreference) .expect("consts: unsizing got non-pointer target type").ty; let ptr_ty = type_of::in_memory_type_of(cx, unsized_ty).ptr_to(); let base = ptrcast(base, ptr_ty); @@ -642,7 +642,8 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, } if type_is_fat_ptr(cx.tcx(), t_expr) { // Fat pointer casts. - let t_cast_inner = t_cast.builtin_deref(true).expect("cast to non-pointer").ty; + let t_cast_inner = + t_cast.builtin_deref(true, ty::NoPreference).expect("cast to non-pointer").ty; let ptr_ty = type_of::in_memory_type_of(cx, t_cast_inner).ptr_to(); let addr = ptrcast(const_get_elt(cx, v, &[abi::FAT_PTR_ADDR as u32]), ptr_ty); diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index 730ab22e1e0..9e33dd0f109 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -780,7 +780,7 @@ fn trans_index<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let ref_ty = // invoked methods have LB regions instantiated: bcx.tcx().no_late_bound_regions(&method_ty.fn_ret()).unwrap().unwrap(); - let elt_ty = match ref_ty.builtin_deref(true) { + let elt_ty = match ref_ty.builtin_deref(true, ty::NoPreference) { None => { bcx.tcx().sess.span_bug(index_expr.span, "index method didn't return a \ @@ -1971,7 +1971,8 @@ pub fn cast_is_noop<'tcx>(tcx: &ty::ctxt<'tcx>, return true; } - match (t_in.builtin_deref(true), t_out.builtin_deref(true)) { + match (t_in.builtin_deref(true, ty::NoPreference), + t_out.builtin_deref(true, ty::NoPreference)) { (Some(ty::TypeAndMut{ ty: t_in, .. }), Some(ty::TypeAndMut{ ty: t_out, .. })) => { t_in == t_out } diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index a17d97a824a..92972c45ede 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -15,10 +15,10 @@ use middle::pat_util::{PatIdMap, pat_id_map, pat_is_binding}; use middle::pat_util::pat_is_resolved_const; use middle::privacy::{AllPublic, LastMod}; use middle::subst::Substs; -use middle::ty::{self, Ty, HasTypeFlags}; +use middle::ty::{self, Ty, HasTypeFlags, LvaluePreference}; use check::{check_expr, check_expr_has_type, check_expr_with_expectation}; use check::{check_expr_coercable_to_type, demand, FnCtxt, Expectation}; -use check::{check_expr_with_lvalue_pref, LvaluePreference}; +use check::{check_expr_with_lvalue_pref}; use check::{instantiate_path, resolve_ty_and_def_ufcs, structurally_resolved_type}; use require_same_types; use util::nodemap::FnvHashMap; @@ -292,7 +292,7 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, let region = fcx.infcx().next_region_var(infer::PatternRegion(pat.span)); tcx.mk_ref(tcx.mk_region(region), ty::TypeAndMut { ty: tcx.mk_slice(inner_ty), - mutbl: expected_ty.builtin_deref(true).map(|mt| mt.mutbl) + mutbl: expected_ty.builtin_deref(true, ty::NoPreference).map(|mt| mt.mutbl) .unwrap_or(hir::MutImmutable) }) } @@ -310,7 +310,7 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, } if let Some(ref slice) = *slice { let region = fcx.infcx().next_region_var(infer::PatternRegion(pat.span)); - let mutbl = expected_ty.builtin_deref(true) + let mutbl = expected_ty.builtin_deref(true, ty::NoPreference) .map_or(hir::MutImmutable, |mt| mt.mutbl); let slice_ty = tcx.mk_ref(tcx.mk_region(region), ty::TypeAndMut { @@ -399,7 +399,7 @@ pub fn check_dereferencable<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, let tcx = pcx.fcx.ccx.tcx; if pat_is_binding(&tcx.def_map, inner) { let expected = fcx.infcx().shallow_resolve(expected); - expected.builtin_deref(true).map_or(true, |mt| match mt.ty.sty { + expected.builtin_deref(true, ty::NoPreference).map_or(true, |mt| match mt.ty.sty { ty::TyTrait(_) => { // This is "x = SomeTrait" being reduced from // "let &x = &SomeTrait" or "let box x = Box", an error. diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index 65366fb9176..948b7dd1561 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -18,7 +18,6 @@ use super::err_args; use super::Expectation; use super::expected_types_for_fn_args; use super::FnCtxt; -use super::LvaluePreference; use super::method; use super::structurally_resolved_type; use super::TupleArgumentsFlag; @@ -28,7 +27,7 @@ use super::write_call; use CrateCtxt; use middle::def_id::{DefId, LOCAL_CRATE}; use middle::infer; -use middle::ty::{self, Ty}; +use middle::ty::{self, LvaluePreference, Ty}; use syntax::codemap::Span; use syntax::parse::token; use syntax::ptr::P; diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index d6140fbe954..65409afa52d 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -60,13 +60,13 @@ //! 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::{autoderef, FnCtxt, LvaluePreference, UnresolvedTypeAction}; +use check::{autoderef, FnCtxt, UnresolvedTypeAction}; use middle::infer::{self, Coercion}; use middle::traits::{self, ObligationCause}; use middle::traits::{predicate_for_trait_def, report_selection_error}; use middle::ty::{AutoDerefRef, AdjustDerefRef}; -use middle::ty::{self, TypeAndMut, Ty, TypeError}; +use middle::ty::{self, LvaluePreference, TypeAndMut, Ty, TypeError}; use middle::ty_relate::RelateResult; use util::common::indent; diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index 217c9ebacb3..040e644a94d 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -10,12 +10,12 @@ use super::probe; -use check::{self, FnCtxt, NoPreference, PreferMutLvalue, callee, demand}; +use check::{self, FnCtxt, callee, demand}; use check::UnresolvedTypeAction; use middle::def_id::DefId; use middle::subst::{self}; use middle::traits; -use middle::ty::{self, Ty}; +use middle::ty::{self, NoPreference, PreferMutLvalue, Ty}; use middle::ty_fold::TypeFoldable; use middle::infer; use middle::infer::InferCtxt; @@ -534,7 +534,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { } Some(ty::AutoPtr(_, _)) => { (adr.autoderefs, adr.unsize.map(|target| { - target.builtin_deref(false) + target.builtin_deref(false, NoPreference) .expect("fixup: AutoPtr is not &T").ty })) } diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 5adc71b229f..b04899f5ecd 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -14,13 +14,13 @@ use super::{CandidateSource, ImplSource, TraitSource}; use super::suggest; use check; -use check::{FnCtxt, NoPreference, UnresolvedTypeAction}; +use check::{FnCtxt, UnresolvedTypeAction}; use middle::def_id::DefId; use middle::fast_reject; use middle::subst; use middle::subst::Subst; use middle::traits; -use middle::ty::{self, RegionEscape, Ty, ToPolyTraitRef, TraitRef}; +use middle::ty::{self, NoPreference, RegionEscape, Ty, ToPolyTraitRef, TraitRef}; use middle::ty::HasTypeFlags; use middle::ty_fold::TypeFoldable; use middle::infer; diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index d9f691d065b..6961f3444d9 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -324,7 +324,7 @@ fn type_derefs_to_local<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, } check::autoderef(fcx, span, rcvr_ty, None, - check::UnresolvedTypeAction::Ignore, check::NoPreference, + check::UnresolvedTypeAction::Ignore, ty::NoPreference, |ty, _| { if is_local(ty) { Some(()) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index c838243a2ba..edd4f785821 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -76,7 +76,6 @@ type parameter). */ -pub use self::LvaluePreference::*; pub use self::Expectation::*; pub use self::compare_method::{compare_impl_method, compare_const_impl}; use self::TupleArgumentsFlag::*; @@ -95,6 +94,7 @@ use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace, TypeSpace use middle::traits::{self, report_fulfillment_errors}; use middle::ty::{FnSig, GenericPredicates, TypeScheme}; use middle::ty::{Disr, ParamTy, ParameterEnvironment}; +use middle::ty::{LvaluePreference, NoPreference, PreferMutLvalue}; use middle::ty::{self, HasTypeFlags, RegionEscape, ToPolyTraitRef, Ty}; use middle::ty::{MethodCall, MethodCallee}; use middle::ty_fold::{TypeFolder, TypeFoldable}; @@ -2086,21 +2086,6 @@ impl<'a, 'tcx> RegionScope for FnCtxt<'a, 'tcx> { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum LvaluePreference { - PreferMutLvalue, - NoPreference -} - -impl LvaluePreference { - pub fn from_mutbl(m: hir::Mutability) -> Self { - match m { - hir::MutMutable => PreferMutLvalue, - hir::MutImmutable => NoPreference, - } - } -} - /// Whether `autoderef` requires types to resolve. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum UnresolvedTypeAction { @@ -2156,7 +2141,7 @@ pub fn autoderef<'a, 'tcx, T, F>(fcx: &FnCtxt<'a, 'tcx>, } // Otherwise, deref if type is derefable: - let mt = match resolved_t.builtin_deref(false) { + let mt = match resolved_t.builtin_deref(false, lvalue_pref) { Some(mt) => Some(mt), None => { let method_call = @@ -2245,7 +2230,7 @@ fn make_overloaded_lvalue_return_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, } // method returns &T, but the type as visible to user is T, so deref - ret_ty.builtin_deref(true) + ret_ty.builtin_deref(true, NoPreference) } None => None, } @@ -3293,7 +3278,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, } hir::UnDeref => { oprnd_t = structurally_resolved_type(fcx, expr.span, oprnd_t); - oprnd_t = match oprnd_t.builtin_deref(true) { + oprnd_t = match oprnd_t.builtin_deref(true, NoPreference) { Some(mt) => mt.ty, None => match try_overloaded_deref(fcx, expr.span, Some(MethodCall::expr(expr.id)), diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index bdca8ae444b..07754e8506b 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -17,12 +17,11 @@ use super::{ demand, method, FnCtxt, - PreferMutLvalue, structurally_resolved_type, }; use middle::def_id::DefId; use middle::traits; -use middle::ty::{Ty, HasTypeFlags}; +use middle::ty::{Ty, HasTypeFlags, PreferMutLvalue}; use syntax::ast; use syntax::parse::token; use rustc_front::hir; diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index ab67dfebdac..844e143b9fa 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -1031,7 +1031,7 @@ fn constrain_autoderefs<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>, r_deref_expr, *r_ptr); } - match derefd_ty.builtin_deref(true) { + match derefd_ty.builtin_deref(true, ty::NoPreference) { Some(mt) => derefd_ty = mt.ty, /* if this type can't be dereferenced, then there's already an error in the session saying so. Just bail out for now */ diff --git a/src/test/run-pass/issue-26205.rs b/src/test/run-pass/issue-26205.rs new file mode 100644 index 00000000000..dd34612af0f --- /dev/null +++ b/src/test/run-pass/issue-26205.rs @@ -0,0 +1,39 @@ +// Copyright 2015 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::ops::{Deref, DerefMut}; + +struct Foo; + +impl Foo { + fn foo_mut(&mut self) {} +} + +struct Bar(Foo); + +impl Deref for Bar { + type Target = Foo; + + fn deref(&self) -> &Foo { + &self.0 + } +} + +impl DerefMut for Bar { + fn deref_mut(&mut self) -> &mut Foo { + &mut self.0 + } +} + +fn test(mut bar: Box) { + bar.foo_mut(); +} + +fn main() {}