aboutsummaryrefslogtreecommitdiff
path: root/docs/notes/own.txt
blob: 2d16876c40191f8e5904c26e3ea83604899048a7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154


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);