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