Skip to content
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

Introduce X.., ..X, and ..=X range patterns #67258

Merged
merged 1 commit into from
Jan 10, 2020
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
95 changes: 73 additions & 22 deletions src/librustc/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@
use crate::hir::map::DefPathData;
use crate::ich::NodeIdHashingMode;
use crate::mir::interpret::{sign_extend, truncate};
use crate::ty::layout::{Integer, IntegerExt};
use crate::ty::layout::{Integer, IntegerExt, Size};
use crate::ty::query::TyCtxtAt;
use crate::ty::subst::{GenericArgKind, InternalSubsts, Subst, SubstsRef};
use crate::ty::TyKind::*;
use crate::ty::{self, DefIdTree, GenericParamDefKind, Ty, TyCtxt, TypeFoldable};
use crate::util::common::ErrorReported;
use rustc_apfloat::Float as _;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;

use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_macros::HashStable;
use rustc_span::Span;
use std::{cmp, fmt};
Expand Down Expand Up @@ -43,41 +43,54 @@ impl<'tcx> fmt::Display for Discr<'tcx> {
}
}

fn signed_min(size: Size) -> i128 {
Centril marked this conversation as resolved.
Show resolved Hide resolved
sign_extend(1_u128 << (size.bits() - 1), size) as i128
}

fn signed_max(size: Size) -> i128 {
i128::max_value() >> (128 - size.bits())
}

fn unsigned_max(size: Size) -> u128 {
u128::max_value() >> (128 - size.bits())
}

fn int_size_and_signed<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (Size, bool) {
let (int, signed) = match ty.kind {
Int(ity) => (Integer::from_attr(&tcx, SignedInt(ity)), true),
Uint(uty) => (Integer::from_attr(&tcx, UnsignedInt(uty)), false),
_ => bug!("non integer discriminant"),
};
(int.size(), signed)
}

impl<'tcx> Discr<'tcx> {
/// Adds `1` to the value and wraps around if the maximum for the type is reached.
pub fn wrap_incr(self, tcx: TyCtxt<'tcx>) -> Self {
self.checked_add(tcx, 1).0
}
pub fn checked_add(self, tcx: TyCtxt<'tcx>, n: u128) -> (Self, bool) {
let (int, signed) = match self.ty.kind {
Int(ity) => (Integer::from_attr(&tcx, SignedInt(ity)), true),
Uint(uty) => (Integer::from_attr(&tcx, UnsignedInt(uty)), false),
_ => bug!("non integer discriminant"),
};

let size = int.size();
let bit_size = int.size().bits();
let shift = 128 - bit_size;
if signed {
let sext = |u| sign_extend(u, size) as i128;
let min = sext(1_u128 << (bit_size - 1));
let max = i128::max_value() >> shift;
let val = sext(self.val);
let (size, signed) = int_size_and_signed(tcx, self.ty);
let (val, oflo) = if signed {
let min = signed_min(size);
let max = signed_max(size);
let val = sign_extend(self.val, size) as i128;
assert!(n < (i128::max_value() as u128));
let n = n as i128;
let oflo = val > max - n;
let val = if oflo { min + (n - (max - val) - 1) } else { val + n };
// zero the upper bits
let val = val as u128;
let val = truncate(val, size);
(Self { val: val as u128, ty: self.ty }, oflo)
(val, oflo)
} else {
let max = u128::max_value() >> shift;
let max = unsigned_max(size);
let val = self.val;
let oflo = val > max - n;
let val = if oflo { n - (max - val) - 1 } else { val + n };
(Self { val: val, ty: self.ty }, oflo)
}
(val, oflo)
};
(Self { val, ty: self.ty }, oflo)
}
}

Expand Down Expand Up @@ -621,6 +634,44 @@ impl<'tcx> TyCtxt<'tcx> {
}

impl<'tcx> ty::TyS<'tcx> {
/// Returns the maximum value for the given numeric type (including `char`s)
/// or returns `None` if the type is not numeric.
pub fn numeric_max_val(&'tcx self, tcx: TyCtxt<'tcx>) -> Option<&'tcx ty::Const<'tcx>> {
let val = match self.kind {
ty::Int(_) | ty::Uint(_) => {
let (size, signed) = int_size_and_signed(tcx, self);
let val = if signed { signed_max(size) as u128 } else { unsigned_max(size) };
Some(val)
}
ty::Char => Some(std::char::MAX as u128),
ty::Float(fty) => Some(match fty {
ast::FloatTy::F32 => ::rustc_apfloat::ieee::Single::INFINITY.to_bits(),
ast::FloatTy::F64 => ::rustc_apfloat::ieee::Double::INFINITY.to_bits(),
}),
_ => None,
};
val.map(|v| ty::Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self)))
}

/// Returns the minimum value for the given numeric type (including `char`s)
/// or returns `None` if the type is not numeric.
pub fn numeric_min_val(&'tcx self, tcx: TyCtxt<'tcx>) -> Option<&'tcx ty::Const<'tcx>> {
let val = match self.kind {
ty::Int(_) | ty::Uint(_) => {
let (size, signed) = int_size_and_signed(tcx, self);
let val = if signed { truncate(signed_min(size) as u128, size) } else { 0 };
Some(val)
}
ty::Char => Some(0),
ty::Float(fty) => Some(match fty {
ast::FloatTy::F32 => (-::rustc_apfloat::ieee::Single::INFINITY).to_bits(),
ast::FloatTy::F64 => (-::rustc_apfloat::ieee::Double::INFINITY).to_bits(),
}),
_ => None,
};
val.map(|v| ty::Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self)))
}

