Skip to content

Commit a203862

Browse files
authored
Add and fix contracts for inline functions (#3535)
1 parent 5026cfd commit a203862

File tree

29 files changed

+2084
-613
lines changed

29 files changed

+2084
-613
lines changed

arrow-libs/core/arrow-atomic/src/commonMain/kotlin/arrow/atomic/Atomic.kt

+37-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
@file:OptIn(ExperimentalContracts::class)
2+
13
package arrow.atomic
24

5+
import kotlin.contracts.ExperimentalContracts
6+
import kotlin.contracts.InvocationKind
7+
import kotlin.contracts.contract
8+
39
/**
410
* [Atomic] value of [V].
511
*
@@ -44,31 +50,55 @@ public var <T> Atomic<T>.value: T
4450
/**
4551
* Infinite loop that reads this atomic variable and performs the specified [action] on its value.
4652
*/
47-
public inline fun <V> Atomic<V>.loop(action: (V) -> Unit): Nothing { while(true) { action(value) } }
53+
public inline fun <V> Atomic<V>.loop(action: (V) -> Unit): Nothing {
54+
contract { callsInPlace(action, InvocationKind.AT_LEAST_ONCE) }
55+
do { action(value) } while(true)
56+
}
4857

49-
public inline fun <V> Atomic<V>.tryUpdate(function: (V) -> V): Boolean = tryUpdate(function) { _, _ -> }
58+
public inline fun <V> Atomic<V>.tryUpdate(function: (V) -> V): Boolean {
59+
contract { callsInPlace(function, InvocationKind.EXACTLY_ONCE) }
60+
return tryUpdate(function) { _, _ -> }
61+
}
5062

51-
public inline fun <V> Atomic<V>.update(function: (V) -> V): Unit = update(function) { _, _ -> }
63+
public inline fun <V> Atomic<V>.update(function: (V) -> V) {
64+
contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) }
65+
update(function) { _, _ -> }
66+
}
5267

5368
/**
5469
* Updates variable atomically using the specified [function] of its value and returns its old value.
5570
*/
56-
public inline fun <V> Atomic<V>.getAndUpdate(function: (V) -> V): V = update(function) { old, _ -> old }
71+
public inline fun <V> Atomic<V>.getAndUpdate(function: (V) -> V): V {
72+
contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) }
73+
return update(function) { old, _ -> old }
74+
}
5775

5876
/**
5977
* Updates variable atomically using the specified [function] of its value and returns its new value.
6078
*/
61-
public inline fun <V> Atomic<V>.updateAndGet(function: (V) -> V): V = update(function) { _, new -> new }
79+
public inline fun <V> Atomic<V>.updateAndGet(function: (V) -> V): V {
80+
contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) }
81+
return update(function) { _, new -> new }
82+
}
6283

