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