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;
+    }
+}