Skip to content

Commit 611e300

Browse files
committed
Merge branch 'master' into new-normalizer
2 parents eccde6b + 4d7605b commit 611e300

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+631
-69
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: 'Restore Build Cache'
2+
description: 'Restores cached build artifacts'
3+
4+
runs:
5+
using: "composite"
6+
steps:
7+
- name: Restore cached build
8+
uses: actions/cache/restore@v4
9+
with:
10+
path: |
11+
target
12+
project/target
13+
project/project/target
14+
kiama/jvm/target
15+
kiama/js/target
16+
effekt/jvm/target
17+
effekt/js/target
18+
~/.ivy2/cache
19+
~/.sbt
20+
~/.cache/coursier
21+
key: effekt-build-${{ github.sha }}
22+
fail-on-cache-miss: true

.github/workflows/ci-master.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Master CI
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
8+
jobs:
9+
full-test-suite:
10+
name: Full Test Suite
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v5
14+
with:
15+
submodules: 'true'
16+
17+
- uses: ./.github/actions/setup-effekt
18+
with:
19+
install-dependencies: 'true'
20+
install-valgrind: 'true'
21+
22+
- uses: ./.github/actions/run-effekt-tests
23+
with:
24+
full-test: 'true'
25+
use-retry: 'true'
26+
27+
windows-smoke-test:
28+
name: Windows Smoke Test
29+
runs-on: windows-latest
30+
steps:
31+
- uses: actions/checkout@v5
32+
with:
33+
submodules: 'true'
34+
35+
- uses: ./.github/actions/setup-effekt
36+
37+
- uses: ./.github/actions/run-effekt-tests
38+
with:
39+
full-test: 'false'

.github/workflows/ci-pr.yml

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
6+
jobs:
7+
build-and-compile:
8+
name: "Build & Compile"
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v5
12+
with:
13+
submodules: 'true'
14+
15+
- uses: ./.github/actions/setup-effekt
16+
17+
- name: Compile project
18+
run: sbt compile Test/compile
19+
20+
- name: Cache compiled artifacts
21+
uses: actions/cache/save@v4
22+
with:
23+
path: |
24+
target
25+
project/target
26+
project/project/target
27+
kiama/jvm/target
28+
kiama/js/target
29+
effekt/jvm/target
30+
effekt/js/target
31+
~/.ivy2/cache
32+
~/.sbt
33+
~/.cache/coursier
34+
key: effekt-build-${{ github.sha }}
35+
36+
internal-tests:
37+
name: "Internal Tests & Verify Binary"
38+
needs: build-and-compile
39+
runs-on: ubuntu-latest
40+
steps:
41+
- uses: actions/checkout@v5
42+
with:
43+
submodules: 'true'
44+
45+
- uses: ./.github/actions/setup-effekt
46+
47+
- uses: ./.github/actions/restore-build-cache
48+
49+
- name: Run internal tests
50+
run: |
51+
sbt kiamaJVM/test
52+
sbt effektJVM/testRemaining
53+
sbt effektJS/test
54+
55+
- name: Assemble fully optimized JS file
56+
run: sbt effektJS/fullOptJS
57+
58+
- name: Install effekt binary
59+
run: sbt install
60+
61+
- name: Test effekt binary
62+
run: effekt.sh --help
63+
64+
windows-tests:
65+
name: "Windows Smoke Test"
66+
needs: build-and-compile
67+
runs-on: windows-latest
68+
steps:
69+
- uses: actions/checkout@v5
70+
with:
71+
submodules: 'true'
72+
73+
- uses: ./.github/actions/setup-effekt
74+
75+
- name: Compile project
76+
run: sbt Test/compile
77+
78+
- name: Run Windows smoke test
79+
run: sbt "effektJVM/testOnly effekt.JavaScriptTests -- --tests=.*examples[\\/]*pos[\\/]*sideeffects.*"
80+
81+
js-tests:
82+
name: "JS Backend"
83+
needs: build-and-compile
84+
runs-on: ubuntu-latest
85+
steps:
86+
- uses: actions/checkout@v5
87+
with:
88+
submodules: 'true'
89+
90+
- uses: ./.github/actions/setup-effekt
91+
92+
- uses: ./.github/actions/restore-build-cache
93+
94+
- name: Run JavaScript backend tests
95+
run: sbt effektJVM/testBackendJS
96+
97+
chez-tests:
98+
name: "Chez Backend"
99+
needs: build-and-compile
100+
runs-on: ubuntu-latest
101+
steps:
102+
- uses: actions/checkout@v5
103+
with:
104+
submodules: 'true'
105+
106+
- uses: ./.github/actions/setup-effekt
107+
with:
108+
install-dependencies: 'true'
109+
110+
- uses: ./.github/actions/restore-build-cache
111+
112+
- name: Run Chez Scheme backend tests
113+
run: sbt effektJVM/testBackendChez
114+
115+
llvm-tests:
116+
name: "LLVM Backend"
117+
needs: build-and-compile
118+
runs-on: ubuntu-latest
119+
steps:
120+
- uses: actions/checkout@v5
121+
with:
122+
submodules: 'true'
123+
124+
- uses: ./.github/actions/setup-effekt
125+
with:
126+
install-dependencies: 'true'
127+
install-valgrind: 'true'
128+
129+
- uses: ./.github/actions/restore-build-cache
130+
131+
- name: Run LLVM backend tests
132+
run: EFFEKT_VALGRIND=1 EFFEKT_DEBUG=1 sbt effektJVM/testBackendLLVM

