aboutsummaryrefslogtreecommitdiff
path: root/docs/notes/misc_safety.txt
blob: 95e31bd584c472a1e74582de056746465a20c68a (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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185



do not allow assignment expressions where booleans are expected
---------------------------------------------------------------

prevents common errors like these:

    bool a, b;
    
    if (a = b) { ... }
    while (a = b) { ... }


but we still want to allow these:

    bool a, b;
    
    a = b = true;

we could make an exception for assignment expressions, like this:

  * If a boolean expression is NOT expected in a given node in the parse tree,
  * Or, if the node is an assignment node,
  * Then accept the node.


unary plus/minus
----------------

should we allow things like this?

    c = a + -b

or only allow unary minus when it's at the start?
or only allow unary minus when it's not before a plus?


incomplete types
----------------

It's easy to accidentally create an incomplete type:

    typedef bintree[T] = (bintree[T]^? left, right; T value); // incomplete!
    typedef bintree[T] = (bintree[T]^? left, right; T^ value);

Such mistakes ought to be detected when the type is compiled, not when it's
used. One way of detecting this early is to require a qualifier on
incomplete types:

    typedef bintree[T] = incomplete (bintree[T]^? left, right; T value);

    typedef item = incomplete (
        int x;
        private;
    );


optional private types
----------------------

An optional type can be merged with it's target type sometimes. For
instance, pointers can have a NULL value in their underlying type, so
making a pointer optional will simply allow the NULL value to be stored
as well. On the other hand, struct types will be prepended with a boolean
flag which tells whether the value has been set or not.

Because the underlying structure of optional types depend on the target
type it's not possible to have optional private types:

    typedef A = private; // could be a pointer, or something else
    A?^ value; // not allowed
    
    // types from type parameters are private
    typedef B[T] = (
        T? x; // not allowed
    );


But these types can be rewritten as incomplete structs:

    typedef A = private;
    (A)?^ value; // Optional struct. This is OK
    
    typedef B[T] = (
        (T)? x; // OK
    );

floating point NaN
------------------

Operations with NaN is always false. This probably useful in many cases, but
not when you want to test for NaN (a friend encountered this problem).
Solution: forbid comparison with a constant NaN (always false) and add a
.isNaN() method.


then-else and subtypes
----------------------

    // B and C are subtypes of A
    
    (i < 1 then b else c).print();


  * Should method overriding be allowed in subtypes?
  * Should then-else be allowed in this case?
  * What should the code above mean?
  
        // This is not going to work with multiple then-elses in the same
        // expression due to combinatorial explosion.
        if (i < 1) b.print();
        else c.print();
    
    or
    
        // We don't know which function to call, if it has been overridden!
        A^ temp = (i < 1 then @b else @c);
        temp^.print();
    
    or
    
        // Not allowed if it can cause slicing!
        // (could probably be avoided by making the decision when the
        //  function "this" argument is passed)
        A temp = (i < 1 then b else c);
        temp.print();


private types and constraints
-----------------------------

    // private definition
    typedef t = (
        count length;
        int[length]^ entries;
    );
    
    // public definition - incorrect/unsafe
    typedef t = (
        count length;  // ERROR! length is used in a constraint, so it
        private;       // may not be writable by code which is not aware
    );                 // of the constraint.
    
    // public definition - correct
    typedef t = (
        readonly count length;  // OK
        private;
    );


exported functions/symbols in .lc files
---------------------------------------

It should be possible to see in an .lc file which things are exported and not
(similar to how you can look for the "static" keyword in a .c file)

E.g

    int prepare_internal()
    {
    }

    public int dosomething()
    {
        prepare_internal();
        ...
    }

    // or
    export int dosomething()
    {
        prepare_internal();
        ...
    }


However, the code at the caller side should *not* need to change if a function
is changed to be public or no longer public. So underscore prefixing
("_interal") or significant upper/lowercase characters (e.g. "PublicFunction" and
"private_function") aren't any good ideas IMO.