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                            name: None,
89                            owner: None,
90                        },
91                    )))
92                }
93
94                _ => Ok(AnalysedTypeWithUnit::analysed_type(str())),
95            },
96            TypeInternal::Range { from, to } => {
97                let from: AnalysedType = AnalysedType::try_from(from)?;
98                let to: Option<AnalysedType> =
99                    to.as_ref().map(AnalysedType::try_from).transpose()?;
100                let analysed_type = match (from, to) {
101                    (from_type, Some(to_type)) => record(vec![
102                        field("from", from_type),
103                        field("to", to_type),
104                        field("inclusive", bool()),
105                    ]),
106
107                    (from_type, None) => {
108                        record(vec![field("from", from_type), field("inclusive", bool())])
109                    }
110                };
111                Ok(AnalysedTypeWithUnit::analysed_type(analysed_type))
112            }
113            TypeInternal::Bool => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::Bool(
114                TypeBool,
115            ))),
116            TypeInternal::S8 => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::S8(
117                TypeS8,
118            ))),
119            TypeInternal::U8 => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::U8(
120                TypeU8,
121            ))),
122            TypeInternal::S16 => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::S16(
123                TypeS16,
124            ))),
125            TypeInternal::U16 => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::U16(
126                TypeU16,
127            ))),
128            TypeInternal::S32 => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::S32(
129                TypeS32,
130            ))),
131            TypeInternal::U32 => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::U32(
132                TypeU32,
133            ))),
134            TypeInternal::S64 => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::S64(
135                TypeS64,
136            ))),
137            TypeInternal::U64 => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::U64(
138                TypeU64,
139            ))),
140            TypeInternal::F32 => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::F32(
141                TypeF32,
142            ))),
143            TypeInternal::F64 => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::F64(
144                TypeF64,
145            ))),
146            TypeInternal::Chr => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::Chr(
147                TypeChr,
148            ))),
149            TypeInternal::Str => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::Str(
150                TypeStr,
151            ))),
152            TypeInternal::List(inferred_type) => Ok(AnalysedTypeWithUnit::analysed_type(
153                AnalysedType::List(TypeList {
154                    inner: Box::new(inferred_type.try_into()?),
155                    name: None,
156                    owner: None,
157                }),
158            )),
159            TypeInternal::Tuple(tuple) => Ok(AnalysedTypeWithUnit::analysed_type(
160                AnalysedType::Tuple(TypeTuple {
161                    items: tuple
162                        .iter()
163                        .map(|t| t.try_into())
164                        .collect::<Result<Vec<AnalysedType>, String>>()?,
165                    name: None,
166                    owner: None,
167                }),
168            )),
169            TypeInternal::Record(record) => Ok(AnalysedTypeWithUnit::analysed_type(
170                AnalysedType::Record(TypeRecord {
171                    fields: record
172                        .iter()
173                        .map(|(name, typ)| {
174                            Ok(NameTypePair {
175                                name: name.to_string(),
176                                typ: typ.try_into()?,
177                            })
178                        })
179                        .collect::<Result<Vec<NameTypePair>, String>>()?,
180                    name: None,
181                    owner: None,
182                }),
183            )),
184            TypeInternal::Flags(flags) => Ok(AnalysedTypeWithUnit::analysed_type(
185                AnalysedType::Flags(TypeFlags {
186                    names: flags.clone(),
187                    name: None,
188                    owner: None,
189                }),
190            )),
191            TypeInternal::Enum(enums) => Ok(AnalysedTypeWithUnit::analysed_type(
192                AnalysedType::Enum(TypeEnum {
193                    cases: enums.clone(),
194                    name: None,
195                    owner: None,
196                }),
197            )),
198            TypeInternal::Option(option) => Ok(AnalysedTypeWithUnit::analysed_type(
199                AnalysedType::Option(TypeOption {
200                    inner: Box::new(option.try_into()?),
201                    name: None,
202                    owner: None,
203                }),
204            )),
205            TypeInternal::Result { ok, error } => Ok(AnalysedTypeWithUnit::analysed_type(
206                // In the case of result, there are instances users give just 1 value with zero function calls, we need to be flexible here
207                AnalysedType::Result(TypeResult {
208                    ok: ok.as_ref().and_then(|t| t.try_into().ok().map(Box::new)),
209                    err: error.as_ref().and_then(|t| t.try_into().ok().map(Box::new)),
210                    name: None,
211                    owner: None,
212                }),
213            )),
214            TypeInternal::Variant(variant) => Ok(AnalysedTypeWithUnit::analysed_type(
215                AnalysedType::Variant(TypeVariant {
216                    cases: variant
217                        .iter()
218                        .map(|(name, typ)| {
219                            Ok(NameOptionTypePair {
220                                name: name.clone(),
221                                typ: typ.as_ref().map(|t| t.try_into()).transpose()?,
222                            })
223                        })
224                        .collect::<Result<Vec<NameOptionTypePair>, String>>()?,
225                    name: None,
226                    owner: None,
227                }),
228            )),
229            TypeInternal::Resource {
230                resource_id,
231                resource_mode,
232                name: _,
233                owner: _,
234            } => Ok(AnalysedTypeWithUnit::analysed_type(AnalysedType::Handle(
235                TypeHandle {
236                    resource_id: AnalysedResourceId(*resource_id),
237                    mode: if resource_mode == &0 {
238                        AnalysedResourceMode::Owned
239                    } else {
240                        AnalysedResourceMode::Borrowed
241                    },
242                    name: None,
243                    owner: None,
244                },
245            ))),
246
247            TypeInternal::AllOf(types) => Err(format!(
248                "ambiguous types {}",
249                types
250                    .iter()
251                    .map(|x| x.get_type_hint().to_string())
252                    .collect::<Vec<_>>()
253                    .join(", ")
254            )),
255            TypeInternal::Unknown => Err("failed to infer type".to_string()),
256            // We don't expect to have a sequence type in the inferred type.as
257            // This implies Rib will not support multiple types from worker-function results
258            TypeInternal::Sequence(vec) => {
259                if vec.is_empty() {
260                    Ok(AnalysedTypeWithUnit::unit())
261                } else if vec.len() == 1 {
262                    let first = &vec[0];
263                    Ok(first.try_into()?)
264                } else {
265                    Err("function with multiple return types not supported".to_string())
266                }
267            }
268        }
269    }
270}