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 DuplicateFlagsName(Name<'a>),
122 DuplicateEnumName(Name<'a>),
124 DuplicateExternName(&'a str, bool),
127 NotAResource(subtype::Error<'a>),
129 BorrowOutsideParam,
131}
132
133fn error_if_duplicates_by<T, U: Eq + std::hash::Hash, E>(
134 i: impl Iterator<Item = T>,
135 f: impl FnMut(&T) -> U,
136 e: impl Fn(T) -> E,
137) -> Result<(), E> {
138 let mut duplicates = i.duplicates_by(f);
139 if let Some(x) = duplicates.next() {
140 Err(e(x))
141 } else {
142 Ok(())
143 }
144}
145
146impl<'p, 'a> Ctx<'p, 'a> {
151 fn wf_record_fields<'r>(
152 &'r self,
153 p: ValueTypePosition,
154 rfs: &'r [RecordField<'a>],
155 ) -> Result<(), Error<'a>> {
156 rfs.iter()
157 .try_for_each(|rf: &'r RecordField<'a>| self.wf_value(p, &rf.ty))?;
158 error_if_duplicates_by(
159 rfs.iter(),
160 |&rf| rf.name.name,
161 |rf| Error::DuplicateRecordField(rf.name),
162 )?;
163 Ok(())
164 }
165 fn wf_variant_cases<'r>(
166 &'r self,
167 p: ValueTypePosition,
168 vcs: &'r [VariantCase<'a>],
169 ) -> Result<(), Error<'a>> {
170 vcs.iter()
171 .try_for_each(|vc: &'r VariantCase<'a>| self.wf_value_option(p, &vc.ty))?;
172 error_if_duplicates_by(
173 vcs.iter(),
174 |&vc| vc.name.name,
175 |vc| Error::DuplicateVariantField(vc.name),
176 )?;
177 Ok(())
178 }
179 fn wf_value<'r>(&'r self, p: ValueTypePosition, vt: &'r Value<'a>) -> Result<(), Error<'a>> {
180 let anon_err: Result<(), Error<'a>> = if p.dtp.is_export && p.dtp.is_anon_export {
181 Err(Error::BareComplexValTypeExport(vt.clone()))
182 } else {
183 Ok(())
184 };
185 let p_ = p.anon_export();
186 let resource_err = |h| {
187 self.wf_handleable(p.dtp, h).and(
188 self.subtype_handleable_is_resource(h)
189 .map_err(Error::NotAResource),
190 )
191 };
192 match vt {
193 Value::Bool => Ok(()),
194 Value::S(_) => Ok(()),
195 Value::U(_) => Ok(()),
196 Value::F(_) => Ok(()),
197 Value::Char => Ok(()),
198 Value::String => Ok(()),
199 Value::List(vt) => self.wf_value(p_, vt),
200 Value::FixList(vt, _) => self.wf_value(p_, vt),
201 Value::Record(rfs) => anon_err.and(self.wf_record_fields(p_, rfs)),
202 Value::Variant(vcs) => anon_err.and(self.wf_variant_cases(p_, vcs)),
203 Value::Flags(ns) => anon_err.and(error_if_duplicates_by(
204 ns.iter(),
205 |&n| n.name,
206 |n| Error::DuplicateFlagsName(*n),
207 )),
208 Value::Enum(ns) => anon_err.and(error_if_duplicates_by(
209 ns.iter(),
210 |&n| n.name,
211 |n| Error::DuplicateEnumName(*n),
212 )),
213 Value::Option(vt) => self.wf_value(p_, vt),
214 Value::Tuple(vs) => vs
215 .iter()
216 .try_for_each(|vt: &'r Value<'a>| self.wf_value(p_, vt)),
217 Value::Result(vt1, vt2) => self
218 .wf_value_option(p_, vt1)
219 .and(self.wf_value_option(p_, vt2)),
220 Value::Own(h) => resource_err(h),
221 Value::Borrow(h) => {
222 if p.is_param {
223 resource_err(h)
224 } else {
225 Err(Error::BorrowOutsideParam)
226 }
227 }
228 Value::Var(tv, vt) => tv
229 .as_ref()
230 .map(|tv| self.wf_type_bound(p.dtp, self.var_bound(tv)))
231 .unwrap_or(Ok(()))
232 .and(self.wf_value(p.not_anon_export(), vt)),
233 }
234 }
235 fn wf_value_option<'r>(
236 &'r self,
237 p: ValueTypePosition,
238 vt: &'r Option<Value<'a>>,
239 ) -> Result<(), Error<'a>> {
240 vt.as_ref().map_or(Ok(()), |ty| self.wf_value(p, ty))
241 }
242 fn wf_func<'r>(&'r self, p: DefinedTypePosition, ft: &'r Func<'a>) -> Result<(), Error<'a>> {
243 let p_ = p.anon_export();
244 let param_pos = ValueTypePosition {
245 is_param: true,
246 dtp: p_,
247 };
248 let result_pos = ValueTypePosition {
249 is_param: false,
250 dtp: p_,
251 };
252 ft.params
253 .iter()
254 .try_for_each(|fp: &'r Param<'a>| self.wf_value(param_pos, &fp.ty))?;
255 match &ft.result {
256 Some(vt) => self.wf_value(result_pos, vt),
257 None => Ok(()),
258 }
259 }
260 fn wf_type_bound<'r>(
261 &'r self,
262 p: DefinedTypePosition,
263 tb: &'r TypeBound<'a>,
264 ) -> Result<(), Error<'a>> {
265 match tb {
266 TypeBound::SubResource => Ok(()),
267 TypeBound::Eq(dt) => self.wf_defined(p.not_anon_export(), dt),
268 }
269 }
270 fn wf_bounded_tyvar<'r>(
271 &'r self,
272 p: DefinedTypePosition,
273 btv: &'r BoundedTyvar<'a>,
274 ) -> Result<(), Error<'a>> {
275 match &btv.bound {
276 TypeBound::SubResource => Ok(()),
277 TypeBound::Eq(dt) => self.wf_defined(p, dt),
278 }
279 }
280
281 fn wf_handleable<'r>(
282 &'r self,
283 p: DefinedTypePosition,
284 ht: &'r Handleable,
285 ) -> Result<(), Error<'a>> {
286 match ht {
287 Handleable::Var(tv) => self.wf_type_bound(p, self.var_bound(tv)),
288 Handleable::Resource(rid) => {
289 if p.is_export {
290 Err(Error::BareResourceExport)
291 } else {
292 assert!((rid.id as usize) < self.rtypes.len());
294 Ok(())
295 }
296 }
297 }
298 }
299 pub fn wf_defined<'r>(
300 &'r self,
301 p: DefinedTypePosition,
302 dt: &'r Defined<'a>,
303 ) -> Result<(), Error<'a>> {
304 match dt {
305 Defined::Handleable(ht) => self.wf_handleable(p, ht),
306 Defined::Value(vt) => self.wf_value(p.into(), vt),
307 Defined::Func(ft) => self.wf_func(p, ft),
308 Defined::Instance(it) => self.wf_qualified_instance(p, it),
309 Defined::Component(ct) => self.wf_component(p, ct),
310 }
311 }
312 fn wf_extern_desc<'r>(
313 &self,
314 p: DefinedTypePosition,
315 ed: &'r ExternDesc<'a>,
316 ) -> Result<(), Error<'a>> {
317 match ed {
318 ExternDesc::CoreModule(_) => Ok(()),
319 ExternDesc::Func(ft) => self.wf_func(p, ft),
320 ExternDesc::Type(dt) => self.wf_defined(p, dt),
321 ExternDesc::Instance(it) => self.wf_instance(p, it),
322 ExternDesc::Component(ct) => self.wf_component(p, ct),
323 }
324 }
325 fn wf_extern_decl<'r>(
326 &self,
327 p: DefinedTypePosition,
328 ed: &'r ExternDecl<'a>,
329 ) -> Result<(), Error<'a>> {
330 self.wf_extern_desc(p, &ed.desc)
331 }
332 fn wf_instance<'r>(
333 &self,
334 p: DefinedTypePosition,
335 it: &'r Instance<'a>,
336 ) -> Result<(), Error<'a>> {
337 error_if_duplicates_by(
338 it.exports.iter(),
339 |&ex| ex.kebab_name,
340 |ex| Error::DuplicateExternName(ex.kebab_name, false),
341 )?;
342 it.exports
343 .iter()
344 .try_for_each(|ed| self.wf_extern_decl(p, ed))
345 }
346 pub fn wf_qualified_instance<'r>(
347 &self,
348 p: DefinedTypePosition,
349 qit: &'r QualifiedInstance<'a>,
350 ) -> Result<(), Error<'a>> {
351 let mut ctx_ = self.clone();
352 let subst = ctx_.bound_to_evars(None, &qit.evars);
353 ctx_.evars
354 .iter()
355 .try_for_each(|(btv, _)| ctx_.wf_bounded_tyvar(p, btv))?;
356 let it = subst.instance(&qit.unqualified).not_void();
357 ctx_.wf_instance(p, &it)
358 }
359 pub fn wf_component<'r>(
360 &self,
361 p: DefinedTypePosition,
362 ct: &'r Component<'a>,
363 ) -> Result<(), Error<'a>> {
364 let mut ctx_ = self.clone();
365 let subst = ctx_.bound_to_uvars(None, &ct.uvars, false);
366 ctx_.uvars
367 .iter()
368 .try_for_each(|(btv, _)| ctx_.wf_bounded_tyvar(p, btv))?;
369 error_if_duplicates_by(
370 ct.imports.iter(),
371 |&im| im.kebab_name,
372 |im| Error::DuplicateExternName(im.kebab_name, true),
373 )?;
374 ct.imports
375 .iter()
376 .map(|ed| subst.extern_decl(ed).not_void())
377 .try_for_each(|ed| ctx_.wf_extern_decl(p, &ed))?;
378 let it = subst.qualified_instance(&ct.instance).not_void();
379 ctx_.wf_qualified_instance(p, &it)
380 }
381}