From 599b4208fb64b18aaddf9add5d8ce2319c7e7cfe Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 24 Oct 2012 14:36:00 -0700 Subject: [PATCH] rustc: Translate tuple struct constructors --- src/libsyntax/ast.rs | 2 +- src/libsyntax/ast_map.rs | 17 ++++ src/libsyntax/ast_util.rs | 6 ++ src/libsyntax/fold.rs | 4 +- src/libsyntax/parse/parser.rs | 9 +- src/libsyntax/parse/token.rs | 1 + src/rustc/middle/resolve.rs | 14 ++-- src/rustc/middle/trans/base.rs | 91 +++++++++++++++++++-- src/rustc/middle/trans/callee.rs | 5 +- src/rustc/middle/trans/monomorphize.rs | 13 +++ src/rustc/middle/ty.rs | 12 ++- src/rustc/middle/typeck/collect.rs | 59 +++++++------ src/test/run-pass/tuple-struct-construct.rs | 7 ++ 13 files changed, 192 insertions(+), 48 deletions(-) create mode 100644 src/test/run-pass/tuple-struct-construct.rs diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index e25fb470bb9b8..60f22f1b844e1 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1480,7 +1480,7 @@ type struct_def = { dtor: Option, /* ID of the constructor. This is only used for tuple- or enum-like * structs. */ - ctor_id: node_id + ctor_id: Option }; /* diff --git a/src/libsyntax/ast_map.rs b/src/libsyntax/ast_map.rs index bcda838b2486c..e63de5aefebe3 100644 --- a/src/libsyntax/ast_map.rs +++ b/src/libsyntax/ast_map.rs @@ -74,6 +74,7 @@ enum ast_node { // Destructor for a class node_dtor(~[ty_param], @class_dtor, def_id, @path), node_block(blk), + node_struct_ctor(@struct_def, @item, @path) } type map = std::map::HashMap; @@ -284,6 +285,19 @@ fn map_struct_def(struct_def: @ast::struct_def, parent_node: ast_node, for vec::each(struct_def.methods) |m| { map_method(d_id, p, *m, cx); } + // If this is a tuple-like struct, register the constructor. + match struct_def.ctor_id { + None => {} + Some(ctor_id) => { + match parent_node { + node_item(item, _) => { + cx.map.insert(ctor_id, + node_struct_ctor(struct_def, item, p)); + } + _ => fail ~"struct def parent wasn't an item" + } + } + } } fn map_view_item(vi: @view_item, cx: ctx, _v: vt) { @@ -375,6 +389,9 @@ fn node_id_to_str(map: map, id: node_id, itr: @ident_interner) -> ~str { Some(node_block(_)) => { fmt!("block") } + Some(node_struct_ctor(*)) => { + fmt!("struct_ctor") + } } } // Local Variables: diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 6fd84c3317f72..ea49cce50477e 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -589,6 +589,12 @@ fn view_path_id(p: @view_path) -> node_id { } } +/// Returns true if the given struct def is tuple-like; i.e. that its fields +/// are unnamed. +fn struct_def_is_tuple_like(struct_def: @ast::struct_def) -> bool { + struct_def.ctor_id.is_some() +} + // Local Variables: // mode: rust // fill-column: 78; diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index d1b975492254a..e2b6e6daa2fa2 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -281,7 +281,7 @@ fn fold_struct_def(struct_def: @ast::struct_def, fld: ast_fold) fields: vec::map(struct_def.fields, |f| fold_struct_field(*f, fld)), methods: vec::map(struct_def.methods, |m| fld.fold_method(*m)), dtor: dtor, - ctor_id: fld.new_id(struct_def.ctor_id) + ctor_id: option::map(&struct_def.ctor_id, |cid| fld.new_id(*cid)) }; } @@ -565,7 +565,7 @@ fn noop_fold_variant(v: variant_, fld: ast_fold) -> variant_ { methods: vec::map(struct_def.methods, |m| fld.fold_method(*m)), dtor: dtor, - ctor_id: fld.new_id(struct_def.ctor_id) + ctor_id: option::map(&struct_def.ctor_id, |c| fld.new_id(*c)) }) } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 1af516424ded2..23ddf61606d02 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2659,9 +2659,11 @@ impl Parser { let mut fields: ~[@struct_field]; let mut methods: ~[@method] = ~[]; let mut the_dtor: Option<(blk, ~[attribute], codemap::span)> = None; + let is_tuple_like; if self.eat(token::LBRACE) { // It's a record-like struct. + is_tuple_like = false; fields = ~[]; while self.token != token::RBRACE { match self.parse_class_item() { @@ -2694,6 +2696,7 @@ impl Parser { self.bump(); } else if self.token == token::LPAREN { // It's a tuple-like struct. + is_tuple_like = true; fields = do self.parse_unspanned_seq(token::LPAREN, token::RPAREN, seq_sep_trailing_allowed (token::COMMA)) |p| { @@ -2708,6 +2711,7 @@ impl Parser { self.expect(token::SEMI); } else if self.eat(token::SEMI) { // It's a unit-like struct. + is_tuple_like = true; fields = ~[]; } else { self.fatal(fmt!("expected `{`, `(`, or `;` after struct name \ @@ -2723,13 +2727,14 @@ impl Parser { body: d_body}, span: d_s}}; let _ = self.get_id(); // XXX: Workaround for crazy bug. + let new_id = self.get_id(); (class_name, item_class(@{ traits: traits, fields: move fields, methods: move methods, dtor: actual_dtor, - ctor_id: self.get_id() + ctor_id: if is_tuple_like { Some(new_id) } else { None } }, ty_params), None) } @@ -3076,7 +3081,7 @@ impl Parser { fields: move fields, methods: move methods, dtor: actual_dtor, - ctor_id: self.get_id() + ctor_id: Some(self.get_id()) }; } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 5151fd1bac837..53c1ce1c7f535 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -311,6 +311,7 @@ mod special_idents { const static : ident = ident { repr: 31u }; const intrinsic : ident = ident { repr: 32u }; const clownshoes_foreign_mod: ident = ident { repr: 33 }; + const unnamed_field: ident = ident { repr: 34 }; } struct ident_interner { diff --git a/src/rustc/middle/resolve.rs b/src/rustc/middle/resolve.rs index 1f7b3b1c8612f..97551162de58f 100644 --- a/src/rustc/middle/resolve.rs +++ b/src/rustc/middle/resolve.rs @@ -1187,12 +1187,14 @@ impl Resolver { // If this struct is tuple-like or enum-like, define a name // in the value namespace. - if struct_def.fields.len() == 0 || - struct_def.fields[0].node.kind == unnamed_field { - name_bindings.define_value( - privacy, - def_class(local_def(struct_def.ctor_id)), - sp); + match struct_def.ctor_id { + None => {} + Some(ctor_id) => { + name_bindings.define_value( + privacy, + def_class(local_def(ctor_id)), + sp); + } } // Record the def ID of this struct. diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index 3896fdf0e7a06..822fb4f417a8e 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -1648,7 +1648,8 @@ fn trans_enum_variant(ccx: @crate_ctxt, enum_id: ast::node_id, variant: ast::variant, args: ~[ast::variant_arg], - disr: int, is_degen: bool, + disr: int, + is_degen: bool, param_substs: Option, llfndecl: ValueRef) { let _icx = ccx.insn_ctxt("trans_enum_variant"); @@ -1698,6 +1699,51 @@ fn trans_enum_variant(ccx: @crate_ctxt, finish_fn(fcx, lltop); } +// NB: In theory this should be merged with the function above. But the AST +// structures are completely different, so very little code would be shared. +fn trans_tuple_struct(ccx: @crate_ctxt, + fields: ~[@ast::struct_field], + ctor_id: ast::node_id, + param_substs: Option, + llfndecl: ValueRef) { + let _icx = ccx.insn_ctxt("trans_tuple_struct"); + + // Translate struct fields to function arguments. + let fn_args = do fields.map |field| { + { + mode: ast::expl(ast::by_copy), + ty: field.node.ty, + ident: special_idents::arg, + id: field.node.id + } + }; + + let fcx = new_fn_ctxt_w_id(ccx, ~[], llfndecl, ctor_id, None, + param_substs, None); + let raw_llargs = create_llargs_for_fn_args(fcx, no_self, fn_args); + + let bcx = top_scope_block(fcx, None); + let lltop = bcx.llbb; + let arg_tys = ty::ty_fn_args(node_id_type(bcx, ctor_id)); + let bcx = copy_args_to_allocas(fcx, bcx, fn_args, raw_llargs, arg_tys); + + for fields.eachi |i, field| { + let lldestptr = GEPi(bcx, fcx.llretptr, [0, 0, i]); + let llarg = match fcx.llargs.get(field.node.id) { + local_mem(x) => x, + _ => { + ccx.tcx.sess.bug(~"trans_tuple_struct: llarg wasn't \ + local_mem") + } + }; + let arg_ty = arg_tys[i].ty; + memmove_ty(bcx, lldestptr, llarg, arg_ty); + } + + build_return(bcx); + finish_fn(fcx, lltop); +} + fn trans_class_dtor(ccx: @crate_ctxt, path: path, body: ast::blk, dtor_id: ast::node_id, psubsts: Option, @@ -1835,15 +1881,27 @@ fn trans_item(ccx: @crate_ctxt, item: ast::item) { fn trans_struct_def(ccx: @crate_ctxt, struct_def: @ast::struct_def, tps: ~[ast::ty_param], path: @ast_map::path, ident: ast::ident, id: ast::node_id) { + // If there are type parameters, the destructor and constructor will be + // monomorphized, so we don't translate them here. if tps.len() == 0u { - do option::iter(&struct_def.dtor) |dtor| { - trans_class_dtor(ccx, *path, dtor.node.body, - dtor.node.id, None, None, local_def(id)); - }; + // Translate the destructor. + do option::iter(&struct_def.dtor) |dtor| { + trans_class_dtor(ccx, *path, dtor.node.body, + dtor.node.id, None, None, local_def(id)); + }; + + // If this is a tuple-like struct, translate the constructor. + match struct_def.ctor_id { + None => {} + Some(ctor_id) => { + let llfndecl = get_item_val(ccx, ctor_id); + trans_tuple_struct(ccx, struct_def.fields, ctor_id, None, + llfndecl); + } + } } - // If there are ty params, the ctor will get monomorphized - // Translate methods + // Translate methods. meth::trans_impl(ccx, *path, ident, struct_def.methods, tps, None, id); } @@ -2128,8 +2186,25 @@ fn get_item_val(ccx: @crate_ctxt, id: ast::node_id) -> ValueRef { set_inline_hint(llfn); llfn } + + ast_map::node_struct_ctor(struct_def, struct_item, struct_path) => { + // Only register the constructor if this is a tuple-like struct. + match struct_def.ctor_id { + None => { + ccx.tcx.sess.bug(~"attempt to register a constructor of \ + a non-tuple-like struct") + } + Some(ctor_id) => { + let llfn = register_fn(ccx, struct_item.span, + *struct_path, ctor_id); + set_inline_hint(llfn); + llfn + } + } + } + _ => { - ccx.sess.bug(~"get_item_val(): unexpected variant"); + ccx.sess.bug(~"get_item_val(): unexpected variant") } }; if !(exprt || ccx.reachable.contains_key(id)) { diff --git a/src/rustc/middle/trans/callee.rs b/src/rustc/middle/trans/callee.rs index 175381a7bd1ee..2c1e9b21c6d4f 100644 --- a/src/rustc/middle/trans/callee.rs +++ b/src/rustc/middle/trans/callee.rs @@ -90,6 +90,9 @@ fn trans(bcx: block, expr: @ast::expr) -> Callee { vid).args.len() > 0u; fn_callee(bcx, trans_fn_ref(bcx, vid, ref_expr.id)) } + ast::def_class(def_id) => { + fn_callee(bcx, trans_fn_ref(bcx, def_id, ref_expr.id)) + } ast::def_arg(*) | ast::def_local(*) | ast::def_binding(*) | @@ -99,7 +102,7 @@ fn trans(bcx: block, expr: @ast::expr) -> Callee { } ast::def_mod(*) | ast::def_foreign_mod(*) | ast::def_const(*) | ast::def_ty(*) | ast::def_prim_ty(*) | - ast::def_use(*) | ast::def_class(*) | ast::def_typaram_binder(*) | + ast::def_use(*) | ast::def_typaram_binder(*) | ast::def_region(*) | ast::def_label(*) | ast::def_ty_param(*) => { bcx.tcx().sess.span_bug( ref_expr.span, diff --git a/src/rustc/middle/trans/monomorphize.rs b/src/rustc/middle/trans/monomorphize.rs index c2f106f631b19..4a569e2cf4de4 100644 --- a/src/rustc/middle/trans/monomorphize.rs +++ b/src/rustc/middle/trans/monomorphize.rs @@ -96,6 +96,7 @@ fn monomorphic_fn(ccx: @crate_ctxt, ast_map::node_local(*) => { ccx.tcx.sess.bug(~"Can't monomorphize a local") } + ast_map::node_struct_ctor(_, i, pt) => (pt, i.ident, i.span) }; // Look up the impl type if we're translating a default method. @@ -208,6 +209,18 @@ fn monomorphic_fn(ccx: @crate_ctxt, impl_did_opt.get()); d } + ast_map::node_struct_ctor(struct_def, _, _) => { + let d = mk_lldecl(); + set_inline_hint(d); + base::trans_tuple_struct(ccx, + struct_def.fields, + option::expect(struct_def.ctor_id, + ~"ast-mapped tuple struct \ + didn't have a ctor id"), + psubsts, + d); + d + } // Ugh -- but this ensures any new variants won't be forgotten ast_map::node_expr(*) | diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index 2daf40dbf841f..d6364e5515ed1 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -3630,6 +3630,10 @@ fn item_path(cx: ctxt, id: ast::def_id) -> ast_map::path { syntax::parse::token::special_idents::literally_dtor)) } + ast_map::node_struct_ctor(_, item, path) => { + vec::append_one(*path, ast_map::path_name(item.ident)) + } + ast_map::node_stmt(*) | ast_map::node_expr(*) | ast_map::node_arg(*) | ast_map::node_local(*) | ast_map::node_export(*) | ast_map::node_block(*) => { @@ -3874,7 +3878,13 @@ fn class_field_tys(fields: ~[@struct_field]) -> ~[field_ty] { vis: visibility, mutability: mutability}); } - unnamed_field => {} + unnamed_field => { + rslt.push({ident: + syntax::parse::token::special_idents::unnamed_field, + id: ast_util::local_def(field.node.id), + vis: ast::public, + mutability: ast::class_immutable}); + } } } rslt diff --git a/src/rustc/middle/typeck/collect.rs b/src/rustc/middle/typeck/collect.rs index b6ae922ed1a2f..8610fb046b593 100644 --- a/src/rustc/middle/typeck/collect.rs +++ b/src/rustc/middle/typeck/collect.rs @@ -597,35 +597,40 @@ fn convert_struct(ccx: @crate_ctxt, // If this struct is enum-like or tuple-like, create the type of its // constructor. - if struct_def.fields.len() == 0 { - // Enum-like. - write_ty_to_tcx(tcx, struct_def.ctor_id, selfty); - tcx.tcache.insert(local_def(struct_def.ctor_id), tpt); - } else if struct_def.fields[0].node.kind == ast::unnamed_field { - // Tuple-like. - let ctor_fn_ty = ty::mk_fn(tcx, FnTyBase { - meta: FnMeta { - purity: ast::pure_fn, - proto: ty::proto_bare, - bounds: @~[], - ret_style: ast::return_val, - }, - sig: FnSig { - inputs: do struct_def.fields.map |field| { - { - mode: ast::expl(ast::by_copy), - ty: ccx.tcx.tcache.get(local_def(field.node.id)).ty + match struct_def.ctor_id { + None => {} + Some(ctor_id) => { + if struct_def.fields.len() == 0 { + // Enum-like. + write_ty_to_tcx(tcx, ctor_id, selfty); + tcx.tcache.insert(local_def(ctor_id), tpt); + } else if struct_def.fields[0].node.kind == ast::unnamed_field { + // Tuple-like. + let ctor_fn_ty = ty::mk_fn(tcx, FnTyBase { + meta: FnMeta { + purity: ast::pure_fn, + proto: ty::proto_bare, + bounds: @~[], + ret_style: ast::return_val, + }, + sig: FnSig { + inputs: do struct_def.fields.map |field| { + { + mode: ast::expl(ast::by_copy), + ty: ccx.tcx.tcache.get(local_def(field.node.id)).ty + } + }, + output: selfty } - }, - output: selfty + }); + write_ty_to_tcx(tcx, ctor_id, ctor_fn_ty); + tcx.tcache.insert(local_def(ctor_id), { + bounds: tpt.bounds, + region_param: tpt.region_param, + ty: ctor_fn_ty + }); } - }); - write_ty_to_tcx(tcx, struct_def.ctor_id, ctor_fn_ty); - tcx.tcache.insert(local_def(struct_def.ctor_id), { - bounds: tpt.bounds, - region_param: tpt.region_param, - ty: ctor_fn_ty - }); + } } } diff --git a/src/test/run-pass/tuple-struct-construct.rs b/src/test/run-pass/tuple-struct-construct.rs new file mode 100644 index 0000000000000..3d2630e7a5cac --- /dev/null +++ b/src/test/run-pass/tuple-struct-construct.rs @@ -0,0 +1,7 @@ +struct Foo(int, int); + +fn main() { + let x = Foo(1, 2); + io::println(fmt!("%?", x)); +} +