[BUG] Unable to implement one trait with default functions from another trait
Nobody has claimed this yet.
- Dominant language
- Mojo
- Stars
- 29.8k
- Forks
- 3.2k
- PR merge metrics
- No merged PRs in 30d
Description
Bug description
Actual behavior
Compiler error: error: attempt to resolve a recursive reference to declaration 'MojoDefaultGPA.allocate'
Expected behavior
Program prints 523776.0
Steps to reproduce
Sorry, more minimal reproducers didn't seem to work.
from std.memory import alloc, UnsafePointer
from std.sys.info import CompilationTarget, size_of
from std.ffi import external_call
@fieldwise_init
struct MemoryAllocationError(Defaultable, ImplicitlyCopyable, Movable):
"""A generic "something went wrong" error type for allocators. It
**SHOULD** generally be treated as equivalent to POSIX's ENOMEM.
Allocators **SHOULD** provide more specific error types if they can, but
**SHOULD** provide an implicit conversion to MemoryAllocationError if
they do to aid in ecosystem compatibility.
"""
pass
trait GeneralPurposeAllocator:
"""The base trait for allocator-like things.
An allocator is used to manage memory, typically from the heap but not always, and provide users with a structured view of it. Users should use implementations to obtain pointers for use in data structures or higher-level abstractions over memory, and use deallocators to free memory when it is no longer needed. Users should carefully read the documentation of the allocator and the implemented traits, as this interfaces is **UNSAFE** and has requirements which the Mojo compiler cannot enforce that users must uphold. It is also encouraged to investigate `std.collections` for higher level data structures which may be more appropriate for users who do not need the full flexibility of this interface.
Implementations **MUST** document what deallocators may be used to free memory return from their `allocate` method, provide other information about how memory is freed (ex: garbage collection), or state that all memory must be leaked. Users **MUST** ensure that they only use compatible allocators and deallocators together, and failure to do so is considered undefined behavior for the purposes of this interface. In the absence of documentation about compatible allocators, users **MUST** assume that all memory must be leaked. Types which implement both `GeneralPurposeAllocator` and `GeneralPurposeDeallocator` are not an exception to this documentation requirement.
"""
comptime AllocationErrorType: AnyType
comptime AllocateSingleRequiresMut: Bool
fn allocate[
mut: Bool,
//,
origin: Origin[mut = (True if Self.AllocateSingleRequiresMut else mut)],
ElementType: AnyType,
count: Int,
](ref[origin] self) raises (Self.AllocationErrorType) -> UnsafePointer[
ElementType, MutExternalOrigin
]:
"""Used to allocate memory for `count` elements of type `ElementType`.
WARNING: The alignment of the returned pointer is not specified by this trait. Implementations **SHOULD** document the alignment guarantees they provide, and users **MUST** ensure that they only use implementations whose alignment guarantees are sufficient for their use case. In the absence of documentation about alignment guarantees, users **MUST** assume that the returned pointer has an alignment equal to the platform's minimum access alignment (ex: 1 byte on x86_64). Software emulation for unaligned accesses which is transparent to programs (such as the "trap and emulate" strategy) is considered to decrease the minimum access alignment.
Implementations **SHOULD** define `allocate` such that it produces a pointer to a unique location in memory which is not presently "allocated". The precise meaning of "allocated" is implementation defined, but generally refers to memory that has been produced by an allocator not passed to a deallocator. Implementations **MAY** use `comptime assert` or other mechanisms to restrict `count` to supported values. Implementations **SHOULD** ensure that `count` is non-negative and non-zero, using mechanisms such as `comptime assert` to provide feedback to the user if the implementation does not have a well defined behavior for such cases.
"""
...
trait RuntimeSizedGPA(GeneralPurposeAllocator):
fn allocate_runtime_count[
mut: Bool,
//,
origin: Origin[mut = (True if Self.AllocateSingleRequiresMut else mut)],
ElementType: AnyType,
](ref[origin] self, count: Int) raises (
Self.AllocationErrorType
) -> UnsafePointer[ElementType, MutExternalOrigin]:
"""Used to allocate memory for `count` elements of type `ElementType`.
WARNING: The alignment of the returned pointer is not specified by this trait. Implementations **SHOULD** document the alignment guarantees they provide, and users **MUST** ensure that they only use implementations whose alignment guarantees are sufficient for their use case. In the absence of documentation about alignment guarantees, users **MUST** assume that the returned pointer has an alignment equal to the platform's minimum access alignment (ex: 1 byte on x86_64). Software emulation for unaligned accesses which is transparent to programs (such as the "trap and emulate" strategy) is considered to decrease the minimum access alignment.
Implementations **SHOULD** define `allocate` such that it produces a pointer to a unique location in memory which is not presently "allocated". The precise meaning of "allocated" is implementation defined, but generally refers to memory that has been produced by an allocator not passed to a deallocator. Implementations **SHOULD** ensure that `count` is non-negative and non-zero, avoiding undefined behavior if the implementation does not have a well defined behavior for such cases.
"""
...
fn allocate[
mut: Bool,
//,
origin: Origin[mut = (True if Self.AllocateSingleRequiresMut else mut)],
ElementType: AnyType,
count: Int,
](ref[origin] self) raises (Self.AllocationErrorType) -> UnsafePointer[
ElementType, MutExternalOrigin
]:
return self.allocate_runtime_count[origin, ElementType](count)
@fieldwise_init
struct MojoDefaultGPA(
Defaultable,
RuntimeSizedGPA,
ImplicitlyCopyable,
Movable,
):
comptime AllocationErrorType: AnyType = MemoryAllocationError
comptime AllocateSingleRequiresMut: Bool = False
fn allocate_runtime_count[
mut: Bool,
//,
origin: Origin[mut = (True if Self.AllocateSingleRequiresMut else mut)],
ElementType: AnyType,
](ref[origin] self, count: Int) raises (
Self.AllocationErrorType
) -> UnsafePointer[ElementType, MutExternalOrigin]:
return alloc[ElementType](count)
def sum(buffer: UnsafePointer[Float32, MutExternalOrigin]) -> Float32:
var result: Float32 = 0.0
for i in range(1024):
result += buffer[i]
return result
def main():
var alloc = MojoDefaultGPA()
var ptr = alloc.allocate[Float32](1024)
for i in range(1024):
ptr[i] = Float32(i)
print(sum(ptr))
Interestingly, the following DOES NOT reproduce the bug, meaning it's likely going to involve something else I did:
trait T1:
def foo(self) -> Int32:
...
trait T2(T1):
def bar(self) -> Int32:
...
def foo(self) -> Int32:
return self.bar()
trait T3(T2):
def baz(self) -> Int32:
...
def bar(self) -> Int32:
return self.baz()
@fieldwise_init
struct Foo(T3):
def baz(self) -> Int32:
return 42
def main():
var foo = Foo()
print(foo.foo())
System information
System
------------
Pixi version: 0.58.0
Platform: linux-64
Virtual packages: __unix=0=0
: __linux=6.18.16=0
: __glibc=2.42=0
: __archspec=1=zen4
Cache dir: /home/ohilyard/.cache/rattler/cache
Auth storage: /home/ohilyard/.rattler/credentials.json
Config locations: No config files found
Global
------------
Bin dir: /home/ohilyard/.pixi/bin
Environment dir: /home/ohilyard/.pixi/envs
Manifest dir: /home/ohilyard/.pixi/manifests/pixi-global.toml
Workspace
------------
Name: Mojo
Manifest file: /home/ohilyard/Documents/projects/mojo/stdlib/mojo/mojo/pixi.toml
Last updated: 29-03-2026 21:17:49
Environments
------------
Environment: default
Features: default
Channels: conda-forge, https://conda.modular.com/max-nightly
Dependency count: 1
Dependencies: mojo
Target platforms: linux-64, linux-aarch64, osx-arm64
Prefix location: /home/ohilyard/Documents/projects/mojo/stdlib/mojo/mojo/.pixi/envs/default
Tasks: format, tests
Mojo 0.26.2.0.dev2026021805 (5733ca77)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by running the supplied Mojo reproducer and confirm the recursive-reference compiler error, then compare it with the smaller T1/T2/T3 example that succeeds. Trace the trait default-function resolution involved in MojoDefaultGPA and verify the fix by producing the expected output, 523776.0, without regressing the working example.
Written by the indexing model from the issue text.
Assessment
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100