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