aboutsummaryrefslogtreecommitdiffhomepage
path: root/notes/aliasing_problems.txt
blob: 75982e712274ceb9a8f0a0027d6147e4a0956cb6 (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

Aliasing rules, and problems
============================

Basic aliasing rules:

    # These have different types, so they may not alias each other
    ref aliased int ia
    ref aliased byte ba

    # These do not have the alias keyword, so they may not alias
    # any other currently visible & actually used variable
    # (this goes both ways obviously; aliased types cannot alias
    #  any non-aliased types)
    ref int i
    ref byte b

    type Point = struct {
        int x
        int y
    }
    # p1 and p2 may alias
    # but the x and y fields may not alias ia
    ref aliased Point p1
    ref aliased Point p2

    type Obj = struct {
        int v
        aliased int w
    }
    # o1.v may not alias
    # o1.w may alias other int types
    ref Obj o1

Tricky parts:

    type Obj = private
    func work(ref var Obj o) -> void
    # If o.w could alias any int, then that could cause miscompiled code
    # due to unsafe optimizations (and also broken expectations from
    # programmers who call work())
    #
    # To solve this, we could say that data marked with "aliased" may ONLY
    # alias data that is visible both "from" and "to" the data.
    # --> Data can only alias data "that can see" the actual definition.
    #     "can see" = is in the same module.
    # --> If we let another "aliased int" alias w, then we may not leak
    #     that reference to any other module.
    # --> We need to have a "aliased(private)" syntax for things that can
    #     alias anything in the local module, but not other things:
    type Obj2 = struct {
        int v
        aliased(private) int w
    }
    # But we might also want a "totally free" aliasing qualifier, that
    # allows anything to alias anything... This is tricky, though, because
    # aliased data could be updated by any function
    # --> This would require that function parameters are annotated:
    func work_free(ref var aliased(*) Obj o) -> void
    # ^ this function can modify any object that is either
    #   1. "aliased(*)" or 2. "aliased", regardless of types,
    #   as long as both are visible and actually used.

Notes:
- An "actually used" object is something were there is any code path,
  reachable from the currently executed operation, that accesses the
  object in any way and/or passes it into a function that declares that
  it can do so.


Internal aliasing
-----------------

    type Thing = object {
        aliased copy OtherThing obj1
        aliased copy OtherThing obj2
        # Always require either "copy" or "ref" in records/variants/objects/arrays
        aliased ref OtherThing ref1
        aliased ref OtherThing ref2
    }

    # Parameter is not qualified with "aliased"
    # Should obj1/obj2 and/or ref1/ref2 be allowed to alias?
    # - obj1/obj2 obviously cannot alias with anything else
    # - what about ref1/ref2?
    func work1(var Thing t)
    # How about this (now obj1/obj2 could alias, if aliasing is allowed)
    func work2(var Thing t, OtherThing ot)


Post-return aliasing
--------------------

How to handle when a function captures a parameter, and keeps aliasing it
even after return?


A simple (but less safe) solution
=================================

- Allow that different paths can lead to the same object.
- Just disallow that local variables or parameters point to the same
  object (unless marked "aliased").
    - And require that this is be enforced.
    - I.e. require "if a != b { ... }" or "assert a != b"
        - after both a and b are last assigned.
        - before the *last accessed* variable (a or b) is first accessed

Controlling aliasing via "variable rankings"
===========================================

Also a simple solution: Each object has a "ranking", and an object with a
higher ranking cannot contain references to an object with a lower ranking.
(Within the same ranking, it is possible to have mutual/circular aliasing).