Skip to content

Commit

Permalink
Implement tuple and tuple struct indexing
Browse files Browse the repository at this point in the history
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);
  • Loading branch information
ftxqxd committed Sep 9, 2014
1 parent 6511064 commit bf274bc
Show file tree
Hide file tree
Showing 34 changed files with 549 additions and 14 deletions.
2 changes: 2 additions & 0 deletions src/doc/rust.md
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions src/librustc/front/feature_gate.rs
Expand Up @@ -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

Expand Down Expand Up @@ -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, ());
Expand Down
1 change: 1 addition & 0 deletions src/librustc/lint/builtin.rs
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/borrowck/mod.rs
Expand Up @@ -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());
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/librustc/middle/cfg/construct.rs
Expand Up @@ -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])
}

Expand Down
1 change: 1 addition & 0 deletions src/librustc/middle/check_const.rs
Expand Up @@ -173,6 +173,7 @@ fn check_expr(v: &mut CheckCrateVisitor, e: &Expr, is_const: bool) {
ExprAddrOf(MutImmutable, _) |
ExprParen(..) |
ExprField(..) |
ExprTupField(..) |
ExprIndex(..) |
ExprTup(..) |
ExprRepeat(..) |
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/check_static.rs
Expand Up @@ -106,7 +106,7 @@ impl<'a, 'tcx> Visitor<bool> 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);
}
Expand Down
2 changes: 2 additions & 0 deletions src/librustc/middle/const_eval.rs
Expand Up @@ -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)),

Expand Down
14 changes: 14 additions & 0 deletions src/librustc/middle/dead.rs
Expand Up @@ -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,
Expand Down Expand Up @@ -255,6 +266,9 @@ impl<'a, 'tcx> Visitor<MarkSymbolVisitorContext> 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);
}
_ => ()
}

Expand Down
4 changes: 4 additions & 0 deletions src/librustc/middle/expr_use_visitor.rs
Expand Up @@ -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.<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);
Expand Down
9 changes: 7 additions & 2 deletions src/librustc/middle/liveness.rs
Expand Up @@ -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(_) |
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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)
}
}
Expand Down Expand Up @@ -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(..) |
Expand Down
20 changes: 20 additions & 0 deletions src/librustc/middle/mem_categorization.rs
Expand Up @@ -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) {
Expand Down Expand Up @@ -737,6 +742,21 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
})
}

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)
}
Expand Down
8 changes: 8 additions & 0 deletions src/librustc/middle/privacy.rs
Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions src/librustc/middle/region.rs
Expand Up @@ -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<Expr> = subexpr; // FIXME(#11586)
Expand Down
28 changes: 28 additions & 0 deletions src/librustc/middle/save/mod.rs
Expand Up @@ -1314,6 +1314,34 @@ impl<'l, 'tcx> Visitor<DxrVisitorEnv> 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
Expand Down
7 changes: 7 additions & 0 deletions src/librustc/middle/trans/consts.rs
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/librustc/middle/trans/debuginfo.rs
Expand Up @@ -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),

Expand Down
38 changes: 30 additions & 8 deletions src/librustc/middle/trans/expr.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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))
}
Expand Down Expand Up @@ -666,20 +669,18 @@ 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");

let base_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, base, "field"));
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,
Expand All @@ -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>,
Expand Down Expand Up @@ -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 {
Expand Down
21 changes: 21 additions & 0 deletions src/librustc/middle/ty.rs
Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down

5 comments on commit bf274bc

@bors
Copy link
Contributor

@bors bors commented on bf274bc Sep 11, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saw approval from brson
at ftxqxd@bf274bc

@bors
Copy link
Contributor

@bors bors commented on bf274bc Sep 11, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merging P1start/rust/tuple-indexing = bf274bc into auto

@bors
Copy link
Contributor

@bors bors commented on bf274bc Sep 11, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1start/rust/tuple-indexing = bf274bc merged ok, testing candidate = 09abbbd

@bors
Copy link
Contributor

@bors bors commented on bf274bc Sep 11, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bors
Copy link
Contributor

@bors bors commented on bf274bc Sep 11, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fast-forwarding master to auto = 09abbbd

Please sign in to comment.