From 21ea66f47ad059279628736b31a3a8bddd2710d3 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Sat, 13 Dec 2014 16:58:48 +1300 Subject: [PATCH 1/6] Add structs for ranges to core::ops. --- src/libcore/lib.rs | 2 +- src/libcore/ops.rs | 113 +++++++++++++++++++++++++++++++++++++++++ src/libcoretest/ops.rs | 27 ++++++++++ 3 files changed, 141 insertions(+), 1 deletion(-) 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..63451d06cb5 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -51,7 +51,12 @@ //! See the documentation for each trait for a minimum implementation that prints //! something to the screen. +use clone::Clone; +use cmp::Ord; +use iter::{Iterator,DoubleEndedIterator}; use kinds::Sized; +use kinds::Copy; +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 +838,114 @@ pub trait SliceMut for Sized? { fn slice_or_fail_mut<'a>(&'a mut self, from: &Idx, to: &Idx) -> &'a mut Result; } + + +/// REVIEW could be in a better module +/// The `Countable` trait identifies objects which are countable, i.e., are +/// analogous to the natural numbers. A countable object can be incremented and +/// and decremented and ordered. The `difference` function provides a way to +/// compare two Countable objects (it could be provided using increment and Ord, +/// but the implementation would be so inefficient as to be useless). +#[unstable = "Trait is unstable."] +pub trait Countable: Ord { + // FIXME(#19391) needs a snapshot + //type T; + + /// Change self to the next object. + fn increment(&mut self); + /// Change self to the previous object. + fn decrement(&mut self); + /// The difference between two countable objects. + /// Temporarily a uint, should be an associated type, but + // FIXME(#19391) needs a snapshot + fn difference(a: &Self, b: &Self) -> uint; + //fn difference(a: &Self, b: &Self) -> ::T; +} + +macro_rules! countable_impl( + ($($t:ty)*) => ($( + #[unstable = "Trait is unstable."] + impl Countable for $t { + // FIXME(#19391) needs a snapshot + //type T = uint; + + #[inline] + fn increment(&mut self) { *self += 1; } + #[inline] + fn decrement(&mut self) { *self -= 1; } + #[inline] + fn difference(a: &$t, b: &$t) -> uint { (*a - *b) as uint } + } + )*) +) + +countable_impl!(uint u8 u16 u32 u64 int i8 i16 i32 i64) + +/// An unbounded range. +pub struct FullRange; + +/// A range which i bounded at both ends. +pub struct Range { + /// 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> Iterator for Range { +impl Iterator for Range { + #[inline] + fn next(&mut self) -> Option { + if self.start < self.end { + let result = self.start.clone(); + self.start.increment(); + return Some(result); + } + + return None; + } + + #[inline] + fn size_hint(&self) -> (uint, Option) { + let hint = Countable::difference(&self.end, &self.start); + (hint, Some(hint)) + } +} + +impl DoubleEndedIterator for Range { + #[inline] + fn next_back(&mut self) -> Option { + if self.start < self.end { + self.end.decrement(); + return Some(self.end.clone()); + } + + return None; + } +} + +/// A range which is only bounded below. +pub struct RangeFrom { + /// The lower bound of the range (inclusive). + pub start: Idx, +} + +impl Iterator for RangeFrom { + #[inline] + fn next(&mut self) -> Option { + // Deliberately overflow so we loop forever. + let result = self.start.clone(); + self.start.increment(); + return Some(result); + } +} + +impl Copy for Range {} +impl Copy for RangeFrom {} +impl Copy for FullRange {} + + /// 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..c4acef32ee8 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,29 @@ 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 }; + for (i, ri) in r.enumerate() { + assert!(ri == i + 2); + assert!(ri >= 2u && ri < 10u); + } +} + +#[test] +fn test_range_from() { + let r = RangeFrom { start: 2u }; + for (i, ri) in r.take(10).enumerate() { + assert!(ri == i + 2); + assert!(ri >= 2u && ri < 12u); + } +} + +#[test] +fn test_full_range() { + // Not much to test. + let _ = FullRange; +} From 53c5fcb99fc8c62b8723032280fab3dc06fef973 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Sat, 13 Dec 2014 17:19:54 +1300 Subject: [PATCH 2/6] Add lang items for ranges. --- src/libcore/ops.rs | 3 +++ src/librustc/middle/lang_items.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index 63451d06cb5..4e95849111e 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -882,9 +882,11 @@ macro_rules! countable_impl( countable_impl!(uint u8 u16 u32 u64 int i8 i16 i32 i64) /// An unbounded range. +#[lang="full_range"] pub struct FullRange; /// A range which i bounded at both ends. +#[lang="range"] pub struct Range { /// The lower bound of the range (inclusive). pub start: Idx, @@ -926,6 +928,7 @@ impl DoubleEndedIterator for Range { } /// A range which is only bounded below. +#[lang="range_from"] pub struct RangeFrom { /// The lower bound of the range (inclusive). pub start: Idx, 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; From 8a357e1d87971574817a033e5467785402d5fcfb Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Sat, 13 Dec 2014 18:41:02 +1300 Subject: [PATCH 3/6] Add syntax for ranges --- src/librustc/middle/cfg/construct.rs | 4 +++ src/librustc/middle/expr_use_visitor.rs | 4 +++ src/librustc/middle/liveness.rs | 10 ++++++ src/librustc/middle/mem_categorization.rs | 3 ++ src/librustc/middle/ty.rs | 3 ++ src/librustc_back/svh.rs | 2 ++ src/librustc_trans/trans/debuginfo.rs | 5 +++ src/librustc_typeck/check/mod.rs | 4 +++ src/libsyntax/ast.rs | 1 + src/libsyntax/fold.rs | 4 +++ src/libsyntax/parse/parser.rs | 40 ++++++++++++++++++----- src/libsyntax/print/pprust.rs | 7 ++++ src/libsyntax/visit.rs | 4 +++ 13 files changed, 83 insertions(+), 8 deletions(-) diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index 2d50757782d..fe8e90bc32c 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -439,6 +439,10 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { start.iter().chain(end.iter()).map(|x| &**x)) } + ast::ExprRange(..) => { + self.tcx.sess.span_bug(expr.span, "non-desugared range"); + } + ast::ExprUnary(_, ref e) if self.is_method_call(expr) => { self.call(expr, pred, &**e, None::.iter()) } diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 7e31ae04ae0..6a2bb2fc5a3 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -465,6 +465,10 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { assert!(overloaded); } + ast::ExprRange(..) => { + self.tcx().sess.span_bug(expr.span, "non-desugared range"); + } + ast::ExprCall(ref callee, ref args) => { // callee(args) self.walk_callee(expr, &**callee); self.consume_exprs(args); diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 798daf8d541..fe2d7d47cb9 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -486,6 +486,9 @@ fn visit_expr(ir: &mut IrMaps, expr: &Expr) { ast::ExprWhileLet(..) => { ir.tcx.sess.span_bug(expr.span, "non-desugared ExprWhileLet"); } + ast::ExprRange(..) => { + ir.tcx.sess.span_bug(expr.span, "non-desugared range"); + } ast::ExprForLoop(ref pat, _, _, _) => { pat_util::pat_bindings(&ir.tcx.def_map, &**pat, |bm, p_id, sp, path1| { debug!("adding local variable {} from for loop with bm {}", @@ -1197,6 +1200,10 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { self.propagate_through_expr(&**e1, succ) } + ast::ExprRange(..) => { + self.ir.tcx.sess.span_bug(expr.span, "non-desugared range"); + } + ast::ExprBox(None, ref e) | ast::ExprAddrOf(_, ref e) | ast::ExprCast(ref e, _) | @@ -1498,6 +1505,9 @@ fn check_expr(this: &mut Liveness, expr: &Expr) { ast::ExprWhileLet(..) => { this.ir.tcx.sess.span_bug(expr.span, "non-desugared ExprWhileLet"); } + ast::ExprRange(..) => { + this.ir.tcx.sess.span_bug(expr.span, "non-desugared range"); + } } } diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 006515ea0a0..e605471fc06 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -559,6 +559,9 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { ast::ExprWhileLet(..) => { self.tcx().sess.span_bug(expr.span, "non-desugared ExprWhileLet"); } + ast::ExprRange(..) => { + self.tcx().sess.span_bug(expr.span, "non-desugared range"); + } } } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 22fdea8afb5..4c5d3cb5c74 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -4273,6 +4273,9 @@ pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind { ast::ExprWhileLet(..) => { tcx.sess.span_bug(expr.span, "non-desugared ExprWhileLet"); } + ast::ExprRange(..) => { + tcx.sess.span_bug(expr.span, "non-desugared range"); + } ast::ExprLit(ref lit) if lit_is_str(&**lit) => { RvalueDpsExpr 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..f402f1d7c31 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(..) => { + cx.sess().span_bug(exp.span, "debuginfo::populate_scope_map() - \ + Found unexpanded range."); + } + ast::ExprVec(ref init_expressions) | ast::ExprTup(ref init_expressions) => { for ie in init_expressions.iter() { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 3139a17f998..02811861551 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -4278,6 +4278,10 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, } } } + ast::ExprRange(..) => { + tcx.sess.span_bug(expr.span, "non-desugared range"); + } + } 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, Spanned), ExprIndex(P, P), ExprSlice(P, Option>, Option>, Mutability), + ExprRange(P, Option>), /// Variable reference, possibly containing `::` and/or /// type parameters, e.g. foo::bar:: 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(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> { + pub fn maybe_parse_fixed_length_of_vec(&mut self) -> Option> { 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, + pub fn mk_slice(&mut self, + expr: P, start: Option>, end: Option>, mutbl: Mutability) @@ -2138,6 +2140,13 @@ impl<'a> Parser<'a> { ExprSlice(expr, start, end, mutbl) } + pub fn mk_range(&mut self, + start: P, + end: Option>) + -> ast::Expr_ { + ExprRange(start, end) + } + pub fn mk_field(&mut self, expr: P, 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) } From 17826e10a2fbaeb7e75dcfe2e8c7d243536f7d6d Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Mon, 15 Dec 2014 13:17:11 +1300 Subject: [PATCH 4/6] Type checking and trans for ranges --- src/librustc/middle/cfg/construct.rs | 6 ++- src/librustc/middle/expr_use_visitor.rs | 5 ++- src/librustc/middle/liveness.rs | 16 +++----- src/librustc/middle/mem_categorization.rs | 5 +-- src/librustc/middle/ty.rs | 4 +- src/librustc_trans/trans/debuginfo.rs | 6 +-- src/librustc_trans/trans/expr.rs | 46 ++++++++++++++++++++++- src/librustc_typeck/check/mod.rs | 43 +++++++++++++++++++-- 8 files changed, 102 insertions(+), 29 deletions(-) diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index fe8e90bc32c..f50790f7e9b 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -439,8 +439,10 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { start.iter().chain(end.iter()).map(|x| &**x)) } - ast::ExprRange(..) => { - self.tcx.sess.span_bug(expr.span, "non-desugared range"); + 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) => { diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 6a2bb2fc5a3..bf939e98338 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -465,8 +465,9 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { assert!(overloaded); } - ast::ExprRange(..) => { - self.tcx().sess.span_bug(expr.span, "non-desugared range"); + 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) diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index fe2d7d47cb9..f59a67e2e80 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -486,9 +486,6 @@ fn visit_expr(ir: &mut IrMaps, expr: &Expr) { ast::ExprWhileLet(..) => { ir.tcx.sess.span_bug(expr.span, "non-desugared ExprWhileLet"); } - ast::ExprRange(..) => { - ir.tcx.sess.span_bug(expr.span, "non-desugared range"); - } ast::ExprForLoop(ref pat, _, _, _) => { pat_util::pat_bindings(&ir.tcx.def_map, &**pat, |bm, p_id, sp, path1| { debug!("adding local variable {} from for loop with bm {}", @@ -517,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); } } @@ -1200,8 +1197,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { self.propagate_through_expr(&**e1, succ) } - ast::ExprRange(..) => { - self.ir.tcx.sess.span_bug(expr.span, "non-desugared range"); + 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) | @@ -1496,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(..) => { @@ -1505,9 +1504,6 @@ fn check_expr(this: &mut Liveness, expr: &Expr) { ast::ExprWhileLet(..) => { this.ir.tcx.sess.span_bug(expr.span, "non-desugared ExprWhileLet"); } - ast::ExprRange(..) => { - this.ir.tcx.sess.span_bug(expr.span, "non-desugared range"); - } } } diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index e605471fc06..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(..) | @@ -559,9 +559,6 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { ast::ExprWhileLet(..) => { self.tcx().sess.span_bug(expr.span, "non-desugared ExprWhileLet"); } - ast::ExprRange(..) => { - self.tcx().sess.span_bug(expr.span, "non-desugared range"); - } } } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 4c5d3cb5c74..236312dac6e 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -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(..) | @@ -4273,9 +4274,6 @@ pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind { ast::ExprWhileLet(..) => { tcx.sess.span_bug(expr.span, "non-desugared ExprWhileLet"); } - ast::ExprRange(..) => { - tcx.sess.span_bug(expr.span, "non-desugared range"); - } ast::ExprLit(ref lit) if lit_is_str(&**lit) => { RvalueDpsExpr diff --git a/src/librustc_trans/trans/debuginfo.rs b/src/librustc_trans/trans/debuginfo.rs index f402f1d7c31..8fadaa05203 100644 --- a/src/librustc_trans/trans/debuginfo.rs +++ b/src/librustc_trans/trans/debuginfo.rs @@ -3494,9 +3494,9 @@ fn populate_scope_map(cx: &CrateContext, end.as_ref().map(|x| walk_expr(cx, &**x, scope_stack, scope_map)); } - ast::ExprRange(..) => { - cx.sess().span_bug(exp.span, "debuginfo::populate_scope_map() - \ - Found unexpanded range."); + 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) | 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::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 02811861551..4a10954729d 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -4238,7 +4238,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; } }; @@ -4278,8 +4278,45 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, } } } - ast::ExprRange(..) => { - tcx.sess.span_bug(expr.span, "non-desugared range"); + ast::ExprRange(ref start, ref end) => { + let mut some_err = false; + + check_expr(fcx, &**start); + let t_start = fcx.expr_ty(&**start); + if ty::type_is_error(t_start) { + fcx.write_ty(start.id, t_start); + some_err = true; + } + + if let &Some(ref e) = end { + check_expr_has_type(fcx, &**e, t_start); + let t_end = fcx.expr_ty(&**e); + if ty::type_is_error(t_end) { + fcx.write_ty(e.id, t_end); + some_err = true; + } + } + + // Note that we don't check the type of the 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. + + if !some_err { + // Find the did from the appropriate lang item. + let did = if end.is_some() { + // Range + fcx.tcx().lang_items.range_struct() + } else { + // RangeFrom + fcx.tcx().lang_items.range_from_struct() + }; + if let Some(did) = did { + let substs = Substs::new_type(vec![t_start], vec![]); + fcx.write_ty(id, ty::mk_struct(tcx, did, substs)); + } else { + fcx.write_ty(id, ty::mk_err()); + } + } } } From e840e49b21ba5af547e8e76003312f97f13f7b4f Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Mon, 15 Dec 2014 14:23:00 +1300 Subject: [PATCH 5/6] Tests --- src/test/compile-fail/range-1.rs | 26 ++++++++++++++++++++ src/test/compile-fail/range-2.rs | 26 ++++++++++++++++++++ src/test/run-pass/range.rs | 41 ++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 src/test/compile-fail/range-1.rs create mode 100644 src/test/compile-fail/range-2.rs create mode 100644 src/test/run-pass/range.rs diff --git a/src/test/compile-fail/range-1.rs b/src/test/compile-fail/range-1.rs new file mode 100644 index 00000000000..f1751f36b39 --- /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 or the MIT license +// , 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 mismatched types: expected `uint`, found `int` + + // Float => does not implement iterator. + for i in 0f32..42f32 {} + //~^ ERROR `for` loop expression has type `core::ops::Range` 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..e9bb14835ae --- /dev/null +++ b/src/test/compile-fail/range-2.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 or the MIT license +// , 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 x = &42i; + { + let y = 42i; + let r = x..&y; + //~^ ERROR `y` does not live long enough + } + + 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..64eabe19b44 --- /dev/null +++ b/src/test/run-pass/range.rs @@ -0,0 +1,41 @@ +// 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 or the MIT license +// , 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(); +} From e82215d4e28cc8376a3623673c00f1f65f588927 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Tue, 16 Dec 2014 16:25:33 +1300 Subject: [PATCH 6/6] Review changes --- src/libcore/iter.rs | 58 +++++++++++++++ src/libcore/ops.rs | 78 +++++--------------- src/libcoretest/ops.rs | 6 ++ src/librustc/middle/infer/error_reporting.rs | 4 + src/librustc/middle/infer/mod.rs | 7 ++ src/librustc_typeck/check/mod.rs | 59 +++++++++------ src/test/compile-fail/range-1.rs | 2 +- src/test/compile-fail/range-2.rs | 7 -- src/test/run-pass/range.rs | 7 ++ 9 files changed, 139 insertions(+), 89 deletions(-) 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 Iterator for RangeStepInclusive { } } + +/// 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; +} + +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 { + 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 { + 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/ops.rs b/src/libcore/ops.rs index 4e95849111e..0cd8c1d69d1 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -52,10 +52,8 @@ //! something to the screen. use clone::Clone; -use cmp::Ord; -use iter::{Iterator,DoubleEndedIterator}; +use iter::{Step, Iterator,DoubleEndedIterator,ExactSizeIterator}; use kinds::Sized; -use kinds::Copy; use option::Option::{mod, Some, None}; /// The `Drop` trait is used to run some code when a value goes out of scope. This @@ -839,53 +837,13 @@ pub trait SliceMut for Sized? { } - -/// REVIEW could be in a better module -/// The `Countable` trait identifies objects which are countable, i.e., are -/// analogous to the natural numbers. A countable object can be incremented and -/// and decremented and ordered. The `difference` function provides a way to -/// compare two Countable objects (it could be provided using increment and Ord, -/// but the implementation would be so inefficient as to be useless). -#[unstable = "Trait is unstable."] -pub trait Countable: Ord { - // FIXME(#19391) needs a snapshot - //type T; - - /// Change self to the next object. - fn increment(&mut self); - /// Change self to the previous object. - fn decrement(&mut self); - /// The difference between two countable objects. - /// Temporarily a uint, should be an associated type, but - // FIXME(#19391) needs a snapshot - fn difference(a: &Self, b: &Self) -> uint; - //fn difference(a: &Self, b: &Self) -> ::T; -} - -macro_rules! countable_impl( - ($($t:ty)*) => ($( - #[unstable = "Trait is unstable."] - impl Countable for $t { - // FIXME(#19391) needs a snapshot - //type T = uint; - - #[inline] - fn increment(&mut self) { *self += 1; } - #[inline] - fn decrement(&mut self) { *self -= 1; } - #[inline] - fn difference(a: &$t, b: &$t) -> uint { (*a - *b) as uint } - } - )*) -) - -countable_impl!(uint u8 u16 u32 u64 int i8 i16 i32 i64) - /// An unbounded range. +#[deriving(Copy)] #[lang="full_range"] pub struct FullRange; -/// A range which i bounded at both ends. +/// A (half-open) range which is bounded at both ends. +#[deriving(Copy)] #[lang="range"] pub struct Range { /// The lower bound of the range (inclusive). @@ -895,13 +853,13 @@ pub struct Range { } // FIXME(#19391) needs a snapshot -//impl> Iterator for Range { -impl Iterator for Range { +//impl> Iterator for Range { +impl Iterator for Range { #[inline] fn next(&mut self) -> Option { if self.start < self.end { let result = self.start.clone(); - self.start.increment(); + self.start.step(); return Some(result); } @@ -910,16 +868,19 @@ impl Iterator for Range { #[inline] fn size_hint(&self) -> (uint, Option) { - let hint = Countable::difference(&self.end, &self.start); - (hint, Some(hint)) + if let Some(hint) = Step::steps_between(&self.end, &self.start) { + (hint, Some(hint)) + } else { + (0, None) + } } } -impl DoubleEndedIterator for Range { +impl DoubleEndedIterator for Range { #[inline] fn next_back(&mut self) -> Option { if self.start < self.end { - self.end.decrement(); + self.end.step_back(); return Some(self.end.clone()); } @@ -927,27 +888,26 @@ impl DoubleEndedIterator for Range { } } +impl ExactSizeIterator for Range {} + /// A range which is only bounded below. +#[deriving(Copy)] #[lang="range_from"] pub struct RangeFrom { /// The lower bound of the range (inclusive). pub start: Idx, } -impl Iterator for RangeFrom { +impl Iterator for RangeFrom { #[inline] fn next(&mut self) -> Option { // Deliberately overflow so we loop forever. let result = self.start.clone(); - self.start.increment(); + self.start.step(); return Some(result); } } -impl Copy for Range {} -impl Copy for RangeFrom {} -impl Copy for FullRange {} - /// 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 c4acef32ee8..a8889ce9e34 100644 --- a/src/libcoretest/ops.rs +++ b/src/libcoretest/ops.rs @@ -34,19 +34,25 @@ fn alloc_obj_with_dtor(b: &mut Bencher) { #[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] 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_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 4a10954729d..380f34b1445 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -4279,44 +4279,59 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, } } ast::ExprRange(ref start, ref end) => { - let mut some_err = false; - check_expr(fcx, &**start); let t_start = fcx.expr_ty(&**start); - if ty::type_is_error(t_start) { - fcx.write_ty(start.id, t_start); - some_err = true; - } - if let &Some(ref e) = end { - check_expr_has_type(fcx, &**e, t_start); - let t_end = fcx.expr_ty(&**e); - if ty::type_is_error(t_end) { - fcx.write_ty(e.id, t_end); - some_err = true; - } - } + 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 the start/end satisfy any + // 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. - if !some_err { + 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 - fcx.tcx().lang_items.range_struct() + tcx.lang_items.range_struct() } else { // RangeFrom - fcx.tcx().lang_items.range_from_struct() + tcx.lang_items.range_from_struct() }; + if let Some(did) = did { - let substs = Substs::new_type(vec![t_start], vec![]); - fcx.write_ty(id, ty::mk_struct(tcx, did, substs)); + 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 { - fcx.write_ty(id, ty::mk_err()); + ty::mk_err() } - } + }; + fcx.write_ty(id, range_type); } } diff --git a/src/test/compile-fail/range-1.rs b/src/test/compile-fail/range-1.rs index f1751f36b39..ca7401dc26c 100644 --- a/src/test/compile-fail/range-1.rs +++ b/src/test/compile-fail/range-1.rs @@ -13,7 +13,7 @@ pub fn main() { // Mixed types. let _ = 0u..10i; - //~^ ERROR mismatched types: expected `uint`, found `int` + //~^ ERROR start and end of range have incompatible types // Float => does not implement iterator. for i in 0f32..42f32 {} diff --git a/src/test/compile-fail/range-2.rs b/src/test/compile-fail/range-2.rs index e9bb14835ae..40690bd844b 100644 --- a/src/test/compile-fail/range-2.rs +++ b/src/test/compile-fail/range-2.rs @@ -11,13 +11,6 @@ // Test range syntax - borrow errors. pub fn main() { - let x = &42i; - { - let y = 42i; - let r = x..&y; - //~^ ERROR `y` does not live long enough - } - let r = { (&42i)..&42 //~^ ERROR borrowed value does not live long enough diff --git a/src/test/run-pass/range.rs b/src/test/run-pass/range.rs index 64eabe19b44..027121dd422 100644 --- a/src/test/run-pass/range.rs +++ b/src/test/run-pass/range.rs @@ -38,4 +38,11 @@ pub fn main() { 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; + } }