iyi

playground evidence

This page is the recording behind the playground: which bytes came from where, what checked them, and what the compiler actually said when it refused a program.

What is real here, precisely

  • The modules are real compiler output. Each came from iyi build --cross-compile --target wasm32-wasi on the machine stamped below, then a link step, because for this target the fork prints a cc --target=wasm32-wasi command rather than calling wasm-ld. Both are recorded in records/wasm/manifest.json, and scripts/records.mjs fails the build if a module's digest does not match the bytes on disk or if a sample's source has changed since the module was built from it.
  • The execution is real, and it is in your tab. The host in src/playground/engines/wasi-preview1.ts implements the WASI preview1 calls the modules import, and it grants what a page can honestly grant: standard output and standard error captured per write, standard input open and at end of file, an empty environment, real clocks, real randomness, and no filesystem at all. A call it does not implement returns an errno rather than trapping, so a program can see the failure instead of dying.
  • One execution path, whatever produced the module. A recorded sample today, and a program compiled in the tab when iyi's compiler runs there, are the same bytes flowing through the same host, so a defect in either is a defect in both and cannot hide on one side. That is why engines/execute.ts exists once rather than twice.
  • Nothing is instantiated without a digest, unless this tab made it. Every module the page runs today is a recorded one, checked against the manifest before it is instantiated, because its provenance is a claim about a machine you were not at. A module the compiler in the page produces needs no digest, because its provenance is that this tab made it, and the page will say which of the two it ran.
  • The coloring is the compiler's lexer or it is nothing, and this build checked that. There is no grammar in the playground and there will not be one. A loaded sample paints from the token stream the compiler's own highlighter recorded. When iyi's compiler runs in the page, text you type paints from that same highlighter, and only after the page recovers the text out of the stream and finds it byte identical to the characters on screen. Otherwise it paints plain ink, because a listing with no emphasis beats a listing whose every keyword sits where no compiler put it. This build turned all 57 recordings back into what a live lexer sends, re-emphasized them through the live path, and required 3036 lines and 136 rule words to come back identical. It also required the check to refuse a stream that lies, and the emphasis pass to leave an already marked recording alone. Any one of those failing stops this page from building.
  • The type checking here is recorded, and the reason changed. It used to be that the compiler could not report a diagnostic from wasm at all. Work on wasm/compiler-in-browser cleared that, so a checker in the page is reachable now and when one lands it feeds this same event path. The limit is running the program afterwards: a browser has no linker and producing a program out of a module needs one. So the diagnostics below are recorded.
  • 2 of the samples do not print the same thing on both targets, on purpose. The recording marks them, the menu marks them, and the card carries the sentence. They are in the set because a target that cannot open a file is a fact about the target, and hiding them would make the other 55 less believable.
  • 3 samples do not compile for this target at all. It has no module and no page here; the recorder kept the compiler's refusal and the reason, below, so the absence is recorded rather than quiet.

Every recorded module

One card per sample. A byte count and an exit status are recorded numbers, so they appear only inside these frames and never as bare figures; the compiler, the machine and the command that recorded every one of them are in the rail under the last card.

measured · hello.wasm from samples/tour/hello.iyi

The module printed exactly what the native binary printed

module
hello.wasm, 122096 bytes
sha256
d8d85975c2068cc2da1fc65f6399f9eaec1a1f386d9a5171b230d74294070a11
recorded exit
0
open it
hello
measured · modules.wasm from samples/tour/modules.iyi

The module printed exactly what the native binary printed

module
modules.wasm, 123851 bytes
sha256
8d4664b217911e3ba42e798ba1e038afe923b32c470228a055d94a372a8ea0f8
recorded exit
0
open it
modules
measured · exports.wasm from samples/tour/exports.iyi

The module printed exactly what the native binary printed

module
exports.wasm, 123847 bytes
sha256
bc2e1341183ac2e7c50ef8f5e8263221df78b087a6ae1f9937824c6abbc4b30d
recorded exit
0
open it
exports
measured · variables.wasm from samples/tour/variables.iyi

The module printed exactly what the native binary printed

module
variables.wasm, 155118 bytes
sha256
1cd3534d7b4f1a6460822ace0f288e030693a548294515b3c64d0c10c7c1db19
recorded exit
0
open it
variables
measured · types.wasm from samples/tour/types.iyi

The module printed exactly what the native binary printed

module
types.wasm, 157866 bytes
sha256
0ee936e81b05606b1ffc61da3735003df0f291027aee3d0c88d017b7934fdfb0
recorded exit
0
open it
types
measured · conversions.wasm from samples/tour/conversions.iyi

The module printed exactly what the native binary printed

