own qualifier ------------- The own qualifier can be placed on pointers, and means that the data that the pointer points to is "owned" by owner of the pointer. There must be exactly one owner of each dynamically allocated object at all times, except for objects that are garbage collected (these have the gc qualifier on each pointer instead). To prevent stale pointers, owned pointers may only be assigned to pointers that have a shorter (or equal) life span than the owned pointer. () use_object(SomeClass^ obj); () take_object(SomeClass own^ obj); SomeClass own^ ourobj = SomeClass:new(); SomeClass^ unowned = ourobj; // assign owned to unowned. OK use_object(unowned); take_object(unowned); // assign unowned to owned pointer! ERROR! // unowned is not use past this pointer, // so it's not considered to be live. // ERROR! get_name can't return an owned pointer to a part of an // object, unless it takes ownership and then frees the object. string own^ name = ourobj.get_name(); use_object(ourobj); take_object(ourobj); // ourobj is given away // no live pointers point to ourobj. OK lifetime qualifier ------------------ namespace SomeClass { typedef this = ( string own^ name; ); // the return value of get_name can be used as long as the object // that the function argument points to is valid. string own[this]^ get_name(const SomeClass^ this); // PROBLEM: // this.name can be changed and freed after get_name() returns! // How to solve this? // 1. require this.name to be "string own const^", i.e. immutable // 2. track who uses the object and if they call set_name (hard!) // 3. require the object to be immutable while name is being used: // string own[const this]^ get_name(const SomeClass^ this); // the string will become owned by the function (and then the given object). // note that "this" must be owned by *some* pointer, so we can transfer // ownership! () set_name(SomeClass^ this, string own^ name); } namespace TreeNode { typedef this = ( // a node owns it's children treenode own^? left, right; treenode^? parent; ); () add_left(TreeNode^ this, own TreeNode^ subtree) { if (this^.left) { free(this^.left?); } this^.left = subtree; // OK: takes ownership // "this" owns "subtree", so the lifetime of "parent" <= "this". OK // "parent" does not take ownership of "this" which we don't own. OK subtree^.parent = this; } () free(own TreeNode^ this) { // "own" is transitive, so we're now the owner of the entire subtree free(this^.left); free(this^.right); sys:free(this); } } move operator ------------- Works like in rust-lang: http://doc.rust-lang.org/doc/tutorial.html#unique-boxes It's equivalent to: // a <- b; a = b; b = undefined; // b may not be used now! In LRL it will probably not be necessary to use it (as long as the life time of "b" ends at the assignment). But IMO it makes the code easier to read, and it makes it easier for the compiler to know "where" the error is in violations against "own" (in the assignment or in the use afterwards). basic but strict rules for "own" pointers ----------------------------------------- These are a bit too strict and could probably be improved. But on the other hand, violating the rules isn't supposed to generate an error, but only a warning, unless the compiler can prove that there actually is an error. For any "own" pointer which is obtained in a function: 1. All code paths in the function must either: a) return the pointer, b) assign it to some place (including global variables) which existed on or before the time the "own" pointer was obtained (this prevents cycles), c) call another function which does b). 2. Lifetime of the new location must be at least as long as the current lifetime (this prevents dangling pointers). For example, 1b) prevents global "own" pointers from being passed into "own" qualified pointers that are not known to be a or be owned by a global variable. refown/refvar type qualifiers ----------------------------- Sometimes you want to have the same ownership (or constness) as another piece of data. This could be acheived with two new type qualifiers. Here's an example: // this function doesn't require a modifiable string as input, but if you // pass an modifiable string into it, then the result will also be // modifiable. refvar(s^) char^ find_space(char^ s); // basing the refvar on the return type might make the implementation // easier, but would also limit how it can be used (e.g. you can base a // refvar on another parameter) char^ find_space(refvar char^ s);