#Unsendable

#Unsendable() marks a struct, variant or union whose values must stay on the thread that made them, such as a handle whose count is not atomic. A type declared with it, and any type that holds one in a field, element or payload, does not satisfy the compiler-known bound Core::Sendable, which is what a thread's context and result are written with.

unsendable-attribute = "#Unsendable" "(" ")"

A type that stays on its thread

import Core::Sendable;

#Unsendable()
struct Session {
    id: uint64;
}

struct Request {
    session: Session;
}

func Run<T: Sendable>(value: T) {}

func Main() -> int {
    Run(Request { session: Session { id: 1 } });
    return 0;
}

Request is not declared #Unsendable(), but it holds a Session, so it is refused too:

error: type argument 'Request' does not satisfy interface bound 'Sendable' on type parameter 'T'
  note: 'Request' holds 'Session', which is declared '#Unsendable()'
  note: type parameter 'T' of function 'Run' is bound by 'Sendable'
  help: keep the value on the thread that made it, or share it through a type made for threads, such as 'Arc' in place of 'Rc'

Sendable is the one interface the compiler decides for itself: no extension implements it, and every type satisfies it except a reference and a type that is or holds one declared #Unsendable(). A raw pointer satisfies it, since what a pointer shares is the program's to vouch for, and a type parameter satisfies it only when bound by it. The standard Rc is declared #Unsendable(), so Thread::Spawn, which bounds its context and result by Sendable, refuses one:

error: type argument 'Rc<uint64>' does not satisfy interface bound 'Sendable' on type parameter 'T'
  note: 'Rc<uint64>' is declared '#Unsendable()', so it stays on the thread that made it

Rules

MistakeError
arguments to the attribute'#Unsendable' does not accept arguments
the attribute twiceduplicate '#Unsendable' attribute
anything but a type declaration'#Unsendable' can only be applied to a struct, variant or union

See also