module
conversions.wasm, 186501 bytes
sha256
f219dadcc4543f3a531c773c11b7ffa06aa60a998cdd90f69c89b42fc427a634
recorded exit
0
measured · constants.wasm from samples/tour/constants.iyi

The module printed exactly what the native binary printed

module
constants.wasm, 159500 bytes
sha256
4144f9bbbf3eb25d55eb06522c04764fc40258ff9281cfc4f957c767affece7e
recorded exit
0
open it
constants
measured · functions.wasm from samples/tour/functions.iyi

The module printed exactly what the native binary printed

module
functions.wasm, 124193 bytes
sha256
9b1e589f87c9c4fa07a1c1b3376925f65ec03c9c72d930800dad450e40902072
recorded exit
0
open it
functions
measured · returns.wasm from samples/tour/returns.iyi

The module printed exactly what the native binary printed

module
returns.wasm, 126434 bytes
sha256
f3173fd034b6d9b95fa858fd03f6cac7d6b2538704001c478d8170bd92746657
recorded exit
0
open it
returns
measured · nil.wasm from samples/tour/nil.iyi

The module printed exactly what the native binary printed

module
nil.wasm, 124485 bytes
sha256
bd9d12d8fbedd7ca6102cd3ea69f5988422d815e046877225590a3b76a436410
recorded exit
0
open it
nil
measured · loops.wasm from samples/tour/loops.iyi

The module printed exactly what the native binary printed

module
loops.wasm, 125458 bytes
sha256
9d56bbd1f00c959a14fa6d802af486710261b1422e6816ec5c3f9ae6f5ffe9e9
recorded exit
0
open it
loops
measured · ranges.wasm from samples/tour/ranges.iyi

The module printed exactly what the native binary printed

module
ranges.wasm, 139692 bytes
sha256
771193e793116140d70d1d077cce45f3651e07f169bd880379783a1a778ebfb3
recorded exit
0
open it
ranges
measured · conditions.wasm from samples/tour/conditions.iyi

The module printed exactly what the native binary printed

module
conditions.wasm, 123837 bytes
sha256
c6147bb1a69a4c1ead224fea9e2c7d40450282836f87804fa722c30b0b9106d1
recorded exit
0
open it
conditions
measured · case.wasm from samples/tour/case.iyi

The module printed exactly what the native binary printed

module
case.wasm, 126815 bytes
sha256
76446fb365856668f336a7176ca70b2a65b21682fa9a2504d1348ca6e7e14c4e
recorded exit
0
open it
case
measured · defer.wasm from samples/tour/defer.iyi

The module printed exactly what the native binary printed

module
defer.wasm, 127140 bytes
sha256
ba4bfce71cbd6e63c0ce10685d3d3f302feeb5787002753eaa7076689842e038
recorded exit
0
open it
defer
measured · structs.wasm from samples/tour/structs.iyi

The module printed exactly what the native binary printed

module
structs.wasm, 124581 bytes
sha256
bce492322c202429148d06972aba6bde47e429c04c4be160ab5ade6c9533b6e7
recorded exit
0
open it
structs
measured · classes.wasm from samples/tour/classes.iyi

The module printed exactly what the native binary printed

module
classes.wasm, 123407 bytes
sha256
6fdb00be51c3c8a8947ee039d7eb1f064bec2b184ffae7f4804b7dc64003e7d3
recorded exit
0
open it
classes
measured · arrays.wasm from samples/tour/arrays.iyi

The module printed exactly what the native binary printed

module
arrays.wasm, 143866 bytes
sha256
552e337e429793603390dd18026cf9082c863aa255bf608a5a366d8466057c59
recorded exit
0
open it
arrays
measured · hashes.wasm from samples/tour/hashes.iyi

The module printed exactly what the native binary printed

module
hashes.wasm, 163805 bytes
sha256
e7bdc623f09de975cab17d4f7467b33343cb6128407d718514b25bf719b3b4a7
recorded exit
0
open it
hashes
measured · tuples.wasm from samples/tour/tuples.iyi

The module printed exactly what the native binary printed

module
tuples.wasm, 126788 bytes
sha256
57086980e47e4c49ae6e0466a9479ec2f6624fd9923890e0e8d6f2faf1c32e61
recorded exit
0
open it
tuples
measured · blocks.wasm from samples/tour/blocks.iyi

The module printed exactly what the native binary printed

module
blocks.wasm, 126911 bytes
sha256
62b4d9b570f5485aa2d319fd5167a311a8e1c76e49d315ea05385272d6d20cd3
recorded exit
0
open it
blocks
measured · methods.wasm from samples/tour/methods.iyi

The module printed exactly what the native binary printed

