Skip to content

Commit 4e9aaae

Browse files
committed
Only allow maximally divergent functions for in-place update of local vector variables
1 parent 76e0d08 commit 4e9aaae

File tree

4 files changed

+54
-49
lines changed

4 files changed

+54
-49
lines changed

doc/spec/tour.kk.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -677,7 +677,7 @@ Koka can even [reuse][#sec-fbip] the memory of the elements of the vector if the
677677
```
678678
fun reuse()
679679
var myvec := vector-init(10, fn(i) [i - 1, i, i + 1])
680-
myvec.update(0, fn(l) l.map(fn(x) x + 1))
680+
std/core/types/@byref(myvec).update(0, fn(l) l.map(fn(x) x + 1))
681681
```
682682

683683
Because the `myvec` is unique and the value at that position is also unique, the memory of both the old vector and the old value can be used in place thanks to Perceus reference counting.

lib/std/core/vector.kk

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,21 +80,23 @@ pub inline extern copy(v: vector<a>): vector<a>
8080
// var v[0] := 1 // updates v in place
8181
// ```
8282
// TODO: Is this safe in cs / js? I think we might need to conservatively copy?
83-
pub inline extern assign/@index( ^self : local-var<s,vector<a>>, ^index : int, assigned : a ) : <local<s>,exn> ()
83+
pub inline extern assign/@index( ^self : local-var<h,vector<a>>, ^index : int, assigned : a ) : <local<h>> ()
8484
c "kk_ref_vector_assign_borrow"
8585
cs inline "(#1)[(int)#2] = #3" // check bounds?
8686
js inline "(#1)[#2] = #3" // todo: check bounds!
8787

8888
// Assign to an entry in a local `:vector` variable.
89-
pub inline fun assign/update( ^self : local-var<s,vector<a>>, ^index : int, f : a -> <local<s>,exn,div|e> a ) : <local<s>,exn,div|e> ()
89+
pub inline fun assign/update( ^self : local-var<h,vector<a>>, ^index : int, f : a -> <local<h>,div> a ) : <local<h>,div> ()
9090
val i = index.ssize_t
91+
// TODO: Somehow get rid of div & allow linear effects
9192
ensure-unique(std/core/types/@byref(self))
9293
val x = self.unsafe-extract(i)
9394
val res = f(x)
9495
self[index] := res
9596

9697
// Ensures that a vector in a local variable is unique
97-
pub inline fun local/ensure-unique(^v: local-var<s,vector<a>>): <local<s>,exn,div|e> ()
98+
// TODO: Remove div (need hdiv constraint)
99+
pub inline fun local/ensure-unique(^v: local-var<h,vector<a>>): <local<h>,div> ()
98100
if v.unsafe-is-unique then () else v := unsafe-copy(v)
99101

100102
// Length of a vector.

test/cgen/vec-unique.kk

Lines changed: 48 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ fun update(xs: list<int>): console list<int>
33
println(if xs.unsafe-is-unique then "unique a" else "not unique a")
44
xs.map fn(x)
55
x + 1
6+
7+
fun update-trace(xs: list<int>): list<int>
8+
trace(if xs.unsafe-is-unique then "unique a" else "not unique a")
9+
xs.map fn(x)
10+
x + 1
611

712
// This is unsafe, since the behavior that depends on this check needs to be observationally equivalent
813
inline fip extern unsafe-is-unique( ^v : list<a> ) : bool
@@ -51,13 +56,13 @@ fun main()
5156
println("Fip update to item in local vector variable")
5257
var a1 := vector-init-total(3, fn(i) [i])
5358
a1[1] := [5]
54-
update(std/core/types/@byref(a1), 2, update)
59+
std/core/types/@byref(a1).update(2, update-trace)
5560
a1.map(fn(x) x.show).join("").println
5661

5762
println("Non-fip update to item in local vector variable")
5863
var a2 := vector-init-total(3, fn(i) [i])
5964
val a3 = a2
60-
update(std/core/types/@byref(a2), 2, update)
65+
std/core/types/@byref(a2).update(2, update-trace)
6166
a2.map(fn(x) x.show).join("").println
6267
a3.map(fn(x) x.show).join("").println
6368

@@ -77,48 +82,48 @@ fun update-throw2(x: list<int>)
7782
// The nice thing about multiple references is that it will always be copied - so there is no issue
7883
// The problem with exceptions, is the reference count is too low
7984
//
80-
fun test-error1()
81-
println("Non-fip update to item in local vector variable")
82-
var a2 := vector-init-total(3, fn(i) [i])
83-
val a3 = a2
84-
try {
85-
update(std/core/types/@byref(a2), 2, update-throw)
86-
a2.map(fn(x) x.show).join("").println
87-
} fn(err) {
88-
match err.info
89-
ExnL(l) -> println(if l.unsafe-is-unique then "unique a" else "not unique a")
90-
a2.foreach(fn(x) x.show.trace)
91-
}
85+
// fun test-error1()
86+
// println("Non-fip update to item in local vector variable")
87+
// var a2 := vector-init-total(3, fn(i) [i])
88+
// val a3 = a2
89+
// try {
90+
// update(std/core/types/@byref(a2), 2, update-throw)
91+
// a2.map(fn(x) x.show).join("").println
92+
// } fn(err) {
93+
// match err.info
94+
// ExnL(l) -> println(if l.unsafe-is-unique then "unique a" else "not unique a")
95+
// a2.foreach(fn(x) x.show.trace)
96+
// }
9297

93-
fun test-error2()
94-
println("Non-fip update to item in local vector variable")
95-
val a4 = vector-init-total(3, fn(i) [i])
96-
var a5 := a4
97-
try {
98-
update(std/core/types/@byref(a5), 2, update-throw)
99-
a5.map(fn(x) x.show).join("").println
100-
} fn(err) {
101-
match err.info
102-
ExnL(l) -> println(if l.unsafe-is-unique then "unique a" else "not unique a")
103-
a5.foreach(fn(x) x.show.trace)
104-
}
98+
// fun test-error2()
99+
// println("Non-fip update to item in local vector variable")
100+
// val a4 = vector-init-total(3, fn(i) [i])
101+
// var a5 := a4
102+
// try {
103+
// update(std/core/types/@byref(a5), 2, update-throw)
104+
// a5.map(fn(x) x.show).join("").println
105+
// } fn(err) {
106+
// match err.info
107+
// ExnL(l) -> println(if l.unsafe-is-unique then "unique a" else "not unique a")
108+
// a5.foreach(fn(x) x.show.trace)
109+
// }
105110

106-
fun test-error3()
107-
println("Non-fip update to item in local vector variable")
108-
val a6 = vector-init-total(3, fn(i) [i])
109-
var a7 := a6
110-
try {
111-
update(std/core/types/@byref(a7), 2, update-throw)
112-
a7.map(fn(x) x.show).join("").println
113-
} fn(err) {
114-
match err.info
115-
ExnL(l) -> println(if l.unsafe-is-unique then "unique a" else "not unique a")
116-
a7.foreach(fn(x) x.show.trace)
117-
}
111+
// fun test-error3()
112+
// println("Non-fip update to item in local vector variable")
113+
// val a6 = vector-init-total(3, fn(i) [i])
114+
// var a7 := a6
115+
// try {
116+
// update(std/core/types/@byref(a7), 2, update-throw)
117+
// a7.map(fn(x) x.show).join("").println
118+
// } fn(err) {
119+
// match err.info
120+
// ExnL(l) -> println(if l.unsafe-is-unique then "unique a" else "not unique a")
121+
// a7.foreach(fn(x) x.show.trace)
122+
// }
118123

119124
// This works for some reason?
120-
fun test-error4()
121-
println("Non-fip update to item in local vector variable")
122-
val a6 = vector-init-total(3, fn(i) [i])
123-
var a7 := a6
124-
update(std/core/types/@byref(a7), 2, update-throw)
125+
// fun test-error4()
126+
// println("Non-fip update to item in local vector variable")
127+
// val a6 = vector-init-total(3, fn(i) [i])
128+
// var a7 := a6
129+
// update(std/core/types/@byref(a7), 2, update-throw)

test/cgen/vec-unique.kk.out

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ unique a
1717
Non-fip copies of vector in multiple resumptions
1818
["0,1,0","0,1,1","0,0,0","0,0,1","1,1,0","1,1,1","1,0,0","1,0,1"]
1919
Fip update to item in local vector variable
20-
unique a
2120
[0][5][3]
2221
Non-fip update to item in local vector variable
23-
not unique a
2422
[0][1][3]
2523
[0][1][2]

0 commit comments

Comments
 (0)