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::FixList(vt, _) => self.wf_value(p_, vt),
217 Value::Record(rfs) => anon_err.and(self.wf_record_fields(p_, rfs)),
218 Value::Variant(vcs) => anon_err.and(self.wf_variant_cases(p_, vcs)),
219 Value::Flags(ns) => anon_err.and(error_if_duplicates_by(
220 ns.iter(),
221 |&n| n.name,
222 |n| Error::DuplicateFlagsName(*n),
223 )),
224 Value::Enum(ns) => anon_err.and(error_if_duplicates_by(
225 ns.iter(),
226 |&n| n.name,
227 |n| Error::DuplicateEnumName(*n),
228 )),
229 Value::Option(vt) => self.wf_value(p_, vt),
230 Value::Tuple(vs) => vs
231 .iter()
232 .try_for_each(|vt: &'r Value<'a>| self.wf_value(p_, vt)),
233 Value::Result(vt1, vt2) => self
234 .wf_value_option(p_, vt1)
235 .and(self.wf_value_option(p_, vt2)),
236 Value::Own(h) => resource_err(h),
237 Value::Borrow(h) => {
238 if p.is_param {
239 resource_err(h)
240 } else {
241 Err(Error::BorrowOutsideParam)
242 }
243 }
244 Value::Var(tv, vt) => tv
245 .as_ref()
246 .map(|tv| self.wf_type_bound(p.dtp, self.var_bound(tv)))
247 .unwrap_or(Ok(()))
248 .and(self.wf_value(p.not_anon_export(), vt)),
249 }
250 }
251 fn wf_value_option<'r>(
252 &'r self,
253 p: ValueTypePosition,
254 vt: &'r Option<Value<'a>>,
255 ) -> Result<(), Error<'a>> {
256 vt.as_ref().map_or(Ok(()), |ty| self.wf_value(p, ty))
257 }
258 fn wf_func<'r>(&'r self, p: DefinedTypePosition, ft: &'r Func<'a>) -> Result<(), Error<'a>> {
259 let p_ = p.anon_export();
260 let param_pos = ValueTypePosition {
261 is_param: true,
262 dtp: p_,
263 };
264 let result_pos = ValueTypePosition {
265 is_param: false,
266 dtp: p_,
267 };
268 ft.params
269 .iter()
270 .try_for_each(|fp: &'r Param<'a>| self.wf_value(param_pos, &fp.ty))?;
271 match &ft.result {
272 Some(vt) => self.wf_value(result_pos, vt),
273 None => Ok(()),
274 }
275 }
276 fn wf_type_bound<'r>(
277 &'r self,
278 p: DefinedTypePosition,
279 tb: &'r TypeBound<'a>,
280 ) -> Result<(), Error<'a>> {
281 match tb {
282 TypeBound::SubResource => Ok(()),
283 TypeBound::Eq(dt) => self.wf_defined(p.not_anon_export(), dt),
284 }
285 }
286 fn wf_bounded_tyvar<'r>(
287 &'r self,
288 p: DefinedTypePosition,
289 btv: &'r BoundedTyvar<'a>,
290 ) -> Result<(), Error<'a>> {
291 match &btv.bound {
292 TypeBound::SubResource => Ok(()),
293 TypeBound::Eq(dt) => self.wf_defined(p, dt),
294 }
295 }
296
297 fn wf_handleable<'r>(
298 &'r self,
299 p: DefinedTypePosition,
300 ht: &'r Handleable,
301 ) -> Result<(), Error<'a>> {
302 match ht {
303 Handleable::Var(tv) => self.wf_type_bound(p, self.var_bound(tv)),
304 Handleable::Resource(rid) => {
305 if p.is_export {
306 Err(Error::BareResourceExport)
307 } else {
308 assert!((rid.id as usize) < self.rtypes.len());
310 Ok(())
311 }
312 }
313 }
314 }
315 pub fn wf_defined<'r>(
316 &'r self,
317 p: DefinedTypePosition,
318 dt: &'r Defined<'a>,
319 ) -> Result<(), Error<'a>> {
320 match dt {
321 Defined::Handleable(ht) => self.wf_handleable(p, ht),
322 Defined::Value(vt) => self.wf_value(p.into(), vt),
323 Defined::Func(ft) => self.wf_func(p, ft),
324 Defined::Instance(it) => self.wf_qualified_instance(p, it),
325 Defined::Component(ct) => self.wf_component(p, ct),
326 }
327 }
328 fn wf_extern_desc<'r>(
329 &self,
330 p: DefinedTypePosition,
331 ed: &'r ExternDesc<'a>,
332 ) -> Result<(), Error<'a>> {
333 match ed {
334 ExternDesc::CoreModule(_) => Ok(()),
335 ExternDesc::Func(ft) => self.wf_func(p, ft),
336 ExternDesc::Type(dt) => self.wf_defined(p, dt),
337 ExternDesc::Instance(it) => self.wf_instance(p, it),
338 ExternDesc::Component(ct) => self.wf_component(p, ct),
339 }
340 }
341 fn wf_extern_decl<'r>(
342 &self,
343 p: DefinedTypePosition,
344 ed: &'r ExternDecl<'a>,
345 ) -> Result<(), Error<'a>> {
346 self.wf_extern_desc(p, &ed.desc)
347 }
348 fn wf_instance<'r>(
349 &self,
350 p: DefinedTypePosition,
351 it: &'r Instance<'a>,
352 ) -> Result<(), Error<'a>> {
353 error_if_duplicates_by(
354 it.exports.iter(),
355 |&ex| ex.kebab_name,
356 |ex| Error::DuplicateExternName(ex.kebab_name, false),
357 )?;
358 it.exports
359 .iter()
360 .try_for_each(|ed| self.wf_extern_decl(p, ed))
361 }
362 pub fn wf_qualified_instance<'r>(
363 &self,
364 p: DefinedTypePosition,
365 qit: &'r QualifiedInstance<'a>,
366 ) -> Result<(), Error<'a>> {
367 let mut ctx_ = self.clone();
368 let subst = ctx_.bound_to_evars(None, &qit.evars);
369 ctx_.evars
370 .iter()
371 .try_for_each(|(btv, _)| ctx_.wf_bounded_tyvar(p, btv))?;
372 let it = subst.instance(&qit.unqualified).not_void();
373 ctx_.wf_instance(p, &it)
374 }
375 pub fn wf_component<'r>(
376 &self,
377 p: DefinedTypePosition,
378 ct: &'r Component<'a>,
379 ) -> Result<(), Error<'a>> {
380 let mut ctx_ = self.clone();
381 let subst = ctx_.bound_to_uvars(None, &ct.uvars, false);
382 ctx_.uvars
383 .iter()
384 .try_for_each(|(btv, _)| ctx_.wf_bounded_tyvar(p, btv))?;
385 error_if_duplicates_by(
386 ct.imports.iter(),
387 |&im| im.kebab_name,
388 |im| Error::DuplicateExternName(im.kebab_name, true),
389 )?;
390 ct.imports
391 .iter()
392 .map(|ed| subst.extern_decl(ed).not_void())
393 .try_for_each(|ed| ctx_.wf_extern_decl(p, &ed))?;
394 let it = subst.qualified_instance(&ct.instance).not_void();
395 ctx_.wf_qualified_instance(p, &it)
396 }
397}