module
methods.wasm, 160134 bytes
sha256
bf95ec7eabc12af0182e97029cb9037daa18502038b4044f39253ddfbb91155c
recorded exit
0
open it
methods
measured · traits.wasm from samples/tour/traits.iyi

The module printed exactly what the native binary printed

module
traits.wasm, 159592 bytes
sha256
ba83810977ebb13f0be4fa8375255b8d6a4c4ba96353f42512401ea06d059f61
recorded exit
0
open it
traits
measured · dispatch.wasm from samples/tour/dispatch.iyi

The module printed exactly what the native binary printed

module
dispatch.wasm, 134609 bytes
sha256
dcd380f33eb32c1c484ae4dab221e69d43fa01a0de05431095481d8ee8ec95bd
recorded exit
0
open it
dispatch
measured · defaults.wasm from samples/tour/defaults.iyi

The module printed exactly what the native binary printed

module
defaults.wasm, 126119 bytes
sha256
cbd2abebebe224d22ced7aa7d06c9c5e379ac622ff64cca62be365706f21ef1c
recorded exit
0
open it
defaults
measured · unions.wasm from samples/tour/unions.iyi

The module printed exactly what the native binary printed

module
unions.wasm, 134222 bytes
sha256
bac598ec5c9bfb7d30c535eddbd7d2b3284d6dc72389791233ae57a0871ae330
recorded exit
0
open it
unions
measured · errors.wasm from samples/tour/errors.iyi

The module printed exactly what the native binary printed

module
errors.wasm, 159584 bytes
sha256
9f4bab334b32c9ca883b781fa6647f8ef6f5cd4dabf9930999f42bac57e1c1a8
recorded exit
0
open it
errors
measured · strings.wasm from samples/tour/strings.iyi

The module printed exactly what the native binary printed

module
strings.wasm, 159121 bytes
sha256
3f87fc3881e0a2d3674c0d8c0e1ae9b6abd5dbea32c3aca8aa3c95e2b789331a
recorded exit
0
open it
strings
measured · generic-functions.wasm from samples/tour/generic-functions.iyi

The module printed exactly what the native binary printed

module
generic-functions.wasm, 150801 bytes
sha256
4d9b0b77e65e3e8b700d72a4030b211f3a1d137ff4e029873414c5eb5537efe8
recorded exit
0
measured · generic-types.wasm from samples/tour/generic-types.iyi

The module printed exactly what the native binary printed

module
generic-types.wasm, 133417 bytes
sha256
3e14278baa5d11be7f9055e13e565dd13b637beda249467bd0fae79dc94952f2
recorded exit
0
measured · bounds.wasm from samples/tour/bounds.iyi

The module printed exactly what the native binary printed

module
bounds.wasm, 128615 bytes
sha256
b684d251752741a006601e38fa1049a9e3cd4f19ff213c50dd6941710297812c
recorded exit
0
open it
bounds
measured · iyi-basics.wasm from samples/iyi/basics.iyi

The module printed exactly what the native binary printed

module
iyi-basics.wasm, 209967 bytes
sha256
c58a25d4c6471c5bdfae499ceff4528377a6b87dc5042dc16cf12e650ca29507
recorded exit
0
open it
iyi/basics
measured · iyi-calc.wasm from samples/iyi/calc.iyi

The module printed exactly what the native binary printed

module
iyi-calc.wasm, 198605 bytes
sha256
1aeedad67db85314146f76bffa5e4d3e18d1780032735d26b5064b5d34df307e
recorded exit
0
open it
iyi/calc
measured · iyi-collections.wasm from samples/iyi/collections.iyi

The module printed exactly what the native binary printed

module
iyi-collections.wasm, 225717 bytes
sha256
5bd53b43df4d81866f367608f26d432d91994971f3f11943508d8b0efe526c5d
recorded exit
0
measured · iyi-config.wasm from samples/iyi/config.iyi

The module printed exactly what the native binary printed

module
iyi-config.wasm, 175995 bytes
sha256
ae5cc6635a089a05372b0ba1e2c5c29e487f7f625fba8e19933579080b412e79
recorded exit
0
open it
iyi/config
measured · iyi-derive.wasm from samples/iyi/derive.iyi

The module printed exactly what the native binary printed

module
iyi-derive.wasm, 127243 bytes
sha256
843198831452d7035c9f99138fb988ddd3adb33f32cd938db2f46b7f770666e4
recorded exit
0
open it
iyi/derive
measured · iyi-enums.wasm from samples/iyi/enums.iyi

The module printed exactly what the native binary printed

