///|
fn type_children(kind : RTypeKind) -> Array[RType] {
match kind {
Record(fields) => fields.map(f => f.field_type)
Variant(cases) => cases.filter_map(c => c.case_type)
List(t) | FixedLengthList(t, _) | Option(t) | Alias(t) => [t]
Future(t) | Stream(t) =>
match t {
Some(t) => [t]
None => []
}
Map(k, v) => [k, v]
Result(ok~, err~) => [ok, err].filter_map(t => t)
Tuple(ts) => ts
_ => []
}
}
///|
fn validate_type_cycle(
types : Array[RTypeDef],
states : Array[Int32],
id : Int32,
) -> Unit raise ParseError {
if states[id] == 2 {
return
}
if states[id] == 1 {
raise ParseError::Message("recursive WIT type", 0)
}
states[id] = 1
for ty in type_children(types[id].kind) {
if ty is Id(child) {
validate_type_cycle(types, states, child)
}
}
states[id] = 2
}
///|
fn map_key_allowed(types : Array[RTypeDef], ty : RType) -> Bool {
match ty {
Bool | U8 | U16 | U32 | U64 | S8 | S16 | S32 | S64 | Char | String_ => true
Id(id) =>
match types[id].kind {
Alias(t) => map_key_allowed(types, t)
_ => false
}
_ => false
}
}
///|
fn map_value_allowed(
types : Array[RTypeDef],
ty : RType,
seen : Array[Bool],
) -> Bool {
match ty {
ErrorContext => false
Id(id) => {
if seen[id] {
return true
}
seen[id] = true
match types[id].kind {
Resource | Handle(_) | Future(_) | Stream(_) => false
kind => type_children(kind).all(t => map_value_allowed(types, t, seen))
}
}
_ => true
}
}
///|
fn validate_resolved_types(types : Array[RTypeDef]) -> Unit raise ParseError {
let states = Array::make(types.length(), 0)
for id in 0.. RType {
match ty {
Id(id) =>
match types[id].kind {
Alias(t) => canonical_type(types, t)
_ => ty
}
_ => ty
}
}
///|
fn validate_accessor_pairs(
functions : Map[String, RFunction],
types : Array[RTypeDef],
) -> Unit raise ParseError {
for _, setter in functions {
match setter.kind {
Setter | MethodSetter(_) | StaticSetter(_) => {
if setter.result is Some(result) {
match resolved_result_payloads(types, result) {
Some((None, _)) => ()
_ =>
raise ParseError::Message(
"setter result must have no ok payload", 0,
)
}
}
let getter_name = setter.name.replace(old="[set]", new="[get]")
let getter = match functions.get(getter_name) {
Some(getter) => getter
None =>
raise ParseError::Message(
"setter requires a matching getter: " + setter.name,
0,
)
}
let value = setter.params[setter.params.length() - 1].1
match getter.result {
Some(result) => {
let property_type = match resolved_result_payloads(types, result) {
Some((Some(ok), _)) => ok
Some((None, _)) =>
raise ParseError::Message(
"getter result requires an ok payload", 0,
)
None => result
}
if canonical_type(types, property_type) !=
canonical_type(types, value) {
raise ParseError::Message(
"getter/setter type mismatch: " + setter.name,
0,
)
}
}
_ =>
raise ParseError::Message(
"getter/setter type mismatch: " + setter.name,
0,
)
}
}
_ => ()
}
}
}
///|
fn resolved_result_payloads(
types : Array[RTypeDef],
ty : RType,
) -> (RType?, RType?)? {
match canonical_type(types, ty) {
Id(id) =>
match types[id].kind {
Result(ok~, err~) => Some((ok, err))
_ => None
}
_ => None
}
}
///|
fn validate_constructor_result(
types : Array[RTypeDef],
resource : Int32,
ty : RType,
) -> Unit raise ParseError {
match resolved_result_payloads(types, ty) {
Some((Some(ok), _)) =>
match canonical_type(types, ok) {
Id(id) if id == resource => return
Id(id) =>
match types[id].kind {
Handle(Own(id)) if id == resource => return
_ => ()
}
_ => ()
}
_ => ()
}
raise ParseError::Message(
"constructor result must have its own resource as the ok payload", 0,
)
}