diff --git a/src/doc/unstable-book/src/language-features/match_default_bindings.md b/src/doc/unstable-book/src/language-features/match_default_bindings.md new file mode 100644 index 0000000000000..cc542931cbe1f --- /dev/null +++ b/src/doc/unstable-book/src/language-features/match_default_bindings.md @@ -0,0 +1,58 @@ +# `match_default_bindings` + +The tracking issue for this feature is: [#42640] + +[#42640]: https://github.com/rust-lang/rust/issues/42640 + +------------------------ + +Match default bindings (also called "default binding modes in match") improves ergonomics for +pattern-matching on references by introducing automatic dereferencing (and a corresponding shift +in binding modes) for large classes of patterns that would otherwise not compile. + +For example, under match default bindings, + +```rust +#![feature(match_default_bindings)] + +fn main() { + let x: &Option<_> = &Some(0); + + match x { + Some(y) => { + println!("y={}", *y); + }, + None => {}, + } +} +``` + +compiles and is equivalent to either of the below: + +```rust +fn main() { + let x: &Option<_> = &Some(0); + + match *x { + Some(ref y) => { + println!("y={}", *y); + }, + None => {}, + } +} +``` + +or + +```rust +fn main() { + let x: &Option<_> = &Some(0); + + match x { + &Some(ref y) => { + println!("y={}", *y); + }, + &None => {}, + } +} +``` diff --git a/src/librustc/hir/pat_util.rs b/src/librustc/hir/pat_util.rs index 144cb34ee356e..2bec224362ea6 100644 --- a/src/librustc/hir/pat_util.rs +++ b/src/librustc/hir/pat_util.rs @@ -160,11 +160,13 @@ impl hir::Pat { variants } - /// Checks if the pattern contains any `ref` or `ref mut` bindings, - /// and if yes whether it contains mutable or just immutables ones. + /// Checks if the pattern contains any `ref` or `ref mut` bindings, and if + /// yes whether it contains mutable or just immutables ones. /// - /// FIXME(tschottdorf): this is problematic as the HIR is being scraped, - /// but ref bindings may be implicit after #42640. + /// FIXME(tschottdorf): this is problematic as the HIR is being scraped, but + /// ref bindings are be implicit after #42640 (default match binding modes). + /// + /// See #44848. pub fn contains_explicit_ref_binding(&self) -> Option { let mut result = None; self.each_binding(|annotation, _, _, _| { @@ -188,7 +190,9 @@ impl hir::Arm { /// bindings, and if yes whether its containing mutable ones or just immutables ones. pub fn contains_explicit_ref_binding(&self) -> Option { // FIXME(tschottdorf): contains_explicit_ref_binding() must be removed - // for #42640. + // for #42640 (default match binding modes). + // + // See #44848. self.pats.iter() .filter_map(|pat| pat.contains_explicit_ref_binding()) .max_by_key(|m| match *m { diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 5102b41598d6f..c973881c980ee 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -1094,7 +1094,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { } // FIXME(#19596) This is a workaround, but there should be a better way to do this - fn cat_pattern_(&self, cmt: cmt<'tcx>, pat: &hir::Pat, op: &mut F) -> McResult<()> + fn cat_pattern_(&self, mut cmt: cmt<'tcx>, pat: &hir::Pat, op: &mut F) -> McResult<()> where F : FnMut(cmt<'tcx>, &hir::Pat) { // Here, `cmt` is the categorization for the value being @@ -1144,6 +1144,56 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { debug!("cat_pattern: {:?} cmt={:?}", pat, cmt); + // If (pattern) adjustments are active for this pattern, adjust the `cmt` correspondingly. + // `cmt`s are constructed differently from patterns. For example, in + // + // ``` + // match foo { + // &&Some(x, ) => { ... }, + // _ => { ... }, + // } + // ``` + // + // the pattern `&&Some(x,)` is represented as `Ref { Ref { TupleStruct }}`. To build the + // corresponding `cmt` we start with a `cmt` for `foo`, and then, by traversing the + // pattern, try to answer the question: given the address of `foo`, how is `x` reached? + // + // `&&Some(x,)` `cmt_foo` + // `&Some(x,)` `deref { cmt_foo}` + // `Some(x,)` `deref { deref { cmt_foo }}` + // (x,)` `field0 { deref { deref { cmt_foo }}}` <- resulting cmt + // + // The above example has no adjustments. If the code were instead the (after adjustments, + // equivalent) version + // + // ``` + // match foo { + // Some(x, ) => { ... }, + // _ => { ... }, + // } + // ``` + // + // Then we see that to get the same result, we must start with `deref { deref { cmt_foo }}` + // instead of `cmt_foo` since the pattern is now `Some(x,)` and not `&&Some(x,)`, even + // though its assigned type is that of `&&Some(x,)`. + for _ in 0..self.tables + .pat_adjustments() + .get(pat.hir_id) + .map(|v| v.len()) + .unwrap_or(0) { + cmt = self.cat_deref(pat, cmt, true /* implicit */)?; + } + let cmt = cmt; // lose mutability + + // Invoke the callback, but only now, after the `cmt` has adjusted. + // + // To see that this makes sense, consider `match &Some(3) { Some(x) => { ... }}`. In that + // case, the initial `cmt` will be that for `&Some(3)` and the pattern is `Some(x)`. We + // don't want to call `op` with these incompatible values. As written, what happens instead + // is that `op` is called with the adjusted cmt (that for `*&Some(3)`) and the pattern + // `Some(x)` (which matches). Recursing once more, `*&Some(3)` and the pattern `Some(x)` + // result in the cmt `Downcast(*&Some(3)).0` associated to `x` and invoke `op` with + // that (where the `ref` on `x` is implied). op(cmt.clone(), pat); match pat.node { diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 315ba622ccf6d..15e10f8c5e8d5 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -337,9 +337,24 @@ pub struct TypeckTables<'tcx> { adjustments: ItemLocalMap>>, - // Stores the actual binding mode for all instances of hir::BindingAnnotation. + /// Stores the actual binding mode for all instances of hir::BindingAnnotation. pat_binding_modes: ItemLocalMap, + /// Stores the types which were implicitly dereferenced in pattern binding modes + /// for later usage in HAIR lowering. For example, + /// + /// ``` + /// match &&Some(5i32) { + /// Some(n) => {}, + /// _ => {}, + /// } + /// ``` + /// leads to a `vec![&&Option, &Option]`. Empty vectors are not stored. + /// + /// See: + /// https://github.com/rust-lang/rfcs/blob/master/text/2005-match-ergonomics.md#definitions + pat_adjustments: ItemLocalMap>>, + /// Borrows pub upvar_capture_map: ty::UpvarCaptureMap<'tcx>, @@ -394,6 +409,7 @@ impl<'tcx> TypeckTables<'tcx> { node_substs: ItemLocalMap(), adjustments: ItemLocalMap(), pat_binding_modes: ItemLocalMap(), + pat_adjustments: ItemLocalMap(), upvar_capture_map: FxHashMap(), generator_sigs: ItemLocalMap(), generator_interiors: ItemLocalMap(), @@ -574,6 +590,21 @@ impl<'tcx> TypeckTables<'tcx> { } } + pub fn pat_adjustments(&self) -> LocalTableInContext>> { + LocalTableInContext { + local_id_root: self.local_id_root, + data: &self.pat_adjustments, + } + } + + pub fn pat_adjustments_mut(&mut self) + -> LocalTableInContextMut>> { + LocalTableInContextMut { + local_id_root: self.local_id_root, + data: &mut self.pat_adjustments, + } + } + pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> ty::UpvarCapture<'tcx> { self.upvar_capture_map[&upvar_id] } @@ -699,6 +730,7 @@ impl<'gcx> HashStable> for TypeckTables<'gcx> { ref node_substs, ref adjustments, ref pat_binding_modes, + ref pat_adjustments, ref upvar_capture_map, ref closure_tys, ref closure_kinds, @@ -720,6 +752,7 @@ impl<'gcx> HashStable> for TypeckTables<'gcx> { node_substs.hash_stable(hcx, hasher); adjustments.hash_stable(hcx, hasher); pat_binding_modes.hash_stable(hcx, hasher); + pat_adjustments.hash_stable(hcx, hasher); hash_stable_hashmap(hcx, hasher, upvar_capture_map, |up_var_id, hcx| { let ty::UpvarId { var_id, diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_const_eval/pattern.rs index 7586ad5a75f81..d7a16e9d2fc75 100644 --- a/src/librustc_const_eval/pattern.rs +++ b/src/librustc_const_eval/pattern.rs @@ -301,6 +301,44 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> { } pub fn lower_pattern(&mut self, pat: &'tcx hir::Pat) -> Pattern<'tcx> { + // When implicit dereferences have been inserted in this pattern, the unadjusted lowered + // pattern has the type that results *after* dereferencing. For example, in this code: + // + // ``` + // match &&Some(0i32) { + // Some(n) => { ... }, + // _ => { ... }, + // } + // ``` + // + // the type assigned to `Some(n)` in `unadjusted_pat` would be `Option` (this is + // determined in rustc_typeck::check::match). The adjustments would be + // + // `vec![&&Option, &Option]`. + // + // Applying the adjustments, we want to instead output `&&Some(n)` (as a HAIR pattern). So + // we wrap the unadjusted pattern in `PatternKind::Deref` repeatedly, consuming the + // adjustments in *reverse order* (last-in-first-out, so that the last `Deref` inserted + // gets the least-dereferenced type). + let unadjusted_pat = self.lower_pattern_unadjusted(pat); + self.tables + .pat_adjustments() + .get(pat.hir_id) + .unwrap_or(&vec![]) + .iter() + .rev() + .fold(unadjusted_pat, |pat, ref_ty| { + debug!("{:?}: wrapping pattern with type {:?}", pat, ref_ty); + Pattern { + span: pat.span, + ty: ref_ty, + kind: Box::new(PatternKind::Deref { subpattern: pat }), + } + }, + ) + } + + fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat) -> Pattern<'tcx> { let mut ty = self.tables.node_id_to_type(pat.hir_id); let kind = match pat.node { diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index d942b2d123070..ab8994bcae253 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -27,25 +27,111 @@ use syntax::ptr::P; use syntax_pos::Span; impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { - pub fn check_pat(&self, pat: &'gcx hir::Pat, expected: Ty<'tcx>) { - self.check_pat_arg(pat, expected, false); - } - /// The `is_arg` argument indicates whether this pattern is the /// *outermost* pattern in an argument (e.g., in `fn foo(&x: /// &u32)`, it is true for the `&x` pattern but not `x`). This is /// used to tailor error reporting. - pub fn check_pat_arg(&self, pat: &'gcx hir::Pat, expected: Ty<'tcx>, is_arg: bool) { + pub fn check_pat_walk( + &self, + pat: &'gcx hir::Pat, + mut expected: Ty<'tcx>, + mut def_bm: ty::BindingMode, + is_arg: bool) + { let tcx = self.tcx; - debug!("check_pat(pat={:?},expected={:?},is_arg={})", pat, expected, is_arg); + debug!("check_pat_walk(pat={:?},expected={:?},def_bm={:?},is_arg={})", + pat, expected, def_bm, is_arg); + + let is_non_ref_pat = match pat.node { + PatKind::Struct(..) | + PatKind::TupleStruct(..) | + PatKind::Tuple(..) | + PatKind::Box(_) | + PatKind::Range(..) | + PatKind::Slice(..) => true, + PatKind::Lit(ref lt) => { + let ty = self.check_expr(lt); + match ty.sty { + ty::TypeVariants::TyRef(..) => false, + _ => true, + } + } + PatKind::Path(ref qpath) => { + let (def, _, _) = self.resolve_ty_and_def_ufcs(qpath, pat.id, pat.span); + match def { + Def::Const(..) | Def::AssociatedConst(..) => false, + _ => true, + } + } + PatKind::Wild | + PatKind::Binding(..) | + PatKind::Ref(..) => false, + }; + if is_non_ref_pat && tcx.sess.features.borrow().match_default_bindings { + debug!("pattern is non reference pattern"); + let mut exp_ty = self.resolve_type_vars_with_obligations(&expected); + + // Peel off as many `&` or `&mut` from the discriminant as possible. For example, + // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches + // the `Some(5)` which is not of type TyRef. + // + // For each ampersand peeled off, update the binding mode and push the original + // type into the adjustments vector. + // + // See the examples in `run-pass/match-defbm*.rs`. + let mut pat_adjustments = vec![]; + expected = loop { + debug!("inspecting {:?} with type {:?}", exp_ty, exp_ty.sty); + match exp_ty.sty { + ty::TypeVariants::TyRef(_, ty::TypeAndMut{ + ty: inner_ty, mutbl: inner_mutability, + }) => { + debug!("current discriminant is TyRef, inserting implicit deref"); + // Preserve the reference type. We'll need it later during HAIR lowering. + pat_adjustments.push(exp_ty); + + exp_ty = inner_ty; + def_bm = match def_bm { + // If default binding mode is by value, make it `ref` or `ref mut` + // (depending on whether we observe `&` or `&mut`). + ty::BindByValue(_) => + ty::BindByReference(inner_mutability), + + // Once a `ref`, always a `ref`. This is because a `& &mut` can't mutate + // the underlying value. + ty::BindByReference(hir::Mutability::MutImmutable) => + ty::BindByReference(hir::Mutability::MutImmutable), + + // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` + // (on `&`). + ty::BindByReference(hir::Mutability::MutMutable) => + ty::BindByReference(inner_mutability), + }; + }, + _ => break exp_ty, + } + }; + if pat_adjustments.len() > 0 { + debug!("default binding mode is now {:?}", def_bm); + self.inh.tables.borrow_mut() + .pat_adjustments_mut() + .insert(pat.hir_id, pat_adjustments); + } + } + + // Lose mutability now that we know binding mode and discriminant type. + let def_bm = def_bm; + let expected = expected; let ty = match pat.node { PatKind::Wild => { expected } PatKind::Lit(ref lt) => { - let ty = self.check_expr(<); + // We've already computed the type above (when checking for a non-ref pat), so + // avoid computing it again. + let ty = self.node_ty(lt.hir_id); // Byte string patterns behave the same way as array patterns // They can denote both statically and dynamically sized byte arrays @@ -114,10 +200,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { common_type } PatKind::Binding(ba, var_id, _, ref sub) => { - // Note the binding mode in the typeck tables. For now, what we store is always - // identical to what could be scraped from the HIR, but this will change with - // default binding modes (#42640). - let bm = ty::BindingMode::convert(ba); + let bm = if ba == hir::BindingAnnotation::Unannotated { + def_bm + } else { + ty::BindingMode::convert(ba) + }; self.inh .tables .borrow_mut() @@ -155,19 +242,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } if let Some(ref p) = *sub { - self.check_pat(&p, expected); + self.check_pat_walk(&p, expected, def_bm, true); } typ } PatKind::TupleStruct(ref qpath, ref subpats, ddpos) => { - self.check_pat_tuple_struct(pat, qpath, &subpats, ddpos, expected) + self.check_pat_tuple_struct(pat, qpath, &subpats, ddpos, expected, def_bm) } PatKind::Path(ref qpath) => { self.check_pat_path(pat, qpath, expected) } PatKind::Struct(ref qpath, ref fields, etc) => { - self.check_pat_struct(pat, qpath, fields, etc, expected) + self.check_pat_struct(pat, qpath, fields, etc, expected, def_bm) } PatKind::Tuple(ref elements, ddpos) => { let mut expected_len = elements.len(); @@ -188,7 +275,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let pat_ty = tcx.mk_ty(ty::TyTuple(element_tys, false)); self.demand_eqtype(pat.span, expected, pat_ty); for (i, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) { - self.check_pat(elem, &element_tys[i]); + self.check_pat_walk(elem, &element_tys[i], def_bm, true); } pat_ty } @@ -201,10 +288,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // think any errors can be introduced by using // `demand::eqtype`. self.demand_eqtype(pat.span, expected, uniq_ty); - self.check_pat(&inner, inner_ty); + self.check_pat_walk(&inner, inner_ty, def_bm, true); uniq_ty } else { - self.check_pat(&inner, tcx.types.err); + self.check_pat_walk(&inner, tcx.types.err, def_bm, true); tcx.types.err } } @@ -219,7 +306,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // can, to avoid creating needless variables. This // also helps with the bad interactions of the given // hack detailed in (*) below. - debug!("check_pat_arg: expected={:?}", expected); + debug!("check_pat_walk: expected={:?}", expected); let (rptr_ty, inner_ty) = match expected.sty { ty::TyRef(_, mt) if mt.mutbl == mutbl => { (expected, mt.ty) @@ -230,7 +317,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let mt = ty::TypeAndMut { ty: inner_ty, mutbl: mutbl }; let region = self.next_region_var(infer::PatternRegion(pat.span)); let rptr_ty = tcx.mk_ref(region, mt); - debug!("check_pat_arg: demanding {:?} = {:?}", expected, rptr_ty); + debug!("check_pat_walk: demanding {:?} = {:?}", expected, rptr_ty); let err = self.demand_eqtype_diag(pat.span, expected, rptr_ty); // Look for a case like `fn foo(&foo: u32)` and suggest @@ -253,10 +340,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } }; - self.check_pat(&inner, inner_ty); + self.check_pat_walk(&inner, inner_ty, def_bm, true); rptr_ty } else { - self.check_pat(&inner, tcx.types.err); + self.check_pat_walk(&inner, tcx.types.err, def_bm, true); tcx.types.err } } @@ -314,13 +401,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }; for elt in before { - self.check_pat(&elt, inner_ty); + self.check_pat_walk(&elt, inner_ty, def_bm, true); } if let Some(ref slice) = *slice { - self.check_pat(&slice, slice_ty); + self.check_pat_walk(&slice, slice_ty, def_bm, true); } for elt in after { - self.check_pat(&elt, inner_ty); + self.check_pat_walk(&elt, inner_ty, def_bm, true); } expected_ty } @@ -329,7 +416,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.write_ty(pat.hir_id, ty); // (*) In most of the cases above (literals and constants being - // the exception), we relate types using strict equality, evewn + // the exception), we relate types using strict equality, even // though subtyping would be sufficient. There are a few reasons // for this, some of which are fairly subtle and which cost me // (nmatsakis) an hour or two debugging to remember, so I thought @@ -339,7 +426,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // cause some inconvenience. What we are saying is that the type // of `x` becomes *exactly* what is expected. This can cause unnecessary // errors in some cases, such as this one: - // it will cause errors in a case like this: // // ``` // fn foo<'x>(x: &'x int) { @@ -409,11 +495,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // use the *precise* type of the discriminant, *not* some supertype, as // the "discriminant type" (issue #23116). // - // FIXME(tschottdorf): don't call contains_explicit_ref_binding, which - // is problematic as the HIR is being scraped, but ref bindings may be - // implicit after #42640. We need to make sure that pat_adjustments - // (once introduced) is populated by the time we get here. - // // arielb1 [writes here in this comment thread][c] that there // is certainly *some* potential danger, e.g. for an example // like: @@ -455,7 +536,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // assert_eq!(foo.0.0, 42); // } // ``` - + // + // FIXME(tschottdorf): don't call contains_explicit_ref_binding, which + // is problematic as the HIR is being scraped, but ref bindings may be + // implicit after #42640. We need to make sure that pat_adjustments + // (once introduced) is populated by the time we get here. + // + // See #44848. let contains_ref_bindings = arms.iter() .filter_map(|a| a.contains_explicit_ref_binding()) .max_by_key(|m| match *m { @@ -495,7 +582,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let mut all_pats_diverge = Diverges::WarnedAlways; for p in &arm.pats { self.diverges.set(Diverges::Maybe); - self.check_pat(&p, discrim_ty); + self.check_pat_walk(&p, discrim_ty, + ty::BindingMode::BindByValue(hir::Mutability::MutImmutable), true); all_pats_diverge &= self.diverges.get(); } @@ -576,14 +664,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { qpath: &hir::QPath, fields: &'gcx [Spanned], etc: bool, - expected: Ty<'tcx>) -> Ty<'tcx> + expected: Ty<'tcx>, + def_bm: ty::BindingMode) -> Ty<'tcx> { // Resolve the path and check the definition for errors. let (variant, pat_ty) = if let Some(variant_ty) = self.check_struct_path(qpath, pat.id) { variant_ty } else { for field in fields { - self.check_pat(&field.node.pat, self.tcx.types.err); + self.check_pat_walk(&field.node.pat, self.tcx.types.err, def_bm, true); } return self.tcx.types.err; }; @@ -592,7 +681,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.demand_eqtype(pat.span, expected, pat_ty); // Type check subpatterns. - self.check_struct_pat_fields(pat_ty, pat.id, pat.span, variant, fields, etc); + self.check_struct_pat_fields(pat_ty, pat.id, pat.span, variant, fields, etc, def_bm); pat_ty } @@ -637,12 +726,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { qpath: &hir::QPath, subpats: &'gcx [P], ddpos: Option, - expected: Ty<'tcx>) -> Ty<'tcx> + expected: Ty<'tcx>, + def_bm: ty::BindingMode) -> Ty<'tcx> { let tcx = self.tcx; let on_error = || { for pat in subpats { - self.check_pat(&pat, tcx.types.err); + self.check_pat_walk(&pat, tcx.types.err, def_bm, true); } }; let report_unexpected_def = |def: Def| { @@ -678,6 +768,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Replace constructor type with constructed type for tuple struct patterns. let pat_ty = pat_ty.fn_sig(tcx).output(); let pat_ty = tcx.no_late_bound_regions(&pat_ty).expect("expected fn type"); + self.demand_eqtype(pat.span, expected, pat_ty); // Type check subpatterns. @@ -689,7 +780,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }; for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) { let field_ty = self.field_ty(subpat.span, &variant.fields[i], substs); - self.check_pat(&subpat, field_ty); + self.check_pat_walk(&subpat, field_ty, def_bm, true); self.tcx.check_stability(variant.fields[i].did, pat.id, subpat.span); } @@ -715,7 +806,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { span: Span, variant: &'tcx ty::VariantDef, fields: &'gcx [Spanned], - etc: bool) { + etc: bool, + def_bm: ty::BindingMode) { let tcx = self.tcx; let (substs, kind_name) = match adt_ty.sty { @@ -772,7 +864,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } }; - self.check_pat(&field.pat, field_ty); + self.check_pat_walk(&field.pat, field_ty, def_bm, true); } // Report an error if incorrect number of the fields were specified. diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 33ac95ffcd15c..9c6a4abfbd7c0 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1037,7 +1037,8 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, // Add formal parameters. for (arg_ty, arg) in fn_sig.inputs().iter().zip(&body.arguments) { // Check the pattern. - fcx.check_pat_arg(&arg.pat, arg_ty, true); + fcx.check_pat_walk(&arg.pat, arg_ty, + ty::BindingMode::BindByValue(hir::Mutability::MutImmutable), true); // Check that argument is Sized. // The check for a non-trivial pattern is a hack to avoid duplicate warnings @@ -4106,6 +4107,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { (self.to_ty(qself), segment) } }; + let hir_id = self.tcx.hir.node_to_hir_id(node_id); + if let Some(cached_def) = self.tables.borrow().type_dependent_defs().get(hir_id) { + // Return directly on cache hit. This is useful to avoid doubly reporting + // errors with default match binding modes. See #44614. + return (*cached_def, Some(ty), slice::ref_slice(&**item_segment)) + } let item_name = item_segment.name; let def = match self.resolve_ufcs(span, item_name, ty, node_id) { Ok(def) => def, @@ -4122,7 +4129,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }; // Write back the new resolution. - let hir_id = self.tcx.hir.node_to_hir_id(node_id); self.tables.borrow_mut().type_dependent_defs_mut().insert(hir_id, def); (def, Some(ty), slice::ref_slice(&**item_segment)) } @@ -4132,7 +4138,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { init: &'gcx hir::Expr) -> Ty<'tcx> { // FIXME(tschottdorf): contains_explicit_ref_binding() must be removed - // for #42640. + // for #42640 (default match binding modes). + // + // See #44848. let ref_bindings = local.pat.contains_explicit_ref_binding(); let local_ty = self.local_ty(init.span, local.id); @@ -4164,7 +4172,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } - self.check_pat(&local.pat, t); + self.check_pat_walk(&local.pat, t, + ty::BindingMode::BindByValue(hir::Mutability::MutImmutable), + true); let pat_ty = self.node_ty(local.pat.hir_id); if pat_ty.references_error() { self.write_ty(local.hir_id, pat_ty); diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index 3c650718a4bfd..b3648d357e515 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -197,6 +197,8 @@ impl<'cx, 'gcx, 'tcx> Visitor<'gcx> for WritebackCx<'cx, 'gcx, 'tcx> { _ => {} }; + self.visit_pat_adjustments(p.span, p.hir_id); + self.visit_node_id(p.span, p.hir_id); intravisit::walk_pat(self, p); } @@ -366,6 +368,25 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { } } + fn visit_pat_adjustments(&mut self, span: Span, hir_id: hir::HirId) { + let adjustment = self.fcx + .tables + .borrow_mut() + .pat_adjustments_mut() + .remove(hir_id); + match adjustment { + None => { + debug!("No pat_adjustments for node {:?}", hir_id); + } + + Some(adjustment) => { + let resolved_adjustment = self.resolve(&adjustment, &span); + debug!("pat_adjustments for node {:?}: {:?}", hir_id, resolved_adjustment); + self.tables.pat_adjustments_mut().insert(hir_id, resolved_adjustment); + } + } + } + fn visit_generator_interiors(&mut self) { let common_local_id_root = self.fcx.tables.borrow().local_id_root.unwrap(); for (&id, interior) in self.fcx.tables.borrow().generator_interiors().iter() { diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 1dac0433f1479..a2f29181a20d3 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -395,6 +395,9 @@ declare_features! ( // allow `..=` in patterns (RFC 1192) (active, dotdoteq_in_patterns, "1.22.0", Some(28237)), + + // Default match binding modes (RFC 2005) + (active, match_default_bindings, "1.22.0", Some(42640)), ); declare_features! ( diff --git a/src/test/compile-fail/feature-gate-match_default_bindings.rs b/src/test/compile-fail/feature-gate-match_default_bindings.rs new file mode 100644 index 0000000000000..2b3bf94eadce5 --- /dev/null +++ b/src/test/compile-fail/feature-gate-match_default_bindings.rs @@ -0,0 +1,16 @@ +// 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. + +pub fn main() { + match &Some(3) { + Some(n) => {}, //~ ERROR mismatched types [E0308] + _ => panic!(), + } +} diff --git a/src/test/compile-fail/issue-20261.rs b/src/test/compile-fail/issue-20261.rs index 2f1910b26bbef..7b5e61380f2ba 100644 --- a/src/test/compile-fail/issue-20261.rs +++ b/src/test/compile-fail/issue-20261.rs @@ -9,7 +9,8 @@ // except according to those terms. fn main() { - for (ref i,) in [].iter() { //~ ERROR mismatched types + // NB: this (almost) typechecks when default binding modes are enabled. + for (ref i,) in [].iter() { //~ ERROR mismatched types [E0308] i.clone(); } } diff --git a/src/test/compile-fail/issue-7867.rs b/src/test/compile-fail/issue-7867.rs index 7d9f8e9058521..016df6cb6ef81 100644 --- a/src/test/compile-fail/issue-7867.rs +++ b/src/test/compile-fail/issue-7867.rs @@ -21,17 +21,4 @@ fn main() { //~| expected tuple, found enum `A` _ => () } - - match &Some(42) { - Some(x) => (), - //~^ ERROR mismatched types - //~| expected type `&std::option::Option<{integer}>` - //~| found type `std::option::Option<_>` - //~| expected reference, found enum `std::option::Option` - None => () - //~^ ERROR mismatched types - //~| expected type `&std::option::Option<{integer}>` - //~| found type `std::option::Option<_>` - //~| expected reference, found enum `std::option::Option` - } } diff --git a/src/test/compile-fail/match-vec-mismatch.rs b/src/test/compile-fail/match-vec-mismatch.rs index 11c04e3803538..d72ec8ba40868 100644 --- a/src/test/compile-fail/match-vec-mismatch.rs +++ b/src/test/compile-fail/match-vec-mismatch.rs @@ -17,8 +17,9 @@ fn main() { _ => { } }; + // Note that this one works with default binding modes. match &[0, 1, 2] { - [..] => {} //~ ERROR expected an array or slice, found `&[{integer}; 3]` + [..] => {} //~ ERROR expected an array or slice, found `&[{integer}; 3]` [E0529] }; match &[0, 1, 2] { diff --git a/src/test/compile-fail/pat-slice-old-style.rs b/src/test/compile-fail/pat-slice-old-style.rs index ccb25d859acba..54028ffa63ffc 100644 --- a/src/test/compile-fail/pat-slice-old-style.rs +++ b/src/test/compile-fail/pat-slice-old-style.rs @@ -10,13 +10,19 @@ #![feature(slice_patterns)] +// NB: this test was introduced in #23121 and will have to change when default match binding modes +// stabilizes. + fn slice_pat(x: &[u8]) { // OLD! match x { - [a, b..] => {} + [a, b..] => {}, //~^ ERROR expected an array or slice, found `&[u8]` //~| HELP the semantics of slice patterns changed recently; see issue #23121 + _ => panic!(), } } -fn main() {} +fn main() { + slice_pat("foo".as_bytes()); +} diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/box.rs b/src/test/run-pass/rfc-2005-default-binding-mode/box.rs new file mode 100644 index 0000000000000..85453f32208cb --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/box.rs @@ -0,0 +1,27 @@ +// 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. + +#![feature(box_syntax, box_patterns)] +#![feature(match_default_bindings)] + +struct Foo{} + +pub fn main() { + let b = box Foo{}; + let box f = &b; + let _: &Foo = f; + + match &&&b { + box f => { + let _: &Foo = f; + }, + _ => panic!(), + } +} diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/constref.rs b/src/test/run-pass/rfc-2005-default-binding-mode/constref.rs new file mode 100644 index 0000000000000..1b8fdbaa4d75c --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/constref.rs @@ -0,0 +1,51 @@ +// 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. + +#![feature(match_default_bindings)] + +const CONST_REF: &[u8; 3] = b"foo"; + +trait Foo { + const CONST_REF_DEFAULT: &'static [u8; 3] = b"bar"; + const CONST_REF: &'static [u8; 3]; +} + +impl Foo for i32 { + const CONST_REF: &'static [u8; 3] = b"jjj"; +} + +impl Foo for i64 { + const CONST_REF_DEFAULT: &'static [u8; 3] = b"ggg"; + const CONST_REF: &'static [u8; 3] = b"fff"; +} + +// Check that (associated and free) const references are not mistaken for a +// non-reference pattern (in which case they would be auto-dereferenced, making +// the types mismatched). + +fn const_ref() -> bool { + let f = b"foo"; + match f { + CONST_REF => true, + _ => false, + } +} + +fn associated_const_ref() -> bool { + match (b"bar", b"jjj", b"ggg", b"fff") { + (i32::CONST_REF_DEFAULT, i32::CONST_REF, i64::CONST_REF_DEFAULT, i64::CONST_REF) => true, + _ => false, + } +} + +pub fn main() { + assert!(const_ref()); + assert!(associated_const_ref()); +} diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/enum.rs b/src/test/run-pass/rfc-2005-default-binding-mode/enum.rs new file mode 100644 index 0000000000000..a7b3db021b021 --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/enum.rs @@ -0,0 +1,56 @@ +// 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. + +#![feature(match_default_bindings)] + +enum Wrapper { + Wrap(i32), +} + +use Wrapper::Wrap; + +pub fn main() { + let Wrap(x) = &Wrap(3); + println!("{}", *x); + + let Wrap(x) = &mut Wrap(3); + println!("{}", *x); + + if let Some(x) = &Some(3) { + println!("{}", *x); + } else { + panic!(); + } + + if let Some(x) = &mut Some(3) { + println!("{}", *x); + } else { + panic!(); + } + + if let Some(x) = &mut Some(3) { + *x += 1; + } else { + panic!(); + } + + while let Some(x) = &Some(3) { + println!("{}", *x); + break; + } + while let Some(x) = &mut Some(3) { + println!("{}", *x); + break; + } + while let Some(x) = &mut Some(3) { + *x += 1; + break; + } +} diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/for.rs b/src/test/run-pass/rfc-2005-default-binding-mode/for.rs new file mode 100644 index 0000000000000..4feab94a7edf5 --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/for.rs @@ -0,0 +1,31 @@ +// 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. + +#![feature(match_default_bindings)] + +pub fn main() { + let mut tups = vec![(0u8, 1u8)]; + + for (n, m) in &tups { + let _: &u8 = n; + let _: &u8 = m; + } + + for (n, m) in &mut tups { + *n += 1; + *m += 2; + } + + assert_eq!(tups, vec![(1u8, 3u8)]); + + for (n, m) in tups { + println!("{} {}", m, n); + } +} diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/general.rs b/src/test/run-pass/rfc-2005-default-binding-mode/general.rs new file mode 100644 index 0000000000000..779a38bdb1672 --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/general.rs @@ -0,0 +1,259 @@ +// 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. + +#![feature(match_default_bindings)] + +fn some_or_wildcard(r: &Option, b: &i32) { + let _: &i32 = match r { + Some(a) => a, + _ => b, + }; +} + +fn none_or_wildcard(r: &Option, b: &i32) { + let _: &i32 = match r { + None => b, + _ => b, + }; +} + +fn some_or_ref_none(r: &Option, b: &i32) { + let _: &i32 = match r { + Some(a) => a, + &None => b, + }; +} + +fn ref_some_or_none(r: &Option, b: &i32) { + let _: &i32 = match r { + &Some(ref a) => a, + None => b, + }; +} + +fn some_or_self(r: &Option) { + let _: &Option = match r { + Some(n) => { + let _: &i32 = n; + r + }, + x => x, + }; +} + +fn multiple_deref(r: &&&&&Option) { + let _: i32 = match r { + Some(a) => *a, + None => 5, + }; +} + +fn match_with_or() { + // FIXME(tschottdorf): #44912. + // + // let x = &Some((3, 3)); + // let _: &i32 = match x { + // Some((x, 3)) | &Some((ref x, 5)) => x, + // _ => &5i32, + // }; +} + +fn nested_mixed() { + match (&Some(5), &Some(6)) { + (Some(a), &Some(mut b)) => { + // Here, the `a` will be `&i32`, because in the first half of the tuple + // we hit a non-reference pattern and shift into `ref` mode. + // + // In the second half of the tuple there's no non-reference pattern, + // so `b` will be `i32` (bound with `move` mode). Moreover, `b` is + // mutable. + let _: &i32 = a; + b = 7; + let _: i32 = b; + }, + _ => {}, + }; +} + +fn nested_mixed_multiple_deref_1() { + let x = (1, &Some(5)); + let y = &Some(x); + match y { + Some((a, Some(b))) => { + let _: &i32 = a; + let _: &i32 = b; + }, + _ => {}, + }; +} + +fn nested_mixed_multiple_deref_2() { + let x = &Some(5); + let y = &x; + match y { + Some(z) => { + let _: &i32 = z; + }, + _ => {}, + } +} + +fn new_mutable_reference() { + let mut x = &mut Some(5); + match &mut x { + Some(y) => { + *y = 5; + }, + None => { }, + } + + match &mut x { + Some(y) => { + println!("{}", *y); + }, + None => {}, + } +} + +fn let_implicit_ref_binding() { + struct Foo(i32); + + // Note that these rules apply to any pattern matching + // whether it be in a `match` or a `let`. + // For example, `x` here is a `ref` binding: + let Foo(x) = &Foo(3); + let _: &i32 = x; +} + +fn explicit_mut_binding() { + match &Some(5i32) { + Some(mut n) => { + n += 1; + let _ = n; + } + None => {}, + }; + + match &mut Some(5i32) { + Some(n) => { + *n += 1; + let _ = n; + } + None => {}, + }; + + match &mut &mut Some(5i32) { + Some(n) => { + let _: &mut i32 = n; + } + None => {}, + }; +} + +fn tuple_mut_and_mut_mut() { + match (Some(5i32), &Some(5i32)) { + (Some(n), Some(m)) => { + // `n` and `m` are bound as immutable references. Make new references from them to + // assert that. + let r = n; + let _ = r; + let q = m; + let _ = q; + + // Assert the types. Note that we use `n` and `m` here which would fail had they been + // moved due to the assignments above. + let _: i32 = n; + let _: &i32 = m; + } + (_, _) => {}, + }; + + match (&Some(5i32), &&Some(5i32)) { + (Some(n), Some(m)) => { + let _: &i32 = n; + let _: &i32 = m; + } + (_, _) => {}, + }; + + match &mut &mut (Some(5i32), Some(5i32)) { + (Some(n), Some(m)) => { + // Dereferenced through &mut &mut, so a mutable binding results. + let _: &mut i32 = n; + let _: &mut i32 = m; + } + (_, _) => {}, + }; + + match (&mut Some(5i32), &mut &mut Some(5i32)) { + (Some(n), Some(m)) => { + let _: &mut i32 = n; + let _: &mut i32 = m; + } + (_, _) => {}, + }; +} + +fn min_mir_embedded_type() { + // The reduced invocation that an ICE was diagnosed with (was consuming + // adjustments in wrong order). + match (0u8, &&Some(5i32)) { + (_, Some(m)) => { + let _: &i32 = m; + } + (_, _) => {}, + }; +} + +fn no_autoderef() { + // Binding. + let x = &3; + println!("{}", *x); + + // Wildcard. + let _ = &3; + + // Constant of generic type (string) + const Y: &'static str = "foo"; + assert_eq!(0, match "foo" { + Y => 0, + _ => 1, + }); + + // Reference pattern. + let &x = &3; +} + +pub fn main() { + let r: &Option = &Some(3); + let b = &4i32; + + none_or_wildcard(r, b); + some_or_wildcard(r, b); + some_or_ref_none(r, b); + ref_some_or_none(r, b); + + some_or_self(r); + multiple_deref(&&&&r); + match_with_or(); + + nested_mixed(); + nested_mixed_multiple_deref_1(); + nested_mixed_multiple_deref_2(); + + new_mutable_reference(); + explicit_mut_binding(); + tuple_mut_and_mut_mut(); + min_mir_embedded_type(); + + let_implicit_ref_binding(); + + no_autoderef(); +} diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/lit.rs b/src/test/run-pass/rfc-2005-default-binding-mode/lit.rs new file mode 100644 index 0000000000000..0b2a8e52fbf77 --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/lit.rs @@ -0,0 +1,44 @@ +// 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. + +#![feature(match_default_bindings)] + +fn with_u8() { + let s = 5u8; + let r = match &s { + 4 => false, + 5 => true, + _ => false, + }; + assert!(r); +} + +// A string literal isn't mistaken for a non-ref pattern (in which case we'd +// deref `s` and mess things up). +fn with_str() { + let s: &'static str = "abc"; + match s { + "abc" => true, + _ => panic!(), + }; +} + +// Ditto with byte strings. +fn with_bytes() { + let s: &'static [u8] = b"abc"; + match s { + b"abc" => true, + _ => panic!(), + }; +} + +pub fn main() { + with_str(); +} diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/range.rs b/src/test/run-pass/rfc-2005-default-binding-mode/range.rs new file mode 100644 index 0000000000000..aafaa4cca82ca --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/range.rs @@ -0,0 +1,20 @@ +// 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. + +#![feature(match_default_bindings)] + +pub fn main() { + let i = 5; + match &&&&i { + 1 ... 3 => panic!(), + 3 ... 8 => {}, + _ => panic!(), + } +} diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/ref-region.rs b/src/test/run-pass/rfc-2005-default-binding-mode/ref-region.rs new file mode 100644 index 0000000000000..de7df011b56fd --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/ref-region.rs @@ -0,0 +1,27 @@ +// 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. + +#![feature(match_default_bindings)] + +fn foo<'a, 'b>(x: &'a &'b Option) -> &'a u32 { + let x: &'a &'a Option = x; + match x { + Some(r) => { + let _: &u32 = r; + r + }, + &None => panic!(), + } +} + +pub fn main() { + let x = Some(5); + foo(&&x); +} diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/slice.rs b/src/test/run-pass/rfc-2005-default-binding-mode/slice.rs new file mode 100644 index 0000000000000..1717d0d54c026 --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/slice.rs @@ -0,0 +1,36 @@ +// Copyright 2016 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(slice_patterns)] +#![feature(match_default_bindings)] + +fn slice_pat() { + let sl: &[u8] = b"foo"; + + match sl { + [first, remainder..] => { + let _: &u8 = first; + assert_eq!(first, &b'f'); + assert_eq!(remainder, b"oo"); + } + [] => panic!(), + } +} + +fn slice_pat_omission() { + match &[0, 1, 2] { + [..] => {} + }; +} + +fn main() { + slice_pat(); + slice_pat_omission(); +} diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/struct.rs b/src/test/run-pass/rfc-2005-default-binding-mode/struct.rs new file mode 100644 index 0000000000000..11a675c0c72af --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/struct.rs @@ -0,0 +1,33 @@ +// 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. + +#![feature(match_default_bindings)] + +#[derive(Debug, PartialEq)] +struct Foo { + x: u8, +} + +pub fn main() { + let mut foo = Foo { + x: 1, + }; + + match &mut foo { + Foo{x: n} => { + *n += 1; + }, + }; + + assert_eq!(foo, Foo{x: 2}); + + let Foo{x: n} = &foo; + assert_eq!(*n, 2); +} diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/tuple-struct.rs b/src/test/run-pass/rfc-2005-default-binding-mode/tuple-struct.rs new file mode 100644 index 0000000000000..7867d6529050d --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/tuple-struct.rs @@ -0,0 +1,29 @@ +// 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. + +#![feature(match_default_bindings)] + +enum Foo { + Bar(Option, (), (), Vec), + Baz, +} + +pub fn main() { + let foo = Foo::Bar(Some(1), (), (), vec![2, 3]); + + match &foo { + Foo::Baz => panic!(), + Foo::Bar(None, ..) => panic!(), + Foo::Bar(Some(n), .., v) => { + assert_eq!((*v).len(), 2); + assert_eq!(*n, 1); + } + } +} diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/tuple.rs b/src/test/run-pass/rfc-2005-default-binding-mode/tuple.rs new file mode 100644 index 0000000000000..cf27265b2ed56 --- /dev/null +++ b/src/test/run-pass/rfc-2005-default-binding-mode/tuple.rs @@ -0,0 +1,23 @@ +// 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. + +#![feature(match_default_bindings)] + +pub fn main() { + let foo = (Some(1), (), (), vec![2, 3]); + + match &foo { + (Some(n), .., v) => { + assert_eq!((*v).len(), 2); + assert_eq!(*n, 1); + } + (None, (), (), ..) => panic!(), + } +} diff --git a/src/test/ui/const-expr-addr-operator.rs b/src/test/ui/const-expr-addr-operator.rs new file mode 100644 index 0000000000000..282b0d4e45b70 --- /dev/null +++ b/src/test/ui/const-expr-addr-operator.rs @@ -0,0 +1,20 @@ +// 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. + +// Encountered while testing #44614. + +pub fn main() { + // Constant of generic type (int) + const X: &'static u32 = &22; + assert_eq!(0, match &22 { + X => 0, + _ => 1, + }); +} \ No newline at end of file diff --git a/src/test/ui/const-expr-addr-operator.stderr b/src/test/ui/const-expr-addr-operator.stderr new file mode 100644 index 0000000000000..5bda81451d0e2 --- /dev/null +++ b/src/test/ui/const-expr-addr-operator.stderr @@ -0,0 +1,14 @@ +error[E0080]: constant evaluation error + --> $DIR/const-expr-addr-operator.rs:15:29 + | +15 | const X: &'static u32 = &22; + | ^^^ unimplemented constant expression: address operator + | +note: for pattern here + --> $DIR/const-expr-addr-operator.rs:17:9 + | +17 | X => 0, + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/mismatched_types/issue-38371.rs b/src/test/ui/mismatched_types/issue-38371.rs index cf66330017f58..6b49079c47210 100644 --- a/src/test/ui/mismatched_types/issue-38371.rs +++ b/src/test/ui/mismatched_types/issue-38371.rs @@ -25,6 +25,8 @@ fn qux(foo: &Foo) { fn zar(&foo: &Foo) { } +// The somewhat unexpected help message in this case is courtesy of +// match_default_bindings. fn agh(&&bar: &u32) { } diff --git a/src/test/ui/mismatched_types/issue-38371.stderr b/src/test/ui/mismatched_types/issue-38371.stderr index 9efee4cc5593c..c80f075021ec8 100644 --- a/src/test/ui/mismatched_types/issue-38371.stderr +++ b/src/test/ui/mismatched_types/issue-38371.stderr @@ -9,27 +9,28 @@ error[E0308]: mismatched types = help: did you mean `foo: &Foo`? error[E0308]: mismatched types - --> $DIR/issue-38371.rs:28:9 + --> $DIR/issue-38371.rs:30:9 | -28 | fn agh(&&bar: &u32) { +30 | fn agh(&&bar: &u32) { | ^^^^ expected u32, found reference | = note: expected type `u32` found type `&_` + = help: did you mean `bar: &u32`? error[E0308]: mismatched types - --> $DIR/issue-38371.rs:31:8 + --> $DIR/issue-38371.rs:33:8 | -31 | fn bgh(&&bar: u32) { +33 | fn bgh(&&bar: u32) { | ^^^^^ expected u32, found reference | = note: expected type `u32` found type `&_` error[E0529]: expected an array or slice, found `u32` - --> $DIR/issue-38371.rs:34:9 + --> $DIR/issue-38371.rs:36:9 | -34 | fn ugh(&[bar]: &u32) { +36 | fn ugh(&[bar]: &u32) { | ^^^^^ pattern cannot match with input type `u32` error: aborting due to 4 previous errors diff --git a/src/test/ui/rfc-2005-default-binding-mode/const.rs b/src/test/ui/rfc-2005-default-binding-mode/const.rs new file mode 100644 index 0000000000000..31923343b6aa7 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/const.rs @@ -0,0 +1,29 @@ +// 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. + +// FIXME(tschottdorf): this test should pass. + +#![feature(match_default_bindings)] + +#[derive(PartialEq, Eq)] +struct Foo { + bar: i32, +} + +const FOO: Foo = Foo{bar: 5}; + +fn main() { + let f = Foo{bar:6}; + + match &f { + FOO => {}, + _ => panic!(), + } +} diff --git a/src/test/ui/rfc-2005-default-binding-mode/const.stderr b/src/test/ui/rfc-2005-default-binding-mode/const.stderr new file mode 100644 index 0000000000000..0dfd79f356545 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/const.stderr @@ -0,0 +1,11 @@ +error[E0308]: mismatched types + --> $DIR/const.rs:26:9 + | +26 | FOO => {}, + | ^^^ expected &Foo, found struct `Foo` + | + = note: expected type `&Foo` + found type `Foo` + +error: aborting due to previous error + diff --git a/src/test/ui/rfc-2005-default-binding-mode/enum.rs b/src/test/ui/rfc-2005-default-binding-mode/enum.rs new file mode 100644 index 0000000000000..58902bf06b314 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/enum.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. + +#![feature(match_default_bindings)] + +enum Wrapper { + Wrap(i32), +} + +use Wrapper::Wrap; + +pub fn main() { + let Wrap(x) = &Wrap(3); + *x += 1; + + + if let Some(x) = &Some(3) { + *x += 1; + } else { + panic!(); + } + + while let Some(x) = &Some(3) { + *x += 1; + break; + } +} diff --git a/src/test/ui/rfc-2005-default-binding-mode/enum.stderr b/src/test/ui/rfc-2005-default-binding-mode/enum.stderr new file mode 100644 index 0000000000000..ad08ae83a49ef --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/enum.stderr @@ -0,0 +1,26 @@ +error[E0594]: cannot assign to immutable borrowed content `*x` + --> $DIR/enum.rs:21:5 + | +20 | let Wrap(x) = &Wrap(3); + | - consider changing this to `x` +21 | *x += 1; + | ^^^^^^^ cannot borrow as mutable + +error[E0594]: cannot assign to immutable borrowed content `*x` + --> $DIR/enum.rs:25:9 + | +24 | if let Some(x) = &Some(3) { + | - consider changing this to `x` +25 | *x += 1; + | ^^^^^^^ cannot borrow as mutable + +error[E0594]: cannot assign to immutable borrowed content `*x` + --> $DIR/enum.rs:31:9 + | +30 | while let Some(x) = &Some(3) { + | - consider changing this to `x` +31 | *x += 1; + | ^^^^^^^ cannot borrow as mutable + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/rfc-2005-default-binding-mode/explicit-mut.rs b/src/test/ui/rfc-2005-default-binding-mode/explicit-mut.rs new file mode 100644 index 0000000000000..b5287b7cccc76 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/explicit-mut.rs @@ -0,0 +1,40 @@ +// 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. + +#![feature(match_default_bindings)] + +// Verify the binding mode shifts - only when no `&` are auto-dereferenced is the +// final default binding mode mutable. + +fn main() { + match &&Some(5i32) { + Some(n) => { + *n += 1; + let _ = n; + } + None => {}, + }; + + match &mut &Some(5i32) { + Some(n) => { + *n += 1; + let _ = n; + } + None => {}, + }; + + match &&mut Some(5i32) { + Some(n) => { + *n += 1; + let _ = n; + } + None => {}, + }; +} diff --git a/src/test/ui/rfc-2005-default-binding-mode/explicit-mut.stderr b/src/test/ui/rfc-2005-default-binding-mode/explicit-mut.stderr new file mode 100644 index 0000000000000..1dbd769373b9d --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/explicit-mut.stderr @@ -0,0 +1,26 @@ +error[E0594]: cannot assign to immutable borrowed content `*n` + --> $DIR/explicit-mut.rs:19:13 + | +18 | Some(n) => { + | - consider changing this to `n` +19 | *n += 1; + | ^^^^^^^ cannot borrow as mutable + +error[E0594]: cannot assign to immutable borrowed content `*n` + --> $DIR/explicit-mut.rs:27:13 + | +26 | Some(n) => { + | - consider changing this to `n` +27 | *n += 1; + | ^^^^^^^ cannot borrow as mutable + +error[E0594]: cannot assign to immutable borrowed content `*n` + --> $DIR/explicit-mut.rs:35:13 + | +34 | Some(n) => { + | - consider changing this to `n` +35 | *n += 1; + | ^^^^^^^ cannot borrow as mutable + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/rfc-2005-default-binding-mode/for.rs b/src/test/ui/rfc-2005-default-binding-mode/for.rs new file mode 100644 index 0000000000000..35f8fbb9b71f7 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/for.rs @@ -0,0 +1,20 @@ +// 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. + +#![feature(match_default_bindings)] + +struct Foo {} + +pub fn main() { + let mut tups = vec![(Foo{}, Foo{})]; + // The below desugars to &(ref n, mut m). + for (n, mut m) in &tups { + } +} diff --git a/src/test/ui/rfc-2005-default-binding-mode/for.stderr b/src/test/ui/rfc-2005-default-binding-mode/for.stderr new file mode 100644 index 0000000000000..795dffb722a15 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/for.stderr @@ -0,0 +1,10 @@ +error[E0009]: cannot bind by-move and by-ref in the same pattern + --> $DIR/for.rs:18:13 + | +18 | for (n, mut m) in &tups { + | - ^^^^^ by-move pattern here + | | + | both by-ref and by-move used + +error: aborting due to previous error + diff --git a/src/test/ui/rfc-2005-default-binding-mode/issue-44912-or.rs b/src/test/ui/rfc-2005-default-binding-mode/issue-44912-or.rs new file mode 100644 index 0000000000000..294d6b88596bf --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/issue-44912-or.rs @@ -0,0 +1,21 @@ +// 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. + +#![feature(match_default_bindings)] + +// FIXME(tschottdorf): This should compile. See #44912. + +pub fn main() { + let x = &Some((3, 3)); + let _: &i32 = match x { + Some((x, 3)) | &Some((ref x, 5)) => x, + _ => &5i32, + }; +} diff --git a/src/test/ui/rfc-2005-default-binding-mode/issue-44912-or.stderr b/src/test/ui/rfc-2005-default-binding-mode/issue-44912-or.stderr new file mode 100644 index 0000000000000..7430dc2c87f95 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/issue-44912-or.stderr @@ -0,0 +1,8 @@ +error[E0409]: variable `x` is bound in inconsistent ways within the same match arm + --> $DIR/issue-44912-or.rs:18:35 + | +18 | Some((x, 3)) | &Some((ref x, 5)) => x, + | - first binding ^ bound in different ways + +error: aborting due to previous error + diff --git a/src/test/ui/rfc-2005-default-binding-mode/lit.rs b/src/test/ui/rfc-2005-default-binding-mode/lit.rs new file mode 100644 index 0000000000000..54cee39209ba3 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/lit.rs @@ -0,0 +1,36 @@ +// 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. + +#![feature(match_default_bindings)] + +// FIXME(tschottdorf): we want these to compile, but they don't. + +fn with_str() { + let s: &'static str = "abc"; + + match &s { + "abc" => true, + _ => panic!(), + }; +} + +fn with_bytes() { + let s: &'static [u8] = b"abc"; + + match &s { + b"abc" => true, + _ => panic!(), + }; +} + +pub fn main() { + with_str(); + with_bytes(); +} diff --git a/src/test/ui/rfc-2005-default-binding-mode/lit.stderr b/src/test/ui/rfc-2005-default-binding-mode/lit.stderr new file mode 100644 index 0000000000000..811d3b8074f53 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/lit.stderr @@ -0,0 +1,20 @@ +error[E0308]: mismatched types + --> $DIR/lit.rs:19:13 + | +19 | "abc" => true, + | ^^^^^ expected &str, found str + | + = note: expected type `&&str` + found type `&'static str` + +error[E0308]: mismatched types + --> $DIR/lit.rs:28:9 + | +28 | b"abc" => true, + | ^^^^^^ expected &[u8], found array of 3 elements + | + = note: expected type `&&[u8]` + found type `&'static [u8; 3]` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/rfc-2005-default-binding-mode/no-double-error.rs b/src/test/ui/rfc-2005-default-binding-mode/no-double-error.rs new file mode 100644 index 0000000000000..a0134c499bb01 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/no-double-error.rs @@ -0,0 +1,21 @@ +// 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. + +// Without caching type lookups in FnCtxt.resolve_ty_and_def_ufcs +// the error below would be reported twice (once when checking +// for a non-ref pattern, once when processing the pattern). + +fn main() { + let foo = 22; + match foo { + u32::XXX => { } + _ => { } + } +} diff --git a/src/test/ui/rfc-2005-default-binding-mode/no-double-error.stderr b/src/test/ui/rfc-2005-default-binding-mode/no-double-error.stderr new file mode 100644 index 0000000000000..da065eea897b7 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/no-double-error.stderr @@ -0,0 +1,8 @@ +error[E0599]: no associated item named `XXX` found for type `u32` in the current scope + --> $DIR/no-double-error.rs:18:9 + | +18 | u32::XXX => { } + | ^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/rfc-2005-default-binding-mode/slice.rs b/src/test/ui/rfc-2005-default-binding-mode/slice.rs new file mode 100644 index 0000000000000..fb87ed72abc3f --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/slice.rs @@ -0,0 +1,20 @@ +// Copyright 2016 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(slice_patterns)] +#![feature(match_default_bindings)] + +pub fn main() { + let sl: &[u8] = b"foo"; + + match sl { + [first, remainder..] => {}, + }; +} diff --git a/src/test/ui/rfc-2005-default-binding-mode/slice.stderr b/src/test/ui/rfc-2005-default-binding-mode/slice.stderr new file mode 100644 index 0000000000000..90a2f75c07fb0 --- /dev/null +++ b/src/test/ui/rfc-2005-default-binding-mode/slice.stderr @@ -0,0 +1,8 @@ +error[E0004]: non-exhaustive patterns: `&[]` not covered + --> $DIR/slice.rs:17:11 + | +17 | match sl { + | ^^ pattern `&[]` not covered + +error: aborting due to previous error +