Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make a common public parent for raise exception #3349

Merged
merged 6 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions arrow-libs/core/arrow-core/api/arrow-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -3396,6 +3396,9 @@ public final class arrow/core/raise/DefaultRaise : arrow/core/raise/Raise {
public fun shift (Ljava/lang/Object;)Ljava/lang/Object;
}

public abstract interface annotation class arrow/core/raise/DelicateRaiseApi : java/lang/annotation/Annotation {
}

public abstract interface annotation class arrow/core/raise/ExperimentalTraceApi : java/lang/annotation/Annotation {
}

Expand Down Expand Up @@ -3579,6 +3582,10 @@ public class arrow/core/raise/RaiseAccumulate : arrow/core/raise/Raise {
public final fun withNel (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
}

public abstract class arrow/core/raise/RaiseCancellationException : java/util/concurrent/CancellationException {
public synthetic fun <init> (Ljava/lang/Object;Larrow/core/raise/Raise;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
}

public abstract interface annotation class arrow/core/raise/RaiseDSL : java/lang/annotation/Annotation {
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,6 @@ public inline fun <Error, A> Raise<Error>.traced(
@Suppress("UNCHECKED_CAST")
internal fun <R> CancellationException.raisedOrRethrow(raise: DefaultRaise): R =
when {
this is RaiseCancellationExceptionNoTrace && this.raise === raise -> raised as R
this is RaiseCancellationException && this.raise === raise -> raised as R
else -> throw this
}
Expand All @@ -253,18 +252,32 @@ internal class DefaultRaise(@PublishedApi internal val isTraced: Boolean) : Rais
@PublishedApi
internal fun complete(): Boolean = isActive.getAndSet(false)
override fun raise(r: Any?): Nothing = when {
isActive.value -> throw if (isTraced) RaiseCancellationException(r, this) else RaiseCancellationExceptionNoTrace(r, this)
isActive.value -> throw if (isTraced) Traced(r, this) else NoTrace(r, this)
else -> throw RaiseLeakedException()
}
}

/** CancellationException is required to cancel coroutines when raising from within them. */
private class RaiseCancellationExceptionNoTrace(val raised: Any?, val raise: Raise<Any?>) :
CancellationExceptionNoTrace()
@MustBeDocumented
@Retention(AnnotationRetention.BINARY)
@RequiresOptIn(RaiseCancellationExceptionCaptured, RequiresOptIn.Level.WARNING)
public annotation class DelicateRaiseApi

private class RaiseCancellationException(val raised: Any?, val raise: Raise<Any?>) : CancellationException()
/**
* [RaiseCancellationException] is a _delicate_ api, and should be used with care.
* It drives the short-circuiting behavior of [Raise].
*/
@DelicateRaiseApi
public sealed class RaiseCancellationException(
kyay10 marked this conversation as resolved.
Show resolved Hide resolved
internal val raised: Any?,
internal val raise: Raise<Any?>
) : CancellationException(RaiseCancellationExceptionCaptured)

@DelicateRaiseApi
@Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
internal expect class NoTrace(raised: Any?, raise: Raise<Any?>) : RaiseCancellationException

internal expect open class CancellationExceptionNoTrace() : CancellationException
@DelicateRaiseApi
internal class Traced(raised: Any?, raise: Raise<Any?>): RaiseCancellationException(raised, raise)

private class RaiseLeakedException : IllegalStateException(
"""
Expand All @@ -276,6 +289,6 @@ private class RaiseLeakedException : IllegalStateException(
)

internal const val RaiseCancellationExceptionCaptured: String =
"kotlin.coroutines.cancellation.CancellationException should never get cancelled. Always re-throw it if captured." +
"kotlin.coroutines.cancellation.CancellationException should never get swallowed. Always re-throw it if captured." +
"This swallows the exception of Arrow's Raise, and leads to unexpected behavior." +
"When working with Arrow prefer Either.catch or arrow.core.raise.catch to automatically rethrow CancellationException."
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import kotlin.jvm.JvmInline
import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName

@MustBeDocumented
@RequiresOptIn("This API is experimental, and may change in the future.")
@Retention(AnnotationRetention.BINARY)
public annotation class ExperimentalTraceApi

/** Tracing result. Allows to inspect the traces from where raise was called. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class EffectSpec : StringSpec({
effect {
try {
raise(s())
} catch (e: Throwable) {
} catch (e: RaiseCancellationException) {
i()
}
}.getOrElse { unreachable() } shouldBe i()
Expand All @@ -69,7 +69,7 @@ class EffectSpec : StringSpec({
effect<String, Int> {
try {
raise(s())
} catch (e: Throwable) {
} catch (e: RaiseCancellationException) {
i()
}
raise(s2())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package arrow.core.raise

import kotlin.coroutines.cancellation.CancellationException

internal actual open class CancellationExceptionNoTrace : CancellationException(RaiseCancellationExceptionCaptured)
@Suppress(
"EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING",
"SEALED_INHERITOR_IN_DIFFERENT_MODULE"
)
internal actual class NoTrace actual constructor(raised: Any?, raise: Raise<Any?>) : RaiseCancellationException(raised, raise)
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import kotlin.coroutines.cancellation.CancellationException
* Inspired by KotlinX Coroutines:
* https://github.com/Kotlin/kotlinx.coroutines/blob/3788889ddfd2bcfedbff1bbca10ee56039e024a2/kotlinx-coroutines-core/jvm/src/Exceptions.kt#L29
*/
internal actual open class CancellationExceptionNoTrace : CancellationException(RaiseCancellationExceptionCaptured) {
@Suppress(
"EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING",
"SEALED_INHERITOR_IN_DIFFERENT_MODULE"
)
internal actual class NoTrace actual constructor(raised: Any?, raise: Raise<Any?>) : RaiseCancellationException(raised, raise) {
nomisRev marked this conversation as resolved.
Show resolved Hide resolved
override fun fillInStackTrace(): Throwable {
// Prevent Android <= 6.0 bug.
stackTrace = emptyArray()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package arrow.core.raise

import kotlin.coroutines.cancellation.CancellationException

internal actual open class CancellationExceptionNoTrace : CancellationException(RaiseCancellationExceptionCaptured)
@Suppress(
"EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING",
"SEALED_INHERITOR_IN_DIFFERENT_MODULE"
)
internal actual class NoTrace actual constructor(raised: Any?, raise: Raise<Any?>): RaiseCancellationException(raised, raise)