6384
@PublishedApi
6485
internal inline fun <V, U: V, R> Atomic<V>.update(function: (V) -> U, transform: (old: V, new: U) -> R): R {
65-
while (true) {
66-
tryUpdate(function) { old, new -> return transform(old, new) }
86+
contract {
87+
callsInPlace(function, InvocationKind.AT_LEAST_ONCE)
88+
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
89+
}
90+
loop { cur ->
91+
val upd = function(value)
92+
if(compareAndSet(cur, upd)) return transform(cur, upd)
6793
}
6894
}
6995

7096
@PublishedApi
7197
internal inline fun <V, U: V> Atomic<V>.tryUpdate(function: (V) -> U, onUpdated: (old: V, new: U) -> Unit): Boolean {
98+
contract {
99+
callsInPlace(function, InvocationKind.EXACTLY_ONCE)
100+
callsInPlace(onUpdated, InvocationKind.AT_MOST_ONCE)
101+
}
72102
val cur = value
73103
val upd = function(cur)
74104
return compareAndSet(cur, upd).also { if (it) onUpdated(cur, upd) }

arrow-libs/core/arrow-atomic/src/commonMain/kotlin/arrow/atomic/AtomicBoolean.kt

+37-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
@file:OptIn(ExperimentalContracts::class)
2+
13
package arrow.atomic
24

5+
import kotlin.contracts.ExperimentalContracts
6+
import kotlin.contracts.InvocationKind
7+
import kotlin.contracts.contract
8+
39
public class AtomicBoolean(value: Boolean) {
410
private val inner = AtomicInt(value.toInt())
511

@@ -28,31 +34,55 @@ public class AtomicBoolean(value: Boolean) {
2834
/**
2935
* Infinite loop that reads this atomic variable and performs the specified [action] on its value.
3036
*/
31-
public inline fun AtomicBoolean.loop(action: (Boolean) -> Unit): Nothing { while(true) { action(value) } }
37+
public inline fun AtomicBoolean.loop(action: (Boolean) -> Unit): Nothing {
38+
contract { callsInPlace(action, InvocationKind.AT_LEAST_ONCE) }
39+
do { action(value) } while(true)
40+
}
3241

33-
public inline fun AtomicBoolean.tryUpdate(function: (Boolean) -> Boolean): Boolean = tryUpdate(function) { _, _ -> }
42+
public inline fun AtomicBoolean.tryUpdate(function: (Boolean) -> Boolean): Boolean {
43+
contract { callsInPlace(function, InvocationKind.EXACTLY_ONCE) }
44+
return tryUpdate(function) { _, _ -> }
45+
}
3446

35-
public inline fun AtomicBoolean.update(function: (Boolean) -> Boolean): Unit = update(function) { _, _ -> }
47+
public inline fun AtomicBoolean.update(function: (Boolean) -> Boolean) {
48+
contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) }
49+
update(function) { _, _ -> }
50+
}
3651

3752
/**
3853
* Updates variable atomically using the specified [function] of its value and returns its old value.
3954
*/
40-
public inline fun AtomicBoolean.getAndUpdate(function: (Boolean) -> Boolean): Boolean = update(function) { old, _ -> old }
55+
public inline fun AtomicBoolean.getAndUpdate(function: (Boolean) -> Boolean): Boolean {
56+
contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) }
57+
return update(function) { old, _ -> old }
58+
}
4159

4260
/**
4361
* Updates variable atomically using the specified [function] of its value and returns its new value.
4462
*/
45-
public inline fun AtomicBoolean.updateAndGet(function: (Boolean) -> Boolean): Boolean = update(function) { _, new -> new }
63+
public inline fun AtomicBoolean.updateAndGet(function: (Boolean) -> Boolean): Boolean {
64+
contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) }
65+
return update(function) { _, new -> new }
66+
}
4667

