]> git.lizzy.rs Git - rust.git/commitdiff
Implement tuple and tuple struct indexing
authorP1start <rewi-github@whanau.org>
Sun, 10 Aug 2014 03:54:33 +0000 (15:54 +1200)
committerP1start <rewi-github@whanau.org>
Tue, 9 Sep 2014 22:25:12 +0000 (10:25 +1200)
This allows code to access the fields of tuples and tuple structs:

    let x = (1i, 2i);
    assert_eq!(x.1, 2);

    struct Point(int, int);
    let origin = Point(0, 0);
    assert_eq!(origin.0, 0);
    assert_eq!(origin.1, 0);

34 files changed:
src/doc/rust.md
src/librustc/front/feature_gate.rs
src/librustc/lint/builtin.rs
src/librustc/middle/borrowck/mod.rs
src/librustc/middle/cfg/construct.rs
src/librustc/middle/check_const.rs
src/librustc/middle/check_static.rs
src/librustc/middle/const_eval.rs
src/librustc/middle/dead.rs
src/librustc/middle/expr_use_visitor.rs
src/librustc/middle/liveness.rs
src/librustc/middle/mem_categorization.rs
src/librustc/middle/privacy.rs
src/librustc/middle/region.rs
src/librustc/middle/save/mod.rs
src/librustc/middle/trans/consts.rs
src/librustc/middle/trans/debuginfo.rs
src/librustc/middle/trans/expr.rs
src/librustc/middle/ty.rs
src/librustc/middle/typeck/check/mod.rs
src/librustc_back/svh.rs
src/libsyntax/ast.rs
src/libsyntax/ext/build.rs
src/libsyntax/fold.rs
src/libsyntax/parse/parser.rs
src/libsyntax/print/pprust.rs
src/libsyntax/visit.rs
src/test/compile-fail/borrow-tuple-fields.rs [new file with mode: 0644]
src/test/compile-fail/move-out-of-tuple-field.rs [new file with mode: 0644]
src/test/compile-fail/tuple-index-not-tuple.rs [new file with mode: 0644]
src/test/compile-fail/tuple-index-out-of-bounds.rs [new file with mode: 0644]
src/test/run-pass/borrow-tuple-fields.rs [new file with mode: 0644]
src/test/run-pass/tuple-index-fat-types.rs [new file with mode: 0644]
src/test/run-pass/tuple-index.rs [new file with mode: 0644]

index eb97a75e7660b0ef192a99a1c2662980666c4a2c..fb2407e5163818560aa1bb7d0afc0101da42ebf0 100644 (file)
@@ -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
index c83b81660d58fb296231cd22d37bb74673c6512b..225fc28cd6d9888b8a2ff022f6e4566a9b4509ee 100644 (file)
@@ -70,6 +70,7 @@
     ("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 @@ fn visit_expr(&mut self, e: &ast::Expr, _: ()) {
                                   "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, ());
index 721e5f296e2e7b7f218d9de42d96ea1b189c6550..138947e8a873b5cdbf754e24d628f0e4bb711a30 100644 (file)
@@ -1056,6 +1056,7 @@ fn contains_exterior_struct_lit(value: &ast::Expr) -> bool {
                 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)
index f4d3678271c0ded7c8d6c26a980f60b9ce947688..5969e7e0c42d5a8a286035d370be9d324e5e04fa 100644 (file)
@@ -807,7 +807,7 @@ pub fn append_loan_path_to_string(&self,
                         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());
                     }
                 }
