Use forward slash as the path separator on all platforms
All paths in the files package now use / as the separator on every platform, including Windows. Paths from Path.cwd() and Path.canonical() are /-separated, Path.sep() returns "/" unconditionally, and Path.clean() converts \ to / on Windows.
Path.is_sep() still accepts \ on Windows for parsing external input. Use Path.to_os() when a backslash path is needed for Windows shell commands.
Path.normcase() now only lowercases the path on case-insensitive platforms. It no longer replaces / with \ on Windows.
Before:
if path == "C:\\Users\\me\\project" then
// ...
endAfter:
if path == "C:/Users/me/project" then
// ...
endCode that passes paths to Windows shell commands should use Path.to_os():
let os_path = Path.to_os(path)Remove Path.from_slash and Path.to_slash
Path.from_slash() and Path.to_slash() have been removed. All paths use / as the separator on every platform, so these functions served no purpose. Remove any calls to them.
Fix iftype specialization on method-level type parameters
When a method had its own type parameter with an iftype specialization guard, the compiler selected the specialized body unconditionally — even when the concrete type argument did not satisfy the guard. Class-level type parameter guards were not affected.
primitive Foo
fun bar[S: ReadSeq[U8] box](seq: S): U16 ? =>
(seq(0)?.u16() << 8) or seq(1)?.u16()
fun bar[S: ReadSeq[U8] box](seq: S): U16
iftype S <: Array[U8] box
? =>
seq.read_u16(0)?Calling Foo.bar[WrappedSeq](seq)? would fail to compile with "can't find a compatible 'read_u16' method on type 'WrappedSeq ref'" because the compiler chose the Array[U8]-specialized body instead of the default.
Fix match without else in trait default method always taking the first arm
A match without an explicit else in a trait default method body always took the first capture arm, regardless of the runtime type of the matched value. The first capture arm always ran, which could crash the program or silently produce wrong results when the matched value was a different type.
trait T
fun get(): (R | None)
fun check() =>
match get()
| let r: R => r.n // always taken, even when get() returns None
endThe same match written in a class body, or with an explicit else, worked correctly.
Remove the classic pool allocator
The pool_classic, pool_retain, and pooltrack build options are removed. The arena allocator, the default since 0.69.0, is now the only full allocator. pool_memalign remains for builds that need AddressSanitizer or Valgrind.
Builds that used pool_classic,valgrind should switch to pool_memalign,valgrind:
Before:
cmake --preset debug -DPONY_USES=pool_classic,valgrindAfter:
cmake --preset debug -DPONY_USES=pool_memalign,valgrindBuilds that used pool_classic on its own should drop it — the default arena allocator is what runs.
Fix subtype checking for intersections that collectively satisfy an interface
The compiler rejected an intersection type (A & B) as a subtype of an interface when neither A nor B alone provides every method the interface requires, even when together they cover all of them.
interface Sized
fun rows(): USize
fun cols(): USize
class HasRows
fun rows(): USize => 10
class HasCols
fun cols(): USize => 20
primitive Checker
fun check(s: Sized) => None
fun example(x: (HasRows & HasCols)) =>
check(x) // previously rejected: neither HasRows nor HasCols alone is SizedThe call check(x) failed with a subtype error because the compiler checked each intersection member individually. It now checks the intersection's members collectively against the interface's method list, so (HasRows & HasCols) is accepted as a subtype of Sized.
Detect locals and parameters that shadow inherited methods
The compiler now reports an error when a local variable or parameter reuses the name of a method inherited from a trait or interface. Previously, only methods defined directly on the type were checked — inherited methods were silently shadowed, which could hide the inherited method and cause unexpected behavior.
Locals and parameters that shadow inherited methods are now a compile error
Code that previously compiled may now fail if a local variable or parameter has the same name as a method inherited from a trait or interface. Rename the variable or parameter to fix the error.
Before:
interface Tickable
fun label(): String => "default"
class MyTimer is Tickable
fun format(label: String): String =>
"Timer: " + labelAfter:
interface Tickable
fun label(): String => "default"
class MyTimer is Tickable
fun format(msg: String): String =>
"Timer: " + msg[0.77.0] - 2026-10-17
Fixed
- Fix iftype specialization on method-level type parameters (PR #6292)
- Fix match without else in trait default method always taking the first arm (PR #6297)
- Fix subtype checking for intersections that collectively satisfy an interface (PR #6304)
- Detect locals and parameters that shadow inherited methods (PR #6305)
Changed
- Use forward slash as the path separator on all platforms (PR #6276)
- Remove Path.from_slash and Path.to_slash (PR #6276)
- Use forward slash as the path separator on all platforms (PR #6276)
- Remove the classic pool allocator (PR #6301)
- Locals and parameters that shadow inherited methods are now a compile error (PR #6305)