4768
@PublishedApi
4869
internal inline fun <R> AtomicBoolean.update(function: (Boolean) -> Boolean, transform: (old: Boolean, new: Boolean) -> R): R {
49-
while (true) {
50-
tryUpdate(function) { old, new -> return transform(old, new) }
70+
contract {
71+
callsInPlace(function, InvocationKind.AT_LEAST_ONCE)
72+
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
73+
}
74+
loop { cur ->
75+
val upd = function(value)
76+
if(compareAndSet(cur, upd)) return transform(cur, upd)
5177
}
5278
}
5379

5480
@PublishedApi
5581
internal inline fun AtomicBoolean.tryUpdate(function: (Boolean) -> Boolean, onUpdated: (old: Boolean, new: Boolean) -> Unit): Boolean {
82+
contract {
83+
callsInPlace(function, InvocationKind.EXACTLY_ONCE)
84+
callsInPlace(onUpdated, InvocationKind.AT_MOST_ONCE)
85+
}
5686
val cur = value
5787
val upd = function(cur)
5888
return compareAndSet(cur, upd).also { if (it) onUpdated(cur, upd) }

arrow-libs/core/arrow-atomic/src/commonMain/kotlin/arrow/atomic/AtomicInt.kt

+37-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
@file:OptIn(ExperimentalContracts::class)
2+
13
package arrow.atomic
24

5+
import kotlin.contracts.ExperimentalContracts
6+
import kotlin.contracts.InvocationKind
7+
import kotlin.contracts.contract
8+
39
public expect class AtomicInt(initialValue: Int) {
410

511
public fun get(): Int
@@ -23,31 +29,55 @@ public var AtomicInt.value: Int
2329
/**
2430
* Infinite loop that reads this atomic variable and performs the specified [action] on its value.
2531
*/
26-
public inline fun AtomicInt.loop(action: (Int) -> Unit): Nothing { while(true) { action(value) } }
32+
public inline fun AtomicInt.loop(action: (Int) -> Unit): Nothing {
33+
contract { callsInPlace(action, InvocationKind.AT_LEAST_ONCE) }
34+
do { action(value) } while(true)
35+
}
2736

28-
public inline fun AtomicInt.tryUpdate(function: (Int) -> Int): Boolean = tryUpdate(function) { _, _ -> }
37+
public inline fun AtomicInt.tryUpdate(function: (Int) -> Int): Boolean {
38+
contract { callsInPlace(function, InvocationKind.EXACTLY_ONCE) }
39+
return tryUpdate(function) { _, _ -> }
40+
}
2941

30-
public inline fun AtomicInt.update(function: (Int) -> Int) : Unit = update(function) { _, _ -> }
42+
public inline fun AtomicInt.update(function: (Int) -> Int) {
43+
contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) }
44+
update(function) { _, _ -> }
45+
}
3146

3247
/**
3348
* Updates variable atomically using the specified [function] of its value and returns its old value.
3449
*/
35-
public inline fun AtomicInt.getAndUpdate(function: (Int) -> Int): Int = update(function) { old, _ -> old }
50+
public inline fun AtomicInt.getAndUpdate(function: (Int) -> Int): Int {
51+
contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) }
52+
return update(function) { old, _ -> old }
53+
}
3654

3755
/**
3856
* Updates variable atomically using the specified [function] of its value and returns its new value.
3957
*/
40-
public inline fun AtomicInt.updateAndGet(function: (Int) -> Int): Int = update(function) { _, new -> new }
58+
public inline fun AtomicInt.updateAndGet(function: (Int) -> Int): Int {
59+
contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) }
60+
return update(function) { _, new -> new }
61+
}
4162

4263
@PublishedApi
4364
internal inline fun <R> AtomicInt.update(function: (Int) -> Int, transform: (old: Int, new: Int) -> R): R {
44-
while (true) {
45-
tryUpdate(function) { old, new -> return transform(old, new) }
65+
contract {
66+
callsInPlace(function, InvocationKind.AT_LEAST_ONCE)
67+
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
68+
}
69+
loop { cur ->
70+
val upd = function(value)
71+
if(compareAndSet(cur, upd)) return transform(cur, upd)
4672
}
4773
}
4874

4975
@PublishedApi
5076
internal inline fun AtomicInt.tryUpdate(function: (Int) -> Int, onUpdated: (old: Int, new: Int) -> Unit): Boolean {
77+
contract {
78+
callsInPlace(function, InvocationKind.EXACTLY_ONCE)
79+
callsInPlace(onUpdated, InvocationKind.AT_MOST_ONCE)
80+
}
5181
val cur = value
5282
val upd = function(cur)
5383
return compareAndSet(cur, upd).also { if (it) onUpdated(cur, upd) }

arrow-libs/core/arrow-atomic/src/commonMain/kotlin/arrow/atomic/AtomicLong.kt

+37-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
@file:OptIn(ExperimentalContracts::class)
2+
13
package arrow.atomic
24

5+
import kotlin.contracts.ExperimentalContracts
6+
import kotlin.contracts.InvocationKind
7+
import kotlin.contracts.contract
8+
39
public expect class AtomicLong(initialValue: Long) {
410

511
public fun get(): Long
@@ -23,31 +29,55 @@ public var AtomicLong.value: Long
2329
/**
2430
* Infinite loop that reads this atomic variable and performs the specified [action] on its value.
2531
*/
26-
public inline fun AtomicLong.loop(action: (Long) -> Unit): Nothing { while(true) { action(value) } }
32+
public inline fun AtomicLong.loop(action: (Long) -> Unit): Nothing {
33+
contract { callsInPlace(action, InvocationKind.AT_LEAST_ONCE) }
34+
do { action(value) } while(true)
35+
}
2736

28-
public inline fun AtomicLong.tryUpdate(function: (Long) -> Long): Boolean = tryUpdate(function) { _, _ -> }
37+
public inline fun AtomicLong.tryUpdate(function: (Long) -> Long): Boolean {
38+
contract { callsInPlace(function, InvocationKind.EXACTLY_ONCE) }
39+
return tryUpdate(function) { _, _ -> }
40+
}
2941

30-
public inline fun AtomicLong.update(function: (Long) -> Long): Unit = update(function) { _, _ -> }
42+
public inline fun AtomicLong.update(function: (Long) -> Long) {
43+
contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) }
44+
update(function) { _, _ -> }
45+
}
3146

3247
/**
3348
* Updates variable atomically using the specified [function] of its value and returns its old value.
3449
*/
35-
public inline fun AtomicLong.getAndUpdate(function: (Long) -> Long): Long = update(function) { old, _ -> old }
50+
public inline fun AtomicLong.getAndUpdate(function: (Long) -> Long): Long {
51+
contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) }
52+
return update(function) { old, _ -> old }
53+
}
3654