module
iyi-enums.wasm, 149063 bytes
sha256
085e1a645d59ff3824ec459d635f35cd97ee9e66968b1a2d9d761e00f1f7f625
recorded exit
0
open it
iyi/enums
measured · iyi-errors.wasm from samples/iyi/errors.iyi

The module printed exactly what the native binary printed

module
iyi-errors.wasm, 137637 bytes
sha256
8b4619f04af492b2207666c584aded0fcdefabd8ef0d206e021d40b2354a4573
recorded exit
0
open it
iyi/errors
measured · iyi-files.wasm from samples/iyi/files.iyi

On this target the module and the native binary do not print the same thing

module
iyi-files.wasm, 124173 bytes
sha256
36b49dd306972eded05da6ebc001ecbefc37901f09d4551941a495d3a22dff69
recorded exit
1
open it
iyi/files
the lesson
The wasm build refuses File at run time by design: src/iyi/prelude.iyi panics with "File is not available on wasm32-wasi: path_open needs a preopened directory fd", because a WASI module reaches the filesystem only through directory handles the host granted it, and this module asks for none.
measured · iyi-format.wasm from samples/iyi/format.iyi

The module printed exactly what the native binary printed

module
iyi-format.wasm, 305987 bytes
sha256
96b91976e60702adfaa6d826605ae10fcd3074504abbf28ccd4e7ad6893bef89
recorded exit
0
open it
iyi/format
measured · iyi-formatting.wasm from samples/iyi/formatting.iyi

The module printed exactly what the native binary printed

module
iyi-formatting.wasm, 142136 bytes
sha256
ac72388d20522939cc55442459d112285825966672679c36f5e163e1b3660788
recorded exit
0
measured · iyi-generics.wasm from samples/iyi/generics.iyi

The module printed exactly what the native binary printed

module
iyi-generics.wasm, 142973 bytes
sha256
601687dad67b1543438b59ac2df1fc1948f73aa2fa70af53ea3b592ed26f0659
recorded exit
0
measured · iyi-grid.wasm from samples/iyi/grid.iyi

The module printed exactly what the native binary printed

module
iyi-grid.wasm, 184691 bytes
sha256
2322c849d0c0de208abdce4d859241ecd39fa93082af7de9fb1ca31dbf94a99e
recorded exit
0
open it
iyi/grid
measured · iyi-hello.wasm from samples/iyi/hello.iyi

The module printed exactly what the native binary printed

module
iyi-hello.wasm, 129713 bytes
sha256
1acbae015df99f4903adef5307879aba69198efcbc9eaa7bb3195222ec7e6477
recorded exit
0
open it
iyi/hello
measured · iyi-immutable.wasm from samples/iyi/immutable.iyi

The module printed exactly what the native binary printed

module
iyi-immutable.wasm, 195488 bytes
sha256
58c89ab0eefa139a7792c940bc20a14f2cd685300574b2029d04092713b9d4f5
recorded exit
0
measured · iyi-init_order.wasm from samples/iyi/init_order.iyi

The module printed exactly what the native binary printed

module
iyi-init_order.wasm, 123772 bytes
sha256
185d364aa2311cbda4c14add19b531eace60c1e336a3d64c85205f62a7858d8b
recorded exit
0
measured · iyi-inventory.wasm from samples/iyi/inventory.iyi

The module printed exactly what the native binary printed

module
iyi-inventory.wasm, 215622 bytes
sha256
0c81eccc6cf69bb58eb5f1d6c6c98f97ff7d00151f9022276a728c2761d8647c
recorded exit
0
measured · iyi-io.wasm from samples/iyi/io.iyi

The module printed exactly what the native binary printed

module
iyi-io.wasm, 130653 bytes
sha256
463ed1f747c8f509297ef35a5a6e6fb67e089a00464c0b6450f2f523203c530f
recorded exit
0
open it
iyi/io
measured · iyi-modules.wasm from samples/iyi/modules.iyi

The module printed exactly what the native binary printed

module
iyi-modules.wasm, 125729 bytes
sha256
b150d37a31db7df338514312ddeafdcc8c08f90bc5f0120def3b7b08a7cb0774
recorded exit
0
measured · iyi-sessions.wasm from samples/iyi/sessions.iyi

The module printed exactly what the native binary printed

module
iyi-sessions.wasm, 138729 bytes
sha256
2aa3e82d9ae07300b532c328d8907507262da5cf162957b6e9d919d2b5bbbe26
recorded exit
0
measured · iyi-shapes.wasm from samples/iyi/shapes.iyi

The module printed exactly what the native binary printed

module
iyi-shapes.wasm, 194621 bytes
sha256
ce5153b0d7d47987f25f4a8ae9d69abc090349c2d4ef12c7e240e671c3755e1e
recorded exit
0
open it
iyi/shapes
measured · iyi-socket.wasm from samples/iyi/socket.iyi

