A reasonable configuration language
rcl-lang.org
configuration-language
json
1// RCL -- A reasonable configuration language.
2// Copyright 2023 Ruud van Asseldonk
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// A copy of the License has been included in the root of the repository.
7
8//! Representations of types.
9
10use std::cmp::Ordering;
11use std::rc::Rc;
12
13use crate::ast::{CallArg, Ident};
14use crate::error::{Error, IntoError, Result};
15use crate::fmt_type::format_type;
16use crate::markup::Markup;
17use crate::pprint::{concat, Doc};
18use crate::source::Span;
19use crate::type_diff::{Mismatch, TypeDiff};
20use crate::type_source::Source;
21
22/// A type.
23#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
24pub enum Type {
25 /// Any value, a more concrete type is not statically known.
26 ///
27 /// This is the top of the type lattice, it is a supertype of all types.
28 Any,
29
30 /// The type of unreachable code.
31 ///
32 /// This is the bottom of the type lattice, it is a subtype of any type.
33 Void,
34
35 /// The primitive type `Null`.
36 Null,
37
38 /// The primitive type `Bool`.
39 Bool,
40
41 /// The primitive type `Number`.
42 Number,
43
44 /// The primitive type `String`.
45 String,
46
47 /// A dict with the given key and value types.
48 Dict(Rc<Dict>),
49
50 /// A list with the given element type.
51 List(Rc<SourcedType>),
52
53 /// A set with the given element type.
54 Set(Rc<SourcedType>),
55
56 /// A function.
57 Function(Rc<Function>),
58
59 /// The union of multiple types.
60 Union(Rc<Union>),
61}
62
63impl Type {
64 /// Return whether the type is not composite, i.e. is not composed of other types.
65 pub fn is_atom(&self) -> bool {
66 matches!(
67 self,
68 Type::Any | Type::Bool | Type::Null | Type::Number | Type::String | Type::Void
69 )
70 }
71
72 /// Return a short name for the type, excluding generic arguments.
73 ///
74 /// For atoms this returns the regular name, for non-atoms it returns e.g.
75 /// `List` for any `List[T]`.
76 pub fn short_name(&self) -> &'static str {
77 match self {
78 Type::Any => "Any",
79 Type::Void => "Void",
80 Type::Null => "Null",
81 Type::Bool => "Bool",
82 Type::Number => "Number",
83 Type::String => "String",
84 Type::Dict(..) => "Dict",
85 Type::List(..) => "List",
86 Type::Set(..) => "Set",
87 Type::Function(..) => "Function",
88 Type::Union(..) => "Union",
89 }
90 }
91}
92
93/// The type parameters for the `Dict` type.
94#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
95pub struct Dict {
96 pub key: SourcedType,
97 pub value: SourcedType,
98}
99
100/// An argument in a function type.
101///
102/// The names are ignored for equality and comparison purposes, but we track
103/// them to enable more helpful error messages. The name and span can exist
104/// separately. For example, builtin functions have no argument span, but
105/// user-defined functions do.
106#[derive(Clone, Debug)]
107pub struct FunctionArg {
108 /// The name of this argument.
109 pub name: Option<Ident>,
110 /// The span where the argument is defined in the function definition.
111 pub span: Option<Span>,
112 /// The type of the argument.
113 pub type_: SourcedType,
114}
115
116impl PartialEq for FunctionArg {
117 fn eq(&self, other: &Self) -> bool {
118 self.type_.eq(&other.type_)
119 }
120}
121
122impl PartialOrd for FunctionArg {
123 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
124 Some(self.cmp(other))
125 }
126}
127
128impl Eq for FunctionArg {}
129
130impl Ord for FunctionArg {
131 fn cmp(&self, other: &Self) -> Ordering {
132 self.type_.cmp(&other.type_)
133 }
134}
135
136impl FunctionArg {
137 /// Check whether a function argument is a subtype.
138 ///
139 /// Function arguments are contravariant: the subtype relationship
140 /// goes the other way. For example, `(Any) -> Number` is a subtype of
141 /// `(Number) -> Number`: in every case where we need to call the latter,
142 /// we can call the former. So although `Number ≤ Any`, as function args
143 /// we have the opposite: `Arg(Any) ≤ Arg(Number)`.
144 pub fn is_subtype_of(&self, other: &FunctionArg) -> TypeDiff<FunctionArg> {
145 match other.type_.is_subtype_of(&self.type_) {
146 TypeDiff::Ok(t) => TypeDiff::Ok(FunctionArg {
147 // Ok returns the most specific type, so we take the name and
148 // span from there, though if it has no name, we take the other
149 // name.
150 name: other.name.as_ref().or(self.name.as_ref()).cloned(),
151 span: other.span,
152 type_: t,
153 }),
154 TypeDiff::Defer(t) => TypeDiff::Defer(FunctionArg {
155 // Defer returns the most generic type, so we take the name and
156 // span from there.
157 name: self.name.as_ref().or(other.name.as_ref()).cloned(),
158 span: self.span,
159 type_: t,
160 }),
161 TypeDiff::Error(err) => TypeDiff::Error(err),
162 }
163 }
164}
165
166/// A function type.
167#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
168pub struct Function {
169 /// The function arguments, including optional names.
170 ///
171 /// The names are ignored for equality and comparison purposes, but we track
172 /// them to enable more helpful error messages.
173 pub args: Vec<FunctionArg>,
174
175 /// The result type, also called return type.
176 pub result: SourcedType,
177}
178
179impl Function {
180 /// Confirm that there are as many provided arguments as expected arguments.
181 ///
182 /// If not, report that as an error on the proper spans, with as much
183 /// information as we have.
184 pub fn check_arity<T>(
185 &self,
186 function_name: Option<&str>,
187 provided_args: &[CallArg<T>],
188 call_close: Span,
189 ) -> Result<()> {
190 if provided_args.len() == self.args.len() {
191 return Ok(());
192 }
193
194 let fn_name = match function_name {
195 None => "The function".into(),
196 Some(name) => concat! { "'" Doc::highlight(name) "'" },
197 };
198
199 let n_args = match self.args.len() {
200 1 => "1 argument".to_string(),
201 n => format!("{n} arguments"),
202 };
203
204 if provided_args.len() < self.args.len() {
205 let missing_arg = &self.args[provided_args.len()];
206
207 let missing_msg = match &missing_arg.name {
208 None => "Missing argument. ".into(),
209 Some(name) => concat! {
210 "Missing argument '" Doc::highlight(name.as_ref()) "'. "
211 },
212 };
213
214 let msg = concat! {
215 missing_msg fn_name " takes " n_args ", but got "
216 provided_args.len().to_string()
217 "."
218 };
219
220 let error = call_close.error(msg.into_owned());
221
222 match missing_arg.span {
223 None => error.err(),
224 Some(arg_span) => error.with_note(arg_span, "Argument defined here.").err(),
225 }
226 } else {
227 let excess_arg = &provided_args[self.args.len()];
228 let msg = concat! {
229 "Unexpected argument. " fn_name " takes " n_args ", but got "
230 provided_args.len().to_string()
231 "."
232 };
233 // TODO: Store a reference to the function span in the type,
234 // so we can add a note with the function definition, just like with
235 // the the missing argument.
236 excess_arg.span.error(msg.into_owned()).err()
237 }
238 }
239
240 pub fn is_subtype_of(self: &Rc<Self>, other: &Rc<Function>) -> TypeDiff<Rc<Function>> {
241 // If there is an arity mismatch, report that as a normal diff.
242 // Unfortunately at this point we don't have access to the type sources,
243 // so this check only kicks in in places where we have a `Function` but
244 // not the surrounding type. In `Type::is_subtype_of` we do the check
245 // that preserves the sources.
246 if self.args.len() != other.args.len() {
247 let err = Mismatch::Atom {
248 actual: SourcedType {
249 type_: Type::Function(self.clone()),
250 source: Source::None,
251 },
252 expected: SourcedType {
253 type_: Type::Function(other.clone()),
254 source: Source::None,
255 },
256 };
257 return TypeDiff::Error(err);
258 }
259
260 let mut is_err = false;
261 let mut is_defer = false;
262
263 let mut args = Vec::with_capacity(self.args.len());
264 let mut arg_diffs = Vec::new();
265
266 for (a1, a2) in self.args.iter().zip(other.args.iter()) {
267 // Note, the contravariance is built into the `FunctionArg` check,
268 // so here we do check that a1 ≤ a2.
269 match a1.is_subtype_of(a2) {
270 TypeDiff::Ok(t) | TypeDiff::Defer(t) if is_err => {
271 arg_diffs.push(TypeDiff::Ok(t));
272 }
273 TypeDiff::Ok(t) => args.push(t),
274 TypeDiff::Defer(t) => {
275 is_defer = true;
276 args.push(t);
277 }
278 err if is_err => arg_diffs.push(err),
279 err => {
280 is_err = true;
281 for not_err in args.drain(..) {
282 arg_diffs.push(TypeDiff::Ok(not_err));
283 }
284 arg_diffs.push(err);
285 }
286 }
287 }
288 let result_type = match self.result.is_subtype_of(&other.result) {
289 TypeDiff::Ok(t) | TypeDiff::Defer(t) if is_err => {
290 let err = Mismatch::Function(arg_diffs, TypeDiff::Ok(t).into());
291 return TypeDiff::Error(err);
292 }
293 TypeDiff::Ok(t) => t,
294 TypeDiff::Defer(t) => {
295 is_defer = true;
296 t
297 }
298 err if is_err => {
299 let err = Mismatch::Function(arg_diffs, err.into());
300 return TypeDiff::Error(err);
301 }
302 err => {
303 let err =
304 Mismatch::Function(args.into_iter().map(TypeDiff::Ok).collect(), err.into());
305 return TypeDiff::Error(err);
306 }
307 };
308 let fn_type = Function {
309 args,
310 result: result_type,
311 };
312 if is_defer {
313 TypeDiff::Defer(fn_type.into())
314 } else {
315 // In this case, even though `self` should be equivalent to `fn_type`,
316 // it is not identical. The sources of the types can differ, and we
317 // may have collected names for unnamed arguments.
318 TypeDiff::Ok(fn_type.into())
319 }
320 }
321}
322
323/// The elements of a `Union` type.
324#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
325pub struct Union {
326 // TODO: Ensure the elements are sorted and deduplicated on just type,
327 // meeting the sources if we have multiple.
328 // TODO: Enforce that none of the elements are unions, you need to flatten those.
329 pub members: Vec<SourcedType>,
330}
331
332impl Union {
333 /// Is this union a subtype of a particular type?
334 ///
335 /// We can statically confirm that `self` is a subtype of `other` if all its
336 /// members are a subtype. We can also rule out that `self` is a subtype of
337 /// `other` if all of its members are not a subtype of `other`. If we have
338 /// mixed results, then we defer to runtime.
339 pub fn is_subtype_of(
340 self: &Rc<Self>,
341 source: Source,
342 other: &SourcedType,
343 ) -> TypeDiff<SourcedType> {
344 if let Type::Union(_) = &other.type_ {
345 // For now, union/union typechecks are out of scope, we defer to a
346 // runtime check.
347 return TypeDiff::Defer(other.clone());
348 }
349
350 let mut n_ok: u32 = 0;
351 let mut n_err: u32 = 0;
352
353 for candidate in self.members.iter() {
354 match candidate.is_subtype_of(other) {
355 TypeDiff::Ok(..) => n_ok += 1,
356 TypeDiff::Error(..) => n_err += 1,
357 TypeDiff::Defer(..) => {}
358 }
359 }
360 let all_ok = n_ok == self.members.len() as u32;
361 let all_error = n_err == self.members.len() as u32;
362
363 if all_ok {
364 // If all of the candidates are a subtype, then the entire union is
365 // a subtype. We could meet all the candidates and that might be a
366 // more precise type than the expected type, but it's also expensive
367 // to do, so we just return the expected type as the upper bound.
368 TypeDiff::Ok(other.clone())
369 } else if all_error {
370 // If all of the candidates are an error, then we definitely have an
371 // error. Previously we used a custom TypeDiff variant to report this,
372 // to hold the inner errors, but it turned out to be more noisy in
373 // error messages than helpful, so just report it as an atom. Better
374 // ideas are welcome!
375 TypeDiff::Error(Mismatch::Atom {
376 actual: SourcedType {
377 source,
378 type_: Type::Union(self.clone()),
379 },
380 expected: other.clone(),
381 })
382 } else {
383 // If we can neither confirm nor deny statically, then we need to
384 // check at runtime.
385 TypeDiff::Defer(other.clone())
386 }
387 }
388}
389
390/// What side to explain the source of a type for.
391pub enum Side {
392 Expected,
393 Actual,
394}
395
396/// The element type of a collection type.
397pub enum ElementType {
398 /// We don't know the type, so the element type could be anything.
399 Any,
400 /// The type is certainly a collection (list or set), and this is the element type.
401 Scalar(Rc<SourcedType>),
402 /// The type is certainly a dict, and these are the key and value types.
403 Dict(Rc<Dict>),
404 /// The type is certainly not something that has an inner element type.
405 None,
406}
407
408/// A type and its source.
409#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
410pub struct SourcedType {
411 pub type_: Type,
412 pub source: Source,
413}
414
415impl SourcedType {
416 /// Construct [`Type::Void`] with empty collection source.
417 pub fn void(at: Span) -> SourcedType {
418 SourcedType {
419 type_: Type::Void,
420 source: Source::EmptyCollection(at),
421 }
422 }
423
424 /// Construct [`Type::Any`] without source.
425 pub const fn any() -> SourcedType {
426 SourcedType {
427 type_: Type::Any,
428 source: Source::None,
429 }
430 }
431
432 /// Return the least possible supertype of the two types.
433 ///
434 /// The meet is a type `T` such that `self` and `other` are both subtypes
435 /// of `T`.
436 /// TODO: This should take self and other by value.
437 pub fn meet(&self, other: &SourcedType) -> SourcedType {
438 let src_meet = self.source.meet(&other.source);
439 let (type_, source) = match (&self.type_, &other.type_) {
440 // Anything involving any becomes any, anything involving
441 // void becomes the other thing, these are the top and bottom of
442 // the type lattice.
443 (Type::Any, _) => (Type::Any, self.source),
444 (_, Type::Any) => (Type::Any, other.source),
445 (Type::Void, that) => (that.clone(), other.source),
446 (this, Type::Void) => (this.clone(), self.source),
447
448 // If we have matching primitive types, they are preserved.
449 (Type::Bool, Type::Bool) => (Type::Bool, src_meet),
450 (Type::Number, Type::Number) => (Type::Number, src_meet),
451 (Type::Null, Type::Null) => (Type::Null, src_meet),
452 (Type::String, Type::String) => (Type::String, src_meet),
453
454 // For composite types, we meet on their elements.
455 (Type::Dict(d1), Type::Dict(d2)) => {
456 // TODO: If the meets don't change the key and value type,
457 // we can recycle the original instead of making a new one.
458 let dm = Rc::new(Dict {
459 key: d1.key.meet(&d2.key),
460 value: d1.value.meet(&d2.value),
461 });
462 // TODO: If the types are the same on both sides, we can meet the sources.
463 (Type::Dict(dm), Source::None)
464 }
465 (Type::List(l1), Type::List(l2)) => {
466 let type_ = Type::List(Rc::new(l1.meet(l2)));
467 // TODO: If the types are the same on both sides, we can meet the sources.
468 (type_, Source::None)
469 }
470 (Type::Set(s1), Type::Set(s2)) => {
471 let type_ = Type::Set(Rc::new(s1.meet(s2)));
472 // TODO: If the types are the same on both sides, we can meet the sources.
473 (type_, Source::None)
474 }
475
476 // TODO: Support meeting functions.
477 (Type::Function(_), Type::Function(_)) => (Type::Any, Source::None),
478
479 // Any two values that mismatch, we can't describe with a single
480 // static type, but that doesn't mean it's a type error, the program
481 // may still be valid at runtime. E.g, I have a list with a function
482 // and an int. If the program only ever calls `list[0]` and performs
483 // integer addition on `list[1]`, that is fine. We can type the list
484 // as `List[Any]`.
485 _ => (Type::Any, Source::None),
486 };
487 SourcedType { type_, source }
488 }
489
490 /// Return whether `T` (`self`) is a subtype of `U` (`other`).
491 ///
492 /// What it means to be a subtype: if we take an arbitrary instance `t` of
493 /// type `T`, is it an instance of `U`? There are three possible outcomes:
494 ///
495 /// 1. Yes, irrespective of `t`. `T` is a subtype of `U`: `T ≤ U`.
496 /// 2. No, irrespective of `t`. `T` is not a subtype of `U`: `not (T ≤ U)`.
497 /// 3. It depends on `t`, the two types are not orderable.
498 ///
499 /// Note that case 2 (`not (T ≤ U)`) does not imply the converse! It does
500 /// *not* mean that `U ≤ T` holds!
501 ///
502 /// Also, we do make some exceptions to this, because it's more helpful to
503 /// catch type errors than to be able to type any possible expression that
504 /// can be evaluated. For example, `not (Number ≤ String)` is definitely true.
505 /// We would like `List` to be covariant in its argument, so we could say
506 /// `List[T] ≤ List[U] <=> T ≤ U`. We would get `not (List[Number] ≤ List[String])`.
507 /// But that violates the above definition, because `[]` is an instance of
508 /// both! But in this case, reporting an error if the element types mismatch
509 /// is helpful, so we won't make `[]` an exception that causes a runtime
510 /// check.
511 pub fn is_subtype_of(&self, other: &SourcedType) -> TypeDiff<SourcedType> {
512 match (&self.type_, &other.type_) {
513 // Void is a subtype of everything, Any a supertype of everything,
514 // they are the top and bottom of the lattice.
515 (Type::Void, _) => TypeDiff::Ok(self.clone()),
516 (_, Type::Any) => TypeDiff::Ok(self.clone()),
517
518 // If I take any value from not-Void, it is not a member of Void.
519 (_, Type::Void) => TypeDiff::Error(Mismatch::Atom {
520 actual: self.clone(),
521 expected: other.clone(),
522 }),
523
524 // If I take any arbitrary value, is it a member of some type T,
525 // when T is not `Any` (that case is already covered above)?
526 // We don't know, it depends on T.
527 (Type::Any, _) => TypeDiff::Defer(other.clone()),
528
529 // Every type is a subtype of itself. We preserve the right-hand
530 // side as the type because usually that has the more interesting
531 // source (it has a requirement). TODO: Do I need to meet the sources,
532 // or will it work fine like this?
533 (Type::Bool, Type::Bool) => TypeDiff::Ok(other.clone()),
534 (Type::Number, Type::Number) => TypeDiff::Ok(other.clone()),
535 (Type::Null, Type::Null) => TypeDiff::Ok(other.clone()),
536 (Type::String, Type::String) => TypeDiff::Ok(other.clone()),
537
538 // The collection types are covariant in their argument.
539 // E.g. `List[Number] < List[Any]`.
540 (Type::List(l1), Type::List(l2)) => match l1.is_subtype_of(l2) {
541 TypeDiff::Ok(..) => TypeDiff::Ok(self.clone()),
542 TypeDiff::Defer(..) => TypeDiff::Defer(other.clone()),
543 error => TypeDiff::Error(Mismatch::List(error.into())),
544 },
545 (Type::Set(l1), Type::Set(l2)) => match l1.is_subtype_of(l2) {
546 TypeDiff::Ok(..) => TypeDiff::Ok(self.clone()),
547 TypeDiff::Defer(..) => TypeDiff::Defer(other.clone()),
548 error => TypeDiff::Error(Mismatch::Set(error.into())),
549 },
550 (Type::Dict(d1), Type::Dict(d2)) => {
551 let dk = d1.key.is_subtype_of(&d2.key);
552 let dv = d1.value.is_subtype_of(&d2.value);
553 match (dk, dv) {
554 (TypeDiff::Ok(..), TypeDiff::Ok(..)) => TypeDiff::Ok(self.clone()),
555 // If we are unsure about any, then we are unsure about the
556 // entire thing.
557 (
558 TypeDiff::Ok(tk) | TypeDiff::Defer(tk),
559 TypeDiff::Ok(tv) | TypeDiff::Defer(tv),
560 ) => {
561 let dict = Dict { key: tk, value: tv };
562 let styp = SourcedType {
563 type_: Type::Dict(dict.into()),
564 source: Source::None,
565 };
566 TypeDiff::Defer(styp)
567 }
568 // If either the key or value is not a subtype, then the
569 // entire thing is not.
570 (k_diff, v_diff) => {
571 TypeDiff::Error(Mismatch::Dict(k_diff.into(), v_diff.into()))
572 }
573 }
574 }
575 (Type::Function(f1), Type::Function(f2)) => {
576 if f1.args.len() != f2.args.len() {
577 // If we have an arity mismatch, report that directly, because
578 // then we can preserve the sources of the types.
579 TypeDiff::Error(Mismatch::Atom {
580 actual: self.clone(),
581 expected: other.clone(),
582 })
583 } else {
584 match f1.is_subtype_of(f2) {
585 TypeDiff::Ok(..) => TypeDiff::Ok(self.clone()),
586 TypeDiff::Defer(f) => {
587 let styp = SourcedType {
588 type_: Type::Function(f),
589 source: Source::None,
590 };
591 TypeDiff::Defer(styp)
592 }
593 TypeDiff::Error(err) => TypeDiff::Error(err),
594 }
595 }
596 }
597 (Type::Union(u1), _) => u1.is_subtype_of(self.source, other),
598 (_, Type::Union(u2)) => {
599 // This is the reverse case of `Union::is_subtype_of`. We
600 // already know that `self` is not a union. If `self` is a
601 // subtype of *any* element of `u2`, then it is a subtype.
602 // If it is not a subtype of any, then it's certainly an error.
603 let mut all_error = true;
604 for candidate in u2.members.iter() {
605 match self.is_subtype_of(candidate) {
606 TypeDiff::Ok(t) => return TypeDiff::Ok(t),
607 TypeDiff::Error(..) => continue,
608 TypeDiff::Defer(..) => all_error = false,
609 }
610 }
611 if all_error {
612 // If the actual type is not a subtype of any of the members,
613 // then it's definitely an error, report it as an atom.
614 // I feel like there should be better ways of reporting this.
615 // If we expect e.g. `Union[List[T], Set[T]]`, and we have
616 // a `List[U]`, then normally we would report an inner
617 // mismatch inside List and an Atom on T and U, but with the
618 // union we throw away all of those. Ideas for how to report
619 // this better are welcome.
620 TypeDiff::Error(Mismatch::Atom {
621 expected: other.clone(),
622 actual: self.clone(),
623 })
624 } else {
625 // If we can't prove statically that it's a subtype or error,
626 // defer to runtime. Possibly the checks above ruled out some
627 // error cases that we no longer need to check, and that
628 // enable returning a more precise type. But for now, just
629 // clone the expected type; it's cheaper too.
630 TypeDiff::Defer(other.clone())
631 }
632 }
633
634 // If we have any other combination of types, they are incompatible.
635 _ => TypeDiff::Error(Mismatch::Atom {
636 actual: self.clone(),
637 expected: other.clone(),
638 }),
639 }
640 }
641
642 /// If the type is a list, set, or dict, return its element type(s).
643 pub fn element_type(&self) -> ElementType {
644 match &self.type_ {
645 // If it's any, it could be a collection, but we can't pinpoint
646 // where it came from. If it's a union, if every element is a
647 // collection then we could still conclude something about the
648 // element type, but we are not going to bother, and say `Any`.
649 Type::Any | Type::Union(_) => ElementType::Any,
650 Type::List(inner) => ElementType::Scalar(inner.clone()),
651 Type::Set(inner) => ElementType::Scalar(inner.clone()),
652 Type::Dict(inner) => ElementType::Dict(inner.clone()),
653 _ => ElementType::None,
654 }
655 }
656
657 /// Add context to a type error about why a particular type was expected.
658 pub fn explain_error(&self, side: Side, error: &mut Error) {
659 let side_verb = match side {
660 Side::Expected => "Expected ",
661 Side::Actual => "Found ",
662 };
663
664 // Note, we don't highlight the type name in the usual type color.
665 // The messages become too distracting if we do.
666 let type_name = self.type_.short_name();
667
668 match &self.source {
669 Source::None => (),
670
671 // TODO: Add information about the builtin (function and arg name?).
672 // At this point builtin types are not involved in type errors,
673 // because we don't resolve anything that produces them at typecheck
674 // time, and we don't yet typecheck arguments in function calls.
675 Source::Builtin => panic!("Currently builtins are not involved in type errors."),
676
677 Source::Literal(at) => {
678 let msg = concat! { side_verb type_name " because of this value." };
679 error.add_note(*at, msg)
680 }
681
682 Source::EmptyCollection(at) => {
683 let msg = concat! { side_verb type_name " because this collection is empty." };
684 error.add_note(*at, msg)
685 }
686
687 Source::Annotation(at) => {
688 let msg = concat! { side_verb type_name " because of this annotation." };
689 error.add_note(*at, msg)
690 }
691
692 Source::Operator(at) => error.add_note(
693 *at,
694 concat! { side_verb type_name " because of this operator." },
695 ),
696
697 Source::Condition => {
698 error.set_help("There is no implicit conversion, conditions must be boolean.")
699 }
700
701 // TODO: Using `help` instead of `note` is not great because there
702 // can only be one help per error. Either extend that, but probably
703 // better, add spans to the sources?
704 Source::IndexList => error.set_help("List indices must be integers."),
705
706 Source::BuildFile(reason) => error.set_help(*reason),
707 }
708 }
709}
710
711/// Helper to enable using short names in type errors.
712pub trait AsTypeName {
713 fn format_type(&self) -> Doc<'static>;
714 fn is_atom(&self) -> bool;
715}
716
717impl AsTypeName for &'static str {
718 fn format_type(&self) -> Doc<'static> {
719 Doc::from(*self).with_markup(Markup::Type)
720 }
721 fn is_atom(&self) -> bool {
722 true
723 }
724}
725
726impl AsTypeName for SourcedType {
727 fn format_type(&self) -> Doc<'static> {
728 format_type(&self.type_).into_owned()
729 }
730
731 fn is_atom(&self) -> bool {
732 self.type_.is_atom()
733 }
734}
735
736/// Create a builtin type, used by the [`make_type`] macro.
737///
738/// Not intended to be public, but it has to be for the macro to work.
739/// TODO: Is it worth it? Should we just use the type expr parser? But then we
740/// can't handle type variables ...
741#[doc(hidden)]
742pub fn builtin(type_: Type) -> SourcedType {
743 SourcedType {
744 type_,
745 source: Source::Builtin,
746 }
747}
748
749/// Rust eDSL for writing RCL types.
750///
751/// The syntax is similar to RCL, except for the generic and function types, to
752/// make them fit Rust grammar.
753///
754/// * `List[T]` is written `[T]`.
755/// * `Set[T]` is written `{T}`.
756/// * `Dict[K, V]` is written `{K: V}`.
757/// * `(p: P, q: Q) -> R` is written `(fn (p: P, q: Q) -> R)`
758macro_rules! make_type {
759 (Any) => { builtin(Type::Any) };
760 (Number) => { builtin(Type::Number) };
761 (Bool) => { builtin(Type::Bool) };
762 (String) => { builtin(Type::String) };
763 ([$elem:tt]) => { builtin(Type::List(Rc::new(make_type!($elem)))) };
764 ({$elem:tt}) => { builtin(Type::Set(Rc::new(make_type!($elem)))) };
765 ({$k:tt: $v:tt}) => {{
766 use std::rc::Rc;
767 use crate::types::{Dict, Type};
768 builtin(Type::Dict(Rc::new(Dict {
769 key: make_type!($k),
770 value: make_type!($v),
771 })))
772 }};
773 ((fn ($( $arg_name:ident: $arg_type:tt ),*) -> $result:tt)) => {
774 builtin(Type::Function(Rc::new(
775 make_function!(($( $arg_name:$arg_type ),*) -> $result)
776 )))
777 };
778}
779pub(crate) use make_type;
780
781/// Rust eDSL for writing RCL function types.
782///
783/// See also [`make_type!`] for the syntax. This does not include the enclosing
784/// `(fn ...)`, parens and `fn`, only the `...` is input to this macro.
785macro_rules! make_function {
786 (($( $arg_name:ident: $arg_type:tt ),*) -> $result:tt) => {
787 Function {
788 args: vec![
789 $( FunctionArg {
790 name: Some(stringify!($arg_name).into()),
791 span: None,
792 type_: make_type!($arg_type),
793 }),*
794 ],
795 result: make_type!($result),
796 }
797 };
798}
799pub(crate) use make_function;
800
801#[cfg(test)]
802mod test {
803 use super::{Function, FunctionArg, Source, SourcedType, Type};
804 use crate::source::{DocId, Span};
805
806 fn mk_type(type_: Type) -> SourcedType {
807 SourcedType {
808 type_,
809 source: Source::None,
810 }
811 }
812
813 #[test]
814 fn function_ord_ignores_names() {
815 let mut f1 = Function {
816 args: vec![
817 FunctionArg {
818 name: Some("a".into()),
819 span: None,
820 type_: mk_type(Type::Number),
821 },
822 FunctionArg {
823 name: Some("b".into()),
824 span: None,
825 type_: mk_type(Type::Bool),
826 },
827 ],
828 result: mk_type(Type::String),
829 };
830 let mut f2 = f1.clone();
831 assert_eq!(f1, f2);
832
833 // Even when we delete the names entirely, the functions should still
834 // be equal.
835 f2.args[0].name = None;
836 assert_eq!(f1, f2);
837
838 // Or when we add spans, it shouldn't affect things.
839 f2.args[0].span = Some(Span::new(DocId(0), 0, 0));
840 assert_eq!(f1, f2);
841
842 // Void orders before String.
843 f1.result = mk_type(Type::Void);
844 assert!(f1 < f2);
845
846 // Void orders before Number.
847 f1.result = mk_type(Type::String);
848 f1.args[0].type_ = mk_type(Type::Void);
849 assert!(f1 < f2);
850
851 // Void orders before Bool.
852 f1.args[0].type_ = mk_type(Type::Number);
853 f1.args[1].type_ = mk_type(Type::Void);
854 assert!(f1 < f2);
855 // Also trigger the `cmp` method, because `<` doesn't.
856 assert_eq!(f1.cmp(&f2), std::cmp::Ordering::Less);
857
858 // Now we are back to the initial equality (but with names changed).
859 f1.args[1].type_ = mk_type(Type::Bool);
860 assert_eq!(f1, f2);
861
862 // Having fewer args makes it order before.
863 f1.args.pop();
864 assert!(f1 < f2);
865 }
866}