Skip to content

Implement range syntax #19858

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 6 commits into from
Dec 24, 2014
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
58 changes: 58 additions & 0 deletions src/libcore/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2542,6 +2542,64 @@ impl<A: Int> Iterator<A> for RangeStepInclusive<A> {
}
}


/// The `Step` trait identifies objects which can be stepped over in both
/// directions. The `steps_between` function provides a way to
/// compare two Step objects (it could be provided using `step()` and `Ord`,
/// but the implementation would be so inefficient as to be useless).
#[unstable = "Trait is unstable."]
pub trait Step: Ord {
/// Change self to the next object.
fn step(&mut self);
/// Change self to the previous object.
fn step_back(&mut self);
/// The steps_between two step objects.
/// a should always be less than b, so the result should never be negative.
/// Return None if it is not possible to calculate steps_between without
/// overflow.
fn steps_between(a: &Self, b: &Self) -> Option<uint>;
}

macro_rules! step_impl {
($($t:ty)*) => ($(
#[unstable = "Trait is unstable."]
impl Step for $t {
#[inline]
fn step(&mut self) { *self += 1; }
#[inline]
fn step_back(&mut self) { *self -= 1; }
#[inline]
fn steps_between(a: &$t, b: &$t) -> Option<uint> {
debug_assert!(a < b);
Some((*a - *b) as uint)
}
}
)*)
}

macro_rules! step_impl_no_between {
($($t:ty)*) => ($(
#[unstable = "Trait is unstable."]
impl Step for $t {
#[inline]
fn step(&mut self) { *self += 1; }
#[inline]
fn step_back(&mut self) { *self -= 1; }
#[inline]
fn steps_between(_a: &$t, _b: &$t) -> Option<uint> {
None
}
}
)*)
}

step_impl!(uint u8 u16 u32 int i8 i16 i32);
#[cfg(target_word_size = "64")]
step_impl!(u64 i64);
#[cfg(target_word_size = "32")]
step_impl_no_between!(u64 i64);


/// An iterator that repeats an element endlessly
#[deriving(Clone)]
#[stable]
Expand Down
2 changes: 1 addition & 1 deletion src/libcore/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
#![allow(unknown_features, raw_pointer_deriving)]
#![feature(globs, intrinsics, lang_items, macro_rules, phase)]
#![feature(simd, unsafe_destructor, slicing_syntax)]
#![feature(default_type_params, unboxed_closures)]
#![feature(default_type_params, unboxed_closures, associated_types)]
#![deny(missing_docs)]

mod macros;
Expand Down
76 changes: 76 additions & 0 deletions src/libcore/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@
//! See the documentation for each trait for a minimum implementation that prints
//! something to the screen.

use clone::Clone;
use iter::{Step, Iterator,DoubleEndedIterator,ExactSizeIterator};
use kinds::Sized;
use option::Option::{mod, Some, None};

/// The `Drop` trait is used to run some code when a value goes out of scope. This
/// is sometimes called a 'destructor'.
Expand Down Expand Up @@ -833,6 +836,79 @@ pub trait SliceMut<Sized? Idx, Sized? Result> for Sized? {
fn slice_or_fail_mut<'a>(&'a mut self, from: &Idx, to: &Idx) -> &'a mut Result;
}


/// An unbounded range.
#[deriving(Copy)]
#[lang="full_range"]
pub struct FullRange;

/// A (half-open) range which is bounded at both ends.
#[deriving(Copy)]
#[lang="range"]
pub struct Range<Idx> {
/// The lower bound of the range (inclusive).
pub start: Idx,
/// The upper bound of the range (exclusive).
pub end: Idx,
}

// FIXME(#19391) needs a snapshot
//impl<Idx: Clone + Step<T=uint>> Iterator<Idx> for Range<Idx> {
impl<Idx: Clone + Step> Iterator<Idx> for Range<Idx> {
#[inline]
fn next(&mut self) -> Option<Idx> {
if self.start < self.end {
let result = self.start.clone();
self.start.step();
return Some(result);
}

return None;
}

#[inline]
fn size_hint(&self) -> (uint, Option<uint>) {
if let Some(hint) = Step::steps_between(&self.end, &self.start) {
(hint, Some(hint))
} else {
(0, None)
}
}
}