/// Checks whether values of this type `T` are *moved* or *copied*
/// when referenced -- this amounts to a check for whether `T:
/// Copy`, but note that we **don't** consider lifetimes when
Expand Down
13 changes: 7 additions & 6 deletions src/librustc_ast_lowering/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)),
PatKind::Ref(ref inner, mutbl) => hir::PatKind::Ref(self.lower_pat(inner), mutbl),
PatKind::Range(ref e1, ref e2, Spanned { node: ref end, .. }) => hir::PatKind::Range(
self.lower_expr(e1),
self.lower_expr(e2),
self.lower_range_end(end),
e1.as_deref().map(|e| self.lower_expr(e)),
e2.as_deref().map(|e| self.lower_expr(e)),
self.lower_range_end(end, e2.is_some()),
),
PatKind::Slice(ref pats) => self.lower_pat_slice(pats),
PatKind::Rest => {
Expand Down Expand Up @@ -253,10 +253,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
hir::PatKind::Wild
}

fn lower_range_end(&mut self, e: &RangeEnd) -> hir::RangeEnd {
fn lower_range_end(&mut self, e: &RangeEnd, has_end: bool) -> hir::RangeEnd {
match *e {
RangeEnd::Included(_) => hir::RangeEnd::Included,
RangeEnd::Excluded => hir::RangeEnd::Excluded,
RangeEnd::Excluded if has_end => hir::RangeEnd::Excluded,
// No end; so `X..` behaves like `RangeFrom`.
RangeEnd::Excluded | RangeEnd::Included(_) => hir::RangeEnd::Included,
}
}
}
3 changes: 3 additions & 0 deletions src/librustc_feature/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,9 @@ declare_features! (
/// Allows the use of `#[cfg(sanitize = "option")]`; set when -Zsanitizer is used.
(active, cfg_sanitize, "1.41.0", Some(39699), None),

/// Allows using `..X`, `..=X`, `...X`, and `X..` as a pattern.
(active, half_open_range_patterns, "1.41.0", Some(67264), None),

/// Allows using `&mut` in constant functions.
(active, const_mut_refs, "1.41.0", Some(57349), None),

Expand Down
2 changes: 1 addition & 1 deletion src/librustc_hir/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -905,7 +905,7 @@ pub enum PatKind<'hir> {
Lit(&'hir Expr<'hir>),

/// A range pattern (e.g., `1..=2` or `1..2`).
Range(&'hir Expr<'hir>, &'hir Expr<'hir>, RangeEnd),
Range(Option<&'hir Expr<'hir>>, Option<&'hir Expr<'hir>>, RangeEnd),

/// A slice pattern, `[before_0, ..., before_n, (slice, after_0, ..., after_n)?]`.
///
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_hir/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -766,8 +766,8 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) {
}
PatKind::Lit(ref expression) => visitor.visit_expr(expression),
PatKind::Range(ref lower_bound, ref upper_bound, _) => {
visitor.visit_expr(lower_bound);
visitor.visit_expr(upper_bound)
walk_list!(visitor, visit_expr, lower_bound);
walk_list!(visitor, visit_expr, upper_bound);
}
PatKind::Wild => (),
PatKind::Slice(prepatterns, ref slice_pattern, postpatterns) => {
Expand Down
10 changes: 7 additions & 3 deletions src/librustc_hir/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1767,13 +1767,17 @@ impl<'a> State<'a> {
}
PatKind::Lit(ref e) => self.print_expr(&e),
PatKind::Range(ref begin, ref end, ref end_kind) => {
self.print_expr(&begin);
self.s.space();
if let Some(expr) = begin {
self.print_expr(expr);
self.s.space();
}
match *end_kind {
RangeEnd::Included => self.s.word("..."),
RangeEnd::Excluded => self.s.word(".."),
}
self.print_expr(&end);
if let Some(expr) = end {
self.print_expr(expr);
}
}
PatKind::Slice(ref before, ref slice, ref after) => {
self.s.word("[");
Expand Down
18 changes: 12 additions & 6 deletions src/librustc_lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ use syntax::ast::{self, Expr};
use syntax::attr::{self, HasAttrs};
use syntax::errors::{Applicability, DiagnosticBuilder};
use syntax::print::pprust::{self, expr_to_string};
use syntax::ptr::P;
use syntax::tokenstream::{TokenStream, TokenTree};
use syntax::visit::FnKind;

Expand Down Expand Up @@ -1309,11 +1308,13 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {

/// If `pat` is a `...` pattern, return the start and end of the range, as well as the span
/// corresponding to the ellipsis.
fn matches_ellipsis_pat(pat: &ast::Pat) -> Option<(&P<Expr>, &P<Expr>, Span)> {
fn matches_ellipsis_pat(pat: &ast::Pat) -> Option<(Option<&Expr>, &Expr, Span)> {
match &pat.kind {
PatKind::Range(a, b, Spanned { span, node: RangeEnd::Included(DotDotDot), .. }) => {
Some((a, b, *span))
}
PatKind::Range(
a,
Some(b),
Spanned { span, node: RangeEnd::Included(DotDotDot) },
) => Some((a.as_deref(), b, *span)),
_ => None,
}
}
Expand All @@ -1328,11 +1329,16 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
let suggestion = "use `..=` for an inclusive range";
if parenthesise {
self.node_id = Some(pat.id);
let end = expr_to_string(&end);
let replace = match start {
Some(start) => format!("&({}..={})", expr_to_string(&start), end),
None => format!("&(..={})", end),
};
let mut err = cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, msg);
err.span_suggestion(
pat.span,
suggestion,
format!("&({}..={})", expr_to_string(&start), expr_to_string(&end)),
replace,
Applicability::MachineApplicable,
);
err.emit();
Expand Down
Loading