phase / mars

Minimal programming language with type inference, structures, control flow, and more.
https://jadon.io/mars/
Mozilla Public License 2.0
20 stars 0 forks source link

Copying Primitive Types #8

Open phase opened 7 years ago

phase commented 7 years ago

With 0b6cf3749bf5ff765c0de0d9f2ef137a496fdb53, I introduced a very primitive ownership implementation into the Semantic Analysis pass. The biggest caveat of this is that it will error when moving primitive types, such as ints.

In Rust, you can't use a struct once you've moved it.

struct P {
    x: i32,
}

fn main() {
    let a = P { x: 7 };
    let b = a;
    let c = a.x + b.x;
    println!("{}", c);
}

This code will result in the following error:

rustc 1.16.0 (30cf806ef 2017-03-10)
error[E0382]: use of moved value: `a.x`
 --> <anon>:8:13
  |
7 |     let b = a;
  |         - value moved here
8 |     let c = a.x + b.x;
  |             ^^^ value used here after move
  |
  = note: move occurs because `a` has type `P`, which does not implement the `Copy` trait

error: aborting due to previous error

This behavior has been implemented:

class P
    let x : Int

    init (v : Int)
        x = v
;

main () : Int
    let a = new P(7),
    let b = a,
    let c = a.x + b.x,
    0
11             let b = a,
12             let c = a.x + b.x,
   ~~~~~~~~~~~~^
13             0
Reference to 'a' has already been used.

However, Rust handles primitives differently...

fn main() {
    let a = 7;
    let b = a;
    let c = a + b;
    println!("{}", c);
}

This code is perfectly allowed in Rust, since a is being copied to b when it is moved.

main ()
  let a = 7,
  let b = a,
  let c = a + b,
  0

This will currently error with the following:

4             let b = a,
5             let c = a + b,
  ~~~~~~~~~~~~^
6             0
Reference to 'a' has already been used.

If we look at the above Rust error, we can see that our struct didn't implement the Copy trait. Integers in Rust implement this trait, and thus can be copied. It would be easy to create a special case for our defined primitive types, but having something like "traits" would be beneficial to users who want their classes to be copyable. Maybe an annotation?

phase commented 7 years ago
@copy
class P
    let x : Int
;

I think this could be a good idea. I've always liked the idea of annotations, but making the language annotation heavy might not make it super intuative.

Techcable commented 7 years ago

I think classes should be value-based by default and should have to explicitly request reference schematics (forcing heap-allocation). If this is done, the compiler is free to stack-allocate and copy immutable objects, allowing boxing to be deferred indefinitely.

phase commented 7 years ago

If we're moving large objects, copying would be quite slow.

Think about this:

LargeObject largeObj = new LargeObject();
largeObj.a = 7;
largeObj.b = true;
// etc.

LargeObjectContainer box = new LargeObjectContainer(largeObj);

If we have to copy largeObj, then it'll be slower than just passing the reference.

Objects will be stack allocated once there's a pass implemented to detect that they can be.

Techcable commented 7 years ago

You only need to do the optimization for small immutable objects, which are the most common.