impl<Idx: Clone + Step> DoubleEndedIterator<Idx> for Range<Idx> {
#[inline]
fn next_back(&mut self) -> Option<Idx> {
if self.start < self.end {
self.end.step_back();
return Some(self.end.clone());
}

return None;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might as well implement ExactSizeIterator as well.


impl<Idx: Clone + Step> ExactSizeIterator<Idx> for Range<Idx> {}

/// A range which is only bounded below.
#[deriving(Copy)]
#[lang="range_from"]
pub struct RangeFrom<Idx> {
/// The lower bound of the range (inclusive).
pub start: Idx,
}

impl<Idx: Clone + Step> Iterator<Idx> for RangeFrom<Idx> {
#[inline]
fn next(&mut self) -> Option<Idx> {
// Deliberately overflow so we loop forever.
let result = self.start.clone();
self.start.step();
return Some(result);
}
}


/// The `Deref` trait is used to specify the functionality of dereferencing
/// operations like `*v`.
///
Expand Down
33 changes: 33 additions & 0 deletions src/libcoretest/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
// except according to those terms.

use test::Bencher;
use core::ops::{Range, FullRange, RangeFrom};

// Overhead of dtors

Expand All @@ -27,3 +28,35 @@ fn alloc_obj_with_dtor(b: &mut Bencher) {
HasDtor { _x : 10 };
})
}

// Test the Range structs without the syntactic sugar.

#[test]
fn test_range() {
let r = Range { start: 2u, end: 10 };
let mut count = 0u;
for (i, ri) in r.enumerate() {
assert!(ri == i + 2);
assert!(ri >= 2u && ri < 10u);
count += 1;
}
assert!(count == 8);
}

#[test]
fn test_range_from() {
let r = RangeFrom { start: 2u };
let mut count = 0u;
for (i, ri) in r.take(10).enumerate() {
assert!(ri == i + 2);
assert!(ri >= 2u && ri < 12u);
count += 1;
}
assert!(count == 10);
}

#[test]
fn test_full_range() {
// Not much to test.
let _ = FullRange;
}
6 changes: 6 additions & 0 deletions src/librustc/middle/cfg/construct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,12 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
start.iter().chain(end.iter()).map(|x| &**x))
}

ast::ExprRange(ref start, ref end) => {
let fields = Some(&**start).into_iter()
.chain(end.as_ref().map(|e| &**e).into_iter());
self.straightline(expr, pred, fields)
}

ast::ExprUnary(_, ref e) if self.is_method_call(expr) => {
self.call(expr, pred, &**e, None::<ast::Expr>.iter())
}
Expand Down
5 changes: 5 additions & 0 deletions src/librustc/middle/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,11 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
assert!(overloaded);
}

ast::ExprRange(ref start, ref end) => {
self.consume_expr(&**start);
end.as_ref().map(|e| self.consume_expr(&**e));
}

ast::ExprCall(ref callee, ref args) => { // callee(args)
self.walk_callee(expr, &**callee);
self.consume_exprs(args);
Expand Down
4 changes: 4 additions & 0 deletions src/librustc/middle/infer/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
infer::MatchExpressionArm(_, _) => "match arms have incompatible types",
infer::IfExpression(_) => "if and else have incompatible types",
infer::IfExpressionWithNoElse(_) => "if may be missing an else clause",
infer::RangeExpression(_) => "start and end of range have incompatible types",
infer::EquatePredicate(_) => "equality predicate not satisfied",
};