On this target the module and the native binary do not print the same thing

module
iyi-socket.wasm, 322516 bytes
sha256
a3da1ba9191916a504945d30afe72c50f3358a1bce634cae2c8d908bb6d1d4fd
recorded exit
1
open it
iyi/socket
the lesson
There are no sockets on wasm32-wasi to open: src/std/socket.iyi binds the calls per platform and wasm32 falls to the branch that has none, so the first line panics with "IyiSocket is not supported on this platform" and the module prints nothing. WASI preview1 can accept, read and write a socket the host handed it, and has no call that creates or binds one; natively the same program runs both ends of the exchange on loopback.
measured · iyi-std_iterator.wasm from samples/iyi/std_iterator.iyi

The module printed exactly what the native binary printed

module
iyi-std_iterator.wasm, 205324 bytes
sha256
bb9096be2d7a41cf41fdc3145b94da1610154ed4088a4fd69c9e492ea33180b4
recorded exit
0
measured · iyi-std_text.wasm from samples/iyi/std_text.iyi

The module printed exactly what the native binary printed

module
iyi-std_text.wasm, 192507 bytes
sha256
e5fea273a1e34bac60159c4b2a34021a5c6c06ede16af753e7496d00401483ac
recorded exit
0
measured · iyi-std_time.wasm from samples/iyi/std_time.iyi

The module printed exactly what the native binary printed

module
iyi-std_time.wasm, 272786 bytes
sha256
78a8553c63c8622d6d613ca92513ad0e54c26e1f46921de34c62e43992324808
recorded exit
0
measured · iyi-visited.wasm from samples/iyi/visited.iyi

The module printed exactly what the native binary printed

module
iyi-visited.wasm, 174911 bytes
sha256
1dc4239ebdc57a7089107ab86ab68aa3c0eda49181a24a5a03a291f29825ecf8
recorded exit
0
measured · iyi-webapp.wasm from samples/iyi/webapp.iyi

The module printed exactly what the native binary printed

module
iyi-webapp.wasm, 166939 bytes
sha256
e0e334dd8a7912b996e2957bfbee8b0222c5c29c1f901f73eef7951eebb83e99
recorded exit
0
open it
iyi/webapp
measured · samples/tour/tasks.iyi

The compiler refused this sample for wasm32-wasi, so there is no module

the refusal
Error: group is not available on wasm32-wasi: WebAssembly cannot switch stacks, wasmtime 48 disables the stack-switching proposal, wasi-threads is unsupported, and asyncify cannot run without a host event loop (SPEC.md III.4)
the lesson
Tasks and channels run on Linux, macOS arm64 and Windows x86-64, and not on wasm32 yet: a program that names `group` or `Channel` there does not compile, because src/iyi/concurrency.iyi refuses to ship a spelling without the feature (SPEC.md III.4.8).
measured · samples/tour/channels.iyi

The compiler refused this sample for wasm32-wasi, so there is no module

the refusal
Error: group is not available on wasm32-wasi: WebAssembly cannot switch stacks, wasmtime 48 disables the stack-switching proposal, wasi-threads is unsupported, and asyncify cannot run without a host event loop (SPEC.md III.4)
the lesson
Tasks and channels run on Linux, macOS arm64 and Windows x86-64, and not on wasm32 yet: a program that names `group` or `Channel` there does not compile, because src/iyi/concurrency.iyi refuses to ship a spelling without the feature (SPEC.md III.4.8).
measured · samples/iyi/workers.iyi

The compiler refused this sample for wasm32-wasi, so there is no module

the refusal
Error: group is not available on wasm32-wasi: WebAssembly cannot switch stacks, wasmtime 48 disables the stack-switching proposal, wasi-threads is unsupported, and asyncify cannot run without a host event loop (SPEC.md III.4)
the lesson
Tasks and channels run on Linux, macOS arm64 and Windows x86-64, and not on wasm32 yet: a program that names `group` or `Channel` there does not compile, because src/iyi/concurrency.iyi refuses to ship a spelling without the feature (SPEC.md III.4.8).

What the compiler says when it refuses

Recorded, not live, because nothing in this page compiles: a browser has no linker and producing a program out of a module needs one. The programs below are real files under records/break/, they are really wrong, and this is the real compiler's output on them, captured verbatim, caret line included. Where the compiler cited the rule it was enforcing, the citation is pulled into the footer, because iyi's errors naming their rule is a feature of the language and the design should say so.

two modules that import each other

records/break/r1_import_cycle.iyi, exit 1