3755
/**
3856
* Updates variable atomically using the specified [function] of its value and returns its new value.
3957
*/
40-
public inline fun AtomicLong.updateAndGet(function: (Long) -> Long): Long = update(function) { _, new -> new }
58+
public inline fun AtomicLong.updateAndGet(function: (Long) -> Long): Long {
59+
contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) }
60+
return update(function) { _, new -> new }
61+
}
4162

4263
@PublishedApi
4364
internal inline fun <R> AtomicLong.update(function: (Long) -> Long, transform: (old: Long, new: Long) -> R): R {
44-
while (true) {
45-
tryUpdate(function) { old, new -> return transform(old, new) }
65+
contract {
66+
callsInPlace(function, InvocationKind.AT_LEAST_ONCE)
67+
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
68+
}
69+
loop { cur ->
70+
val upd = function(value)
71+
if(compareAndSet(cur, upd)) return transform(cur, upd)
4672
}
4773
}
4874

4975
@PublishedApi
5076
internal inline fun AtomicLong.tryUpdate(function: (Long) -> Long, onUpdated: (old: Long, new: Long) -> Unit): Boolean {
77+
contract {
78+
callsInPlace(function, InvocationKind.EXACTLY_ONCE)
79+
callsInPlace(onUpdated, InvocationKind.AT_MOST_ONCE)
80+
}
5181
val cur = value
5282
val upd = function(cur)
5383
return compareAndSet(cur, upd).also { if (it) onUpdated(cur, upd) }

arrow-libs/core/arrow-autoclose/src/commonMain/kotlin/arrow/AutoCloseScope.kt

+6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
@file:OptIn(ExperimentalContracts::class)
2+
13
package arrow
24

35
import arrow.atomic.Atomic
46
import arrow.atomic.update
7+
import kotlin.contracts.ExperimentalContracts
8+
import kotlin.contracts.InvocationKind
9+
import kotlin.contracts.contract
510
import kotlin.coroutines.cancellation.CancellationException
611

712
/**
@@ -61,6 +66,7 @@ import kotlin.coroutines.cancellation.CancellationException
6166
* <!--- KNIT example-autocloseable-02.kt -->
6267
*/
6368
public inline fun <A> autoCloseScope(block: AutoCloseScope.() -> A): A {
69+
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
6470
val scope = DefaultAutoCloseScope()
6571
var throwable: Throwable? = null
6672
return try {

0 commit comments

Comments
 (0)