about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2012-08-06 13:15:40 -0700
committerPatrick Walton <pcwalton@mimiga.net>2012-08-06 13:17:42 -0700
commitbff512a90facf2b0064f13737d44c716863f644d (patch)
tree72553ae018b6ed52c06e532346b35f3608d7559d /src
parent1e3143b34e66ca5157b03d3658d64d7c01563186 (diff)
rustc: Implement functional record update for structs
Diffstat (limited to 'src')
-rw-r--r--src/libsyntax/ast.rs4
-rw-r--r--src/libsyntax/fold.rs6
-rw-r--r--src/libsyntax/parse/parser.rs15
-rw-r--r--src/libsyntax/print/pprust.rs13
-rw-r--r--src/libsyntax/visit.rs3
-rw-r--r--src/rustc/middle/liveness.rs3
-rw-r--r--src/rustc/middle/resolve3.rs2
-rw-r--r--src/rustc/middle/trans/base.rs66
-rw-r--r--src/rustc/middle/typeck/check.rs49
-rw-r--r--src/test/run-pass/functional-struct-update.rs12
10 files changed, 127 insertions, 46 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 1e43ae219ba..daf3e20f469 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -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)
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index ec6d7010aa1..71c23ff4fa6 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -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)))
           }
         }
 }
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index a6c4e6c4131..71bd87981bc 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -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);
                 }
             }
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index c81b8b5335f..eca571b9ccd 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -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) => {
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 8d80f9663a3..1c92f26cabe 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -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, _) => {
diff --git a/src/rustc/middle/liveness.rs b/src/rustc/middle/liveness.rs
index fe343d76cc9..38942b40750 100644
--- a/src/rustc/middle/liveness.rs
+++ b/src/rustc/middle/liveness.rs
@@ -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)
             }
diff --git a/src/rustc/middle/resolve3.rs b/src/rustc/middle/resolve3.rs
index fc7f079e172..9f17e38ba54 100644
--- a/src/rustc/middle/resolve3.rs
+++ b/src/rustc/middle/resolve3.rs
@@ -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
diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs
index 6df45f75adb..6fecdead048 100644
--- a/src/rustc/middle/trans/base.rs
+++ b/src/rustc/middle/trans/base.rs
@@ -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) => {
diff --git a/src/rustc/middle/typeck/check.rs b/src/rustc/middle/typeck/check.rs
index d4efdbf7b01..c875ac8c386 100644
--- a/src/rustc/middle/typeck/check.rs
+++ b/src/rustc/middle/typeck/check.rs
@@ -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.
diff --git a/src/test/run-pass/functional-struct-update.rs b/src/test/run-pass/functional-struct-update.rs
new file mode 100644
index 00000000000..50f746a436a
--- /dev/null
+++ b/src/test/run-pass/functional-struct-update.rs
@@ -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));
+}
+