Expand Down Expand Up @@ -1490,6 +1491,9 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> {
infer::IfExpressionWithNoElse(_) => {
format!("if may be missing an else clause")
}
infer::RangeExpression(_) => {
format!("start and end of range have compatible types")
}
infer::EquatePredicate(_) => {
format!("equality where clause is satisfied")
}
Expand Down
7 changes: 7 additions & 0 deletions src/librustc/middle/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ pub enum TypeOrigin {
// Computing common supertype of an if expression with no else counter-part
IfExpressionWithNoElse(Span),

// Computing common supertype in a range expression
RangeExpression(Span),

// `where a == b`
EquatePredicate(Span),
}
Expand Down Expand Up @@ -1084,6 +1087,7 @@ impl TypeOrigin {
MatchExpressionArm(match_span, _) => match_span,
IfExpression(span) => span,
IfExpressionWithNoElse(span) => span,
RangeExpression(span) => span,
EquatePredicate(span) => span,
}
}
Expand Down Expand Up @@ -1117,6 +1121,9 @@ impl<'tcx> Repr<'tcx> for TypeOrigin {
IfExpressionWithNoElse(a) => {
format!("IfExpressionWithNoElse({})", a.repr(tcx))
}
RangeExpression(a) => {
format!("RangeExpression({})", a.repr(tcx))
}
EquatePredicate(a) => {
format!("EquatePredicate({})", a.repr(tcx))
}
Expand Down
3 changes: 3 additions & 0 deletions src/librustc/middle/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@ lets_do_this! {
IndexMutTraitLangItem, "index_mut", index_mut_trait;
SliceTraitLangItem, "slice", slice_trait;
SliceMutTraitLangItem, "slice_mut", slice_mut_trait;
RangeStructLangItem, "range", range_struct;
RangeFromStructLangItem, "range_from", range_from_struct;
FullRangeStructLangItem, "full_range", full_range_struct;

UnsafeTypeLangItem, "unsafe", unsafe_type;

Expand Down
10 changes: 8 additions & 2 deletions src/librustc/middle/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ fn visit_expr(ir: &mut IrMaps, expr: &Expr) {
ast::ExprBlock(..) | ast::ExprAssign(..) | ast::ExprAssignOp(..) |
ast::ExprMac(..) | ast::ExprStruct(..) | ast::ExprRepeat(..) |
ast::ExprParen(..) | ast::ExprInlineAsm(..) | ast::ExprBox(..) |
ast::ExprSlice(..) => {
ast::ExprSlice(..) | ast::ExprRange(..) => {
visit::walk_expr(ir, expr);
}
}
Expand Down Expand Up @@ -1197,6 +1197,11 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
self.propagate_through_expr(&**e1, succ)
}

ast::ExprRange(ref e1, ref e2) => {
let succ = e2.as_ref().map_or(succ, |e| self.propagate_through_expr(&**e, succ));
self.propagate_through_expr(&**e1, succ)
}

ast::ExprBox(None, ref e) |
ast::ExprAddrOf(_, ref e) |
ast::ExprCast(ref e, _) |
Expand Down Expand Up @@ -1489,7 +1494,8 @@ fn check_expr(this: &mut Liveness, expr: &Expr) {
ast::ExprBreak(..) | ast::ExprAgain(..) | ast::ExprLit(_) |
ast::ExprBlock(..) | ast::ExprMac(..) | ast::ExprAddrOf(..) |
ast::ExprStruct(..) | ast::ExprRepeat(..) | ast::ExprParen(..) |
ast::ExprClosure(..) | ast::ExprPath(..) | ast::ExprBox(..) | ast::ExprSlice(..) => {
ast::ExprClosure(..) | ast::ExprPath(..) | ast::ExprBox(..) |
ast::ExprSlice(..) | ast::ExprRange(..) => {
visit::walk_expr(this, expr);
}
ast::ExprIfLet(..) => {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/mem_categorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
ast::ExprAddrOf(..) | ast::ExprCall(..) |
ast::ExprAssign(..) | ast::ExprAssignOp(..) |
ast::ExprClosure(..) | ast::ExprRet(..) |
ast::ExprUnary(..) | ast::ExprSlice(..) |
ast::ExprUnary(..) | ast::ExprSlice(..) | ast::ExprRange(..) |
ast::ExprMethodCall(..) | ast::ExprCast(..) |
ast::ExprVec(..) | ast::ExprTup(..) | ast::ExprIf(..) |
ast::ExprBinary(..) | ast::ExprWhile(..) |
Expand Down
1 change: 1 addition & 0 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4257,6 +4257,7 @@ pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind {
ast::ExprCall(..) |
ast::ExprMethodCall(..) |
ast::ExprStruct(..) |
ast::ExprRange(..) |
ast::ExprTup(..) |
ast::ExprIf(..) |
ast::ExprMatch(..) |
Expand Down
2 changes: 2 additions & 0 deletions src/librustc_back/svh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ mod svh_visitor {
SawExprAssignOp(ast::BinOp),
SawExprIndex,
SawExprSlice,
SawExprRange,
SawExprPath,
SawExprAddrOf(ast::Mutability),
SawExprRet,
Expand Down Expand Up @@ -280,6 +281,7 @@ mod svh_visitor {
ExprTupField(_, id) => SawExprTupField(id.node),
ExprIndex(..) => SawExprIndex,
ExprSlice(..) => SawExprSlice,
ExprRange(..) => SawExprRange,
ExprPath(..) => SawExprPath,
ExprAddrOf(m, _) => SawExprAddrOf(m),
ExprBreak(id) => SawExprBreak(id.map(content)),
Expand Down
5 changes: 5 additions & 0 deletions src/librustc_trans/trans/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3494,6 +3494,11 @@ fn populate_scope_map(cx: &CrateContext,
end.as_ref().map(|x| walk_expr(cx, &**x, scope_stack, scope_map));
}

ast::ExprRange(ref start, ref end) => {
walk_expr(cx, &**start, scope_stack, scope_map);
end.as_ref().map(|e| walk_expr(cx, &**e, scope_stack, scope_map));
}

ast::ExprVec(ref init_expressions) |
ast::ExprTup(ref init_expressions) => {
for ie in init_expressions.iter() {
Expand Down
Loading