index ec414b858187aae059be33986fa1f50f468abe1f..6e9b27655af79a1b9e4c59f507872f593b1e7169 100644 (file)
@@ -467,7 +467,8 @@ fn expr(&mut self, expr: Gc<ast::Expr>, pred: CFGIndex) -> CFGIndex {
             ast::ExprCast(e, _) |
             ast::ExprUnary(_, e) |
             ast::ExprParen(e) |
-            ast::ExprField(e, _, _) => {
+            ast::ExprField(e, _, _) |
+            ast::ExprTupField(e, _, _) => {
                 self.straightline(expr, pred, [e])
             }
 
index 3c8db8d048076cae439776cbb65957c76a6692da..a9a3d94ded897a0ce2bede4f0738b6260b582725 100644 (file)
@@ -173,6 +173,7 @@ fn check_expr(v: &mut CheckCrateVisitor, e: &Expr, is_const: bool) {
           ExprAddrOf(MutImmutable, _) |
           ExprParen(..) |
           ExprField(..) |
+          ExprTupField(..) |
           ExprIndex(..) |
           ExprTup(..) |
           ExprRepeat(..) |
index 7b00bb4589c71e8983c2aaaea38d992b8d655d44..ca58b4b6e60a40d8f498880c7b4c24d5d48b7e2a 100644 (file)
@@ -106,7 +106,7 @@ fn visit_expr(&mut self, e: &ast::Expr, is_const: bool) {
         }
 
         match e.node {
-            ast::ExprField(..) | ast::ExprVec(..) |
+            ast::ExprField(..) | ast::ExprTupField(..) | ast::ExprVec(..) |
             ast::ExprBlock(..) | ast::ExprTup(..)  => {
                 visit::walk_expr(self, e, is_const);
             }
index 605c90a49c6674a375a6611e3199a819a8e7b4f5..2b4b6756f9f859ce046b06e4084a5461423f2f2f 100644 (file)
@@ -225,6 +225,8 @@ fn classify(&mut self, e: &Expr) -> constness {
 
             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)),
 
index b7cddd0c23f964876451b05e06c22b815d9070af..f275c818716599a9fee9ad6577c3ed8aea074299 100644 (file)
@@ -145,6 +145,17 @@ fn handle_field_access(&mut self, lhs: &ast::Expr, name: &ast::Ident) {
         }
     }
 
+    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 @@ fn visit_expr(&mut self, expr: &ast::Expr, ctxt: MarkSymbolVisitorContext) {
             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);
+            }
             _ => ()
         }
 
index 207620b182907b9229985313f9a998bdf3ea29f4..d2362b7e9429b3291801da7dc058878e8ce63275 100644 (file)
@@ -324,6 +324,10 @@ pub fn walk_expr(&mut self, expr: &ast::Expr) {
                 self.select_from_expr(&**base);
             }
 
+            ast::ExprTupField(ref base, _, _) => {         // base.<n>
+                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);
index 26aa51b909944cff987fa77254b257d98d59f700..84fc8ff2c38cae28d24d06b6887f1e6afd08c9f9 100644 (file)
@@ -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 @@ fn propagate_through_expr(&mut self, expr: &Expr, succ: LiveNode)
               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 @@ fn propagate_through_lvalue_components(&mut self,
         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(..) |
index abed04c8f33acf7cf90f98d4dc151af9521822b2..0d3dd8f91d9672128e5f9dee0b4ec8a821dd6fdf 100644 (file)
@@ -465,6 +465,11 @@ pub fn cat_expr_unadjusted(&self, expr: &ast::Expr) -> McResult<cmt> {
             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 @@ pub fn cat_field<N:ast_node>(&self,
         })
     }
 
+    pub fn cat_tup_field<N:ast_node>(&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<N:ast_node>(&self, node: &N, base_cmt: cmt) -> cmt {
         self.cat_deref_common(node, base_cmt, 0, ty::mk_nil(), false)
     }
index 6017444267a4f2b7583962b05c162e0b60ebfab6..cdb7d114af90306c792fd289eea0574c2d46a1bf 100644 (file)
@@ -819,6 +819,14 @@ fn visit_expr(&mut self, expr: &ast::Expr, _: ()) {
                     _ => {}
                 }
             }
