Add hypothetical support for ranges with only an upper bound

Note that this doesn't add the surface syntax.
This commit is contained in:
Nick Cameron 2014-12-18 17:55:04 +13:00
parent 71123902e1
commit ed8f503911
14 changed files with 81 additions and 40 deletions

View file

@ -908,6 +908,14 @@ impl<Idx: Clone + Step> Iterator<Idx> for RangeFrom<Idx> {
}
}
/// A range which is only bounded above.
#[deriving(Copy)]
#[lang="range_to"]
pub struct RangeTo<Idx> {
/// The upper bound of the range (exclusive).
pub end: Idx,
}
/// The `Deref` trait is used to specify the functionality of dereferencing
/// operations like `*v`.

View file

@ -55,6 +55,12 @@ fn test_range_from() {
assert!(count == 10);
}
#[test]
fn test_range_to() {
// Not much to test.
let _ = RangeTo { end: 42u };
}
#[test]
fn test_full_range() {
// Not much to test.

View file

@ -440,7 +440,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
}
ast::ExprRange(ref start, ref end) => {
let fields = Some(&**start).into_iter()
let fields = start.as_ref().map(|e| &**e).into_iter()
.chain(end.as_ref().map(|e| &**e).into_iter());
self.straightline(expr, pred, fields)
}

View file

@ -450,7 +450,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
}
ast::ExprRange(ref start, ref end) => {
self.consume_expr(&**start);
start.as_ref().map(|e| self.consume_expr(&**e));
end.as_ref().map(|e| self.consume_expr(&**e));
}

View file

@ -269,6 +269,7 @@ lets_do_this! {
SliceMutTraitLangItem, "slice_mut", slice_mut_trait;
RangeStructLangItem, "range", range_struct;
RangeFromStructLangItem, "range_from", range_from_struct;
RangeToStructLangItem, "range_to", range_to_struct;
FullRangeStructLangItem, "full_range", full_range_struct;
UnsafeTypeLangItem, "unsafe", unsafe_type;

View file

@ -1199,7 +1199,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
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)
e1.as_ref().map_or(succ, |e| self.propagate_through_expr(&**e, succ))
}
ast::ExprBox(None, ref e) |

View file

@ -3546,7 +3546,7 @@ fn create_scope_map(cx: &CrateContext,
}
ast::ExprRange(ref start, ref end) => {
walk_expr(cx, &**start, scope_stack, scope_map);
start.as_ref().map(|e| walk_expr(cx, &**e, scope_stack, scope_map));
end.as_ref().map(|e| walk_expr(cx, &**e, scope_stack, scope_map));
}

View file

@ -1064,22 +1064,34 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
}
// A range just desugars into a struct.
let (did, fields) = match end {
&Some(ref end) => {
// Note that the type of the start and end may not be the same, but
// they should only differ in their lifetime, which should not matter
// in trans.
let (did, fields, ty_params) = match (start, end) {
(&Some(ref start), &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)
(tcx.lang_items.range_struct(), fields, vec![node_id_type(bcx, start.id)])
}
&None => {
(&Some(ref start), &None) => {
// Desugar to RangeFrom
let fields = vec!(make_field("start", start.clone()));
(tcx.lang_items.range_from_struct(), fields)
(tcx.lang_items.range_from_struct(), fields, vec![node_id_type(bcx, start.id)])
}
(&None, &Some(ref end)) => {
// Desugar to RangeTo
let fields = vec!(make_field("end", end.clone()));
(tcx.lang_items.range_to_struct(), fields, vec![node_id_type(bcx, end.id)])
}
_ => {
// Desugar to FullRange
(tcx.lang_items.full_range_struct(), vec![], vec![])
}
};
if let Some(did) = did {
let substs = Substs::new_type(vec![node_id_type(bcx, start.id)], vec![]);
let substs = Substs::new_type(ty_params, vec![]);
trans_struct(bcx,
fields.as_slice(),
None,

View file

@ -4308,46 +4308,58 @@ 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 {
let t_start = start.as_ref().map(|e| {
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)
fcx.expr_ty(&**e)
});
let t_end = end.as_ref().map(|e| {
check_expr(fcx, &**e);
fcx.expr_ty(&**e)
});
let idx_type = match (t_start, t_end) {
(Some(ty), None) | (None, Some(ty)) => Some(ty),
(Some(t_start), Some(t_end)) if t_start == ty::mk_err() || t_end == ty::mk_err() => {
Some(ty::mk_err())
}
} else {
t_start
(Some(t_start), Some(t_end)) => {
Some(infer::common_supertype(fcx.infcx(),
infer::RangeExpression(expr.span),
true,
t_start,
t_end))
}
_ => None
};
// 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() {
let range_type = if idx_type == Some(ty::mk_err()) {
ty::mk_err()
} else if idx_type.is_none() {
// Neither start nor end => FullRange
if let Some(did) = tcx.lang_items.full_range_struct() {
let substs = Substs::new_type(vec![], vec![]);
ty::mk_struct(tcx, did, substs)
} else {
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()
let did = match (start, end) {
(&Some(_), &Some(_)) => tcx.lang_items.range_struct(),
(&Some(_), &None) => tcx.lang_items.range_from_struct(),
(&None, &Some(_)) => tcx.lang_items.range_to_struct(),
(&None, &None) => {
tcx.sess.span_bug(expr.span,"full range should be dealt with above")
}
};
if let Some(did) = did {
let polytype = ty::lookup_item_type(tcx, did);
let substs = Substs::new_type(vec![idx_type], vec![]);
let substs = Substs::new_type(vec![idx_type.unwrap()], vec![]);
let bounds = polytype.generics.to_bounds(tcx, &substs);
fcx.add_obligations_for_parameters(
traits::ObligationCause::new(expr.span,

View file

@ -724,7 +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>>),
ExprRange(Option<P<Expr>>, Option<P<Expr>>),
/// Variable reference, possibly containing `::` and/or
/// type parameters, e.g. foo::bar::<baz>

View file

@ -1391,7 +1391,7 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span}: Expr, folder: &mut T) ->
m)
}
ExprRange(e1, e2) => {
ExprRange(folder.fold_expr(e1),
ExprRange(e1.map(|x| folder.fold_expr(x)),
e2.map(|x| folder.fold_expr(x)))
}
ExprPath(pth) => ExprPath(folder.fold_path(pth)),

View file

@ -2144,7 +2144,7 @@ impl<'a> Parser<'a> {
start: P<Expr>,
end: Option<P<Expr>>)
-> ast::Expr_ {
ExprRange(start, end)
ExprRange(Some(start), end)
}
pub fn mk_field(&mut self, expr: P<Expr>, ident: ast::SpannedIdent) -> ast::Expr_ {

View file

@ -1760,7 +1760,9 @@ impl<'a> State<'a> {
try!(word(&mut self.s, "]"));
}
ast::ExprRange(ref start, ref end) => {
try!(self.print_expr(&**start));
if let &Some(ref e) = start {
try!(self.print_expr(&**e));
}
try!(word(&mut self.s, ".."));
if let &Some(ref e) = end {
try!(self.print_expr(&**e));

View file

@ -872,7 +872,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
walk_expr_opt(visitor, end)
}
ExprRange(ref start, ref end) => {
visitor.visit_expr(&**start);
walk_expr_opt(visitor, start);
walk_expr_opt(visitor, end)
}
ExprPath(ref path) => {