From 9428a3cea6adfebb78993c8bf563195f726ac475 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 5 Apr 2018 17:16:07 -0400 Subject: [PATCH] make mem-categorization use adjusted type for patterns Fixes #49631 --- src/librustc/middle/mem_categorization.rs | 37 +++++++++++++++++-- .../borrowck-issue-49631.rs | 34 +++++++++++++++++ .../borrowck-issue-49631.stderr | 13 +++++++ 3 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 src/test/ui/rfc-2005-default-binding-mode/borrowck-issue-49631.rs create mode 100644 src/test/ui/rfc-2005-default-binding-mode/borrowck-issue-49631.stderr diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 6bf0c5d1ba3..5875e5e4097 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -503,8 +503,37 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { self.resolve_type_vars_or_error(expr.hir_id, self.tables.expr_ty_adjusted_opt(expr)) } + /// Returns the type of value that this pattern matches against. + /// Some non-obvious cases: + /// + /// - a `ref x` binding matches against a value of type `T` and gives + /// `x` the type `&T`; we return `T`. + /// - a pattern with implicit derefs (thanks to default binding + /// modes #42640) may look like `Some(x)` but in fact have + /// implicit deref patterns attached (e.g., it is really + /// `&Some(x)`). In that case, we return the "outermost" type + /// (e.g., `&Option). fn pat_ty(&self, pat: &hir::Pat) -> McResult> { + // Check for implicit `&` types wrapping the pattern; note + // that these are never attached to binding patterns, so + // actually this is somewhat "disjoint" from the code below + // that aims to account for `ref x`. + if let Some(vec) = self.tables.pat_adjustments().get(pat.hir_id) { + if let Some(first_ty) = vec.first() { + debug!("pat_ty(pat={:?}) found adjusted ty `{:?}`", pat, first_ty); + return Ok(first_ty); + } + } + + self.pat_ty_unadjusted(pat) + } + + + /// Like `pat_ty`, but ignores implicit `&` patterns. + fn pat_ty_unadjusted(&self, pat: &hir::Pat) -> McResult> { let base_ty = self.node_ty(pat.hir_id)?; + debug!("pat_ty(pat={:?}) base_ty={:?}", pat, base_ty); + // This code detects whether we are looking at a `ref x`, // and if so, figures out what the type *being borrowed* is. let ret_ty = match pat.node { @@ -531,8 +560,8 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { } _ => base_ty, }; - debug!("pat_ty(pat={:?}) base_ty={:?} ret_ty={:?}", - pat, base_ty, ret_ty); + debug!("pat_ty(pat={:?}) ret_ty={:?}", pat, ret_ty); + Ok(ret_ty) } @@ -1246,7 +1275,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { self.tcx.adt_def(enum_def).variant_with_id(def_id).fields.len()) } Def::StructCtor(_, CtorKind::Fn) => { - match self.pat_ty(&pat)?.sty { + match self.pat_ty_unadjusted(&pat)?.sty { ty::TyAdt(adt_def, _) => { (cmt, adt_def.non_enum_variant().fields.len()) } @@ -1297,7 +1326,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { PatKind::Tuple(ref subpats, ddpos) => { // (p1, ..., pN) - let expected_len = match self.pat_ty(&pat)?.sty { + let expected_len = match self.pat_ty_unadjusted(&pat)?.sty { ty::TyTuple(ref tys) => tys.len(), ref ty => span_bug!(pat.span, "tuple pattern unexpected type {:?}", ty), }; diff --git a/src/test/ui/rfc-2005-default-binding-mode/borrowck-issue-49631.rs b/src/test/ui/rfc-2005-default-binding-mode/borrowck-issue-49631.rs new file mode 100644 index 00000000000..8dc1627dc8b --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/borrowck-issue-49631.rs @@ -0,0 +1,34 @@ +// Copyright 2017 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. + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct Foo { +} + +impl Foo { + fn get(&self) -> Option<&Result> { + None + } + + fn mutate(&mut self) { } +} + +fn main() { + let mut foo = Foo { }; + + // foo.get() returns type Option<&Result>, so + // using `string` keeps borrow of `foo` alive. Hence calling + // `foo.mutate()` should be an error. + while let Some(Ok(string)) = foo.get() { + foo.mutate(); + //~^ ERROR cannot borrow `foo` as mutable + println!("foo={:?}", *string); + } +} diff --git a/src/test/ui/rfc-2005-default-binding-mode/borrowck-issue-49631.stderr b/src/test/ui/rfc-2005-default-binding-mode/borrowck-issue-49631.stderr new file mode 100644 index 00000000000..2da5ac8d240 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/borrowck-issue-49631.stderr @@ -0,0 +1,13 @@ +error[E0502]: cannot borrow `foo` as mutable because it is also borrowed as immutable + --> $DIR/borrowck-issue-49631.rs:30:9 + | +LL | while let Some(Ok(string)) = foo.get() { + | --- - immutable borrow ends here + | | + | immutable borrow occurs here +LL | foo.mutate(); + | ^^^ mutable borrow occurs here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0502`.