+            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) {
index 21bfcfeec70b51603e6f32ab31fb03a7051dfaf0..0db3864a06bb8fefeb48ac74da23014b0670da5e 100644 (file)
@@ -779,6 +779,7 @@ fn record_rvalue_scope<'a>(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<Expr> = subexpr; // FIXME(#11586)
index 7c7960e0918f46fb1213cc39b77ddb792258e4d4..7350413643c8da460fb03b10e250c2b47f7fff9b 100644 (file)
@@ -1314,6 +1314,34 @@ fn visit_expr(&mut self, ex: &ast::Expr, e: DxrVisitorEnv) {
                                             "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
index bd5132ea4273660ea8f128ecb07e76b8c1957b5d..8f6a3864b37ea12a1e9af93148faa35692877107 100644 (file)
@@ -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);
index b640f9ef5af11dfb5cb6640666de6bd6824e1fc7..e1c7ef4d50f0a45b4791f29cffcb03622664ada9 100644 (file)
@@ -3457,6 +3457,7 @@ fn walk_expr(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),
 
index 0421aef45ef9c4617d866f1e1f99c1889ebd62c7..67dee371083a7e45aebbe70ac8be245a943599ac 100644 (file)
@@ -62,7 +62,7 @@
 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.<idx>`.
+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<R>(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 {
index feed76233d68aaf1b2e384e85d1a2d62119c37c1..ee59de11fc3ff19dc9e11890f22e85700293a5e4 100644 (file)
@@ -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<field_ty> {
     }
 }
 
+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<field> {
+    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,
index 01b5fd6e429eeaec1e675f1c7b5f3774b70c06d2..4f0f6121904a128e5d4011bbfc2f8fc655bb3247 100644 (file)
@@ -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<ty::t> {
+
+    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_field(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<uint>,
+                       _tys: &[ast::P<ast::Ty>]) {
+        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_struct_fields_on_error(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);
index 6af19d948e0e8b467c153ae03fb825d3394ae441..8e24fc1ad5bff80de9df5e5a5443f0ddd24bf2b4 100644 (file)
@@ -222,6 +222,7 @@ pub enum SawExprComponent<'a> {
 
         SawExprLoop(Option<token::InternedString>),
         SawExprField(token::InternedString),
+        SawExprTupField(uint),
         SawExprBreak(Option<token::InternedString>),
         SawExprAgain(Option<token::InternedString>),
 
@@ -276,6 +277,7 @@ fn saw_expr<'a>(node: &'a Expr_) -> SawExprComponent<'a> {
             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),
index 68a1c521f1942e3be312a816969d38811a35c75b..4e65082fe3ad23f83b61f47206c71867aff1392a 100644 (file)
@@ -540,6 +540,7 @@ pub enum Expr_ {
     ExprAssign(Gc<Expr>, Gc<Expr>),
     ExprAssignOp(BinOp, Gc<Expr>, Gc<Expr>),
     ExprField(Gc<Expr>, SpannedIdent, Vec<P<Ty>>),
+    ExprTupField(Gc<Expr>, Spanned<uint>, Vec<P<Ty>>),
     ExprIndex(Gc<Expr>, Gc<Expr>),
 
     /// Variable reference, possibly containing `::` and/or
index 64ab0e5cb191f442b448cabd015443baacff3fb6..6bd1fba4b58a390997ce0a08b5ea9cd67bdbc6fe 100644 (file)
@@ -120,6 +120,8 @@ fn expr_binary(&self, sp: Span, op: ast::BinOp,
     fn expr_mut_addr_of(&self, sp: Span, e: Gc<ast::Expr>) -> Gc<ast::Expr>;
     fn expr_field_access(&self, span: Span, expr: Gc<ast::Expr>,
                          ident: ast::Ident) -> Gc<ast::Expr>;
+    fn expr_tup_field_access(&self, sp: Span, expr: Gc<ast::Expr>,
+                             idx: uint) -> Gc<ast::Expr>;
     fn expr_call(&self, span: Span, expr: Gc<ast::Expr>,
                  args: Vec<Gc<ast::Expr>>) -> Gc<ast::Expr>;
     fn expr_call_ident(&self, span: Span, id: ast::Ident,
@@ -605,6 +607,16 @@ fn expr_field_access(&self, sp: Span, expr: Gc<ast::Expr>, ident: ast::Ident) ->
         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<ast::Expr>, idx: uint) -> Gc<ast::Expr> {
+        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<ast::Expr>) -> Gc<ast::Expr> {
         self.expr(sp, ast::ExprAddrOf(ast::MutImmutable, e))
     }
index 7deabed04b824505eb4c754f9ce60f35f2bffa5b..30b7317fa56f181df008ded97ff8f3e4540deeab 100644 (file)
@@ -134,6 +134,10 @@ fn fold_ident(&mut self, i: Ident) -> Ident {
         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<T: Folder>(i: Ident, _: &mut T) -> Ident {
     i
 }
 
+pub fn noop_fold_uint<T: Folder>(i: uint, _: &mut T) -> uint {
+    i
+}
+
 pub fn noop_fold_path<T: Folder>(p: &Path, fld: &mut T) -> Path {
     ast::Path {
         span: fld.new_span(p.span),
@@ -1180,6 +1188,11 @@ pub fn noop_fold_expr<T: Folder>(e: Gc<Expr>, folder: &mut T) -> Gc<Expr> {
                       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))
         }
index 6aff1152f7e1d18c032cf958ad1fe64c46fd1ef3..718d75aad8908da824d3528e577d615fdfc82144 100644 (file)
@@ -23,7 +23,7 @@
 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 @@ pub fn mk_field(&mut self, expr: Gc<Expr>, ident: ast::SpannedIdent,
         ExprField(expr, ident, tys)
     }
 
+    pub fn mk_tup_field(&mut self, expr: Gc<Expr>, idx: codemap::Spanned<uint>,
+                    tys: Vec<P<Ty>>) -> ast::Expr_ {
+        ExprTupField(expr, idx, tys)
+    }
+
     pub fn mk_assign_op(&mut self, binop: ast::BinOp,
                         lhs: Gc<Expr>, rhs: Gc<Expr>) -> ast::Expr_ {
         ExprAssignOp(binop, lhs, rhs)
@@ -2286,6 +2291,41 @@ pub fn parse_dot_or_call_expr_with(&mut self, e0: Gc<Expr>) -> Gc<Expr> {
                         }
                     }
                   }
+                  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::<uint>(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;
index eaeb6aaab8a75134d2cc159eb30e8b023caa6ff3..a4dff45ad359f96f31bf40b58c049e1f9e4e422d 100644 (file)
@@ -1607,6 +1607,18 @@ pub fn print_expr(&mut self, expr: &ast::Expr) -> IoResult<()> {
                     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 @@ pub fn print_ident(&mut self, ident: ast::Ident) -> IoResult<()> {
         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))
index 65e192e8437f3a6e9490b73d7f5401ea698097c3..50b42ea2c0fd90fcd8df36e514ca24318f8b96fe 100644 (file)
@@ -830,6 +830,12 @@ pub fn walk_expr<E: Clone, V: Visitor<E>>(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 (file)
index 0000000..519bad4
--- /dev/null
@@ -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 <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.
+
+#![feature(tuple_indexing)]
+
+struct Foo(Box<int>, 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 (file)
index 0000000..7f55a78
--- /dev/null
@@ -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 <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.
+
+#![feature(tuple_indexing)]
+
+struct Foo(Box<int>);
+
+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 (file)
index 0000000..d4ef0e2
--- /dev/null
@@ -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 <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.
+
+#![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 (file)
index 0000000..d16f950
--- /dev/null
@@ -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.
+
+#![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 (file)
index 0000000..046d76c
--- /dev/null
@@ -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.
+
+#![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 (file)
index 0000000..fdee1d9
--- /dev/null
@@ -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 <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.
+
+#![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 (file)
index 0000000..107dc40
--- /dev/null
@@ -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 <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.
+
+#![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);
+
+}