Reef Language Quick Syntax Reference
Ruby/Crystal Style - End-Based Blocks
Last reviewed on version: 0.9.0
This is a quick-reference cheat sheet, not the source of truth. The numbered
pages under docs/language/reference/ are verified
current and govern in any conflict — where this page would duplicate their
depth, it links out instead.
Basic Syntax
Functions
fn name(param: Type): ReturnType
// body
return result
end name
Procedures (no return value)
proc name(param: Type)
// body
end name
Lambda Expressions (Closures)
// Function lambda (returns value)
let double = fn(x: int): int => x * 2
let add = fn(a: int, b: int): int => a + b
// Procedure lambda (no return)
let printer = proc(x: int) => println_int(x)
// Block-body lambda
let complex = fn(x: int): int
let y = x * 2
return y + 1
end fn
// Procedure block-body
let logger = proc(msg: string)
print("LOG: ")
println(msg)
end proc
Function Types
// Function taking int, returning int
type Mapper = Fn[int, int]
// Function taking two ints, returning int
type Reducer = Fn[int, int, int]
// Procedure taking int (no return)
type Consumer = Fn[int]
Variable Capture
// Immutable capture (copied)
let factor = 10
let scale = fn(x: int): int => x * factor
// Mutable capture (boxed by reference)
mut counter = 0
let inc = fn(): int
counter = counter + 1
counter
end fn
Variables (Local Scope)
let x = 42 // Immutable local variable
mut y = "hello" // Mutable local variable (use 'mut', not 'var')
let z: int = 100 // Explicit type annotation
Module-Level Declarations
module mymodule
// Constants - compile-time evaluated, exportable
const PI: float = 3.14159
const MAX_SIZE: int = 1024
// Module variables - immutable after init, exportable
let version: string = "1.0.0"
let timeout: int = 30
// Module mutable state - persists across calls, NOT exportable
mut counter: int = 0
mut rng_state: int = 2463534242
fn use_values(): int
counter = counter + 1 // Can modify mut
return MAX_SIZE // Can read const/let
end use_values
end module
Control Flow
// If
if condition
action()
end if
// If-Else
if condition
action1()
else
action2()
end if
// If-Elif-Else
if condition1
action1()
elif condition2
action2()
else
action3()
end if
// Unless (negated if)
unless condition
action()
end unless
Loops
// While
while condition
action()
end while
// For range (use 'to' keyword)
for i in 1 to 10
print_int(i) // 1 to 9 (exclusive end)
end for
// For with .. range syntax
for i in 0..5
print_int(i) // 0 to 4
end for
// For with step
for i in 0 to 100 step 10
print_int(i) // 0, 10, 20, ..., 90
end for
// For-each over array
for item in arr
print_int(item)
end for
// For-each with index
for i, item in arr
print_int(i) // 0, 1, 2, ...
print_int(item) // element value
end for
// Loop (infinite)
loop
action()
if done
break
end if
end loop
// Do-while (body runs at least once)
do while condition
action()
end do
// Do-until (body runs at least once, stops when condition is true)
do until condition
action()
end do
// Break and continue
break // Exit loop
continue // Next iteration
Match (Pattern Matching)
// Match statement
match value
Option_Some(x) =>
process(x)
end
Option_None() =>
skip()
end
end match
// Match expression (returns value)
let result = match x
1 => 100 end
2 => 200 end
_ => 0 end
end match
// Patterns supported:
// - Wildcard: _
// - Variable: name
// - Integer: 42
// - Boolean: true, false
// - String: "hello"
// - Constructor: Option_Some(x), Color_Red()
Operators
Arithmetic
+ // Addition
- // Subtraction
* // Multiplication
/ // Division
% // Modulo
** // Power/exponentiation
Comparison
== // Equal
!= // Not equal
< // Less than
> // Greater than
<= // Less or equal
>= // Greater or equal
Logical
&& // Logical AND (or: and)
|| // Logical OR (or: or)
! // Logical NOT (or: not)
Bitwise
& // Bitwise AND
| // Bitwise OR
^ // Bitwise XOR
~ // Bitwise NOT (complement)
<< // Left shift
>> // Right shift
Compound Assignment
+= // Add and assign
-= // Subtract and assign
*= // Multiply and assign
/= // Divide and assign
%= // Modulo and assign
Set Operations
+ // Union (when operands are sets)
* // Intersection
- // Difference
in // Membership test: if x in set
Types
See 030_TYPES.md for the full picture (struct literal validation, exhaustiveness checking, slicing, subrange bounds checking). Quick syntax:
Primitive Types
bool // true, false
char // 'a', '\n'
string // "hello"
int // 32-bit signed (default)
int8, int16, int32, int64 // Signed integers
uint8, uint16, uint32, uint64 // Unsigned integers
byte // Alias for uint8 (useful for FFI)
float // 64-bit (default)
float32, float64 // Floating point
size_t // C size_t
pointer // C void*
Integer Literal Suffixes
let a = 127i8 // int8
let b = 255u8 // uint8
let c = 1000i16 // int16
let d = 65535u16 // uint16
let e = 1000000i32 // int32
let f = 1000000u32 // uint32
let g = 123456i64 // int64
let h = 999999u64 // uint64
Struct
type Point = struct
x: float
y: float
fn distance(): float
return (self.x ** 2 + self.y ** 2) ** 0.5
end distance
end Point
// Creating instances
let p = new Point()
p.x = 1.0
p.y = 2.0
// Struct literal syntax
let p2 = Point { x: 3.0, y: 4.0 }
Enum (Sum Type)
type Color = enum
Red
Green
Blue
end Color
type Option[T] = enum
Some(T)
None
end Option
type Result[T, E] = enum
Ok(T)
Err(E)
end Result
// Using constructors (prefixed with type name)
let color: Color = Color_Red() // No-payload variants still need ()
let value = Option_Some(42) // Type argument inferred: Option[int]
let none = @Option[int].None() // No value to infer T from -- type argument required
Option[T]/Result[T, E] here are ordinary user-defined generic enums for
illustration. Reef's actual canonical generics live in core.option /
core.result (see 035_ERROR_HANDLING.md)
— import those instead of redeclaring your own Option/Result in real code.
Arrays
let nums = [1, 2, 3, 4, 5]
let names = ["Alice", "Bob"]
let first = nums[0] // Read
nums[1] = 100 // Write
// Runtime array allocation
let arr = new [int](size) // Allocate array of size
// Array methods (return new arrays - immutable style)
mut items = [1, 2, 3]
items = items.append(4) // [1, 2, 3, 4]
items = items.pop() // [1, 2, 3]
items = items.remove(1) // [1, 3]
Sets
let s1: setof[int] = {1, 2, 3}
let s2: setof[int] = {0..9} // Range notation
let s3: setof[int] = {0, 2, 5..10} // Mixed
if 5 in s1
println("5 is in set")
end if
Subranges
type Digit = subrange 0 9
type Hour = 0..23 // Alternative syntax
let d: Digit = 5
Passive Objects (0.9)
Heap classes with single inheritance and virtual methods. Not Active Objects (no monitor). Full chapter: 040_OBJECTS.md.
object Widget
x: int
init(x: int)
self.x = x
end init
shared fn kind(): string
return "widget"
end kind
end Widget
object Button extends Widget
init(x: int)
inherited init(x)
end init
override shared fn kind(): string
return "button"
end kind
end Button
proc main()
let b = new Button(1)
let w: Widget = b
println(w.kind())
typecase w
Button x =>
println("button")
end
else =>
println("other")
end
end typecase
end main
extends/overrideare contextual;inherited/typecaseare reserved.==is reference identity. Arrays of objects are invariant.impl Trait for Class— see 050_TRAITS.md.reefc --owner-harness— opt-in owner-check probes.
Active Objects (Concurrency)
Basic Active Object
active object Counter
value: int
init()
self.value = 0
end init
exclusive proc increment()
self.value = self.value + 1
end increment
exclusive fn get(): int
return self.value
end get
end Counter
// Usage
let counter = new Counter()
counter.increment() // Thread-safe
Active Object with Active Body
active object Worker
running: bool
init()
self.running = true
end init
exclusive proc stop()
self.running = false
end stop
run() // Runs in own thread automatically
while self.running
doWork()
end while
end run
finalize() // Called when GC collects object
cleanup()
end finalize
end Worker
Method Modifiers
exclusive- Mutual exclusion (only one thread at a time)shared- Concurrent reads allowed (no writes)
Both are compiler-enforced (0.7.7): a shared method assigning to
self state is a Type Error ("Cannot assign to Active Object state in a
'shared' method..."), and a shared method calling an exclusive method
on self is also a Type Error ("Cannot call exclusive method '...' on
'self' from a shared method... would deadlock") — see
045_ACTIVE_OBJECTS.md and
spec §8.3 for the full messages.
Generics
See 055_GENERICS.md for the full picture (type inference, default parameters, monomorphization, same-named generics across modules). Quick syntax:
// Generic function definition
fn identity[T](x: T): T
return x
end identity
// Call with [:Type] syntax (colon required!)
let n = identity[:int](42)
let s = identity[:string]("hello")
// Generic struct
type Box[T] = struct
value: T
end Box
let b = new Box[int]()
b.value = 42
// Generic Active Object
active object Container[T]
item: T
exclusive proc set(value: T)
self.item = value
end set
shared fn get(): T
return self.item
end get
end Container
let c = new Container[string]()
Generic Functions with Type Constraints
// Constrained generic function — can call trait methods on T
proc print_item[T](item: T) where T: Printable
item.print() // Dispatches to correct trait impl via monomorphization
end print_item
// Multiple constraints
proc show_info[T](item: T) where T: Printable + Describable
item.print()
println(item.describe())
end show_info
// Call with inferred type
let p = Point { x: 1, y: 2 }
print_item(p) // Monomorphizes to print_item_Point → reef_Point_print()
show_info(p) // Requires Point to implement both Printable and Describable
Traits
See 050_TRAITS.md for the full picture (dynamic dispatch is still not supported — see its "Current Limitations" section). Quick syntax:
// Trait definition with abstract methods
trait Printable
proc print(); // Semicolon marks abstract method
end Printable
// Trait with multiple methods
trait Comparable
fn compare(other: int): int;
fn equals(other: int): bool;
end Comparable
// The implementing types
type Point = struct
x: int
y: int
end Point
type Value = struct
v: int
end Value
// Implement trait for a type
impl Printable for Point
proc print()
println("Point")
end print
end impl
// Implement multiple traits
impl Comparable for Value
fn compare(other: int): int
if self.v < other
return -1
elif self.v > other
return 1
else
return 0
end if
end compare
fn equals(other: int): bool
self.v == other
end equals
end impl
Trait Method Dispatch (Dot Notation)
// Call trait methods directly on concrete types
let p = Point { x: 5, y: 10 }
p.print() // Calls impl Printable for Point → reef_Point_print()
let d = p.describe() // Calls impl Describable for Point → reef_Point_describe()
// Also works through generic functions (see Type Constraints above)
print_item(p) // Generic dispatch via monomorphization
Trait Method Signatures
trait Example
// Abstract method - must be implemented (ends with semicolon)
fn required(): int;
// Procedure (no return) - also abstract
proc do_something();
end Example
String Interpolation
let name = "Alice"
let age = 30
// Simple variable interpolation
println("Hello ${name}!")
println("Age: ${age}")
// Multiple interpolations
println("${name} is ${age} years old")
// Escaped dollar sign
println("Price: \$100")
// Note: Only simple variables supported, not expressions
// This does NOT work: "Result: ${a + b}"
Unsafe Code
Unsafe blocks suppress array/string bounds checking and allow pointer operations.
unsafe
// Low-level operations allowed here
// No bounds checking on array/string access
let ptr: pointer = calloc(100, 1)
let buf: [byte] = ptr // pointer -> array coercion
buf[0] = 65 // int -> byte coercion
some_c_func(buf) // array -> pointer coercion
end unsafe
Unsafe Modules
// Entire module suppresses bounds checks
unsafe module fast_math
// All code here runs without bounds checking
end module
Unsafe-Only Type Coercions
// In unsafe blocks only -- implicit, no `as` needed:
pointer <-> [T] // Pointer to/from array
pointer <-> string // Pointer to/from string
int <-> uint8, int8, uint16, int16 // Integer narrowing/widening
char <-> int // Character to/from code point
Raw Address Casts (as, unsafe-only)
A separate, narrower mechanism from the implicit table above: the explicit
as cast between an integer and pointer/[T], for the bare-metal/MMIO
raw-address idiom. It requires unsafe
and the explicit as operator — plain assignment (let p: pointer = some_uint64) is not in the implicit-coercion set above and stays
rejected even inside unsafe.
unsafe
let addr: uint64 = some_array as pointer as uint64
let p = addr as pointer // integer -> pointer
let view = addr as [int] // integer -> [T]
end unsafe
Use uint64 for the address, not int — int is 32-bit by design and
truncates a 64-bit host address. Outside unsafe, both directions remain a
Type Error ("integer to pointer cast requires unsafe"). See
100_UNSAFE.md and
spec §13.3a for the full worked example.
Nil Literal
nil is a null-pointer constant for unsafe/FFI code — it is not how
Reef represents "no value" in safe code. For absence, use
option.Option[T] (see also
030_TYPES.md); nil cannot be assigned to an
Option[T] or any non-pointer type.
// nil can only be used in unsafe blocks, and only with pointer types
proc main()
unsafe
let p: pointer = nil // Null pointer constant
if p == nil
println("null pointer")
end if
end unsafe
end main
Defer Statement
Defer schedules cleanup code to run at function exit (LIFO order).
proc with_resource()
let r = acquire_resource()
defer
release_resource(r)
end defer
// Use resource...
// Cleanup runs automatically when function exits
end with_resource
// Multiple defers - last registered runs first
proc test()
defer
println("First registered, last to run")
end defer
defer
println("Last registered, first to run")
end defer
end test
Spawn Expression
Spawn creates a fire-and-forget concurrent task.
proc background_task()
println("Running in background")
end background_task
proc main()
spawn background_task() // Runs asynchronously
println("Main continues immediately")
end main
Limitations: spawn only accepts a plain proc/fn call (a free
function) — spawning a call to an Active Object method is rejected at
typecheck; call the method directly instead, since AO
methods already dispatch to the object's own thread. It's still
fire-and-forget: no return value, no cancellation, no error propagation.
For those, use Active Objects. See
070_SPAWN.md for the full picture.
Modules
module mymodule
import other.module
import path.to.module as alias
ifarch amd64
import arch.x86 as arch_impl
end ifarch
export
fn publicFunc(): int
proc publicProc()
type PublicType
end export
fn publicFunc(): int
return helper()
end publicFunc
fn helper(): int // Private (not in export)
return 42
end helper
end module
Qualified Module Access (Required)
All imported symbols must be accessed with their module prefix:
import core.str // Access as str.function()
import io.file as f // Access as f.function() (alias)
import time.time // Access as time.function()
import core.result
proc main()
// Correct - qualified access
let len = str.length("hello")
let now = time.time_now()
// io/fs operations return Result[T, error.Error] -- check before using
let outcome = f.readFile("data.txt")
if result.is_ok(outcome)
let content = result.unwrap_ok(outcome)
println(content)
end if
// WRONG - unqualified access not allowed
// let len = length("hello") // Error!
// let content = readFile("...") // Error!
end main
Rules:
import foo.bar→ access symbols asbar.symbol()import foo.bar as x→ access symbols asx.symbol()- Types also require qualification:
let opt: option.Option[int]
Conditional Imports (ifarch)
Import modules only when compiling for a specific architecture:
ifarch amd64
import drivers.lapic as lapic
import drivers.hpet as timer
end ifarch
ifarch riscv64
import drivers.plic as plic
import drivers.timer as timer
end ifarch
- Supported architectures:
amd64,arm64,riscv64 - Use with
--target <arch>flag (e.g.,reefc --target amd64 kernel.reef) - Without
--target, all conditional imports are included - Non-matching imports are silently skipped (module files are not loaded or parsed)
- The module name is the last component of the import path
Foreign Function Interface (FFI)
extern "C" fn strlen(s: string): size_t
extern "C" proc printf(format: string)
proc main()
let len = strlen("hello")
end main
Labeling Rules
Named Constructs (use name):
fn myFunc(): int
return 42
end myFunc // Label matches name
type MyType = struct
field: int
end MyType // Label matches name
active object Server
// body
end Server // Label matches name
Control Flow (use keyword):
if condition
// body
end if
while condition
// body
end while
for i in 0 to 10
// body
end for
loop
// body
end loop
match value
pattern =>
// body
end
end match
unsafe
// body
end unsafe
Collections
Array Literals
let nums = [1, 2, 3, 4, 5]
let names = ["Alice", "Bob", "Carol"]
let empty = new [int](0) // Empty array: `[]` cannot be inferred, construct it
Set Literals
let small_set: setof[int] = {1, 2, 3}
let range_set: setof[int] = {0..9}
Maps (requires stdlib)
import collections.hashmap
import core.option as option
proc main()
let m = hashmap.create_int_hashmap()
m.set("key", 42)
// Lookups return Option[V] -- a missing key is None, not a sentinel
let v = m.get("key")
if option.is_some(v)
print_int(option.unwrap(v))
println("")
end if
end main
Comments
// Single-line comment
/*
Multi-line comment
/* Nesting is supported */
*/
Key Differences from Other Languages
vs Ruby:
- Statically typed
- Compiled to native code
- Active Objects (not threads)
- Generics with monomorphization
vs Crystal:
- Active Objects (unique feature)
- Different concurrency model
- End-based blocks similar
vs Go:
- Active Objects (vs goroutines)
- Generics (Reef has them)
- End-based syntax (vs braces)
- Inline assembly support
vs Rust:
- Simpler (no borrow checker)
- GC (easier memory management)
- End-based syntax
- Baremetal capable (with inline assembly)
Remember
- Indentation is style, not syntax -
endcloses blocks - Label your ends - makes code self-documenting
- Use
procfor no-return - notfnwithout return type - Use
selfnotthis- inside Active Objects and methods - Active Objects - the primary concurrency primitive
- Generic calls use
[:Type]- colon required to disambiguate - No
nilfor absence - useoption.Option[T];nilis only forunsafepointer values
Inline Assembly
For OS kernels, embedded systems, and performance-critical code:
// Assembly procedure (no return)
asm proc hlt() for amd64
HLT
end hlt
// Assembly function (with return)
asm fn inb(port: int): int for amd64
MOV EDX, port
XOR EAX, EAX
IN AL, DX
MOV result, EAX
end inb
// ARM64 example
asm proc wfi() for arm64
WFI
end wfi
Baremetal Compilation
reefc kernel.reef --target amd64-baremetal --entry none --emit-c
Supported targets: amd64, arm64, riscv64, amd64-baremetal,
arm64-baremetal, riscv64-baremetal
Not Yet Implemented
The following features appear in the language spec but are not yet implemented:
- Exception handling (
try/catch/raise) - UseResult[T,E]instead - Variadic parameters (
...) dyn Trait/ trait objects (traits stay monomorphized)- Generic objects (
object Stack[T]) asynctask-scheduling pattern (asyncis not even a reserved word) — butawaititself IS implemented, as a monitor-style blocking condition inside Active Object methods, not part of an async/await scheduler; see 045_ACTIVE_OBJECTS.md
✅ Recently Implemented (v1.4-1.7):
-
✅ Traits with full dispatch -
trait,impl,where T: Trait, dot notation dispatch, generic dispatch via monomorphization. (Only the trailingwhereclause spells a constraint — the bracket-inline formfn max[T: Comparable](...)is still not implemented.) -
✅
deferfor cleanup (LIFO execution at function exit) -
✅
spawnfor fire-and-forget concurrent tasks -
✅ Generic type inference for constructors and function calls
-
✅ Closures with variable capture (immutable and mutable)
-
✅ Lambda expressions:
fn(x: int): int => x * 2 -
✅ Function types:
Fn[int, int] -
✅ Match guards:
_ when condition => -
✅ Default parameters:
fn greet(name: string, greeting: string = "Hello") -
✅ Multi-line strings:
"""...""" -
✅ Array/string slicing:
arr[0..5],s[3..] -
✅ Inline assembly (
asm fn/asm proc) for AMD64 and ARM64 -
✅ Baremetal compilation for OS/embedded development
-
✅ Do-while and do-until loops -
do while cond ... end do,do until cond ... end do -
✅ Expression interpolation -
"${expr}"(e.g.,"${count + 1}") -
✅ For-each iteration -
for item in collection ... end for,for i, item in collection(with index) -
✅ For loop step -
for i in 0 to 100 step 10 ... end for -
✅
await- blocking condition wait insideexclusive/sharedActive Object methods (see 045_ACTIVE_OBJECTS.md) -
✅ Unsafe modules -
unsafe module ... end module(bounds checks suppressed module-wide) -
✅ Array methods -
arr.append(item),arr.pop(),arr.remove(index)(immutable-style, return new arrays) -
✅ Conditional imports -
ifarch amd64 ... end ifarchfor architecture-specific module imports -
✅ Process management (v0.4.0) -
process_fork(),process_spawn(),process_exec(),process_setsid(),exit_now() -
✅ File descriptor operations (v0.4.0) -
fd_open(),fd_close(),fd_read(),fd_write(),fd_dup(),fd_dup2(),fd_pipe() -
✅ Signal enhancements (v0.4.0) -
signal_block(),signal_unblock(),signal_wait(), self-pipe pattern -
✅ Unix domain sockets (v0.4.0) -
unix_connect(),unix_listen(),unix_accept() -
✅ Event loop / poll(2) (v0.4.0) -
poll_add(),poll_wait(),poll_readable() -
✅ GC configuration (v0.4.0) -
--no-gccompiler flag -
✅ TOML parser enhancements (v0.4.0) - Raised limit to 1024, added
toml_parse_sized() -
✅ Filesystem stat/metadata (v0.5.0) -
fs.statmodule:is_file(),is_directory(),is_symlink(),exists(),file_size(),file_mode() -
✅ Filesystem permissions (v0.5.0) -
fs.permmodule:chmod(),chown(),set_executable(),set_readonly(),is_readable() -
✅ Filesystem links (v0.5.0) -
fs.linkmodule:symlink(),readlink(),hardlink() -
✅ Filesystem operations (v0.5.0) -
fs.opsmodule:copy_file(),copy_file_preserve(),remove_file(),remove_tree(),rename(),copy_tree() -
✅ TLS on system OpenSSL (0.7.0) -
net.tlsbuilds against the OS-provided OpenSSL (>= 3.0); the vendored LibreSSL tree is gone. Thenet.tlsAPI itself is unchanged. -
✅ Result/Option stdlib-wide migration (0.7.5) - fallible stdlib operations return
result.Result[T, core.error.Error](failure with a reason) oroption.Option[T](possible absence) instead of sentinels ("",-1,nil).core.option/core.resultare now the canonical genericOption[T]/Result[T, E]— the old monomorphizedcore.option_generic/core.result_genericfamily is removed. Newpanic(msg)noreturn builtin; strict, validating JSON/TOML parsers. See 035_ERROR_HANDLING.md. -
✅ Same-named generics across modules (0.7.6) - two modules can each define their own
Dup[T]; each is independently specialized under a module-qualified symbol. A module that can see both definitions still can't use the bare name (ambiguity error naming the candidates). See 055_GENERICS.md. -
✅ Full TOML value grammar (0.7.6) -
encoding.tomlvalidates scalar values (ints, floats, bools, RFC 3339 date-times), dotted keys (a.b.c = 1), inline tables (k = { x = 1 }), and inline arrays (k = [1, 2]). Malformed values are a positionedErr, not a silently accepted raw string. -
✅ All-paths-return analysis (0.7.6) - a value-returning
fnthat can fall off the end of its body without areturnon every path is now a compile error, not a runtime UB risk. -
✅ Licensing (0.7.7) - Apache-2.0 WITH LLVM-exception, the first licensed release; generated C carries a provenance header.
-
✅ Newly rejected, previously-accepted-and-unsound (0.7.7) -
sharedmethods mutating Active Object state or self-calling anexclusivemethod (see Method Modifiers);spawnon an Active Object method (see Spawn Expression); a required parameter after a default parameter; Active Object method-level generics. -
✅
setoperators fixed (0.7.7) -+/*/-onsetof[T]now lower to bitwise operations, not integer arithmetic. -
✅ Closure capture completeness (0.7.7) - now walks string interpolation and 16 sibling expression forms it previously missed.
-
✅
deferin Active Object methods (0.7.7) - runs at the implicit end of a voidexclusive/sharedmethod body. -
✅ Contextual arch keywords (0.7.7) -
amd64/arm64/riscv64are ordinary identifiers except right afterforinasm fn ... for <arch>. -
Removed:
--gc-signalsflag (0.7.7). -
✅ Raw address casts restored (0.7.8) - integer ↔
pointer/[T]ascasts,unsafe-only (see Raw Address Casts). -
✅ Baremetal restored (0.7.8) -
riscv64-baremetaland the other freestanding targets compile again. -
✅ Main-thread
awaitfixed (0.7.8) - genuinely blocks now instead of returning immediately on a false condition.