diff --git a/src/doc/rust.md b/src/doc/rust.md index eb97a75e7660b..fb2407e516381 100644 --- a/src/doc/rust.md +++ b/src/doc/rust.md @@ -2555,6 +2555,8 @@ The currently implemented features of the reference compiler are: which is considered wildly unsafe and will be obsoleted by language improvements. +* `tuple_indexing` - Allows use of tuple indexing (expressions like `expr.0`) + If a feature is promoted to a language feature, then all existing programs will start to receive compilation warnings about #[feature] directives which enabled the new feature (because the directive is no longer necessary). However, if diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs index c83b81660d58f..225fc28cd6d98 100644 --- a/src/librustc/front/feature_gate.rs +++ b/src/librustc/front/feature_gate.rs @@ -70,6 +70,7 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[ ("unboxed_closures", Active), ("import_shadowing", Active), ("advanced_slice_patterns", Active), + ("tuple_indexing", Active), // if you change this list without updating src/doc/rust.md, cmr will be sad @@ -338,6 +339,11 @@ impl<'a> Visitor<()> for Context<'a> { "unboxed closures are a work-in-progress \ feature with known bugs"); } + ast::ExprTupField(..) => { + self.gate_feature("tuple_indexing", + e.span, + "tuple indexing is experimental"); + } _ => {} } visit::walk_expr(self, e, ()); diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 721e5f296e2e7..138947e8a873b 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -1056,6 +1056,7 @@ impl UnnecessaryParens { ast::ExprUnary(_, ref x) | ast::ExprCast(ref x, _) | ast::ExprField(ref x, _, _) | + ast::ExprTupField(ref x, _, _) | ast::ExprIndex(ref x, _) => { // &X { y: 1 }, X { y: 1 }.y contains_exterior_struct_lit(&**x) diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index f4d3678271c0d..5969e7e0c42d5 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -807,7 +807,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { out.push_str(token::get_name(fname).get()); } mc::PositionalField(idx) => { - out.push_char('#'); // invent a notation here + out.push_char('.'); out.push_str(idx.to_string().as_slice()); } } diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index ec414b858187a..6e9b27655af79 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -467,7 +467,8 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { ast::ExprCast(e, _) | ast::ExprUnary(_, e) | ast::ExprParen(e) | - ast::ExprField(e, _, _) => { + ast::ExprField(e, _, _) | + ast::ExprTupField(e, _, _) => { self.straightline(expr, pred, [e]) } diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index 3c8db8d048076..a9a3d94ded897 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -173,6 +173,7 @@ fn check_expr(v: &mut CheckCrateVisitor, e: &Expr, is_const: bool) { ExprAddrOf(MutImmutable, _) | ExprParen(..) | ExprField(..) | + ExprTupField(..) | ExprIndex(..) | ExprTup(..) | ExprRepeat(..) | diff --git a/src/librustc/middle/check_static.rs b/src/librustc/middle/check_static.rs index 7b00bb4589c71..ca58b4b6e60a4 100644 --- a/src/librustc/middle/check_static.rs +++ b/src/librustc/middle/check_static.rs @@ -106,7 +106,7 @@ impl<'a, 'tcx> Visitor for CheckStaticVisitor<'a, 'tcx> { } match e.node { - ast::ExprField(..) | ast::ExprVec(..) | + ast::ExprField(..) | ast::ExprTupField(..) | ast::ExprVec(..) | ast::ExprBlock(..) | ast::ExprTup(..) => { visit::walk_expr(self, e, is_const); } diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 605c90a49c667..2b4b6756f9f85 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -225,6 +225,8 @@ impl<'a, 'tcx> ConstEvalVisitor<'a, 'tcx> { ast::ExprField(ref base, _, _) => self.classify(&**base), + ast::ExprTupField(ref base, _, _) => self.classify(&**base), + ast::ExprIndex(ref base, ref idx) => join(self.classify(&**base), self.classify(&**idx)), diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index b7cddd0c23f96..f275c81871659 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -145,6 +145,17 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { } } + fn handle_tup_field_access(&mut self, lhs: &ast::Expr, idx: uint) { + match ty::get(ty::expr_ty_adjusted(self.tcx, lhs)).sty { + ty::ty_struct(id, _) => { + let fields = ty::lookup_struct_fields(self.tcx, id); + let field_id = fields[idx].id; + self.live_symbols.insert(field_id.node); + }, + _ => () + } + } + fn handle_field_pattern_match(&mut self, lhs: &ast::Pat, pats: &[ast::FieldPat]) { let id = match self.tcx.def_map.borrow().get(&lhs.id) { &def::DefVariant(_, id, _) => id, @@ -255,6 +266,9 @@ impl<'a, 'tcx> Visitor for MarkSymbolVisitor<'a, 'tcx> ast::ExprField(ref lhs, ref ident, _) => { self.handle_field_access(&**lhs, &ident.node); } + ast::ExprTupField(ref lhs, idx, _) => { + self.handle_tup_field_access(&**lhs, idx.node); + } _ => () } diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 207620b182907..d2362b7e9429b 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -324,6 +324,10 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,TYPER> { self.select_from_expr(&**base); } + ast::ExprTupField(ref base, _, _) => { // base. + self.select_from_expr(&**base); + } + ast::ExprIndex(ref lhs, ref rhs) => { // lhs[rhs] if !self.walk_overloaded_operator(expr, &**lhs, [rhs.clone()]) { self.select_from_expr(&**lhs); diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 26aa51b909944..84fc8ff2c38ca 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -511,7 +511,7 @@ fn visit_expr(ir: &mut IrMaps, expr: &Expr) { } // otherwise, live nodes are not required: - ExprIndex(..) | ExprField(..) | ExprVec(..) | + ExprIndex(..) | ExprField(..) | ExprTupField(..) | ExprVec(..) | ExprCall(..) | ExprMethodCall(..) | ExprTup(..) | ExprBinary(..) | ExprAddrOf(..) | ExprCast(..) | ExprUnary(..) | ExprBreak(_) | @@ -965,6 +965,10 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { self.propagate_through_expr(&**e, succ) } + ExprTupField(ref e, _, _) => { + self.propagate_through_expr(&**e, succ) + } + ExprFnBlock(_, _, ref blk) | ExprProc(_, ref blk) | ExprUnboxedFn(_, _, _, ref blk) => { @@ -1271,6 +1275,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { match expr.node { ExprPath(_) => succ, ExprField(ref e, _, _) => self.propagate_through_expr(&**e, succ), + ExprTupField(ref e, _, _) => self.propagate_through_expr(&**e, succ), _ => self.propagate_through_expr(expr, succ) } } @@ -1445,7 +1450,7 @@ fn check_expr(this: &mut Liveness, expr: &Expr) { // no correctness conditions related to liveness ExprCall(..) | ExprMethodCall(..) | ExprIf(..) | ExprMatch(..) | ExprWhile(..) | ExprLoop(..) | ExprIndex(..) | ExprField(..) | - ExprVec(..) | ExprTup(..) | ExprBinary(..) | + ExprTupField(..) | ExprVec(..) | ExprTup(..) | ExprBinary(..) | ExprCast(..) | ExprUnary(..) | ExprRet(..) | ExprBreak(..) | ExprAgain(..) | ExprLit(_) | ExprBlock(..) | ExprMac(..) | ExprAddrOf(..) | ExprStruct(..) | ExprRepeat(..) | diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index abed04c8f33ac..0d3dd8f91d967 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -465,6 +465,11 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { Ok(self.cat_field(expr, base_cmt, f_name.node, expr_ty)) } + ast::ExprTupField(ref base, idx, _) => { + let base_cmt = if_ok!(self.cat_expr(&**base)); + Ok(self.cat_tup_field(expr, base_cmt, idx.node, expr_ty)) + } + ast::ExprIndex(ref base, _) => { let method_call = typeck::MethodCall::expr(expr.id()); match self.typer.node_method_ty(method_call) { @@ -737,6 +742,21 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { }) } + pub fn cat_tup_field(&self, + node: &N, + base_cmt: cmt, + f_idx: uint, + f_ty: ty::t) + -> cmt { + Rc::new(cmt_ { + id: node.id(), + span: node.span(), + mutbl: base_cmt.mutbl.inherit(), + cat: cat_interior(base_cmt, InteriorField(PositionalField(f_idx))), + ty: f_ty + }) + } + pub fn cat_deref_obj(&self, node: &N, base_cmt: cmt) -> cmt { self.cat_deref_common(node, base_cmt, 0, ty::mk_nil(), false) } diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs index 6017444267a4f..cdb7d114af903 100644 --- a/src/librustc/middle/privacy.rs +++ b/src/librustc/middle/privacy.rs @@ -819,6 +819,14 @@ impl<'a, 'tcx> Visitor<()> for PrivacyVisitor<'a, 'tcx> { _ => {} } } + ast::ExprTupField(ref base, idx, _) => { + match ty::get(ty::expr_ty_adjusted(self.tcx, &**base)).sty { + ty::ty_struct(id, _) => { + self.check_field(expr.span, id, UnnamedField(idx.node)); + } + _ => {} + } + } ast::ExprMethodCall(ident, _, _) => { let method_call = MethodCall::expr(expr.id); match self.tcx.method_map.borrow().find(&method_call) { diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 21bfcfeec70b5..0db3864a06bb8 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -779,6 +779,7 @@ fn resolve_local(visitor: &mut RegionResolutionVisitor, ast::ExprAddrOf(_, ref subexpr) | ast::ExprUnary(ast::UnDeref, ref subexpr) | ast::ExprField(ref subexpr, _, _) | + ast::ExprTupField(ref subexpr, _, _) | ast::ExprIndex(ref subexpr, _) | ast::ExprParen(ref subexpr) => { let subexpr: &'a Gc = subexpr; // FIXME(#11586) diff --git a/src/librustc/middle/save/mod.rs b/src/librustc/middle/save/mod.rs index 7c7960e0918f4..7350413643c8d 100644 --- a/src/librustc/middle/save/mod.rs +++ b/src/librustc/middle/save/mod.rs @@ -1314,6 +1314,34 @@ impl<'l, 'tcx> Visitor for DxrVisitor<'l, 'tcx> { "Expected struct type, but not ty_struct"), } }, + ast::ExprTupField(sub_ex, idx, _) => { + if generated_code(sub_ex.span) { + return + } + + self.visit_expr(&*sub_ex, e); + + let t = ty::expr_ty_adjusted(&self.analysis.ty_cx, &*sub_ex); + let t_box = ty::get(t); + match t_box.sty { + ty::ty_struct(def_id, _) => { + let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, def_id); + for (i, f) in fields.iter().enumerate() { + if i == idx.node { + let sub_span = self.span.span_for_last_ident(ex.span); + self.fmt.ref_str(recorder::VarRef, + ex.span, + sub_span, + f.id, + e.cur_scope); + break; + } + } + }, + _ => self.sess.span_bug(ex.span, + "Expected struct type, but not ty_struct"), + } + }, ast::ExprFnBlock(_, decl, body) => { if generated_code(body.span) { return diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index bd5132ea42736..8f6a3864b37ea 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -440,6 +440,13 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr, (adt::const_get_field(cx, &*brepr, bv, discr, ix), inlineable) }) } + ast::ExprTupField(ref base, idx, _) => { + let (bv, inlineable, bt) = const_expr(cx, &**base, is_local); + let brepr = adt::represent_type(cx, bt); + expr::with_field_tys(cx.tcx(), bt, None, |discr, _| { + (adt::const_get_field(cx, &*brepr, bv, discr, idx.node), inlineable) + }) + } ast::ExprIndex(ref base, ref index) => { let (bv, inlineable, bt) = const_expr(cx, &**base, is_local); diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index b640f9ef5af11..e1c7ef4d50f0a 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -3457,6 +3457,7 @@ fn populate_scope_map(cx: &CrateContext, ast::ExprCast(ref sub_exp, _) | ast::ExprAddrOf(_, ref sub_exp) | ast::ExprField(ref sub_exp, _, _) | + ast::ExprTupField(ref sub_exp, _, _) | ast::ExprParen(ref sub_exp) => walk_expr(cx, &**sub_exp, scope_stack, scope_map), diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 0421aef45ef9c..67dee371083a7 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -62,7 +62,7 @@ use middle::trans::meth; use middle::trans::inline; use middle::trans::tvec; use middle::trans::type_of; -use middle::ty::struct_fields; +use middle::ty::{struct_fields, tup_fields}; use middle::ty::{AutoDerefRef, AutoAddEnv, AutoUnsafe}; use middle::ty::{AutoPtr}; use middle::ty; @@ -593,6 +593,9 @@ fn trans_datum_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ast::ExprField(ref base, ident, _) => { trans_rec_field(bcx, &**base, ident.node) } + ast::ExprTupField(ref base, idx, _) => { + trans_rec_tup_field(bcx, &**base, idx.node) + } ast::ExprIndex(ref base, ref idx) => { trans_index(bcx, expr, &**base, &**idx, MethodCall::expr(expr.id)) } @@ -666,12 +669,10 @@ fn trans_datum_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, } } -fn trans_rec_field<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - base: &ast::Expr, - field: ast::Ident) - -> DatumBlock<'blk, 'tcx, Expr> { - //! Translates `base.field`. - +fn trans_field<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + base: &ast::Expr, + get_idx: |&'blk ty::ctxt<'tcx>, &[ty::field]| -> uint) + -> DatumBlock<'blk, 'tcx, Expr> { let mut bcx = bcx; let _icx = push_ctxt("trans_rec_field"); @@ -679,7 +680,7 @@ fn trans_rec_field<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let bare_ty = ty::unopen_type(base_datum.ty); let repr = adt::represent_type(bcx.ccx(), bare_ty); with_field_tys(bcx.tcx(), bare_ty, None, |discr, field_tys| { - let ix = ty::field_idx_strict(bcx.tcx(), field.name, field_tys); + let ix = get_idx(bcx.tcx(), field_tys); let d = base_datum.get_element( bcx, field_tys[ix].mt.ty, @@ -697,6 +698,23 @@ fn trans_rec_field<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, } }) + +} + +/// Translates `base.field`. +fn trans_rec_field<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + base: &ast::Expr, + field: ast::Ident) + -> DatumBlock<'blk, 'tcx, Expr> { + trans_field(bcx, base, |tcx, field_tys| ty::field_idx_strict(tcx, field.name, field_tys)) +} + +/// Translates `base.`. +fn trans_rec_tup_field<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + base: &ast::Expr, + idx: uint) + -> DatumBlock<'blk, 'tcx, Expr> { + trans_field(bcx, base, |_, _| idx) } fn trans_index<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, @@ -1238,6 +1256,10 @@ pub fn with_field_tys(tcx: &ty::ctxt, op(0, struct_fields(tcx, did, substs).as_slice()) } + ty::ty_tup(ref v) => { + op(0, tup_fields(v.as_slice()).as_slice()) + } + ty::ty_enum(_, ref substs) => { // We want the *variant* ID here, not the enum ID. match node_id_opt { diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index feed76233d68a..ee59de11fc3ff 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -3599,6 +3599,7 @@ pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind { ast::ExprUnary(ast::UnDeref, _) | ast::ExprField(..) | + ast::ExprTupField(..) | ast::ExprIndex(..) => { LvalueExpr } @@ -4527,6 +4528,11 @@ pub fn lookup_struct_fields(cx: &ctxt, did: ast::DefId) -> Vec { } } +pub fn is_tuple_struct(cx: &ctxt, did: ast::DefId) -> bool { + let fields = lookup_struct_fields(cx, did); + !fields.is_empty() && fields.iter().all(|f| f.name == token::special_names::unnamed_field) +} + pub fn lookup_struct_field(cx: &ctxt, parent: ast::DefId, field_id: ast::DefId) @@ -4554,6 +4560,21 @@ pub fn struct_fields(cx: &ctxt, did: ast::DefId, substs: &Substs) }).collect() } +// Returns a list of fields corresponding to the tuple's items. trans uses +// this. +pub fn tup_fields(v: &[t]) -> Vec { + v.iter().enumerate().map(|(i, &f)| { + field { + // FIXME #6993: change type of field to Name and get rid of new() + ident: ast::Ident::new(token::intern(i.to_string().as_slice())), + mt: mt { + ty: f, + mutbl: MutImmutable + } + } + }).collect() +} + pub struct UnboxedClosureUpvar { pub def: def::Def, pub span: Span, diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 01b5fd6e429ee..4f0f6121904a1 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -2603,6 +2603,16 @@ pub fn lookup_field_ty(tcx: &ty::ctxt, o_field.map(|f| ty::lookup_field_type(tcx, class_id, f.id, substs)) } +pub fn lookup_tup_field_ty(tcx: &ty::ctxt, + class_id: ast::DefId, + items: &[ty::field_ty], + idx: uint, + substs: &subst::Substs) -> Option { + + let o_field = if idx < items.len() { Some(&items[idx]) } else { None }; + o_field.map(|f| ty::lookup_field_type(tcx, class_id, f.id, substs)) +} + // Controls whether the arguments are automatically referenced. This is useful // for overloaded binary and unary operators. pub enum DerefArgs { @@ -3286,6 +3296,68 @@ fn check_expr_with_unifier(fcx: &FnCtxt, fcx.write_error(expr.id); } + // Check tuple index expressions + fn check_tup_field(fcx: &FnCtxt, + expr: &ast::Expr, + lvalue_pref: LvaluePreference, + base: &ast::Expr, + idx: codemap::Spanned, + _tys: &[ast::P]) { + let tcx = fcx.ccx.tcx; + check_expr_with_lvalue_pref(fcx, base, lvalue_pref); + let expr_t = structurally_resolved_type(fcx, expr.span, + fcx.expr_ty(base)); + let mut tuple_like = false; + // FIXME(eddyb) #12808 Integrate privacy into this auto-deref loop. + let (_, autoderefs, field_ty) = + autoderef(fcx, expr.span, expr_t, Some(base.id), lvalue_pref, |base_t, _| { + match ty::get(base_t).sty { + ty::ty_struct(base_id, ref substs) => { + tuple_like = ty::is_tuple_struct(tcx, base_id); + if tuple_like { + debug!("tuple struct named {}", ppaux::ty_to_string(tcx, base_t)); + let fields = ty::lookup_struct_fields(tcx, base_id); + lookup_tup_field_ty(tcx, base_id, fields.as_slice(), + idx.node, &(*substs)) + } else { + None + } + } + ty::ty_tup(ref v) => { + tuple_like = true; + if idx.node < v.len() { Some(v[idx.node]) } else { None } + } + _ => None + } + }); + match field_ty { + Some(field_ty) => { + fcx.write_ty(expr.id, field_ty); + fcx.write_autoderef_adjustment(base.id, autoderefs); + return; + } + None => {} + } + fcx.type_error_message( + expr.span, + |actual| { + if tuple_like { + format!("attempted out-of-bounds tuple index `{}` on \ + type `{}`", + idx.node, + actual) + } else { + format!("attempted tuple index `{}` on type `{}`, but the \ + type was not a tuple or tuple struct", + idx.node, + actual) + } + }, + expr_t, None); + + fcx.write_error(expr.id); + } + fn check_struct_or_variant_fields(fcx: &FnCtxt, struct_ty: ty::t, span: Span, @@ -4065,6 +4137,9 @@ fn check_expr_with_unifier(fcx: &FnCtxt, ast::ExprField(ref base, ref field, ref tys) => { check_field(fcx, expr, lvalue_pref, &**base, field, tys.as_slice()); } + ast::ExprTupField(ref base, idx, ref tys) => { + check_tup_field(fcx, expr, lvalue_pref, &**base, idx, tys.as_slice()); + } ast::ExprIndex(ref base, ref idx) => { check_expr_with_lvalue_pref(fcx, &**base, lvalue_pref); check_expr(fcx, &**idx); diff --git a/src/librustc_back/svh.rs b/src/librustc_back/svh.rs index 6af19d948e0e8..8e24fc1ad5bff 100644 --- a/src/librustc_back/svh.rs +++ b/src/librustc_back/svh.rs @@ -222,6 +222,7 @@ mod svh_visitor { SawExprLoop(Option), SawExprField(token::InternedString), + SawExprTupField(uint), SawExprBreak(Option), SawExprAgain(Option), @@ -276,6 +277,7 @@ mod svh_visitor { ExprAssign(..) => SawExprAssign, ExprAssignOp(op, _, _) => SawExprAssignOp(op), ExprField(_, id, _) => SawExprField(content(id.node)), + ExprTupField(_, id, _) => SawExprTupField(id.node), ExprIndex(..) => SawExprIndex, ExprPath(..) => SawExprPath, ExprAddrOf(m, _) => SawExprAddrOf(m), diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 68a1c521f1942..4e65082fe3ad2 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -540,6 +540,7 @@ pub enum Expr_ { ExprAssign(Gc, Gc), ExprAssignOp(BinOp, Gc, Gc), ExprField(Gc, SpannedIdent, Vec>), + ExprTupField(Gc, Spanned, Vec>), ExprIndex(Gc, Gc), /// Variable reference, possibly containing `::` and/or diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 64ab0e5cb191f..6bd1fba4b58a3 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -120,6 +120,8 @@ pub trait AstBuilder { fn expr_mut_addr_of(&self, sp: Span, e: Gc) -> Gc; fn expr_field_access(&self, span: Span, expr: Gc, ident: ast::Ident) -> Gc; + fn expr_tup_field_access(&self, sp: Span, expr: Gc, + idx: uint) -> Gc; fn expr_call(&self, span: Span, expr: Gc, args: Vec>) -> Gc; fn expr_call_ident(&self, span: Span, id: ast::Ident, @@ -605,6 +607,16 @@ impl<'a> AstBuilder for ExtCtxt<'a> { let id = Spanned { node: ident, span: field_span }; self.expr(sp, ast::ExprField(expr, id, Vec::new())) } + fn expr_tup_field_access(&self, sp: Span, expr: Gc, idx: uint) -> Gc { + let field_span = Span { + lo: sp.lo - Pos::from_uint(idx.to_string().len()), + hi: sp.hi, + expn_info: sp.expn_info, + }; + + let id = Spanned { node: idx, span: field_span }; + self.expr(sp, ast::ExprTupField(expr, id, Vec::new())) + } fn expr_addr_of(&self, sp: Span, e: Gc) -> Gc { self.expr(sp, ast::ExprAddrOf(ast::MutImmutable, e)) } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 7deabed04b824..30b7317fa56f1 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -134,6 +134,10 @@ pub trait Folder { noop_fold_ident(i, self) } + fn fold_uint(&mut self, i: uint) -> uint { + noop_fold_uint(i, self) + } + fn fold_path(&mut self, p: &Path) -> Path { noop_fold_path(p, self) } @@ -466,6 +470,10 @@ pub fn noop_fold_ident(i: Ident, _: &mut T) -> Ident { i } +pub fn noop_fold_uint(i: uint, _: &mut T) -> uint { + i +} + pub fn noop_fold_path(p: &Path, fld: &mut T) -> Path { ast::Path { span: fld.new_span(p.span), @@ -1180,6 +1188,11 @@ pub fn noop_fold_expr(e: Gc, folder: &mut T) -> Gc { respan(id.span, folder.fold_ident(id.node)), tys.iter().map(|&x| folder.fold_ty(x)).collect()) } + ExprTupField(el, id, ref tys) => { + ExprTupField(folder.fold_expr(el), + respan(id.span, folder.fold_uint(id.node)), + tys.iter().map(|&x| folder.fold_ty(x)).collect()) + } ExprIndex(el, er) => { ExprIndex(folder.fold_expr(el), folder.fold_expr(er)) } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 6aff1152f7e1d..718d75aad8908 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -23,7 +23,7 @@ use ast::{DeclLocal, DefaultBlock, UnDeref, BiDiv, EMPTY_CTXT, EnumDef, Explicit use ast::{Expr, Expr_, ExprAddrOf, ExprMatch, ExprAgain}; use ast::{ExprAssign, ExprAssignOp, ExprBinary, ExprBlock, ExprBox}; use ast::{ExprBreak, ExprCall, ExprCast}; -use ast::{ExprField, ExprFnBlock, ExprIf, ExprIndex}; +use ast::{ExprField, ExprTupField, ExprFnBlock, ExprIf, ExprIndex}; use ast::{ExprLit, ExprLoop, ExprMac}; use ast::{ExprMethodCall, ExprParen, ExprPath, ExprProc}; use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary, ExprUnboxedFn}; @@ -1937,6 +1937,11 @@ impl<'a> Parser<'a> { ExprField(expr, ident, tys) } + pub fn mk_tup_field(&mut self, expr: Gc, idx: codemap::Spanned, + tys: Vec>) -> ast::Expr_ { + ExprTupField(expr, idx, tys) + } + pub fn mk_assign_op(&mut self, binop: ast::BinOp, lhs: Gc, rhs: Gc) -> ast::Expr_ { ExprAssignOp(binop, lhs, rhs) @@ -2286,6 +2291,41 @@ impl<'a> Parser<'a> { } } } + token::LIT_INTEGER(n) => { + let index = n.as_str(); + let dot = self.last_span.hi; + hi = self.span.hi; + self.bump(); + let (_, tys) = if self.eat(&token::MOD_SEP) { + self.expect_lt(); + self.parse_generic_values_after_lt() + } else { + (Vec::new(), Vec::new()) + }; + + let num = from_str::(index); + match num { + Some(n) => { + let id = spanned(dot, hi, n); + let field = self.mk_tup_field(e, id, tys); + e = self.mk_expr(lo, hi, field); + } + None => { + let last_span = self.last_span; + self.span_err(last_span, "invalid tuple or tuple struct index"); + } + } + } + token::LIT_FLOAT(n) => { + self.bump(); + let last_span = self.last_span; + self.span_err(last_span, + format!("unexpected token: `{}`", n.as_str()).as_slice()); + self.span_note(last_span, + "try parenthesizing the first index; e.g., `(foo.0).1`"); + self.abort_if_errors(); + + } _ => self.unexpected() } continue; diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index eaeb6aaab8a75..a4dff45ad359f 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1607,6 +1607,18 @@ impl<'a> State<'a> { try!(word(&mut self.s, ">")); } } + ast::ExprTupField(ref expr, id, ref tys) => { + try!(self.print_expr(&**expr)); + try!(word(&mut self.s, ".")); + try!(self.print_uint(id.node)); + if tys.len() > 0u { + try!(word(&mut self.s, "::<")); + try!(self.commasep( + Inconsistent, tys.as_slice(), + |s, ty| s.print_type_ref(ty))); + try!(word(&mut self.s, ">")); + } + } ast::ExprIndex(ref expr, ref index) => { try!(self.print_expr(&**expr)); try!(word(&mut self.s, "[")); @@ -1738,6 +1750,10 @@ impl<'a> State<'a> { self.ann.post(self, NodeIdent(&ident)) } + pub fn print_uint(&mut self, i: uint) -> IoResult<()> { + word(&mut self.s, i.to_string().as_slice()) + } + pub fn print_name(&mut self, name: ast::Name) -> IoResult<()> { try!(word(&mut self.s, token::get_name(name).get())); self.ann.post(self, NodeName(&name)) diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 65e192e8437f3..50b42ea2c0fd9 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -830,6 +830,12 @@ pub fn walk_expr>(visitor: &mut V, expression: &Expr, en visitor.visit_ty(&**typ, env.clone()) } } + ExprTupField(ref subexpression, _, ref types) => { + visitor.visit_expr(&**subexpression, env.clone()); + for typ in types.iter() { + visitor.visit_ty(&**typ, env.clone()) + } + } ExprIndex(ref main_expression, ref index_expression) => { visitor.visit_expr(&**main_expression, env.clone()); visitor.visit_expr(&**index_expression, env.clone()) diff --git a/src/test/compile-fail/borrow-tuple-fields.rs b/src/test/compile-fail/borrow-tuple-fields.rs new file mode 100644 index 0000000000000..519bad4e627b7 --- /dev/null +++ b/src/test/compile-fail/borrow-tuple-fields.rs @@ -0,0 +1,42 @@ +// 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. + +#![feature(tuple_indexing)] + +struct Foo(Box, int); + +struct Bar(int, int); + +fn main() { + let x = (box 1i, 2i); + let r = &x.0; + let y = x; //~ ERROR cannot move out of `x` because it is borrowed + + let mut x = (1i, 2i); + let a = &x.0; + let b = &mut x.0; //~ ERROR cannot borrow `x.0` as mutable because it is also borrowed as + + let mut x = (1i, 2i); + let a = &mut x.0; + let b = &mut x.0; //~ ERROR cannot borrow `x.0` as mutable more than once at a time + + + let x = Foo(box 1i, 2i); + let r = &x.0; + let y = x; //~ ERROR cannot move out of `x` because it is borrowed + + let mut x = Bar(1i, 2i); + let a = &x.0; + let b = &mut x.0; //~ ERROR cannot borrow `x.0` as mutable because it is also borrowed as + + let mut x = Bar(1i, 2i); + let a = &mut x.0; + let b = &mut x.0; //~ ERROR cannot borrow `x.0` as mutable more than once at a time +} diff --git a/src/test/compile-fail/move-out-of-tuple-field.rs b/src/test/compile-fail/move-out-of-tuple-field.rs new file mode 100644 index 0000000000000..7f55a78e8b784 --- /dev/null +++ b/src/test/compile-fail/move-out-of-tuple-field.rs @@ -0,0 +1,23 @@ +// 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. + +#![feature(tuple_indexing)] + +struct Foo(Box); + +fn main() { + let x = (box 1i,); + let y = x.0; + let z = x.0; //~ ERROR use of moved value: `x.0` + + let x = Foo(box 1i); + let y = x.0; + let z = x.0; //~ ERROR use of moved value: `x.0` +} diff --git a/src/test/compile-fail/tuple-index-not-tuple.rs b/src/test/compile-fail/tuple-index-not-tuple.rs new file mode 100644 index 0000000000000..d4ef0e20b266d --- /dev/null +++ b/src/test/compile-fail/tuple-index-not-tuple.rs @@ -0,0 +1,22 @@ +// 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. + +#![feature(tuple_indexing)] + +struct Point { x: int, y: int } +struct Empty; + +fn main() { + let origin = Point { x: 0, y: 0 }; + origin.0; + //~^ ERROR attempted tuple index `0` on type `Point`, but the type was not + Empty.0; + //~^ ERROR attempted tuple index `0` on type `Empty`, but the type was not +} diff --git a/src/test/compile-fail/tuple-index-out-of-bounds.rs b/src/test/compile-fail/tuple-index-out-of-bounds.rs new file mode 100644 index 0000000000000..d16f950a5d4bd --- /dev/null +++ b/src/test/compile-fail/tuple-index-out-of-bounds.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. + +#![feature(tuple_indexing)] + +struct Point(int, int); + +fn main() { + let origin = Point(0, 0); + origin.0; + origin.1; + origin.2; + //~^ ERROR attempted out-of-bounds tuple index `2` on type `Point` + let tuple = (0i, 0i); + tuple.0; + tuple.1; + tuple.2; + //~^ ERROR attempted out-of-bounds tuple index `2` on type `(int,int)` +} diff --git a/src/test/run-pass/borrow-tuple-fields.rs b/src/test/run-pass/borrow-tuple-fields.rs new file mode 100644 index 0000000000000..046d76c4331dc --- /dev/null +++ b/src/test/run-pass/borrow-tuple-fields.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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(tuple_indexing)] + +struct Foo(int, int); + +fn main() { + let x = (1i, 2i); + let a = &x.0; + let b = &x.0; + assert_eq!(*a, 1); + assert_eq!(*b, 1); + + let mut x = (1i, 2i); + { + let a = &x.0; + let b = &mut x.1; + *b = 5; + assert_eq!(*a, 1); + } + assert_eq!(x.0, 1); + assert_eq!(x.1, 5); + + + let x = Foo(1i, 2i); + let a = &x.0; + let b = &x.0; + assert_eq!(*a, 1); + assert_eq!(*b, 1); + + let mut x = Foo(1i, 2i); + { + let a = &x.0; + let b = &mut x.1; + *b = 5; + assert_eq!(*a, 1); + } + assert_eq!(x.0, 1); + assert_eq!(x.1, 5); +} diff --git a/src/test/run-pass/tuple-index-fat-types.rs b/src/test/run-pass/tuple-index-fat-types.rs new file mode 100644 index 0000000000000..fdee1d9f96c4d --- /dev/null +++ b/src/test/run-pass/tuple-index-fat-types.rs @@ -0,0 +1,23 @@ +// 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. + +#![feature(tuple_indexing)] + +struct Foo<'a>(&'a [int]); + +fn main() { + let x: &[int] = &[1i, 2, 3]; + let y = (x,); + assert_eq!(y.0, x); + + let x: &[int] = &[1i, 2, 3]; + let y = Foo(x); + assert_eq!(y.0, x); +} diff --git a/src/test/run-pass/tuple-index.rs b/src/test/run-pass/tuple-index.rs new file mode 100644 index 0000000000000..107dc40e18671 --- /dev/null +++ b/src/test/run-pass/tuple-index.rs @@ -0,0 +1,42 @@ +// 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. + +#![feature(tuple_indexing)] + +struct Point(int, int); + +fn main() { + let mut x = Point(3, 2); + assert_eq!(x.0, 3); + assert_eq!(x.1, 2); + x.0 += 5; + assert_eq!(x.0, 8); + { + let ry = &mut x.1; + *ry -= 2; + x.0 += 3; + assert_eq!(x.0, 11); + } + assert_eq!(x.1, 0); + + let mut x = (3i, 2i); + assert_eq!(x.0, 3); + assert_eq!(x.1, 2); + x.0 += 5; + assert_eq!(x.0, 8); + { + let ry = &mut x.1; + *ry -= 2; + x.0 += 3; + assert_eq!(x.0, 11); + } + assert_eq!(x.1, 0); + +}