/* ----------------------------------------------------------------------------- Copyright 2020 Kevin P. Barry Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ----------------------------------------------------------------------------- */ // Author: Kevin P. Barry [ta0kira@gmail.com] testcase "scoped unconditional" { success Test$run() } @value interface Value {} define Test { run () { scoped { Int x <- 1 } in { x <- 2 } } } concrete Test { @type run () -> () } testcase "unconditional has scoping" { error require "x" } @value interface Value {} define Test { run () { scoped { } in { Int x <- 1 } x <- 2 } } concrete Test { @type run () -> () } testcase "return inside scope" { success Test$run() } @value interface Value {} define Test { @value process () -> (optional Value) process () { scoped { return empty } in \ empty } run () {} } concrete Test { @type run () -> () } testcase "return from scoped" { success Test$run() } @value interface Value {} define Test { @value process () -> (optional Value) process () { scoped { } in return empty } run () {} } concrete Test { @type run () -> () } testcase "update clashes with scoped" { error require "x" } define Test { run () { scoped { Int x <- 2 } in while (false) { } update { Int x <- 1 } } } concrete Test { @type run () -> () } testcase "assign inside scope" { success Test$run() } @value interface Value {} define Test { @value process () -> (optional Value) process () (value) { scoped { value <- empty } in \ empty } run () {} } concrete Test { @type run () -> () } testcase "assign from scoped" { success Test$run() } @value interface Value {} define Test { @value process () -> (optional Value) process () (value) { scoped { } in value <- empty } run () {} } concrete Test { @type run () -> () } testcase "simple cleanup" { success Test$run() } define Test { run () { Int value <- 0 scoped { value <- 1 } cleanup { value <- 2 } in value <- 3 if (value != 2) { fail(value) } } } concrete Test { @type run () -> () } testcase "name clash in cleanup" { error require "value.+already defined" } define Test { run () { scoped { Int value <- 1 } cleanup { Int value <- 2 } in \ empty } } concrete Test { @type run () -> () } testcase "cleanup before return" { success Test$run() } concrete Value { @type create () -> (Value) @value call () -> (Int) @value get () -> (Int) } define Value { @value Int value create () { return Value{ 0 } } call () { value <- 1 scoped { value <- 2 } cleanup { value <- 3 } in return value } get () { return value } } define Test { run () { Value value <- Value$create() Int value1 <- value.call() if (value1 != 2) { fail(value1) } Int value2 <- value.get() if (value2 != 3) { fail(value2) } } } concrete Test { @type run () -> () } testcase "cleanup initializes named return" { success Test$run() } define Test { @type get () -> (Int) get () (value) { scoped { } cleanup { value <- 1 } in return _ } run () { Int value <- get() if (value != 1) { fail(value) } } } concrete Test { @type run () -> () } testcase "cleanup overrides named return" { success Test$run() } define Test { @type get () -> (Int) get () (value) { value <- 1 scoped { } cleanup { value <- 2 } in return _ } run () { Int value <- get() if (value != 2) { fail(value) } } } concrete Test { @type run () -> () } testcase "cannot refer to cleanup variables" { error require "value.+not defined" } define Test { @type get () -> (Int) get () (value) { scoped { } cleanup { Int value2 <- 1 } in return value2 } run () { \ get() } } concrete Test { @type run () -> () } testcase "cleanup skipped in scoped return" { success Test$run() } define Test { @type get () -> (Int) get () { Int value <- 0 scoped { value <- 1 return value } cleanup { value <- 2 } in return 3 } run () { Int value <- get() if (value != 1) { fail(value) } } } concrete Test { @type run () -> () } testcase "no infinite loop in cleanup return" { success Test$run() } define Test { @type get () -> (Int) get () { Int value <- 0 scoped { value <- 1 } cleanup { value <- 2 return value } in return 3 } run () { Int value <- get() if (value != 2) { fail(value) } } } concrete Test { @type run () -> () } testcase "multiple cleanup" { success Test$run() } define Test { run () { Int value1 <- 0 Int value2 <- 0 Int value3 <- 0 scoped { } cleanup { value1 <- 1 value2 <- 1 } in scoped { } cleanup { value2 <- 2 value3 <- 2 } in \ empty if (value1 != 1) { fail(value1) } if (value2 != 1) { fail(value2) } if (value3 != 2) { fail(value3) } } } concrete Test { @type run () -> () } testcase "multiple cleanup with return" { success Test$run() } concrete Value { @type create () -> (Value) @value call () -> (Int,Int,Int) @value get () -> (Int,Int,Int) } define Value { @value Int value1 @value Int value2 @value Int value3 create () { return Value{ 0, 0, 0 } } call () { value1 <- 1 value2 <- 1 value3 <- 1 scoped { } cleanup { value1 <- 2 value2 <- 2 } in scoped { } cleanup { value2 <- 3 value3 <- 3 } in return value1, value2, value3 } get () { return value1, value2, value3 } } define Test { run () { Value value <- Value$create() Int value1, Int value2, Int value3 <- value.call() if (value1 != 1) { fail(value1) } if (value2 != 1) { fail(value2) } if (value3 != 1) { fail(value3) } value1, value2, value3 <- value.get() if (value1 != 2) { fail(value1) } if (value2 != 2) { fail(value2) } if (value3 != 3) { fail(value3) } } } concrete Test { @type run () -> () } testcase "cleanup cannot refer to later variables" { error require "value.+not defined" } define Test { run () { scoped { } cleanup { value <- 1 } in Int value <- 2 } } concrete Test { @type run () -> () } testcase "cleanup not merged" { error require "value.+not defined" } define Test { run () { scoped { } cleanup { Int value <- 0 } in scoped { } cleanup { value <- 1 } in \ empty } } concrete Test { @type run () -> () } testcase "no name clash in nested scope with return" { success Test$run() } define Test { run () { scoped { } cleanup { Int value <- 0 } in scoped { } cleanup { Int value <- 1 } in return _ } } concrete Test { @type run () -> () } testcase "cleanup skipped for fail" { crash Test$run() require "scoped" } define Test { run () { scoped { String message <- "scoped" } cleanup { message <- "cleanup" } in fail(message) } } concrete Test { @type run () -> () } testcase "cleanup not applied to returns outside of scope" { success Test$run() } define Test { run () { Int value <- 0 scoped { } cleanup { if (value != 0) { fail(value) } } in \ empty value <- 1 return _ } } concrete Test { @type run () -> () } testcase "cleanup unconditional" { success Test$run() } @value interface Value {} define Test { run () { scoped { Int x <- 1 } in { \ x } } } concrete Test { @type run () -> () } testcase "scoped empty blocks" { success Test$run() } @value interface Value {} define Test { run () { scoped { // empty } cleanup { // empty } in { // empty } } } concrete Test { @type run () -> () } testcase "just cleanup" { success Test$run() } concrete Value { @type create () -> (Value) @value postIncrement () -> (Int) @value get () -> (Int) } define Value { @value Int value create () { return Value{ 0 } } postIncrement () { cleanup { value <- value+1 } in return value } get () { return value } } define Test { run () { Value value <- Value$create() Int value1 <- value.postIncrement() if (value1 != 0) { fail(value1) } Int value2 <- value.get() if (value2 != 1) { fail(value2) } } } concrete Test { @type run () -> () } testcase "separate trace context for cleanup" { crash Test$run() require "Failed" require "cleanup block" require "Test\.run" exclude "Failed.+Test\.run" exclude "Test\.run.+Failed" } define Test { run () { cleanup { fail("Failed") } in return _ } } concrete Test { @type run () -> () }