Skip to content

Fix multiple mutable autoderefs with Box #28201

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 4, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/librustc/middle/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down
6 changes: 3 additions & 3 deletions src/librustc/middle/mem_categorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(()); }
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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")
},
Expand Down
25 changes: 22 additions & 3 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<TypeAndMut<'tcx>> {
pub fn builtin_deref(&self, explicit: bool, pref: LvaluePreference)
-> Option<TypeAndMut<'tcx>>
{
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),
Expand Down Expand Up @@ -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(
Expand Down
9 changes: 5 additions & 4 deletions src/librustc_trans/trans/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
5 changes: 3 additions & 2 deletions src/librustc_trans/trans/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down Expand Up @@ -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
}
Expand Down
10 changes: 5 additions & 5 deletions src/librustc_typeck/check/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
})
}
Expand All @@ -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 {
Expand Down Expand Up @@ -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<SomeTrait>", an error.
Expand Down
3 changes: 1 addition & 2 deletions src/librustc_typeck/check/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_typeck/check/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
6 changes: 3 additions & 3 deletions src/librustc_typeck/check/method/confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
}))
}
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_typeck/check/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_typeck/check/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
Expand Down
23 changes: 4 additions & 19 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand All @@ -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};
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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,
}
Expand Down Expand Up @@ -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)),
Expand Down
3 changes: 1 addition & 2 deletions src/librustc_typeck/check/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_typeck/check/regionck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
39 changes: 39 additions & 0 deletions src/test/run-pass/issue-26205.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

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>) {
bar.foo_mut();
}

fn main() {}