records/break/dep/loop_b.iyiline 5, column 1
# The other half of the cycle. `dep/loop_a` imports this module and this
# module imports `dep/loop_a`, which is the edge that closes it.
module dep/loop_b

import dep/loop_a

pub def b : String
  "b"
end
Showing last frame. Use --error-trace for full trace.

In records/break/dep/loop_b.iyi:5:1

 5 | import dep/loop_a
     ^
Error: import cycle

    dep/loop_a -> dep/loop_b -> dep/loop_a

`import` forms a DAG (R-1). A cycle has no initialisation order — a module initialises after every module it imports (SPEC.md III.5 rule 1), and here each would have to go after the other — and it is what makes at most one module able to define any given impl (SPEC.md IV.4). Move what the modules in the cycle share into a module they both import.
rule: R-1, SPEC.md. IYI_PATH=<iyi>/src iyi build --no-codegen --no-color records/break/r1_import_cycle.iyi

a name the module never exported

records/break/r2_not_exported.iyi, exit 1

records/break/r2_not_exported.iyiline 11, column 19
# Breaks R-2: only what a module marks `pub` is reachable from outside it, and
# `greeting` in `dep/greeter` carries no `pub`. Qualifying the call does not get
# around that. It has to be genuinely unreachable rather than merely left out
# of an import's names, because a `.iyimod` carries a module's exports and nothing
# else, so if another module could call an unmarked name that artifact would
# not be enough to compile against.
module diagnostics/r2_not_exported

import dep/greeter

puts Dep::Greeter.greeting
Showing last frame. Use --error-trace for full trace.

In records/break/r2_not_exported.iyi:11:19

 11 | puts Dep::Greeter.greeting
                        ^-------
Error: Dep::Greeter does not export 'greeting'. Only what a module marks `pub` is reachable from outside it — see SPEC.md R-2
rule: R-2, SPEC.md. IYI_PATH=<iyi>/src iyi build --no-codegen --no-color records/break/r2_not_exported.iyi

an exported def that does not say what it returns

records/break/r2_missing_return_type.iyi, exit 1

records/break/r2_missing_return_type.iyiline 7, column 9
# Breaks R-2: everything a module exports carries full parameter and return
# types, because the signature is the whole of what a consumer reads. Drop the
# return type and there is nothing to write into the artifact, so the omission
# is refused where it is written rather than inferred from the body.
module diagnostics/r2_missing_return_type

pub def total(numbers : Array(Int32))
  numbers.sum
end

puts total([1, 2, 3])
Showing last frame. Use --error-trace for full trace.

In records/break/r2_missing_return_type.iyi:7:9

 7 | pub def total(numbers : Array(Int32))
             ^----
Error: `total` is exported and does not say what it returns

R-2 asks an exported signature for full types. A consumer types a call to this from the return type alone, since the body stays in this module: without one it infers `Nil`, and the symbol it then asks the linker for is not the symbol this module emitted.

Annotate the return type, or leave the def unexported.
rule: R-2, SPEC.md. IYI_PATH=<iyi>/src iyi build --no-codegen --no-color records/break/r2_missing_return_type.iyi

a body that does not honour its written return type

records/break/r2_wrong_return_type.iyi, exit 1

records/break/r2_wrong_return_type.iyiline 6, column 41
# Breaks R-2 the other way: the type is written, and the body does not honour
# it. An exported signature is a promise to every consumer that reads the
# artifact instead of the body, so the body is the thing that has to give.
module diagnostics/r2_wrong_return_type

pub def total(numbers : Array(Int32)) : Int32
  numbers.join(",")
end

puts total([1, 2, 3])
Showing last frame. Use --error-trace for full trace.

In records/break/r2_wrong_return_type.iyi:6:41

 6 | pub def total(numbers : Array(Int32)) : Int32
                                             ^----
Error: method Diagnostics::R2WrongReturnType.total must return Int32 but it is returning String

String is not in the declared type
rule: R-2, SPEC.md. IYI_PATH=<iyi>/src iyi build --no-codegen --no-color records/break/r2_wrong_return_type.iyi

an unqualified name no import brought in

records/break/r2b_name_not_imported.iyi, exit 1

records/break/r2b_name_not_imported.iyiline 10, column 6
# Breaks R-2b: a bare `import` brings a module into scope under its qualified
# name and nothing more. Unqualified names arrive only through the list or the
# glob on an import line, `::{...}` or `::*`, which the consumer writes, so
# importing a module never quietly changes what a name in this file means.
# `polite` is exported and still not in scope here.
module diagnostics/r2b_name_not_imported

import dep/greeter

puts polite("iyi")
Showing last frame. Use --error-trace for full trace.

