rib/compiler/
type_with_unit.rs

1// Copyright 2024-2025 Golem Cloud
2//
3// Licensed under the Golem Source License v1.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://license.golem.cloud/LICENSE
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::{GetTypeHint, InferredType, InstanceType, TypeInternal};
16use bincode::{Decode, Encode};
17use golem_wasm_ast::analysis::analysed_type::{bool, field, record, str, tuple};
18use golem_wasm_ast::analysis::{
19    AnalysedResourceId, AnalysedResourceMode, AnalysedType, NameOptionTypePair, NameTypePair,
20    TypeBool, TypeChr, TypeEnum, TypeF32, TypeF64, TypeFlags, TypeHandle, TypeList, TypeOption,
21    TypeRecord, TypeResult, TypeS16, TypeS32, TypeS64, TypeS8, TypeStr, TypeTuple, TypeU16,
22    TypeU32, TypeU64, TypeU8, TypeVariant,
23};
24use serde::{Deserialize, Serialize};
25
26// An absence of analysed type is really `Unit`, however, we avoid
27// Option<AnalysedType> in favor of `AnalysedTypeWithUnit` for clarity.
28// and conversions such as what to print if its `unit` becomes more precise
29#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode)]
30pub enum AnalysedTypeWithUnit {
31    Unit,
32    Type(AnalysedType),
33}
34
35impl AnalysedTypeWithUnit {
36    pub fn unit() -> Self {
37        AnalysedTypeWithUnit::Unit
38    }
39
40    pub fn analysed_type(typ: AnalysedType) -> Self {
41        AnalysedTypeWithUnit::Type(typ)
42    }
43}
44
45impl TryFrom<AnalysedTypeWithUnit> for AnalysedType {
46    type Error = String;
47
48    fn try_from(value: AnalysedTypeWithUnit) -> Result<Self, Self::Error> {
49        match value {
50            AnalysedTypeWithUnit::Unit => Ok(tuple(vec![])),
51            AnalysedTypeWithUnit::Type(typ) => Ok(typ),
52        }
53    }
54}
55
56impl TryFrom<&InferredType> for AnalysedType {
57    type Error = String;
58
59    fn try_from(value: &InferredType) -> Result<Self, Self::Error> {
60        let with_unit = AnalysedTypeWithUnit::try_from(value)?;
61        AnalysedType::try_from(with_unit)
62    }
63}
64
65impl TryFrom<&InferredType> for AnalysedTypeWithUnit {
66    type Error = String;
67
68    fn try_from(inferred_type: &InferredType) -> Result<Self, Self::Error> {
69        match inferred_type.internal_type() {
70            TypeInternal::Instance { instance_type } => match instance_type.as_ref() {
71                InstanceType::Resource {
72                    analysed_resource_id,
73                    analysed_resource_mode,
74                    ..
75                } => {
76                    let analysed_resource_id = AnalysedResourceId(*analysed_resource_id);
77
78                    let analysed_resource_mode = if *analysed_resource_mode == 0 {
79                        AnalysedResourceMode::Owned
80                    } else {
81                        AnalysedResourceMode::Borrowed
82                    };
83
84                    Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::Handle(
85                        TypeHandle {
86                            resource_id: analysed_resource_id,
87                            mode: analysed_resource_mode,
88                        },
89                    )))
90                }
91
92                _ => Ok(AnalysedTypeWithUnit::analysed_type(str())),
93            },
94            TypeInternal::Range { from, to } => {
95                let from: AnalysedType = AnalysedType::try_from(from)?;
96                let to: Option<AnalysedType> =
97                    to.as_ref().map(AnalysedType::try_from).transpose()?;
98                let analysed_type = match (from, to) {
99                    (from_type, Some(to_type)) => record(vec![
100                        field("from", from_type),
101                        field("to", to_type),
102                        field("inclusive", bool()),
103                    ]),
104
105                    (from_type, None) => {
106                        record(vec![field("from", from_type), field("inclusive", bool())])
107                    }
108                };
109                Ok(AnalysedTypeWithUnit::analysed_type(analysed_type))
110            }
111            TypeInternal::Bool => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::Bool(
112                TypeBool,
113            ))),
114            TypeInternal::S8 => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::S8(
115                TypeS8,
116            ))),
117            TypeInternal::U8 => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::U8(
118                TypeU8,
119            ))),
120            TypeInternal::S16 => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::S16(
121                TypeS16,
122            ))),
123            TypeInternal::U16 => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::U16(
124                TypeU16,
125            ))),
126            TypeInternal::S32 => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::S32(
127                TypeS32,
128            ))),
129            TypeInternal::U32 => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::U32(
130                TypeU32,
131            ))),
132            TypeInternal::S64 => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::S64(
133                TypeS64,
134            ))),
135            TypeInternal::U64 => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::U64(
136                TypeU64,
137            ))),
138            TypeInternal::F32 => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::F32(
139                TypeF32,
140            ))),
141            TypeInternal::F64 => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::F64(
142                TypeF64,
143            ))),
144            TypeInternal::Chr => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::Chr(
145                TypeChr,
146            ))),
147            TypeInternal::Str => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::Str(
148                TypeStr,
149            ))),
150            TypeInternal::List(inferred_type) => Ok(AnalysedTypeWithUnit::analysed_type(
151                AnalysedType::List(TypeList {
152                    inner: Box::new(inferred_type.try_into()?),
153                }),
154            )),
155            TypeInternal::Tuple(tuple) => Ok(AnalysedTypeWithUnit::analysed_type(
156                AnalysedType::Tuple(TypeTuple {
157                    items: tuple
158                        .iter()
159                        .map(|t| t.try_into())
160                        .collect::<Result<Vec<AnalysedType>, String>>()?,
161                }),
162            )),
163            TypeInternal::Record(record) => Ok(AnalysedTypeWithUnit::analysed_type(
164                AnalysedType::Record(TypeRecord {
165                    fields: record
166                        .iter()
167                        .map(|(name, typ)| {
168                            Ok(NameTypePair {
169                                name: name.to_string(),
170                                typ: typ.try_into()?,
171                            })
172                        })
173                        .collect::<Result<Vec<NameTypePair>, String>>()?,
174                }),
175            )),
176            TypeInternal::Flags(flags) => Ok(AnalysedTypeWithUnit::analysed_type(
177                AnalysedType::Flags(TypeFlags {
178                    names: flags.clone(),
179                }),
180            )),
181            TypeInternal::Enum(enums) => Ok(AnalysedTypeWithUnit::analysed_type(
182                AnalysedType::Enum(TypeEnum {
183                    cases: enums.clone(),
184                }),
185            )),
186            TypeInternal::Option(option) => Ok(AnalysedTypeWithUnit::analysed_type(
187                AnalysedType::Option(TypeOption {
188                    inner: Box::new(option.try_into()?),
189                }),
190            )),
191            TypeInternal::Result { ok, error } => Ok(AnalysedTypeWithUnit::analysed_type(
192                // In the case of result, there are instances users give just 1 value with zero function calls, we need to be flexible here
193                AnalysedType::Result(TypeResult {
194                    ok: ok.as_ref().and_then(|t| t.try_into().ok().map(Box::new)),
195                    err: error.as_ref().and_then(|t| t.try_into().ok().map(Box::new)),
196                }),
197            )),
198            TypeInternal::Variant(variant) => Ok(AnalysedTypeWithUnit::analysed_type(
199                AnalysedType::Variant(TypeVariant {
200                    cases: variant
201                        .iter()
202                        .map(|(name, typ)| {
203                            Ok(NameOptionTypePair {
204                                name: name.clone(),
205                                typ: typ.as_ref().map(|t| t.try_into()).transpose()?,
206                            })
207                        })
208                        .collect::<Result<Vec<NameOptionTypePair>, String>>()?,
209                }),
210            )),
211            TypeInternal::Resource {
212                resource_id,
213                resource_mode,
214            } => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::Handle(
215                TypeHandle {
216                    resource_id: AnalysedResourceId(*resource_id),
217                    mode: if resource_mode == &0 {
218                        AnalysedResourceMode::Owned
219                    } else {
220                        AnalysedResourceMode::Borrowed
221                    },
222                },
223            ))),
224
225            TypeInternal::AllOf(types) => Err(format!(
226                "ambiguous types {}",
227                types
228                    .iter()
229                    .map(|x| x.get_type_hint().to_string())
230                    .collect::<Vec<_>>()
231                    .join(", ")
232            )),
233            TypeInternal::Unknown => Err("failed to infer type".to_string()),
234            // We don't expect to have a sequence type in the inferred type.as
235            // This implies Rib will not support multiple types from worker-function results
236            TypeInternal::Sequence(vec) => {
237                if vec.is_empty() {
238                    Ok(AnalysedTypeWithUnit::unit())
239                } else if vec.len() == 1 {
240                    let first = &vec[0];
241                    Ok(first.try_into()?)
242                } else {
243                    Err("function with multiple return types not supported".to_string())
244                }
245            }
246        }
247    }
248}