1use itertools::Itertools;
9
10use crate::etypes::{
11 BoundedTyvar, Component, Ctx, Defined, ExternDecl, ExternDesc, Func, Handleable, Instance,
12 Name, Param, QualifiedInstance, RecordField, TypeBound, Value, VariantCase,
13};
14use crate::substitute::{Substitution, Unvoidable};
15use crate::subtype;
16
17#[derive(Clone, Copy)]
20struct ValueTypePosition {
21 is_param: bool,
24 dtp: DefinedTypePosition,
25}
26
27impl From<DefinedTypePosition> for ValueTypePosition {
28 fn from(p: DefinedTypePosition) -> ValueTypePosition {
29 ValueTypePosition {
30 is_param: false,
31 dtp: p,
32 }
33 }
34}
35impl ValueTypePosition {
36 fn not_anon_export(self) -> Self {
37 ValueTypePosition {
38 dtp: self.dtp.not_anon_export(),
39 ..self
40 }
41 }
42 fn anon_export(self) -> Self {
43 ValueTypePosition {
44 dtp: self.dtp.anon_export(),
45 ..self
46 }
47 }
48}
49
50#[derive(Clone, Copy)]
53pub struct DefinedTypePosition {
54 is_export: bool,
59 is_anon_export: bool,
65}
66impl DefinedTypePosition {
67 pub fn internal() -> Self {
68 DefinedTypePosition {
69 is_export: false,
70 is_anon_export: false,
71 }
72 }
73 pub fn export() -> Self {
74 DefinedTypePosition {
75 is_export: true,
76 is_anon_export: false,
77 }
78 }
79 fn not_anon_export(self) -> Self {
80 DefinedTypePosition {
81 is_anon_export: false,
82 ..self
83 }
84 }
85 fn anon_export(self) -> Self {
86 DefinedTypePosition {
87 is_anon_export: true,
88 ..self
89 }
90 }
91}
92
93#[derive(Debug)]
95#[allow(dead_code)]
96pub enum Error<'a> {
97 BareResourceExport,
100 BareComplexValTypeExport(Value<'a>),
103 DuplicateRecordField(Name<'a>),
105 DuplicateVariantField(Name<'a>),
107 DuplicateFlagsName(Name<'a>),
109 DuplicateEnumName(Name<'a>),
111 DuplicateExternName(&'a str, bool),
114 NotAResource(subtype::Error<'a>),
116 BorrowOutsideParam,
118}
119
120fn error_if_duplicates_by<T, U: Eq + std::hash::Hash, E>(
121 i: impl Iterator<Item = T>,
122 f: impl FnMut(&T) -> U,
123 e: impl Fn(T) -> E,
124) -> Result<(), E> {
125 let mut duplicates = i.duplicates_by(f);
126 if let Some(x) = duplicates.next() {
127 Err(e(x))
128 } else {
129 Ok(())
130 }
131}
132
133impl<'p, 'a> Ctx<'p, 'a> {
138 fn wf_record_fields<'r>(
139 &'r self,
140 p: ValueTypePosition,
141 rfs: &'r [RecordField<'a>],
142 ) -> Result<(), Error<'a>> {
143 rfs.iter()
144 .try_for_each(|rf: &'r RecordField<'a>| self.wf_value(p, &rf.ty))?;
145 error_if_duplicates_by(
146 rfs.iter(),
147 |&rf| rf.name.name,
148 |rf| Error::DuplicateRecordField(rf.name),
149 )?;
150 Ok(())
151 }
152 fn wf_variant_cases<'r>(
153 &'r self,
154 p: ValueTypePosition,
155 vcs: &'r [VariantCase<'a>],
156 ) -> Result<(), Error<'a>> {
157 vcs.iter()
158 .try_for_each(|vc: &'r VariantCase<'a>| self.wf_value_option(p, &vc.ty))?;
159 error_if_duplicates_by(
160 vcs.iter(),
161 |&vc| vc.name.name,
162 |vc| Error::DuplicateVariantField(vc.name),
163 )?;
164 Ok(())
165 }
166 fn wf_value<'r>(&'r self, p: ValueTypePosition, vt: &'r Value<'a>) -> Result<(), Error<'a>> {
167 let anon_err: Result<(), Error<'a>> = if p.dtp.is_export && p.dtp.is_anon_export {
168 Err(Error::BareComplexValTypeExport(vt.clone()))
169 } else {
170 Ok(())
171 };
172 let p_ = p.anon_export();
173 let resource_err = |h| {
174 self.wf_handleable(p.dtp, h).and(
175 self.subtype_handleable_is_resource(h)
176 .map_err(Error::NotAResource),
177 )
178 };
179 match vt {
180 Value::Bool => Ok(()),
181 Value::S(_) => Ok(()),
182 Value::U(_) => Ok(()),
183 Value::F(_) => Ok(()),
184 Value::Char => Ok(()),
185 Value::String => Ok(()),
186 Value::List(vt) => self.wf_value(p_, vt),
187 Value::FixList(vt, _) => self.wf_value(p_, vt),
188 Value::Record(rfs) => anon_err.and(self.wf_record_fields(p_, rfs)),
189 Value::Variant(vcs) => anon_err.and(self.wf_variant_cases(p_, vcs)),
190 Value::Flags(ns) => anon_err.and(error_if_duplicates_by(
191 ns.iter(),
192 |&n| n.name,
193 |n| Error::DuplicateFlagsName(*n),
194 )),
195 Value::Enum(ns) => anon_err.and(error_if_duplicates_by(
196 ns.iter(),
197 |&n| n.name,
198 |n| Error::DuplicateEnumName(*n),
199 )),
200 Value::Option(vt) => self.wf_value(p_, vt),
201 Value::Tuple(vs) => vs
202 .iter()
203 .try_for_each(|vt: &'r Value<'a>| self.wf_value(p_, vt)),
204 Value::Result(vt1, vt2) => self
205 .wf_value_option(p_, vt1)
206 .and(self.wf_value_option(p_, vt2)),
207 Value::Own(h) => resource_err(h),
208 Value::Borrow(h) => {
209 if p.is_param {
210 resource_err(h)
211 } else {
212 Err(Error::BorrowOutsideParam)
213 }
214 }
215 Value::Var(tv, vt) => tv
216 .as_ref()
217 .map(|tv| self.wf_type_bound(p.dtp, self.var_bound(tv)))
218 .unwrap_or(Ok(()))
219 .and(self.wf_value(p.not_anon_export(), vt)),
220 }
221 }
222 fn wf_value_option<'r>(
223 &'r self,
224 p: ValueTypePosition,
225 vt: &'r Option<Value<'a>>,
226 ) -> Result<(), Error<'a>> {
227 vt.as_ref().map_or(Ok(()), |ty| self.wf_value(p, ty))
228 }
229 fn wf_func<'r>(&'r self, p: DefinedTypePosition, ft: &'r Func<'a>) -> Result<(), Error<'a>> {
230 let p_ = p.anon_export();
231 let param_pos = ValueTypePosition {
232 is_param: true,
233 dtp: p_,
234 };
235 let result_pos = ValueTypePosition {
236 is_param: false,
237 dtp: p_,
238 };
239 ft.params
240 .iter()
241 .try_for_each(|fp: &'r Param<'a>| self.wf_value(param_pos, &fp.ty))?;
242 match &ft.result {
243 Some(vt) => self.wf_value(result_pos, vt),
244 None => Ok(()),
245 }
246 }
247 fn wf_type_bound<'r>(
248 &'r self,
249 p: DefinedTypePosition,
250 tb: &'r TypeBound<'a>,
251 ) -> Result<(), Error<'a>> {
252 match tb {
253 TypeBound::SubResource => Ok(()),
254 TypeBound::Eq(dt) => self.wf_defined(p.not_anon_export(), dt),
255 }
256 }
257 fn wf_bounded_tyvar<'r>(
258 &'r self,
259 p: DefinedTypePosition,
260 btv: &'r BoundedTyvar<'a>,
261 ) -> Result<(), Error<'a>> {
262 match &btv.bound {
263 TypeBound::SubResource => Ok(()),
264 TypeBound::Eq(dt) => self.wf_defined(p, dt),
265 }
266 }
267
268 fn wf_handleable<'r>(
269 &'r self,
270 p: DefinedTypePosition,
271 ht: &'r Handleable,
272 ) -> Result<(), Error<'a>> {
273 match ht {
274 Handleable::Var(tv) => self.wf_type_bound(p, self.var_bound(tv)),
275 Handleable::Resource(rid) => {
276 if p.is_export {
277 Err(Error::BareResourceExport)
278 } else {
279 assert!((rid.id as usize) < self.rtypes.len());
281 Ok(())
282 }
283 }
284 }
285 }
286 pub fn wf_defined<'r>(
287 &'r self,
288 p: DefinedTypePosition,
289 dt: &'r Defined<'a>,
290 ) -> Result<(), Error<'a>> {
291 match dt {
292 Defined::Handleable(ht) => self.wf_handleable(p, ht),
293 Defined::Value(vt) => self.wf_value(p.into(), vt),
294 Defined::Func(ft) => self.wf_func(p, ft),
295 Defined::Instance(it) => self.wf_qualified_instance(p, it),
296 Defined::Component(ct) => self.wf_component(p, ct),
297 }
298 }
299 fn wf_extern_desc<'r>(
300 &self,
301 p: DefinedTypePosition,
302 ed: &'r ExternDesc<'a>,
303 ) -> Result<(), Error<'a>> {
304 match ed {
305 ExternDesc::CoreModule(_) => Ok(()),
306 ExternDesc::Func(ft) => self.wf_func(p, ft),
307 ExternDesc::Type(dt) => self.wf_defined(p, dt),
308 ExternDesc::Instance(it) => self.wf_instance(p, it),
309 ExternDesc::Component(ct) => self.wf_component(p, ct),
310 }
311 }
312 fn wf_extern_decl<'r>(
313 &self,
314 p: DefinedTypePosition,
315 ed: &'r ExternDecl<'a>,
316 ) -> Result<(), Error<'a>> {
317 self.wf_extern_desc(p, &ed.desc)
318 }
319 fn wf_instance<'r>(
320 &self,
321 p: DefinedTypePosition,
322 it: &'r Instance<'a>,
323 ) -> Result<(), Error<'a>> {
324 error_if_duplicates_by(
325 it.exports.iter(),
326 |&ex| ex.kebab_name,
327 |ex| Error::DuplicateExternName(ex.kebab_name, false),
328 )?;
329 it.exports
330 .iter()
331 .try_for_each(|ed| self.wf_extern_decl(p, ed))
332 }
333 pub fn wf_qualified_instance<'r>(
334 &self,
335 p: DefinedTypePosition,
336 qit: &'r QualifiedInstance<'a>,
337 ) -> Result<(), Error<'a>> {
338 let mut ctx_ = self.clone();
339 let subst = ctx_.bound_to_evars(None, &qit.evars);
340 ctx_.evars
341 .iter()
342 .try_for_each(|(btv, _)| ctx_.wf_bounded_tyvar(p, btv))?;
343 let it = subst.instance(&qit.unqualified).not_void();
344 ctx_.wf_instance(p, &it)
345 }
346 pub fn wf_component<'r>(
347 &self,
348 p: DefinedTypePosition,
349 ct: &'r Component<'a>,
350 ) -> Result<(), Error<'a>> {
351 let mut ctx_ = self.clone();
352 let subst = ctx_.bound_to_uvars(None, &ct.uvars, false);
353 ctx_.uvars
354 .iter()
355 .try_for_each(|(btv, _)| ctx_.wf_bounded_tyvar(p, btv))?;
356 error_if_duplicates_by(
357 ct.imports.iter(),
358 |&im| im.kebab_name,
359 |im| Error::DuplicateExternName(im.kebab_name, true),
360 )?;
361 ct.imports
362 .iter()
363 .map(|ed| subst.extern_decl(ed).not_void())
364 .try_for_each(|ed| ctx_.wf_extern_decl(p, &ed))?;
365 let it = subst.qualified_instance(&ct.instance).not_void();
366 ctx_.wf_qualified_instance(p, &it)
367 }
368}