In records/break/r2b_name_not_imported.iyi:10:6

 10 | puts polite("iyi")
           ^-----
Error: undefined method 'polite' for Diagnostics::R2bNameNotImported:Module

`polite` is exported by `dep/greeter`, and this file has not brought it into scope. Import it by name, `import dep/greeter::{polite}`, or call it as `Dep::Greeter.polite` (SPEC.md R-2b)
rule: R-2b, SPEC.md. IYI_PATH=<iyi>/src iyi build --no-codegen --no-color records/break/r2b_name_not_imported.iyi

a body typed at its definition, reaching outside its bound

records/break/r2c_outside_bound.iyi, exit 1

records/break/r2c_outside_bound.iyiline 39, column 5
# Breaks R-2c: a def whose parameters and return are all written is typed at
# its definition, caller or no caller, and a trait-restricted parameter is
# typed against exactly its bound. Two halves of the rule fail here at once.
#
# Nothing in this file calls `loudest`, so under the lazy typing iyi inherited
# from Crystal its body would never be visited and the build would report
# itself clean. And `User`, the only implementer of `Greet` in the program, has
# a `shout` of its own: duck-typed generics would have found it and compiled.
# `Greet` declares `greet` and nothing else, so the body reaches outside the
# bound it wrote down, and the message names the witness type the compiler
# synthesized to type it (SPEC.md R-2c;
# src/compiler/iyi/semantic/definition_typing.cr).
module diagnostics/r2c_outside_bound

pub trait Greet
  abstract def greet : String
end

pub struct User
  getter name : String

  def initialize(@name : String)
  end

  # Present on the concrete type, and irrelevant to the def below: the bound is
  # what that body is typed against, not whatever happens to implement it.
  def shout : String
    "HELLO"
  end
end

impl Greet for User
  def greet : String
    "Hello, #{name}."
  end
end

pub def loudest(g : Greet) : String
  g.shout
end
Showing last frame. Use --error-trace for full trace.

In records/break/r2c_outside_bound.iyi:39:5

 39 | g.shout
        ^----
Error: undefined method 'shout' for IyiDefTypeWitness_Diagnostics_R2cOutsideBound_Greet
rule: R-2c, SPEC.md. IYI_PATH=<iyi>/src iyi build --no-codegen --no-color records/break/r2c_outside_bound.iyi

an impl in a module that owns neither the trait nor the type

records/break/r3_orphan_impl.iyi, exit 1

records/break/r3_orphan_impl.iyiline 11, column 6
# Breaks R-3: `impl Trait for Type` may live in the module that defines the
# trait or the module that defines the type, and nowhere else. `Greet` belongs
# to `dep/greeter` and `User` belongs to `dep/user`, so this module owns
# neither and is the one place the impl cannot be written. That restriction is
# what makes a type's method set knowable from its declarations alone.
module diagnostics/r3_orphan_impl

import dep/greeter::*
import dep/user::*

impl Greet for User
  def greet : String
    "Hello, #{name}."
  end
end

puts User.new("iyi").greet
Showing last frame. Use --error-trace for full trace.

In records/break/r3_orphan_impl.iyi:11:6

 11 | impl Greet for User
           ^
Error: can't implement Dep::Greeter::Greet for Dep::User::User in Diagnostics::R3OrphanImpl: an impl must live in the module that defines the trait (Dep::Greeter) or the module that defines the type (Dep::User). This is R-3, the orphan rule, and it is what lets coherence be checked without a global pass — see SPEC.md IV.4
rule: R-3, SPEC.md. IYI_PATH=<iyi>/src iyi build --no-codegen --no-color records/break/r3_orphan_impl.iyi

a type that does not implement a required trait

records/break/r3_unmet_trait_bound.iyi, exit 1

records/break/r3_unmet_trait_bound.iyiline 29, column 6
# Breaks R-3 from the calling side: a type's method set is closed, so a bound
# on a type parameter is a question with a definite answer at the call. `Int32`
# has no `impl Greet` and cannot acquire one later, so the call is refused as a
# missing trait rather than as a failed overload match.
module diagnostics/r3_unmet_trait_bound

pub trait Greet
  abstract def greet : String
end

pub struct User
  getter name : String

  def initialize(@name : String)
  end
end

impl Greet for User
  def greet : String
    "Hello, #{name}."
  end
end

pub def announce(item : T) : String forall T : Greet
  "-> #{item.greet}"
end

puts announce(User.new("iyi"))
puts announce(42)
Showing last frame. Use --error-trace for full trace.

In records/break/r3_unmet_trait_bound.iyi:29:6

 29 | puts announce(42)
           ^-------
