A reasonable configuration language rcl-lang.org
configuration-language json
at master 866 lines 33 kB view raw
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}