@nonmaterializable

Declares that a type should exist only in the parameter domain.

You can add the @nonmaterializable decorator on a struct to declare that the type can exist only in the parameter domain (it can be used for metaprogramming only, and not as a runtime type). And, if an instance of this type does transition into the runtime domain, this decorator declares what type it becomes there.

To use it, declare your type with @nonmaterializable(TargetType), where TargetType is the type that the object should convert to if it becomes a runtime value (you must declare the TargetType). For example, if a struct is marked as @nonmaterializable(Foo), then anywhere that it goes from a parameter value to a runtime value, it automatically converts into the Foo type.

For example, the following NmStruct type can be used in the parameter domain, but the convertedToHasBool instance of it is converted to HasBool when it’s materialized as a runtime value:

@value
@register_passable("trivial")
struct HasBool:
    var x: Bool

    fn __init__(x: Bool) -> Self:
        return Self {x: x}

    @always_inline("nodebug")
    fn __init__(nms: NmStruct) -> Self:
        return Self {x: True if (nms.x == 77) else False}

@value
@nonmaterializable(HasBool)
@register_passable("trivial")
struct NmStruct:
    var x: Int

    @always_inline("nodebug")
    fn __add__(self: Self, rhs: Self) -> Self:
        return NmStruct(self.x + rhs.x)

alias stillNmStruct = NmStruct(1) + NmStruct(2)
# When materializing to a run-time variable, it is automatically converted,
# even without a type annotation.
let convertedToHasBool = stillNmStruct

Note: A non-materializable struct must have all of its methods annotated as @always_inline, and it must be computable in the parameter domain.