Error: Int32 does not implement Diagnostics::R3UnmetTraitBound::Greet, required by `T` in `announce`
rule: R-3, SPEC.md. IYI_PATH=<iyi>/src iyi build --no-codegen --no-color records/break/r3_unmet_trait_bound.iyi

the annotation R-4 names, asked of a compiler that has no such name

records/break/r4_dictionary.iyi, exit 1

records/break/r4_dictionary.iyiline 16, column 3
# Breaks R-4 by asking for the one clause of it that has nothing behind it.
#
# R-4's third sentence says `@[Monomorphize]` forces specialisation across a
# module boundary. SPEC.md III.9 records what that is worth today: nothing in
# the compiler mentions the annotation, a dictionary or a GC shape. So this is
# not a feature that misbehaves, it is a name the compiler has never heard,
# and the rejection below is a compiler declining to accept a promise it
# cannot keep rather than compiling the program as though the rule were built.
#
# What runs instead of the dictionary is the rule's second sentence applied
# everywhere: a generic's body travels in the artifact and every consumer
# compiles it again for the types it uses. `bench/generic_boundary.py` is the
# measurement of what that costs a consumer's front end.
module diagnostics/r4_dictionary

@[Monomorphize]
pub def twice(x : T) : Array(T) forall T
  [x, x]
end

puts twice(1).size
Showing last frame. Use --error-trace for full trace.

In records/break/r4_dictionary.iyi:16:3

 16 | @[Monomorphize]
        ^-----------
Error: undefined constant Monomorphize
rule: R-4, SPEC.md. IYI_PATH=<iyi>/src iyi build --no-codegen --no-color records/break/r4_dictionary.iyi

a derive that asks about the whole program

records/break/r5_global_question.iyi, exit 1

records/break/dep/global_derive.iyiline 16, column 31
# A module the break program imports, exporting one derive macro. It compiles:
# a macro body is expanded where the `derive` is written, not where the macro
# is defined, so the refusal this module earns lands in its consumer's build.
#
# `counted` asks a question about the whole program. `all_subclasses` answers
# with every type compiled beside it, which is exactly what R-5 keeps a macro
# away from: a derive that read it would generate different code depending on
# what else happened to be in the build, and then a `.iyimod` could not carry
# what it generated (SPEC.md R-5, II.4).
module dep/global_derive

pub macro counted(declaration)
  def counted : Int32
    {% count = 0 %}
    {% for field in declaration[:fields] %}
      {% count = field[:type].all_subclasses.size %}
    {% end %}
    {{ count }}
  end
end
Showing last frame. Use --error-trace for full trace.

In records/break/dep/global_derive.iyi:16:31

 16 | {% count = field[:type].all_subclasses.size %}
                              ^-------------
Error: `all_subclasses` is not available to a derive: it answers with the whole program, and a derive may only read the declaration it is attached to and what that declaration's types implement — see SPEC.md II.4
rule: R-5, SPEC.md. IYI_PATH=<iyi>/src iyi build --no-codegen --no-color records/break/r5_global_question.iyi

an error member left unhandled

records/break/errors_non_exhaustive.iyi, exit 1

records/break/errors_non_exhaustive.iyiline 28, column 1
# Breaks the error rule in SPEC.md III.1: an error is an ordinary member of a
# union, so handling one needs no new machinery, only the totality check a
# `case` already performs. `read` can return an `IOError` and this `case` has
# no branch for it, which is the same mistake as ignoring a returned error and
# is caught by the same check.
module diagnostics/errors_non_exhaustive

pub struct IOError
  getter path : String

  def initialize(@path : String)
  end
end

# `Error` is the compiler's trait, which is what lets a union member be an
# error member without any name in scope deciding it.
impl Error for IOError
  def message : String
    "no such file: #{path}"
  end
end

pub def read(path : String) : String | IOError
  return IOError.new(path) if path == "missing"
  "12"
end

case read("missing")
in String then puts it
end
Showing last frame. Use --error-trace for full trace.

In records/break/errors_non_exhaustive.iyi:28:1

 28 | case read("missing")
      ^
Error: case is not exhaustive.

Missing types:
 - Diagnostics::ErrorsNonExhaustive::IOError
rule: III.1, SPEC.md. IYI_PATH=<iyi>/src iyi build --no-codegen --no-color records/break/errors_non_exhaustive.iyi

The interface underneath

Everything the playground knows about compiling iyi arrives through one interface, PlaygroundEngine, resolved in one place. The engine's own account of itself is the only input to how that page is shaped, so an engine that could check source but not link would get a larger playground without a markup change, and one that could do neither would get a smaller one. To swap it, follow the three steps in src/playground/registry.ts: add an implementation, register it there, and report only what it actually does.