.github/workflows/ci.yml

Lines changed: 0 additions & 48 deletions
This file was deleted.

build.sbt

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ lazy val assembleJS = taskKey[Unit]("Assemble the JS file in out/effekt.js")
1313
lazy val assembleBinary = taskKey[Unit]("Assembles the effekt binary in bin/effekt")
1414
lazy val generateDocumentation = taskKey[Unit]("Generates some documentation.")
1515
lazy val bumpMinorVersion = taskKey[Unit]("Bumps the minor version number (used in CI).")
16+
// custom test tasks for CI
17+
lazy val testBackendJS = taskKey[Unit]("Run JavaScript backend tests")
18+
lazy val testBackendChez = taskKey[Unit]("Run Chez Scheme backend tests")
19+
lazy val testBackendLLVM = taskKey[Unit]("Run LLVM backend tests")
20+
lazy val testRemaining = taskKey[Unit]("Run all non-backend tests (internal tests) on effektJVM")
1621

1722
lazy val noPublishSettings = Seq(
1823
publish := {},
@@ -206,6 +211,55 @@ lazy val effekt: CrossProject = crossProject(JSPlatform, JVMPlatform).in(file("e
206211

207212
println(newVersion)
208213
},
214+
testBackendJS := {
215+
(Test / testOnly).toTask(
216+
" effekt.JavaScriptTests effekt.StdlibJavaScriptTests"
217+
).value
218+
},
219+
220+
testBackendChez := {
221+
(Test / testOnly).toTask(
222+
" effekt.ChezSchemeMonadicTests effekt.ChezSchemeCallCCTests" +
223+
" effekt.StdlibChezSchemeMonadicTests effekt.StdlibChezSchemeCallCCTests"
224+
).value
225+
},
226+
227+
testBackendLLVM := {
228+
(Test / testOnly).toTask(
229+
" effekt.LLVMTests effekt.LLVMNoValgrindTests effekt.StdlibLLVMTests"
230+
).value
231+
},
232+
233+
testRemaining := Def.taskDyn {
234+
val log = streams.value.log
235+
236+
val allTests = (Test / definedTestNames).value.toSet
237+
238+
// Explicit list of backend tests (union of all testBackend targets)
239+
val backendTests = Set(
240+
"effekt.JavaScriptTests",
241+
"effekt.StdlibJavaScriptTests",
242+
"effekt.ChezSchemeMonadicTests",
243+
"effekt.ChezSchemeCallCCTests",
244+
"effekt.StdlibChezSchemeMonadicTests",
245+
"effekt.StdlibChezSchemeCallCCTests",
246+
"effekt.LLVMTests",
247+
"effekt.LLVMNoValgrindTests",
248+
"effekt.StdlibLLVMTests"
249+
)
250+
251+
val remaining = allTests -- backendTests
252+
253+
if (remaining.isEmpty) {
254+
log.info("No remaining tests")
255+
Def.task { () }
256+
} else {
257+
log.info(s"Running ${remaining.size} internal test(s):")
258+
remaining.toSeq.sorted.foreach(t => log.info(s" - $t"))
259+
(Test / testOnly).toTask(s" ${remaining.mkString(" ")}")
260+
}
261+
}.value,
262+
209263

210264
generateDocumentation := TreeDocs.replacer.value,
211265
Compile / sourceGenerators += versionGenerator.taskValue,

effekt/jvm/src/test/scala/effekt/ChezSchemeTests.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ abstract class ChezSchemeTests extends EffektTests {
2727
examplesDir / "pos" / "object",
2828
examplesDir / "pos" / "type_omission_op.effekt",
2929
examplesDir / "pos" / "issue972.effekt",
30+
examplesDir / "pos" / "issue1153.effekt",
3031

3132
// filesystem operations and bytearrays are not yet supported in our Chez backend
3233
examplesDir / "benchmarks" / "input_output" / "word_count_ascii.effekt",

effekt/jvm/src/test/scala/effekt/ParserTests.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,10 +338,15 @@ class ParserTests extends munit.FunSuite {
338338

339339
test("Boxing") {
340340
parseExpr("box f")
341+
parseExpr("unbox f")
341342
assertEqualModuloSpans(
342343
parseExpr("box f"),
343344
parseExpr("(box f)")
344345
)
346+
assertEqualModuloSpans(
347+
parseExpr("unbox box f"),
348+
parseExpr("unbox (box f)")
349+
)
345350
assertNotEqualModuloSpans(
346351
parseExpr("box { 42 }"),
347352
parseExpr("box { 42 } at {}")

effekt/shared/src/main/scala/effekt/Lexer.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ enum TokenKind {
132132
case `extern`
133133
case `record`
134134
case `box`
135+
case `unbox`
135136
case `return`
136137
case `region`
137138
case `resource`
@@ -176,7 +177,7 @@ object TokenKind {
176177
val keywords = Vector(
177178
`let`, `true`, `false`, `val`, `var`, `if`, `else`, `while`, `type`, `effect`, `interface`,
178179
`try`, `with`, `case`, `do`, `match`, `def`, `module`, `import`, `export`, `extern`,
179-
`include`, `record`, `box`, `return`, `region`, `resource`, `new`, `and`, `is`,
180+
`include`, `record`, `box`, `unbox`, `return`, `region`, `resource`, `new`, `and`, `is`,
180181
`namespace`, `private`
181182
)
182183

effekt/shared/src/main/scala/effekt/Namer.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ object Namer extends Phase[Parsed, NameResolved] {
445445
args.foreach(resolve)
446446
}
447447

448-
case source.Unbox(term, _) => resolve(term) // shouldn't occur since unbox is not part of the source
448+
case source.Unbox(term, _) => resolve(term)
449449

450450
case source.New(impl, _) => resolve(impl)
451451

@@ -528,6 +528,10 @@ object Namer extends Phase[Parsed, NameResolved] {
528528
then Context.abort(pp"Cannot resolve operation ${target}, called on a receiver that is a computation.")
529529
}
530530

531+
case source.Unbox(term, _) =>
532+
if !Context.resolveOverloadedFunction(target)
533+
then Context.abort(pp"Cannot resolve operation ${target}, called on an unboxed computation.")
534+
531535
// expr.bar(args) = Call(bar, expr :: args)
532536
case term =>
533537
if !Context.resolveOverloadedFunction(target)

effekt/shared/src/main/scala/effekt/Parser.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,10 @@ class Parser(tokens: Seq[Token], source: Source) {
859859
Box(captures, expr, span())
860860
}
861861

862+
def unboxExpr(): Term =
863+
nonterminal:
864+
Unbox(`unbox` ~> expr(), span())
865+
862866
def newExpr(): Term =
863867
nonterminal:
864868
New(`new` ~> implementation(), span())
@@ -1179,6 +1183,7 @@ class Parser(tokens: Seq[Token], source: Source) {
11791183
case `try` => tryExpr()
11801184
case `region` => regionExpr()
11811185
case `box` => boxExpr()
1186+
case `unbox` => unboxExpr()
11821187
case `new` => newExpr()
11831188
case `do` => doExpr()
11841189
case _ if isString => templateString()

0 commit comments

Comments
 (0)