diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs index 1cd4d7b89d6..9c3e53a1ace 100644 --- a/src/libcore/iter.rs +++ b/src/libcore/iter.rs @@ -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] diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 9b6622a7127..9de723f38ee 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -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; diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index e752fd11ee5..0cd8c1d69d1 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -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'. @@ -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; + } +} + +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`. /// diff --git a/src/libcoretest/ops.rs b/src/libcoretest/ops.rs index 447fd1c699d..a8889ce9e34 100644 --- a/src/libcoretest/ops.rs +++ b/src/libcoretest/ops.rs @@ -9,6 +9,7 @@ // except according to those terms. use test::Bencher; +use core::ops::{Range, FullRange, RangeFrom}; // Overhead of dtors @@ -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; +} diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index 2d50757782d..f50790f7e9b 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -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()) } diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 7e31ae04ae0..bf939e98338 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -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); diff --git a/src/librustc/middle/infer/error_reporting.rs b/src/librustc/middle/infer/error_reporting.rs index 0ea3d415ec5..b57b5554ed6 100644 --- a/src/librustc/middle/infer/error_reporting.rs +++ b/src/librustc/middle/infer/error_reporting.rs @@ -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", }; @@ -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") } diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs index 6d031c86507..07823779216 100644 --- a/src/librustc/middle/infer/mod.rs +++ b/src/librustc/middle/infer/mod.rs @@ -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), } @@ -1084,6 +1087,7 @@ impl TypeOrigin { MatchExpressionArm(match_span, _) => match_span, IfExpression(span) => span, IfExpressionWithNoElse(span) => span, + RangeExpression(span) => span, EquatePredicate(span) => span, } } @@ -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)) } diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 967e7f070c5..ca3087f08c4 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -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; diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 798daf8d541..f59a67e2e80 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -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); } } @@ -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, _) | @@ -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(..) => { diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 006515ea0a0..932a124ed33 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -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(..) | diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index b2cc52ab0a8..9c1259f4120 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -4261,6 +4261,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(..) | diff --git a/src/librustc_back/svh.rs b/src/librustc_back/svh.rs index d40c9ee8af6..c68e9055269 100644 --- a/src/librustc_back/svh.rs +++ b/src/librustc_back/svh.rs @@ -247,6 +247,7 @@ mod svh_visitor { SawExprAssignOp(ast::BinOp), SawExprIndex, SawExprSlice, + SawExprRange, SawExprPath, SawExprAddrOf(ast::Mutability), SawExprRet, @@ -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)), diff --git a/src/librustc_trans/trans/debuginfo.rs b/src/librustc_trans/trans/debuginfo.rs index 6226aace8a8..8fadaa05203 100644 --- a/src/librustc_trans/trans/debuginfo.rs +++ b/src/librustc_trans/trans/debuginfo.rs @@ -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() { diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index 3388a7623e3..6f9990a3e9e 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -39,7 +39,7 @@ use back::abi; use llvm::{mod, ValueRef}; use middle::def; use middle::mem_categorization::Typer; -use middle::subst::{mod, Subst}; +use middle::subst::{mod, Subst, Substs}; use trans::{_match, adt, asm, base, callee, closure, consts, controlflow}; use trans::base::*; use trans::build::*; @@ -66,6 +66,7 @@ use trans::type_::Type; use syntax::{ast, ast_util, codemap}; use syntax::print::pprust::{expr_to_string}; use syntax::ptr::P; +use syntax::parse::token; use std::rc::Rc; // Destinations @@ -1048,8 +1049,49 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, base.as_ref().map(|e| &**e), expr.span, expr.id, + node_id_type(bcx, expr.id), dest) } + ast::ExprRange(ref start, ref end) => { + // FIXME it is just not right that we are synthesising ast nodes in + // trans. Shudder. + fn make_field(field_name: &str, expr: P<ast::Expr>) -> ast::Field { + ast::Field { + ident: codemap::dummy_spanned(token::str_to_ident(field_name)), + expr: expr, + span: codemap::DUMMY_SP, + } + } + + // A range just desugars into a struct. + let (did, fields) = match end { + &Some(ref end) => { + // Desugar to Range + let fields = vec!(make_field("start", start.clone()), + make_field("end", end.clone())); + (tcx.lang_items.range_struct(), fields) + } + &None => { + // Desugar to RangeFrom + let fields = vec!(make_field("start", start.clone())); + (tcx.lang_items.range_from_struct(), fields) + } + }; + + if let Some(did) = did { + let substs = Substs::new_type(vec![node_id_type(bcx, start.id)], vec![]); + trans_struct(bcx, + fields.as_slice(), + None, + expr.span, + expr.id, + ty::mk_struct(tcx, did, substs), + dest) + } else { + tcx.sess.span_bug(expr.span, + "No lang item for ranges (how did we get this far?)") + } + } ast::ExprTup(ref args) => { let numbered_fields: Vec<(uint, &ast::Expr)> = args.iter().enumerate().map(|(i, arg)| (i, &**arg)).collect(); @@ -1347,10 +1389,10 @@ fn trans_struct<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, base: Option<&ast::Expr>, expr_span: codemap::Span, expr_id: ast::NodeId, + ty: Ty<'tcx>, dest: Dest) -> Block<'blk, 'tcx> { let _icx = push_ctxt("trans_rec"); - let ty = node_id_type(bcx, expr_id); let tcx = bcx.tcx(); with_field_tys(tcx, ty, Some(expr_id), |discr, field_tys| { let mut need_base = Vec::from_elem(field_tys.len(), true); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index fbd40ef6fed..abcd00ddcce 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -4251,7 +4251,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, check_expr(fcx, e); let e_t = fcx.expr_ty(e); if ty::type_is_error(e_t) { - fcx.write_ty(id, e_t); + fcx.write_ty(e.id, e_t); some_err = true; } }; @@ -4291,6 +4291,62 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, } } } + ast::ExprRange(ref start, ref end) => { + check_expr(fcx, &**start); + let t_start = fcx.expr_ty(&**start); + + let idx_type = if let &Some(ref e) = end { + check_expr(fcx, &**e); + let t_end = fcx.expr_ty(&**e); + if ty::type_is_error(t_end) { + ty::mk_err() + } else if t_start == ty::mk_err() { + ty::mk_err() + } else { + infer::common_supertype(fcx.infcx(), + infer::RangeExpression(expr.span), + true, + t_start, + t_end) + } + } else { + t_start + }; + + // Note that we don't check the type of start/end satisfy any + // bounds because right the range structs do not have any. If we add + // some bounds, then we'll need to check `t_start` against them here. + + let range_type = if idx_type == ty::mk_err() { + ty::mk_err() + } else { + // Find the did from the appropriate lang item. + let did = if end.is_some() { + // Range + tcx.lang_items.range_struct() + } else { + // RangeFrom + tcx.lang_items.range_from_struct() + }; + + if let Some(did) = did { + let polytype = ty::lookup_item_type(tcx, did); + let substs = Substs::new_type(vec![idx_type], vec![]); + let bounds = polytype.generics.to_bounds(tcx, &substs); + fcx.add_obligations_for_parameters( + traits::ObligationCause::new(expr.span, + fcx.body_id, + traits::ItemObligation(did)), + &bounds); + + ty::mk_struct(tcx, did, substs) + } else { + ty::mk_err() + } + }; + fcx.write_ty(id, range_type); + } + } debug!("type of expr({}) {} is...", expr.id, diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 9d4bf77d4a5..0c8c17b080b 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -724,6 +724,7 @@ pub enum Expr_ { ExprTupField(P<Expr>, Spanned<uint>), ExprIndex(P<Expr>, P<Expr>), ExprSlice(P<Expr>, Option<P<Expr>>, Option<P<Expr>>, Mutability), + ExprRange(P<Expr>, Option<P<Expr>>), /// Variable reference, possibly containing `::` and/or /// type parameters, e.g. foo::bar::<baz> diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 86df5883864..0803de1bb53 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1390,6 +1390,10 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span}: Expr, folder: &mut T) -> e2.map(|x| folder.fold_expr(x)), m) } + ExprRange(e1, e2) => { + ExprRange(folder.fold_expr(e1), + e2.map(|x| folder.fold_expr(x))) + } ExprPath(pth) => ExprPath(folder.fold_path(pth)), ExprBreak(opt_ident) => ExprBreak(opt_ident.map(|x| folder.fold_ident(x))), ExprAgain(opt_ident) => ExprAgain(opt_ident.map(|x| folder.fold_ident(x))), diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 197970317d2..94b61ba56d2 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -26,7 +26,7 @@ use ast::{Expr, Expr_, ExprAddrOf, ExprMatch, ExprAgain}; use ast::{ExprAssign, ExprAssignOp, ExprBinary, ExprBlock, ExprBox}; use ast::{ExprBreak, ExprCall, ExprCast}; use ast::{ExprField, ExprTupField, ExprClosure, ExprIf, ExprIfLet, ExprIndex, ExprSlice}; -use ast::{ExprLit, ExprLoop, ExprMac}; +use ast::{ExprLit, ExprLoop, ExprMac, ExprRange}; use ast::{ExprMethodCall, ExprParen, ExprPath}; use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary}; use ast::{ExprVec, ExprWhile, ExprWhileLet, ExprForLoop, Field, FnDecl}; @@ -95,7 +95,8 @@ bitflags! { const UNRESTRICTED = 0b0000, const RESTRICTION_STMT_EXPR = 0b0001, const RESTRICTION_NO_BAR_OP = 0b0010, - const RESTRICTION_NO_STRUCT_LITERAL = 0b0100 + const RESTRICTION_NO_STRUCT_LITERAL = 0b0100, + const RESTRICTION_NO_DOTS = 0b1000, } } @@ -1547,7 +1548,7 @@ impl<'a> Parser<'a> { // Parse the `; e` in `[ int; e ]` // where `e` is a const expression - let t = match self.maybe_parse_fixed_vstore() { + let t = match self.maybe_parse_fixed_length_of_vec() { None => TyVec(t), Some(suffix) => TyFixedLengthVec(t, suffix) }; @@ -1707,12 +1708,12 @@ impl<'a> Parser<'a> { } } - pub fn maybe_parse_fixed_vstore(&mut self) -> Option<P<ast::Expr>> { + pub fn maybe_parse_fixed_length_of_vec(&mut self) -> Option<P<ast::Expr>> { if self.check(&token::Comma) && self.look_ahead(1, |t| *t == token::DotDot) { self.bump(); self.bump(); - Some(self.parse_expr()) + Some(self.parse_expr_res(RESTRICTION_NO_DOTS)) } else if self.check(&token::Semi) { self.bump(); Some(self.parse_expr()) @@ -2130,7 +2131,8 @@ impl<'a> Parser<'a> { ExprIndex(expr, idx) } - pub fn mk_slice(&mut self, expr: P<Expr>, + pub fn mk_slice(&mut self, + expr: P<Expr>, start: Option<P<Expr>>, end: Option<P<Expr>>, mutbl: Mutability) @@ -2138,6 +2140,13 @@ impl<'a> Parser<'a> { ExprSlice(expr, start, end, mutbl) } + pub fn mk_range(&mut self, + start: P<Expr>, + end: Option<P<Expr>>) + -> ast::Expr_ { + ExprRange(start, end) + } + pub fn mk_field(&mut self, expr: P<Expr>, ident: ast::SpannedIdent) -> ast::Expr_ { ExprField(expr, ident) } @@ -2615,7 +2624,7 @@ impl<'a> Parser<'a> { } // e[e] | e[e..] | e[e..e] _ => { - let ix = self.parse_expr(); + let ix = self.parse_expr_res(RESTRICTION_NO_DOTS); match self.token { // e[e..] | e[e..e] token::DotDot => { @@ -2628,7 +2637,7 @@ impl<'a> Parser<'a> { } // e[e..e] _ => { - let e2 = self.parse_expr(); + let e2 = self.parse_expr_res(RESTRICTION_NO_DOTS); self.commit_expr_expecting(&*e2, token::CloseDelim(token::Bracket)); Some(e2) @@ -2654,6 +2663,21 @@ impl<'a> Parser<'a> { } } + // A range expression, either `expr..expr` or `expr..`. + token::DotDot if !self.restrictions.contains(RESTRICTION_NO_DOTS) => { + self.bump(); + + let opt_end = if self.token.can_begin_expr() { + let end = self.parse_expr_res(RESTRICTION_NO_DOTS); + Some(end) + } else { + None + }; + + let hi = self.span.hi; + let range = self.mk_range(e, opt_end); + return self.mk_expr(lo, hi, range); + } _ => return e } } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 21410395a90..3d53bd8aadf 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1759,6 +1759,13 @@ impl<'a> State<'a> { } try!(word(&mut self.s, "]")); } + ast::ExprRange(ref start, ref end) => { + try!(self.print_expr(&**start)); + try!(word(&mut self.s, "..")); + if let &Some(ref e) = end { + try!(self.print_expr(&**e)); + } + } ast::ExprPath(ref path) => try!(self.print_path(path, true)), ast::ExprBreak(opt_ident) => { try!(word(&mut self.s, "break")); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 9938feb171e..4cc93467a7c 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -871,6 +871,10 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) { walk_expr_opt(visitor, start); walk_expr_opt(visitor, end) } + ExprRange(ref start, ref end) => { + visitor.visit_expr(&**start); + walk_expr_opt(visitor, end) + } ExprPath(ref path) => { visitor.visit_path(path, expression.id) } diff --git a/src/test/compile-fail/range-1.rs b/src/test/compile-fail/range-1.rs new file mode 100644 index 00000000000..ca7401dc26c --- /dev/null +++ b/src/test/compile-fail/range-1.rs @@ -0,0 +1,26 @@ +// Copyright 2014 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test range syntax - type errors. + +pub fn main() { + // Mixed types. + let _ = 0u..10i; + //~^ ERROR start and end of range have incompatible types + + // Float => does not implement iterator. + for i in 0f32..42f32 {} + //~^ ERROR `for` loop expression has type `core::ops::Range<f32>` which does not implement + + // Unsized type. + let arr: &[_] = &[1u, 2, 3]; + let range = (*arr)..; + //~^ ERROR the trait `core::kinds::Sized` is not implemented +} diff --git a/src/test/compile-fail/range-2.rs b/src/test/compile-fail/range-2.rs new file mode 100644 index 00000000000..40690bd844b --- /dev/null +++ b/src/test/compile-fail/range-2.rs @@ -0,0 +1,19 @@ +// Copyright 2014 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test range syntax - borrow errors. + +pub fn main() { + let r = { + (&42i)..&42 + //~^ ERROR borrowed value does not live long enough + //~^^ ERROR borrowed value does not live long enough + }; +} diff --git a/src/test/run-pass/range.rs b/src/test/run-pass/range.rs new file mode 100644 index 00000000000..027121dd422 --- /dev/null +++ b/src/test/run-pass/range.rs @@ -0,0 +1,48 @@ +// Copyright 2014 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test range syntax. + +fn foo() -> int { 42 } + +pub fn main() { + let mut count = 0; + for i in 0u..10 { + assert!(i >= 0 && i < 10); + count += i; + } + assert!(count == 45); + + let mut count = 0; + let mut range = 0u..10; + for i in range { + assert!(i >= 0 && i < 10); + count += i; + } + assert!(count == 45); + + let mut count = 0; + let mut rf = 3u..; + for i in rf.take(10) { + assert!(i >= 3 && i < 13); + count += i; + } + assert!(count == 75); + + let _ = 0u..4+4-3; + let _ = 0..foo(); + + // Test we can use two different types with a common supertype. + let x = &42i; + { + let y = 42i; + let _ = x..&y; + } +}