E08257: MutexLock(...) argument must be a fresh value — a literal or a constructor call (MutexLock(List() of Item) builds an empty payload; MutexLock(String(existing)) copies, but only for a type with a copy constructor - List and Dict have none); not a reference to existing state
What went wrong
The argument passed to a MutexLock(...) constructor is not a fresh value. The argument becomes the lock's protected payload — if the caller still holds a reference to it, the caller can mutate the payload outside the lock, bypassing enter() entirely.
To close this "inbound encapsulation" loophole, the argument must be one of:
- A literal: MutexLock(0), MutexLock(""), MutexLock(true)
- A constructor call: MutexLock(List() of Item), MutexLock(MyClass())
- A copy made by a copy constructor: MutexLock(String(stash)) - note that List and Dict have none ('List(existing)' is a List holding 'existing' as its one item, not a copy of it)
A bare reference to existing state (identifier, field access, non-constructor method call result) is rejected.
Rationale
EK9's compile-time data-race detection guarantees that mutations of the locked payload happen only through enter() callbacks. That guarantee is only meaningful if the caller cannot retain an alias to the payload. By requiring the argument to be fresh (literal, freshly constructed, or explicitly copied), the compiler can be sure no external reference to the payload exists at the moment the MutexLock is built.
How to fix it
- If you have an existing value you want to lock around, give the lock a copy made by the type's copy constructor (the [EXAMPLE] section below: 'MutexLock(String(stash))' rather than 'MutexLock(stash)')
- If you want to start with an empty payload, give it a freshly constructed empty value ('MutexLock(List() of Item)') and add to it through enter()
- Move the construction earlier so the value used as the argument is itself freshly constructed in scope.
Common causes
- Passing a parameter directly to MutexLock: MutexLock(arg)
- Passing a field of 'this': MutexLock(this.field)
- Passing a method result that returns a held reference
See also
Examples
Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.
Fails with E08257: MutexLock(...) argument must be a fresh value — a literal or a constructor call (MutexLock(List() of Item) builds an empty payload; MutexLock(String(existing)) copies, but only for a type with a copy constructor - List and Dict have none); not a reference to existing state
#!ek9 defines module error.e08257.bad defines class BadLeakedRef stash as String: String("retained-by-caller") //Bad: 'stash' is retained by this class as a field; the MutexLock's //payload is therefore aliased outside the lock. lockedName as MutexLock of String: MutexLock(stash) default operator ?
Fixed: Give the lock a fresh value - a literal or a constructor call (a copy)
#!ek9 defines module error.e08257.good defines class GoodFreshCopy stash as String: String("retained-by-caller") lockedName as MutexLock of String: MutexLock(String(stash)) default operator ?