From f2d6413eb276caa4d881da1aad759b02a64c6aea Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sun, 15 Dec 2019 13:13:52 +0100 Subject: [PATCH 1/4] comment -> doc comment --- src/librustc_typeck/check/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 43e7bbcf0c0dc..65a5405c03772 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -5351,9 +5351,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { directly, not through a function pointer"); } - // Resolves `typ` by a single level if `typ` is a type variable. - // If no resolution is possible, then an error is reported. - // Numeric inference variables may be left unresolved. + /// Resolves `typ` by a single level if `typ` is a type variable. + /// If no resolution is possible, then an error is reported. + /// Numeric inference variables may be left unresolved. pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { let ty = self.resolve_vars_with_obligations(ty); if !ty.is_ty_var() { From d848ce0ddf4f20bd6fbae70ad126640247f61a56 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sun, 15 Dec 2019 13:16:01 +0100 Subject: [PATCH 2/4] document check_pat_slice --- src/librustc_typeck/check/pat.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs index 71d1cd869a6a2..4fb57a6562574 100644 --- a/src/librustc_typeck/check/pat.rs +++ b/src/librustc_typeck/check/pat.rs @@ -1154,6 +1154,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.mk_ref(region, mt) } + /// Type check a slice pattern. + /// + /// Syntactically, these look like `[pat_0, ..., pat_n]`. + /// Semantically, we are type checking a pattern with structure: + /// ``` + /// [before_0, ..., before_n, (slice, after_0, ... after_n)?] + /// ``` + /// The type of `slice`, if it is present, depends on the `expected` type. + /// If `slice` is missing, then so is `after_i`. + /// If `slice` is present, it can still represent 0 elements. fn check_pat_slice( &self, span: Span, @@ -1167,27 +1177,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tcx = self.tcx; let expected_ty = self.structurally_resolved_type(span, expected); let (inner_ty, slice_ty) = match expected_ty.kind { + // An array, so we might have something like `let [a, b, c] = [0, 1, 2];`. ty::Array(inner_ty, size) => { let slice_ty = if let Some(size) = size.try_eval_usize(tcx, self.param_env) { + // Now we know the length... let min_len = before.len() as u64 + after.len() as u64; if slice.is_none() { + // ...and since there is no variable-length pattern, + // we require an exact match between the number of elements + // in the array pattern and as provided by the matched type. if min_len != size { self.error_scrutinee_inconsistent_length(span, min_len, size) } tcx.types.err } else if let Some(rest) = size.checked_sub(min_len) { + // The variable-length pattern was there, + // so it has an array type with the remaining elements left as its size... tcx.mk_array(inner_ty, rest) } else { + // ...however, in this case, there were no remaining elements. + // That is, the slice pattern requires more than the array type offers. self.error_scrutinee_with_rest_inconsistent_length(span, min_len, size); tcx.types.err } } else { + // No idea what the length is, which happens if we have e.g., + // `let [a, b] = arr` where `arr: [T; N]` where `const N: usize`. self.error_scrutinee_unfixed_length(span); tcx.types.err }; (inner_ty, slice_ty) } ty::Slice(inner_ty) => (inner_ty, expected_ty), + // The expected type must be an array or slice, but was neither, so error. _ => { if !expected_ty.references_error() { self.error_expected_array_or_slice(span, expected_ty); @@ -1196,12 +1218,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; + // Type check all the patterns before `slice`. for elt in before { self.check_pat(&elt, inner_ty, def_bm, discrim_span); } + // Type check the `slice`, if present, against its expected type. if let Some(slice) = slice { self.check_pat(&slice, slice_ty, def_bm, discrim_span); } + // Type check the elements after `slice`, if present. for elt in after { self.check_pat(&elt, inner_ty, def_bm, discrim_span); } From 66bb978ab65f7c7e1d6c39e275baf8eb22b1a04d Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sun, 15 Dec 2019 13:16:29 +0100 Subject: [PATCH 3/4] improve hir::PatKind::Slice docs --- src/librustc/hir/mod.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 66bb3a8d883a4..6b354b01518ea 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1048,8 +1048,15 @@ pub enum PatKind { /// A range pattern (e.g., `1..=2` or `1..2`). Range(P, P, RangeEnd), - /// `[a, b, ..i, y, z]` is represented as: - /// `PatKind::Slice(box [a, b], Some(i), box [y, z])`. + /// A slice pattern, `[before_0, ..., before_n, (slice, after_0, ..., after_n)?]`. + /// + /// Here, `slice` is lowered from the syntax `($binding_mode $ident @)? ..`. + /// If `slice` exists, then `after` can be non-empty. + /// + /// The representation for e.g., `[a, b, .., c, d]` is: + /// ``` + /// PatKind::Slice([Binding(a), Binding(b)], Some(Wild), [Binding(c), Binding(d)]) + /// ``` Slice(HirVec>, Option>, HirVec>), } From d9d1f2376f9e2f092e6a01cd14235bdc0c129706 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sun, 15 Dec 2019 13:17:39 +0100 Subject: [PATCH 4/4] improve lower_pat_slice docs + while -> for --- src/librustc/hir/lowering.rs | 38 ++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index e13f6cabb5296..ec9d6802defa3 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -2852,19 +2852,23 @@ impl<'a> LoweringContext<'a> { let mut rest = None; let mut iter = pats.iter().enumerate(); - while let Some((idx, pat)) = iter.next() { - // Interpret the first `..` pattern as a subtuple pattern. + for (idx, pat) in iter.by_ref() { + // Interpret the first `..` pattern as a sub-tuple pattern. + // Note that unlike for slice patterns, + // where `xs @ ..` is a legal sub-slice pattern, + // it is not a legal sub-tuple pattern. if pat.is_rest() { rest = Some((idx, pat.span)); break; } - // It was not a subslice pattern so lower it normally. + // It was not a sub-tuple pattern so lower it normally. elems.push(self.lower_pat(pat)); } - while let Some((_, pat)) = iter.next() { - // There was a previous subtuple pattern; make sure we don't allow more. + for (_, pat) in iter { + // There was a previous sub-tuple pattern; make sure we don't allow more... if pat.is_rest() { + // ...but there was one again, so error. self.ban_extra_rest_pat(pat.span, rest.unwrap().1, ctx); } else { elems.push(self.lower_pat(pat)); @@ -2874,6 +2878,12 @@ impl<'a> LoweringContext<'a> { (elems.into(), rest.map(|(ddpos, _)| ddpos)) } + /// Lower a slice pattern of form `[pat_0, ..., pat_n]` into + /// `hir::PatKind::Slice(before, slice, after)`. + /// + /// When encountering `($binding_mode $ident @)? ..` (`slice`), + /// this is interpreted as a sub-slice pattern semantically. + /// Patterns that follow, which are not like `slice` -- or an error occurs, are in `after`. fn lower_pat_slice(&mut self, pats: &[AstP]) -> hir::PatKind { let mut before = Vec::new(); let mut after = Vec::new(); @@ -2881,14 +2891,17 @@ impl<'a> LoweringContext<'a> { let mut prev_rest_span = None; let mut iter = pats.iter(); - while let Some(pat) = iter.next() { - // Interpret the first `((ref mut?)? x @)? ..` pattern as a subslice pattern. + // Lower all the patterns until the first occurence of a sub-slice pattern. + for pat in iter.by_ref() { match pat.kind { + // Found a sub-slice pattern `..`. Record, lower it to `_`, and stop here. PatKind::Rest => { prev_rest_span = Some(pat.span); slice = Some(self.pat_wild_with_node_id_of(pat)); break; }, + // Found a sub-slice pattern `$binding_mode $ident @ ..`. + // Record, lower it to `$binding_mode $ident @ _`, and stop here. PatKind::Ident(ref bm, ident, Some(ref sub)) if sub.is_rest() => { prev_rest_span = Some(sub.span); let lower_sub = |this: &mut Self| Some(this.pat_wild_with_node_id_of(sub)); @@ -2896,14 +2909,13 @@ impl<'a> LoweringContext<'a> { slice = Some(self.pat_with_node_id_of(pat, node)); break; }, - _ => {} + // It was not a subslice pattern so lower it normally. + _ => before.push(self.lower_pat(pat)), } - - // It was not a subslice pattern so lower it normally. - before.push(self.lower_pat(pat)); } - while let Some(pat) = iter.next() { + // Lower all the patterns after the first sub-slice pattern. + for pat in iter { // There was a previous subslice pattern; make sure we don't allow more. let rest_span = match pat.kind { PatKind::Rest => Some(pat.span), @@ -2915,8 +2927,10 @@ impl<'a> LoweringContext<'a> { _ => None, }; if let Some(rest_span) = rest_span { + // We have e.g., `[a, .., b, ..]`. That's no good, error! self.ban_extra_rest_pat(rest_span, prev_rest_span.unwrap(), "slice"); } else { + // Lower the pattern normally. after.push(self.lower_pat(pat)); } }