Skip to content

Commit 10973e4

Browse files
authored
Add stdlib test importing every stdlib module + fix stdlib (#795)
Resolves #614 by adding a stdlib test that imports every single (common) stdlib module. Let's add [ACME](https://en.wikipedia.org/wiki/Acme_Corporation) (in our context "All Common Modules in Effekt" :)) to the stdlib tests: just import and compile all modules. This revealed 3 different bugs in stdlib + codegen bugs on LLVM (moved to #801).
1 parent 94eb038 commit 10973e4

File tree

11 files changed

+213
-30
lines changed

11 files changed

+213
-30
lines changed

.github/workflows/acme.yml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
name: Check every stdlib module is compiled (acme)
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- 'libraries/common/**'
7+
8+
jobs:
9+
check-stdlib-sync:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Checkout repository
13+
uses: actions/checkout@v4
14+
15+
- name: Generate acme and test for differences
16+
id: generate-acme
17+
shell: bash
18+
run: |
19+
# Function to convert file path to module import path
20+
path_to_module() {
21+
local filepath="$1"
22+
# Remove libraries/common/ prefix and .effekt suffix
23+
local module_path="${filepath#libraries/common/}"
24+
module_path="${module_path%.effekt}"
25+
echo "$module_path"
26+
}
27+
28+
# Find all .effekt files in libraries/common, excluding acme.effekt
29+
MODULES=$(find libraries/common -type f -name "*.effekt" | sort | while read -r file; do
30+
path_to_module "$file"
31+
done)
32+
33+
# Create the new acme.effekt content
34+
{
35+
echo "/// This module is auto-generated and checked in CI."
36+
echo "/// It imports **every** stdlib module: ACME = All Common Modules in Effekt"
37+
echo "module acme"
38+
echo ""
39+
for module in $MODULES; do
40+
echo "import $module"
41+
done
42+
echo ""
43+
echo "def main() = ()"
44+
} > generated-acme.effekt
45+
46+
# Compare files, ignoring whitespace, blank lines, and line ending differences
47+
if ! diff -Bbq examples/stdlib/acme.effekt generated-acme.effekt; then
48+
echo "::error::The stdlib import file (examples/stdlib/acme.effekt) is out of sync with the modules in libraries/common."
49+
echo "Differences found:"
50+
diff -Bu examples/stdlib/acme.effekt generated-acme.effekt
51+
exit 1
52+
fi

effekt/jvm/src/test/scala/effekt/StdlibTests.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,9 @@ class StdlibLLVMTests extends StdlibTests {
4949

5050
// String comparison using `<`, `<=`, `>`, `>=` is not implemented yet on LLVM
5151
examplesDir / "stdlib" / "string" / "compare.effekt",
52+
53+
// Wrong codegen for negative types, see #801
54+
examplesDir / "stdlib" / "json.effekt",
55+
examplesDir / "stdlib" / "buffer.effekt",
5256
)
5357
}

examples/stdlib/acme.check

Whitespace-only changes.

examples/stdlib/acme.effekt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/// This module is auto-generated and checked in CI.
2+
/// It imports **every** stdlib module: ACME = All Common Modules in Effekt
3+
module acme
4+
5+
import args
6+
import array
7+
import bench
8+
import buffer
9+
import bytearray
10+
import char
11+
import dequeue
12+
import effekt
13+
import exception
14+
import io
15+
import io/console
16+
import io/error
17+
import io/filesystem
18+
import io/network
19+
import io/time
20+
import json
21+
import list
22+
import map
23+
import option
24+
import process
25+
import queue
26+
import ref
27+
import regex
28+
import result
29+
import scanner
30+
import seq
31+
import set
32+
import stream
33+
import string
34+
import test
35+
import tty
36+
37+
def main() = ()

examples/stdlib/buffer.check

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
1
2+
false
3+
Some(17)
4+
Some(1)
5+
Some(2)
6+
Some(3)
7+
Some(4)
8+
Some(5)

examples/stdlib/buffer.effekt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import buffer
2+
3+
def main() = buffer::examples::main()

examples/stdlib/json.check

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
a
2+
3+
a
4+
5+
a
6+
b
7+
f
8+
9+
{
10+
"x"
11+
:
12+
"Hallo"
13+
,
14+
"y"
15+
:
16+
null
17+
,
18+
"z"
19+
:
20+
12.3783
21+
}
22+
23+
{
24+
"a"
25+
:
26+
null
27+
,
28+
"b"
29+
:
30+
[
31+
true
32+
,
33+
false
34+
,
35+
false
36+
,
37+
true
38+
]
39+
,
40+
"f"
41+
:
42+
12.532
43+
}
44+
45+
{
46+
"a"
47+
:
48+
null
49+
,
50+
"b"
51+
:
52+
[
53+
true
54+
,
55+
false
56+
,
57+
false
58+
,
59+
true
60+
]
61+
,
62+
"f"
63+
:
64+
12.532
65+
}

examples/stdlib/json.effekt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import json
2+
3+
def main() = json::test::main()

libraries/common/buffer.effekt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def emptyBuffer[T](capacity: Int): Buffer[T] at {global} = {
3434
}
3535
def arrayBuffer[T](initialCapacity: Int): Buffer[T] at {global} = {
3636
// TODO allocate buffer (and array) into a region r.
37-
val contents = emptyArray[T](initialCapacity)
37+
val contents = array::allocate[T](initialCapacity)
3838
var head in global = 0
3939
var tail in global = 0
4040

@@ -51,15 +51,15 @@ def arrayBuffer[T](initialCapacity: Int): Buffer[T] at {global} = {
5151
def read() = {
5252
if (buffer.empty?) None()
5353
else {
54-
val result: T = contents.remove(head).getOrElse { <> };
54+
val result: T = contents.unsafeGet(head);
5555
head = mod(head + 1, initialCapacity)
5656
Some(result)
5757
}
5858
}
5959
def write(el: T) = {
6060
if (buffer.full?) <> // raise(BufferOverflow())
6161

62-
contents.put(tail, el)
62+
contents.unsafeSet(tail, el)
6363
tail = mod(tail + 1, initialCapacity)
6464
}
6565
}

libraries/common/io/network.effekt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,17 @@ def server(host: String, port: Int, handler: () => Unit / Socket at {io, async,
5757

5858
namespace examples {
5959
def helloWorldApp(): Unit / Socket = {
60-
val request = do receive();
60+
val request = do receive().toString;
6161

62-
println("Received a request: " ++ request.toUTF8)
62+
println("Received a request: " ++ request)
6363

64-
if (request.toUTF8.startsWith("GET /")) {
65-
do send(fromUTF8("HTTP/1.1 200 OK\r\n\r\nHello from Effekt!"))
64+
def respond(s: String): Unit / Socket =
65+
do send(s.fromString)
66+
67+
if (request.startsWith("GET /")) {
68+
respond("HTTP/1.1 200 OK\r\n\r\nHello from Effekt!")
6669
} else {
67-
do send("HTTP/1.1 400 Bad Request\r\n\r\n".fromUTF8)
70+
respond("HTTP/1.1 400 Bad Request\r\n\r\n")
6871
}
6972
do end()
7073
}

0 commit comments

Comments
 (0)