rustc: Implement functional record update for structs
This commit is contained in:
parent
1e3143b34e
commit
bff512a90f
10 changed files with 127 additions and 46 deletions
|
|
@ -347,9 +347,7 @@ enum expr_ {
|
|||
expr_mac(mac),
|
||||
|
||||
// A struct literal expression.
|
||||
//
|
||||
// XXX: Add functional record update.
|
||||
expr_struct(@path, ~[field]),
|
||||
expr_struct(@path, ~[field], option<@expr>),
|
||||
|
||||
// A vector literal constructed from one repeated element.
|
||||
expr_repeat(@expr /* element */, @expr /* count */, mutability)
|
||||
|
|
|
|||
|
|
@ -478,8 +478,10 @@ fn noop_fold_expr(e: expr_, fld: ast_fold) -> expr_ {
|
|||
fld.fold_expr(e)),
|
||||
expr_assert(e) => expr_assert(fld.fold_expr(e)),
|
||||
expr_mac(mac) => expr_mac(fold_mac(mac)),
|
||||
expr_struct(path, fields) => {
|
||||
expr_struct(fld.fold_path(path), vec::map(fields, fold_field))
|
||||
expr_struct(path, fields, maybe_expr) => {
|
||||
expr_struct(fld.fold_path(path),
|
||||
vec::map(fields, fold_field),
|
||||
option::map(maybe_expr, |x| fld.fold_expr(x)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -912,18 +912,27 @@ class parser {
|
|||
self.bump();
|
||||
let mut fields = ~[];
|
||||
vec::push(fields, self.parse_field(token::COLON));
|
||||
while self.token != token::RBRACE {
|
||||
while self.token != token::RBRACE &&
|
||||
!self.is_keyword(~"with") {
|
||||
self.expect(token::COMMA);
|
||||
if self.token == token::RBRACE {
|
||||
if self.token == token::RBRACE ||
|
||||
self.is_keyword(~"with") {
|
||||
// Accept an optional trailing comma.
|
||||
break;
|
||||
}
|
||||
vec::push(fields, self.parse_field(token::COLON));
|
||||
}
|
||||
|
||||
let base;
|
||||
if self.eat_keyword(~"with") {
|
||||
base = some(self.parse_expr());
|
||||
} else {
|
||||
base = none;
|
||||
}
|
||||
|
||||
hi = pth.span.hi;
|
||||
self.expect(token::RBRACE);
|
||||
ex = expr_struct(pth, fields);
|
||||
ex = expr_struct(pth, fields, base);
|
||||
return self.mk_pexpr(lo, hi, ex);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -973,11 +973,20 @@ fn print_expr(s: ps, &&expr: @ast::expr) {
|
|||
}
|
||||
word(s.s, ~"}");
|
||||
}
|
||||
ast::expr_struct(path, fields) => {
|
||||
ast::expr_struct(path, fields, wth) => {
|
||||
print_path(s, path, true);
|
||||
word(s.s, ~"{");
|
||||
commasep_cmnt(s, consistent, fields, print_field, get_span);
|
||||
word(s.s, ~",");
|
||||
alt wth {
|
||||
some(expr) => {
|
||||
if vec::len(fields) > 0u { space(s.s); }
|
||||
ibox(s, indent_unit);
|
||||
word_space(s, ~"with");
|
||||
print_expr(s, expr);
|
||||
end(s);
|
||||
}
|
||||
_ => word(s.s, ~",")
|
||||
}
|
||||
word(s.s, ~"}");
|
||||
}
|
||||
ast::expr_tup(exprs) => {
|
||||
|
|
|
|||
|
|
@ -364,9 +364,10 @@ fn visit_expr<E>(ex: @expr, e: E, v: vt<E>) {
|
|||
for flds.each |f| { v.visit_expr(f.node.expr, e, v); }
|
||||
visit_expr_opt(base, e, v);
|
||||
}
|
||||
expr_struct(p, flds) => {
|
||||
expr_struct(p, flds, base) => {
|
||||
visit_path(p, e, v);
|
||||
for flds.each |f| { v.visit_expr(f.node.expr, e, v); }
|
||||
visit_expr_opt(base, e, v);
|
||||
}
|
||||
expr_tup(elts) => for elts.each |el| { v.visit_expr(el, e, v); }
|
||||
expr_call(callee, args, _) => {
|
||||
|
|
|
|||
|
|
@ -1071,7 +1071,8 @@ class liveness {
|
|||
}
|
||||
}
|
||||
|
||||
expr_struct(_, fields) => {
|
||||
expr_struct(_, fields, with_expr) => {
|
||||
let succ = self.propagate_through_opt_expr(with_expr, succ);
|
||||
do fields.foldr(succ) |field, succ| {
|
||||
self.propagate_through_expr(field.node.expr, succ)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4272,7 +4272,7 @@ class Resolver {
|
|||
visitor);
|
||||
}
|
||||
|
||||
expr_struct(path, _) => {
|
||||
expr_struct(path, _, _) => {
|
||||
// Resolve the path to the structure it goes to.
|
||||
//
|
||||
// XXX: We might want to support explicit type parameters in
|
||||
|
|
|
|||
|
|
@ -3392,7 +3392,8 @@ fn trans_rec(bcx: block, fields: ~[ast::field],
|
|||
}
|
||||
|
||||
fn trans_struct(block_context: block, span: span, fields: ~[ast::field],
|
||||
id: ast::node_id, dest: dest) -> block {
|
||||
base: option<@ast::expr>, id: ast::node_id, dest: dest)
|
||||
-> block {
|
||||
|
||||
let _instruction_context = block_context.insn_ctxt(~"trans_struct");
|
||||
let mut block_context = block_context;
|
||||
|
|
@ -3433,6 +3434,18 @@ fn trans_struct(block_context: block, span: span, fields: ~[ast::field],
|
|||
}
|
||||
}
|
||||
|
||||
// If the class has a destructor, our GEP is a little more
|
||||
// complicated.
|
||||
fn get_field(block_context: block, dest_address: ValueRef,
|
||||
class_id: ast::def_id, index: uint) -> ValueRef {
|
||||
if ty::ty_dtor(block_context.tcx(), class_id).is_some() {
|
||||
return GEPi(block_context,
|
||||
GEPi(block_context, dest_address, ~[0, 1]),
|
||||
~[0, index]);
|
||||
}
|
||||
return GEPi(block_context, dest_address, ~[0, index]);
|
||||
}
|
||||
|
||||
// Now translate each field.
|
||||
let mut temp_cleanups = ~[];
|
||||
for fields.each |field| {
|
||||
|
|
@ -3455,16 +3468,7 @@ fn trans_struct(block_context: block, span: span, fields: ~[ast::field],
|
|||
}
|
||||
}
|
||||
|
||||
// If the class has a destructor, our GEP is a little more
|
||||
// complicated.
|
||||
let dest;
|
||||
if ty::ty_dtor(block_context.tcx(), class_id).is_some() {
|
||||
dest = GEPi(block_context,
|
||||
GEPi(block_context, dest_address, ~[0, 1]),
|
||||
~[0, index]);
|
||||
} else {
|
||||
dest = GEPi(block_context, dest_address, ~[0, index]);
|
||||
}
|
||||
let dest = get_field(block_context, dest_address, class_id, index);
|
||||
|
||||
block_context = trans_expr_save_in(block_context,
|
||||
field.node.expr,
|
||||
|
|
@ -3476,6 +3480,42 @@ fn trans_struct(block_context: block, span: span, fields: ~[ast::field],
|
|||
vec::push(temp_cleanups, dest);
|
||||
}
|
||||
|
||||
match base {
|
||||
some(base_expr) => {
|
||||
let { bcx: bcx, val: llbasevalue } =
|
||||
trans_temp_expr(block_context, base_expr);
|
||||
block_context = bcx;
|
||||
|
||||
// Copy over inherited fields.
|
||||
for class_fields.eachi |i, class_field| {
|
||||
let exists = do vec::any(fields) |provided_field| {
|
||||
str::eq(provided_field.node.ident, class_field.ident)
|
||||
};
|
||||
if exists {
|
||||
again;
|
||||
}
|
||||
let lldestfieldvalue = get_field(block_context,
|
||||
dest_address,
|
||||
class_id,
|
||||
i);
|
||||
let llbasefieldvalue = GEPi(block_context,
|
||||
llbasevalue,
|
||||
~[0, i]);
|
||||
let field_type = ty::lookup_field_type(block_context.tcx(),
|
||||
class_id,
|
||||
class_field.id,
|
||||
substitutions);
|
||||
let llbasefieldvalue = load_if_immediate(block_context,
|
||||
llbasefieldvalue,
|
||||
field_type);
|
||||
block_context = copy_val(block_context, INIT,
|
||||
lldestfieldvalue, llbasefieldvalue,
|
||||
field_type);
|
||||
}
|
||||
}
|
||||
none => ()
|
||||
}
|
||||
|
||||
// Now revoke the cleanups, as we pass responsibility for the data
|
||||
// structure onto the caller.
|
||||
for temp_cleanups.each |temp_cleanup| {
|
||||
|
|
@ -3633,8 +3673,8 @@ fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block {
|
|||
ast::expr_rec(args, base) => {
|
||||
return trans_rec(bcx, args, base, e.id, dest);
|
||||
}
|
||||
ast::expr_struct(_, fields) => {
|
||||
return trans_struct(bcx, e.span, fields, e.id, dest);
|
||||
ast::expr_struct(_, fields, base) => {
|
||||
return trans_struct(bcx, e.span, fields, base, e.id, dest);
|
||||
}
|
||||
ast::expr_tup(args) => { return trans_tup(bcx, args, dest); }
|
||||
ast::expr_vstore(e, v) => {
|
||||
|
|
|
|||
|
|
@ -1699,7 +1699,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
|||
}
|
||||
}
|
||||
}
|
||||
ast::expr_struct(path, fields) => {
|
||||
ast::expr_struct(path, fields, base_expr) => {
|
||||
// Resolve the path.
|
||||
let class_id;
|
||||
alt tcx.def_map.find(id) {
|
||||
|
|
@ -1804,27 +1804,36 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
|||
}
|
||||
}
|
||||
|
||||
// Make sure the programmer specified all the fields.
|
||||
assert fields_found <= class_fields.len();
|
||||
if fields_found < class_fields.len() {
|
||||
let mut missing_fields = ~[];
|
||||
for class_fields.each |class_field| {
|
||||
let name = *class_field.ident;
|
||||
let (_, seen) = class_field_map.get(name);
|
||||
if !seen {
|
||||
vec::push(missing_fields,
|
||||
~"`" + name + ~"`");
|
||||
match base_expr {
|
||||
none => {
|
||||
// Make sure the programmer specified all the fields.
|
||||
assert fields_found <= class_fields.len();
|
||||
if fields_found < class_fields.len() {
|
||||
let mut missing_fields = ~[];
|
||||
for class_fields.each |class_field| {
|
||||
let name = *class_field.ident;
|
||||
let (_, seen) = class_field_map.get(name);
|
||||
if !seen {
|
||||
vec::push(missing_fields,
|
||||
~"`" + name + ~"`");
|
||||
}
|
||||
}
|
||||
|
||||
tcx.sess.span_err(expr.span,
|
||||
fmt!{"missing field%s: %s",
|
||||
if missing_fields.len() == 1 {
|
||||
~""
|
||||
} else {
|
||||
~"s"
|
||||
},
|
||||
str::connect(missing_fields,
|
||||
~", ")});
|
||||
}
|
||||
}
|
||||
|
||||
tcx.sess.span_err(expr.span,
|
||||
fmt!{"missing field%s: %s",
|
||||
if missing_fields.len() == 1 {
|
||||
~""
|
||||
} else {
|
||||
~"s"
|
||||
},
|
||||
str::connect(missing_fields, ~", ")});
|
||||
some(base_expr) => {
|
||||
// Just check the base expression.
|
||||
check_expr(fcx, base_expr, some(struct_type));
|
||||
}
|
||||
}
|
||||
|
||||
// Write in the resulting type.
|
||||
|
|
|
|||
12
src/test/run-pass/functional-struct-update.rs
Normal file
12
src/test/run-pass/functional-struct-update.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
struct Foo {
|
||||
x: int;
|
||||
y: int;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a = Foo { x: 1, y: 2 };
|
||||
let b = Foo { x: 3 with a };
|
||||
let c = Foo { x: 4, with a };
|
||||
io::println(fmt!("%? %?", b, c));
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue