dyon/
ty.rs

1use std::sync::Arc;
2
3use piston_meta::bootstrap::Convert;
4use range::Range;
5use crate::Dfn;
6
7/// Stores a Dyon type.
8#[derive(Debug, Clone, PartialEq)]
9pub enum Type {
10    /// Whether a statement is never reached.
11    Unreachable,
12    /// A no-type.
13    Void,
14    /// Any type.
15    Any,
16    /// Boolean type.
17    Bool,
18    /// F64 type.
19    F64,
20    /// 4D vector type.
21    Vec4,
22    /// 4D matrix type.
23    Mat4,
24    /// String/text type.
25    Str,
26    /// Link type.
27    Link,
28    /// Array type.
29    Array(Box<Type>),
30    /// Object type.
31    Object,
32    /// Option type.
33    Option(Box<Type>),
34    /// Result type.
35    Result(Box<Type>),
36    /// Secret type.
37    Secret(Box<Type>),
38    /// Thread handle type.
39    #[cfg(all(not(target_family = "wasm"), feature = "threading"))]
40    Thread(Box<Type>),
41    /// In-type.
42    #[cfg(all(not(target_family = "wasm"), feature = "threading"))]
43    In(Box<Type>),
44    /// Ad-hoc type.
45    AdHoc(Arc<String>, Box<Type>),
46    /// Closure type.
47    Closure(Box<Dfn>),
48}
49
50impl Type {
51    /// Returns an extension quantified over ad-hoc types.
52    ///
53    /// For example, `(vec4, vec4) -> vec4` becomes `all T { (T vec4, T vec4) -> T vec4 }`.
54    pub fn all_ext(args: Vec<Type>, ret: Type) -> (Vec<Arc<String>>, Vec<Type>, Type) {
55        use crate::T;
56        use Type::AdHoc;
57
58        (
59            vec![T.clone()],
60            args.into_iter()
61                .map(|arg| AdHoc(T.clone(), Box::new(arg)))
62                .collect(),
63            AdHoc(T.clone(), Box::new(ret)),
64        )
65    }
66
67    /// Returns description of the type.
68    pub fn description(&self) -> String {
69        use Type::*;
70
71        match *self {
72            Unreachable => "unreachable".into(),
73            Void => "void".into(),
74            Any => "any".into(),
75            Bool => "bool".into(),
76            F64 => "f64".into(),
77            Vec4 => "vec4".into(),
78            Mat4 => "mat4".into(),
79            Str => "str".into(),
80            Link => "link".into(),
81            Array(ref ty) => {
82                if let Any = **ty {
83                    "[]".into()
84                } else {
85                    let mut res = String::from("[");
86                    res.push_str(&ty.description());
87                    res.push(']');
88                    res
89                }
90            }
91            Object => "{}".into(),
92            Option(ref ty) => {
93                if let Any = **ty {
94                    "opt".into()
95                } else {
96                    let mut res = String::from("opt[");
97                    res.push_str(&ty.description());
98                    res.push(']');
99                    res
100                }
101            }
102            Result(ref ty) => {
103                if let Any = **ty {
104                    "res".into()
105                } else {
106                    let mut res = String::from("res[");
107                    res.push_str(&ty.description());
108                    res.push(']');
109                    res
110                }
111            }
112            Secret(ref ty) => match **ty {
113                Bool => "sec[bool]".into(),
114                F64 => "sec[f64]".into(),
115                _ => panic!("Secret only supports `bool` and `f64`"),
116            },
117            #[cfg(all(not(target_family = "wasm"), feature = "threading"))]
118            Thread(ref ty) => {
119                if let Any = **ty {
120                    "thr".into()
121                } else {
122                    let mut res = String::from("thr[");
123                    res.push_str(&ty.description());
124                    res.push(']');
125                    res
126                }
127            }
128            #[cfg(all(not(target_family = "wasm"), feature = "threading"))]
129            In(ref ty) => {
130                if let Any = **ty {
131                    "in".into()
132                } else {
133                    let mut res = String::from("in[");
134                    res.push_str(&ty.description());
135                    res.push(']');
136                    res
137                }
138            }
139            AdHoc(ref ad, ref ty) => (&**ad).clone() + " " + &ty.description(),
140            Closure(ref closure) => {
141                let mut s = String::new();
142                s.push_str("\\(");
143                for (i, ty) in closure.tys.iter().enumerate() {
144                    s.push_str(&ty.description());
145                    if i + 1 < closure.tys.len() {
146                        s.push_str(", ");
147                    }
148                }
149                s.push_str(") -> ");
150                s.push_str(&closure.ret.description());
151                s
152            }
153        }
154    }
155
156    /// Returns an array type with an `any` as inner type.
157    pub fn array() -> Type {
158        Type::Array(Box::new(Type::Any))
159    }
160
161    /// Returns an object type.
162    pub fn object() -> Type {
163        Type::Object
164    }
165
166    /// Returns an Option type with an `any` as inner type.
167    pub fn option() -> Type {
168        Type::Option(Box::new(Type::Any))
169    }
170
171    /// Returns a Result type with an `any` as inner type.
172    pub fn result() -> Type {
173        Type::Result(Box::new(Type::Any))
174    }
175
176    /// Returns a thread handle type with an `any` as inner type.
177    #[cfg(all(not(target_family = "wasm"), feature = "threading"))]
178    pub fn thread() -> Type {
179        Type::Thread(Box::new(Type::Any))
180    }
181
182    /// Returns an in-type with an `any` as inner type.
183    #[cfg(all(not(target_family = "wasm"), feature = "threading"))]
184    pub fn in_ty() -> Type {
185        Type::In(Box::new(Type::Any))
186    }
187
188    /// Binds refinement type variables.
189    ///
190    /// Returns the type argument to compare to.
191    pub fn bind_ty_vars(
192        &self,
193        refine: &Type,
194        names: &[Arc<String>],
195        ty_vars: &mut Vec<Option<Arc<String>>>,
196    ) -> Result<Type, String> {
197        if names.is_empty() {
198            return Ok(self.clone());
199        };
200        match (self, refine) {
201            (
202                &Type::AdHoc(ref a_name, ref a_inner_ty),
203                &Type::AdHoc(ref b_name, ref b_inner_ty),
204            ) => {
205                for i in 0..names.len() {
206                    if a_name == &names[i] {
207                        let new_inner = a_inner_ty.bind_ty_vars(b_inner_ty, names, ty_vars)?;
208                        if let Some(ref existing_name) = ty_vars[i] {
209                            if existing_name != b_name
210                                && new_inner.goes_with(b_inner_ty)
211                                && !new_inner.ambiguous(b_inner_ty)
212                            {
213                                return Err(format!(
214                                    "Type mismatch (#1500): Expected `{}`, found `{}`",
215                                    existing_name, b_name
216                                ));
217                            } else {
218                                return Ok(Type::AdHoc(existing_name.clone(), Box::new(new_inner)));
219                            }
220                        } else {
221                            ty_vars[i] = Some(b_name.clone());
222                            return Ok(Type::AdHoc(
223                                b_name.clone(),
224                                Box::new(a_inner_ty.bind_ty_vars(b_inner_ty, names, ty_vars)?),
225                            ));
226                        }
227                    }
228                }
229                Ok(Type::AdHoc(
230                    a_name.clone(),
231                    Box::new(a_inner_ty.bind_ty_vars(b_inner_ty, names, ty_vars)?),
232                ))
233            }
234            (&Type::AdHoc(ref a_name, ref a_inner_ty), b) => {
235                for i in 0..names.len() {
236                    if a_name == &names[i] {
237                        let new_inner = a_inner_ty.bind_ty_vars(refine, names, ty_vars)?;
238                        if let Some(ref n) = ty_vars[i] {
239                            if new_inner.goes_with(b) && !new_inner.ambiguous(b) {
240                                return Err(format!(
241                                    "Type mismatch (#1600): Expected `{}`, found no ad-hoc type",
242                                    n
243                                ));
244                            }
245                        } else {
246                            break;
247                        }
248                    }
249                }
250                a_inner_ty.bind_ty_vars(refine, names, ty_vars)
251            }
252            _ => Ok(self.clone()),
253        }
254    }
255
256    /// Inserts variable name, replacing ad-hoc type name.
257    pub fn insert_var(&mut self, name: &Arc<String>, val: &Arc<String>) {
258        match *self {
259            Type::AdHoc(ref mut n, ref mut inner_ty) => {
260                if n == name {
261                    *n = val.clone();
262                }
263                inner_ty.insert_var(name, val)
264            }
265            _ => {}
266        }
267    }
268
269    /// Inserts a none ad-hoc variable.
270    pub fn insert_none_var(&mut self, name: &Arc<String>) {
271        match *self {
272            Type::AdHoc(_, ref mut inner_ty) => {
273                inner_ty.insert_none_var(name);
274                *self = (**inner_ty).clone();
275            }
276            _ => {}
277        }
278    }
279
280    /// Returns `true` if a type to be refined is ambiguous relative to this type (directional check).
281    ///
282    /// For example, the type ad-hoc type `Foo str` is ambiguous with type `str`.
283    /// If more was known about the `str` type with further refinement,
284    /// then it might turn out to be `Bar str`, which triggers a collision.
285    pub fn ambiguous(&self, refine: &Type) -> bool {
286        use self::Type::*;
287
288        match (self, refine) {
289            (&AdHoc(ref xa, ref xb), &AdHoc(ref ya, ref yb)) if xa == ya => xb.ambiguous(yb),
290            (&AdHoc(_, ref x), y) if x.goes_with(y) => true,
291            (&Array(ref x), &Array(ref y)) if x.ambiguous(y) => true,
292            (&Option(ref x), &Option(ref y)) if x.ambiguous(y) => true,
293            (&Result(ref x), &Result(ref y)) if x.ambiguous(y) => true,
294            #[cfg(all(not(target_family = "wasm"), feature = "threading"))]
295            (&Thread(ref x), &Thread(ref y)) if x.ambiguous(y) => true,
296            #[cfg(all(not(target_family = "wasm"), feature = "threading"))]
297            (&In(ref x), &In(ref y)) if x.ambiguous(y) => true,
298            (&Bool, &Any) => true,
299            (&F64, &Any) => true,
300            (&Str, &Any) => true,
301            (&Vec4, &Any) => true,
302            (&Mat4, &Any) => true,
303            (&Link, &Any) => true,
304            (&Array(_), &Any) => true,
305            (&Option(_), &Any) => true,
306            (&Result(_), &Any) => true,
307            #[cfg(all(not(target_family = "wasm"), feature = "threading"))]
308            (&Thread(_), &Any) => true,
309            (&Secret(_), &Any) => true,
310            #[cfg(all(not(target_family = "wasm"), feature = "threading"))]
311            (&In(_), &Any) => true,
312            _ => false,
313        }
314    }
315
316    /// Returns `true` if the type can be a closure, `false` otherwise.
317    pub fn closure_ret_ty(&self) -> Option<Type> {
318        use self::Type::*;
319
320        match *self {
321            Closure(ref ty) => Some(ty.ret.clone()),
322            AdHoc(_, ref x) => x.closure_ret_ty(),
323            Any => Some(Type::Any),
324            _ => None,
325        }
326    }
327
328    /// Returns `true` if a type goes with another type (directional check).
329    ///
330    /// - `bool` (argument) goes with `sec[bool]` (value)
331    /// - `f64` (argument) goes with `sec[f64]` (value)
332    ///
333    /// The opposite is not true, since `sec` contains extra information.
334    pub fn goes_with(&self, other: &Type) -> bool {
335        use self::Type::*;
336
337        // Invert the order because of complex ad-hoc logic.
338        if let AdHoc(_, _) = *other {
339            if let AdHoc(_, _) = *self {
340            } else {
341                return other.goes_with(self);
342            }
343        }
344        if let Secret(ref other_ty) = *other {
345            return if let Secret(ref this_ty) = *self {
346                this_ty.goes_with(other_ty)
347            } else {
348                self.goes_with(other_ty)
349            };
350        }
351        match self {
352            // Unreachable goes with anything.
353            &Unreachable => true,
354            _ if *other == Unreachable => true,
355            &Any => *other != Void,
356            // Void only goes with void.
357            &Void => *other == Void,
358            &Array(ref arr) => {
359                if let Array(ref other_arr) = *other {
360                    arr.goes_with(other_arr)
361                } else {
362                    matches!(*other, Any)
363                }
364            }
365            &Object => {
366                if let Object = *other {
367                    true
368                } else {
369                    matches!(*other, Any)
370                }
371            }
372            &Option(ref opt) => {
373                if let Option(ref other_opt) = *other {
374                    opt.goes_with(other_opt)
375                } else {
376                    matches!(*other, Any)
377                }
378            }
379            &Result(ref res) => {
380                if let Result(ref other_res) = *other {
381                    res.goes_with(other_res)
382                } else if let Any = *other {
383                    true
384                } else {
385                    false
386                }
387            }
388            #[cfg(all(not(target_family = "wasm"), feature = "threading"))]
389            &Thread(ref thr) => {
390                if let Thread(ref other_thr) = *other {
391                    thr.goes_with(other_thr)
392                } else if let Any = *other {
393                    true
394                } else {
395                    false
396                }
397            }
398            #[cfg(all(not(target_family = "wasm"), feature = "threading"))]
399            &In(ref in_ty) => {
400                if let In(ref other_ty) = *other {
401                    in_ty.goes_with(other_ty)
402                } else if let Any = *other {
403                    true
404                } else {
405                    false
406                }
407            }
408            &Closure(ref cl) => {
409                if let Closure(ref other_cl) = *other {
410                    if cl.tys.len() != other_cl.tys.len() {
411                        return false;
412                    }
413                    if !cl
414                        .tys
415                        .iter()
416                        .zip(other_cl.tys.iter())
417                        .all(|(a, b)| a.goes_with(b))
418                    {
419                        return false;
420                    }
421                    if !cl.ret.goes_with(&other_cl.ret) {
422                        return false;
423                    }
424                    true
425                } else if let Any = *other {
426                    true
427                } else {
428                    false
429                }
430            }
431            &AdHoc(ref name, ref ty) => {
432                if let AdHoc(ref other_name, ref other_ty) = *other {
433                    name == other_name && ty.goes_with(other_ty)
434                } else if let Void = *other {
435                    false
436                } else {
437                    ty.goes_with(other)
438                }
439            }
440            // Bool, F64, Text, Vec4.
441            x if x == other => true,
442            _ if *other == Type::Any => true,
443            _ => false,
444        }
445    }
446
447    /// Infers type from the `+=` operator.
448    pub fn add_assign(&self, other: &Type) -> bool {
449        use self::Type::*;
450
451        match (self, other) {
452            (&AdHoc(ref name, ref ty), &AdHoc(ref other_name, ref other_ty)) => {
453                if name != other_name {
454                    return false;
455                }
456                if !ty.goes_with(other_ty) {
457                    return false;
458                }
459                ty.add_assign(other_ty)
460            }
461            (&AdHoc(_, _), _) | (_, &AdHoc(_, _)) => false,
462            (&Void, _) | (_, &Void) => false,
463            _ => true,
464        }
465    }
466
467    /// Converts meta data into a type.
468    pub fn from_meta_data(
469        node: &str,
470        mut convert: Convert,
471        ignored: &mut Vec<Range>,
472    ) -> Result<(Range, Type), ()> {
473        let start = convert;
474        let start_range = convert.start_node(node)?;
475        convert.update(start_range);
476
477        let mut ty: Option<Type> = None;
478        loop {
479            if let Ok(range) = convert.end_node(node) {
480                convert.update(range);
481                break;
482            } else if let Ok((range, _)) = convert.meta_bool("any") {
483                convert.update(range);
484                ty = Some(Type::Any);
485            } else if let Ok((range, _)) = convert.meta_bool("bool") {
486                convert.update(range);
487                ty = Some(Type::Bool);
488            } else if let Ok((range, _)) = convert.meta_bool("sec_bool") {
489                convert.update(range);
490                ty = Some(Type::Secret(Box::new(Type::Bool)));
491            } else if let Ok((range, _)) = convert.meta_bool("f64") {
492                convert.update(range);
493                ty = Some(Type::F64);
494            } else if let Ok((range, _)) = convert.meta_bool("sec_f64") {
495                convert.update(range);
496                ty = Some(Type::Secret(Box::new(Type::F64)));
497            } else if let Ok((range, _)) = convert.meta_bool("str") {
498                convert.update(range);
499                ty = Some(Type::Str);
500            } else if let Ok((range, _)) = convert.meta_bool("vec4") {
501                convert.update(range);
502                ty = Some(Type::Vec4);
503            } else if let Ok((range, _)) = convert.meta_bool("mat4") {
504                convert.update(range);
505                ty = Some(Type::Mat4);
506            } else if let Ok((range, _)) = convert.meta_bool("link") {
507                convert.update(range);
508                ty = Some(Type::Link);
509            } else if let Ok((range, _)) = convert.meta_bool("opt_any") {
510                convert.update(range);
511                ty = Some(Type::Option(Box::new(Type::Any)));
512            } else if let Ok((range, _)) = convert.meta_bool("res_any") {
513                convert.update(range);
514                ty = Some(Type::Result(Box::new(Type::Any)));
515            } else if let Ok((range, _)) = convert.meta_bool("arr_any") {
516                convert.update(range);
517                ty = Some(Type::Array(Box::new(Type::Any)));
518            } else if let Ok((range, _)) = convert.meta_bool("obj_any") {
519                convert.update(range);
520                ty = Some(Type::Object);
521            } else if let Ok((range, val)) = Type::from_meta_data("opt", convert, ignored) {
522                convert.update(range);
523                ty = Some(Type::Option(Box::new(val)));
524            } else if let Ok((range, val)) = Type::from_meta_data("res", convert, ignored) {
525                convert.update(range);
526                ty = Some(Type::Result(Box::new(val)));
527            } else if let Ok((range, val)) = Type::from_meta_data("arr", convert, ignored) {
528                convert.update(range);
529                ty = Some(Type::Array(Box::new(val)));
530            } else if let Ok((range, val)) = convert.meta_string("ad_hoc") {
531                convert.update(range);
532                let inner_ty =
533                    if let Ok((range, val)) = Type::from_meta_data("ad_hoc_ty", convert, ignored) {
534                        convert.update(range);
535                        val
536                    } else {
537                        Type::Object
538                    };
539                ty = Some(Type::AdHoc(val, Box::new(inner_ty)));
540            } else if let Ok(range) = convert.start_node("closure_type") {
541                convert.update(range);
542                let mut lts = vec![];
543                let mut tys = vec![];
544                while let Ok((range, val)) = Type::from_meta_data("cl_arg", convert, ignored) {
545                    use crate::Lt;
546
547                    convert.update(range);
548                    lts.push(Lt::Default);
549                    tys.push(val);
550                }
551                let (range, ret) = Type::from_meta_data("cl_ret", convert, ignored)?;
552                convert.update(range);
553                let range = convert.end_node("closure_type")?;
554                convert.update(range);
555                ty = Some(Type::Closure(Box::new(Dfn {
556                    lts,
557                    tys,
558                    ret,
559                    ext: vec![],
560                    lazy: crate::LAZY_NO,
561                })));
562            } else {
563                loop {
564                    #[cfg(all(not(target_family = "wasm"), feature = "threading"))]
565                    if let Ok((range, _)) = convert.meta_bool("thr_any") {
566                        convert.update(range);
567                        ty = Some(Type::Thread(Box::new(Type::Any)));
568                        break;
569                    }
570                    #[cfg(all(not(target_family = "wasm"), feature = "threading"))]
571                    if let Ok((range, val)) = Type::from_meta_data("thr", convert, ignored) {
572                        convert.update(range);
573                        ty = Some(Type::Thread(Box::new(val)));
574                        break;
575                    }
576                    #[cfg(all(not(target_family = "wasm"), feature = "threading"))]
577                    if let Ok((range, _)) = convert.meta_bool("in_any") {
578                        convert.update(range);
579                        ty = Some(Type::In(Box::new(Type::Any)));
580                        break;
581                    }
582                    #[cfg(all(not(target_family = "wasm"), feature = "threading"))]
583                    if let Ok((range, val)) = Type::from_meta_data("in", convert, ignored) {
584                        convert.update(range);
585                        ty = Some(Type::In(Box::new(val)));
586                        break;
587                    }
588                    let range = convert.ignore();
589                    convert.update(range);
590                    ignored.push(range);
591                    break;
592                }
593            }
594        }
595
596        Ok((convert.subtract(start), ty.ok_or(())?))
597    }
598}