Infer coroutine effects automatically
This commit is contained in:
parent
bac1183593
commit
5b30e49486
15 changed files with 339 additions and 103 deletions
|
|
@ -7,7 +7,7 @@ import (
|
|||
|
||||
func TestGenerateStructuredCoroutines(t *testing.T) {
|
||||
prog, err := Parse(`package demo
|
||||
suspend fun load(): Int { delay(1); return 42 }
|
||||
fun load(): Int { delay(1); return 42 }
|
||||
fun main() {
|
||||
runBlocking {
|
||||
coroutineScope {
|
||||
|
|
@ -31,16 +31,35 @@ fun main() {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSuspendFunctionRequiresScope(t *testing.T) {
|
||||
func TestInferredCoroutineFunctionRequiresScope(t *testing.T) {
|
||||
prog, err := Parse(`package demo
|
||||
suspend fun load(): Int { return 1 }
|
||||
fun load(): Int { delay(1); return 1 }
|
||||
fun main() { println(load()) }`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = GenerateGo(prog)
|
||||
if err == nil || !strings.Contains(err.Error(), "requires a coroutine scope") {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
out, err := GenerateGo(prog)
|
||||
if err == nil || !strings.Contains(err.Error(), "contextual function main requires a runBlocking boundary") {
|
||||
t.Fatalf("unexpected error: %v\n%s", err, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCoroutineEffectPropagatesTransitively(t *testing.T) {
|
||||
prog, err := Parse(`package demo
|
||||
fun load(): Int { delay(1); return 42 }
|
||||
fun wrapped(): Int { return load() }
|
||||
fun main() { runBlocking { println(wrapped()) } }`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
out, err := GenerateGo(prog)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, expected := range []string{"func load(gotlinScope *GotlinCoroutineScope)", "func wrapped(gotlinScope *GotlinCoroutineScope)", "wrapped(gotlinScope)"} {
|
||||
if !strings.Contains(string(out), expected) {
|
||||
t.Fatalf("missing %q:\n%s", expected, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -52,6 +71,51 @@ func TestLegacyConcurrencySyntaxIsRemoved(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSuspendModifierIsRemoved(t *testing.T) {
|
||||
if _, err := Parse(`package demo suspend fun load() {}`); err == nil {
|
||||
t.Fatal("suspend modifier parsed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnalyzeInfersCoroutineEffects(t *testing.T) {
|
||||
program, err := Parse(`package demo
|
||||
fun direct() { delay(1) }
|
||||
fun transitive() { direct() }
|
||||
fun boundary() { runBlocking { transitive() } }`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
semantic, err := Analyze(program)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !semantic.FunctionEffects["direct"].Has(CoroutineEffect) || !semantic.FunctionEffects["transitive"].Has(CoroutineEffect) {
|
||||
t.Fatalf("effects were not propagated: %#v", semantic.FunctionEffects)
|
||||
}
|
||||
if semantic.FunctionEffects["boundary"].Has(CoroutineEffect) {
|
||||
t.Fatalf("runBlocking did not stop effect propagation: %#v", semantic.FunctionEffects)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCoroutineContextUsesAmbientScope(t *testing.T) {
|
||||
program, err := Parse(`package demo
|
||||
import fmt
|
||||
fun useContext() { fmt.sprint(coroutineContext()) }
|
||||
fun main() { runBlocking { useContext() } }`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
output, err := GenerateGo(program)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, expected := range []string{"func useContext(gotlinScope *GotlinCoroutineScope)", "gotlinScope.Context()", "useContext(gotlinScope)"} {
|
||||
if !strings.Contains(string(output), expected) {
|
||||
t.Fatalf("missing %q:\n%s", expected, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunBlockingCancelsAndJoinsChildrenOnPanic(t *testing.T) {
|
||||
prog, err := Parse(`package demo fun main() { runBlocking { launch { delay(1) }; panic("failed") } }`)
|
||||
if err != nil {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue