1use std::collections::{BTreeMap, BTreeSet};
431use std::fmt;
432use std::sync::Arc;
433
434use cove_diag::{Diagnostic, FileId, Severity, Span};
435use cove_schema::builtins::{
436 BuiltinSchema, BuiltinType, FreeBuiltinKind, FreeBuiltinSchema, MethodSchema, ParamSchema,
437 MAP_ENTRY, NONE_CASE, SCOPE,
438};
439use cove_schema::{
440 HostSchemas, HostType, ModuleSchema, OperationSchema, ResourceSchema, TypeSchema,
441};
442use cove_syntax::ast::{
443 Arg, BinaryOp, Block, EnumDecl, Expr, ExprId, ExprKind, FnDecl, GenericParam, Ident, ItemKind,
444 MatchArm, Param, Pattern, PatternKind, Stmt, StmtKind, StrPart, StructDecl, TraitMethod, Type,
445 TypeKind, UnaryOp,
446};
447
448use crate::facts::{Facts, MethodTarget, Signature};
449use crate::package::Package;
450use crate::resolve::{Conformance, Program, ResolvedModule, TraitEntry};
451
452pub const MISMATCH: &str = "cove::type::mismatch";
455pub const ARITY: &str = "cove::type::arity";
457pub const MISSING_ARGUMENT: &str = "cove::type::missing_argument";
459pub const UNKNOWN_LABEL: &str = "cove::type::unknown_label";
461pub const UNKNOWN_NAME: &str = "cove::type::unknown_name";
463pub const UNRESOLVED_NAME: &str = "cove::type::unresolved_name";
465pub const UNKNOWN_TYPE: &str = "cove::type::unknown_type";
467pub const HOST_TYPE: &str = "cove::type::host_type";
470pub const UNKNOWN_HOST_TYPE: &str = "cove::type::unknown_host_type";
472pub const UNKNOWN_HOST_OPERATION: &str = "cove::type::unknown_host_operation";
475pub const TYPE_ARGUMENTS: &str = "cove::type::type_arguments";
477pub const ALIAS_CYCLE: &str = "cove::type::alias_cycle";
479pub const LAYOUT_CYCLE: &str = "cove::type::layout_cycle";
481pub const UNKNOWN_FIELD: &str = "cove::type::unknown_field";
483pub const OPAQUE_FIELD: &str = "cove::type::opaque_field";
486pub const OPAQUE_CONSTRUCTION: &str = "cove::type::opaque_construction";
489pub const UNKNOWN_METHOD: &str = "cove::type::unknown_method";
491pub const UNKNOWN_ASSOCIATED: &str = "cove::type::unknown_associated_function";
493pub const UNKNOWN_CASE: &str = "cove::type::unknown_case";
495pub const PAYLOAD_ARITY: &str = "cove::type::payload_arity";
497pub const OPERATOR: &str = "cove::type::operator";
499pub const CONDITION: &str = "cove::type::condition";
501pub const BRANCHES: &str = "cove::type::branches";
503pub const TRY_OPERAND: &str = "cove::type::try_operand";
505pub const TRY_RETURN: &str = "cove::type::try_return";
507pub const AWAIT_OPERAND: &str = "cove::type::await_operand";
509pub const SCOPE_CHILD_FAILURE: &str = "cove::type::scope_child_failure";
512pub const ITERABLE: &str = "cove::type::iterable";
514pub const NOT_CALLABLE: &str = "cove::type::not_callable";
516pub const PATTERN: &str = "cove::type::pattern";
518pub const RECEIVER: &str = "cove::type::receiver";
520pub const NOT_A_PLACE: &str = "cove::type::not_a_place";
523pub const READ_ONLY_PLACE: &str = "cove::type::read_only_place";
531pub const LABEL_ORDER: &str = "cove::type::label_order";
534pub const ENTRY: &str = "cove::type::entry";
536pub const UNKNOWN_TRAIT: &str = "cove::type::unknown_trait";
538pub const UNSATISFIED_BOUND: &str = "cove::type::unsatisfied_bound";
540pub const UNBOUNDED_PARAMETER: &str = "cove::type::unbounded_parameter";
542pub const CONFORMANCE_SIGNATURE: &str = "cove::type::conformance_signature";
544pub const DYN_ASSOCIATED: &str = "cove::type::dyn_associated_function";
546pub const DYN_MUTATING: &str = "cove::type::dyn_mutating_method";
548pub const UNSUPPORTED_BOUND: &str = "cove::type::unsupported_bound";
550pub const UNKNOWN_MEMBER: &str = "cove::type::unknown_member";
552pub const TEST: &str = "cove::type::test";
554pub const TASK_SAFETY: &str = "cove::type::task_safety";
556pub const MISSING_PARAMETER_TYPE: &str = "cove::type::missing_parameter_type";
559pub const VARIADIC_POSITION: &str = "cove::type::variadic_position";
562pub const VARIADIC_DEFAULT: &str = "cove::type::variadic_default";
564pub const VARIADIC_LAMBDA: &str = "cove::type::variadic_lambda";
567pub const UNCONSTRAINED_RESULT: &str = "cove::type::unconstrained_result";
570pub const UNCONSTRAINED_FIELD: &str = "cove::type::unconstrained_field";
573pub const VARIADIC_AS_VALUE: &str = "cove::type::variadic_as_value";
576pub const NOT_A_VALUE: &str = "cove::type::not_a_value";
578pub const LAMBDA_RETURN: &str = "cove::type::lambda_return";
580pub const UNCONSTRAINED: &str = "cove::type::unconstrained";
585pub const RECURSIVE_TYPE: &str = "cove::type::recursive_type";
588pub const INFERENCE_CONFLICT: &str = "cove::type::inference_conflict";
591
592const TASK_SAFETY_RULE: &str = "Immutable task-safe values such as arrays may cross task boundaries. A vector cannot cross, even through `let`; finish it as an array or wrap mutable state in `Shared` or another synchronized type. Closures are task-safe only when every capture is.";
598
599const SCOPE_CHILD_RULE: &str = "Leaving a `scope` waits for every task nothing awaited, and a task whose value is `Err` returns that failure from the function the scope was written in, exactly as `?` would.";
604
605const TRY_LAMBDA_RULE: &str = "`expr?` returns the error from the function it is written in, and a function value is one. A function value no place declares a result type for produces what its body's value proves, so that value is what has to carry the failure.";
610
611const LAYOUT_CYCLE_RULE: &str = "A value type may not contain itself. A recursive cycle must pass through a type whose value is a reference: `String`, `Array`, `Map`, `Set`, `Vector`, `Shared`, a closure, or a `dyn` trait object.";
615
616pub fn check(package: &Package, program: &Program) -> Vec<Diagnostic> {
627 check_with(package, program, &HostSchemas::new())
628}
629
630pub fn check_with(package: &Package, program: &Program, schemas: &HostSchemas) -> Vec<Diagnostic> {
638 check_facts(package, program, schemas).0
639}
640
641pub fn check_facts(
651 package: &Package,
652 program: &Program,
653 schemas: &HostSchemas,
654) -> (Vec<Diagnostic>, Facts) {
655 let mut diagnostics = Vec::new();
656 let mut envs: BTreeMap<&str, ImportEnv> = BTreeMap::new();
657 let mut checked: BTreeMap<&str, Checker> = BTreeMap::new();
658 for name in import_order(program) {
659 let module = &program.modules[name];
660 let mut checker = Checker::new(module, program, schemas);
661 checker.import(&envs);
662 checker.prepare();
663 envs.insert(name, checker.export_env());
664 checker.check_bodies();
665 diagnostics.append(&mut checker.diagnostics);
666 checked.insert(name, checker);
667 }
668 check_entries(package, &checked, &mut diagnostics);
669 check_tests(program, &checked, &mut diagnostics);
670 let mut facts = Facts::default();
674 for (_, checker) in checked {
675 facts.merge(checker.facts);
676 }
677 if !diagnostics
682 .iter()
683 .any(|diagnostic| diagnostic.severity == Severity::Error)
684 {
685 diagnostics.extend(crate::unique::check(program, &facts));
686 }
687 (diagnostics, facts)
688}
689
690fn check_tests(
697 program: &Program,
698 checked: &BTreeMap<&str, Checker<'_>>,
699 diagnostics: &mut Vec<Diagnostic>,
700) {
701 let required = Ty::Result(Box::new(Ty::Unit), Box::new(Ty::Error));
702 for test in program.tests() {
703 let Some(checker) = checked.get(test.module) else {
704 continue;
705 };
706 let Some(sig) = checker.functions.get(test.name) else {
707 continue;
708 };
709 let shape = format!("write `test fn {}() -> Result<Unit, Error>`", test.name);
710
711 if let Some(param) = sig.params.first() {
712 diagnostics.push(
713 Diagnostic::error(
714 TEST,
715 format!(
716 "test `{}` declares {} parameter(s)",
717 test.qualified_name(),
718 sig.params.len()
719 ),
720 )
721 .at(param.span)
722 .rule("A `test fn` takes no parameters: the test runner is its only caller, and it passes nothing.")
723 .help(shape.clone()),
724 );
725 }
726
727 if sig.is_async {
728 diagnostics.push(
729 Diagnostic::error(
730 TEST,
731 format!("test `{}` is `async`", test.qualified_name()),
732 )
733 .at(test.entry.decl.name.span)
734 .rule("A `test fn` is an ordinary function the test runner calls and awaits nothing of.")
735 .help(shape.clone()),
736 );
737 }
738
739 if !sig.ret.matches(&required) {
740 diagnostics.push(
741 Diagnostic::error(
742 TEST,
743 format!(
744 "test `{}` returns `{}`, but a test returns `Result<Unit, Error>`",
745 test.qualified_name(),
746 sig.ret
747 ),
748 )
749 .at(sig.ret_span)
750 .rule("A test reports failure the way every other Cove function reports expected failure, so it returns `Result<Unit, Error>` and `?` works inside it.")
751 .help(shape),
752 );
753 }
754 }
755}
756
757fn import_order(program: &Program) -> Vec<&str> {
763 let mut order: Vec<&str> = Vec::new();
764 let mut placed: BTreeSet<&str> = BTreeSet::new();
765 loop {
766 let mut progressed = false;
767 for (name, module) in &program.modules {
768 if placed.contains(name.as_str()) {
769 continue;
770 }
771 let ready = module
772 .dependencies()
773 .iter()
774 .all(|dep| placed.contains(dep) || !program.modules.contains_key(*dep));
775 if ready {
776 order.push(name.as_str());
777 placed.insert(name.as_str());
778 progressed = true;
779 }
780 }
781 if !progressed {
782 break;
783 }
784 }
785 order.extend(
786 program
787 .modules
788 .keys()
789 .map(String::as_str)
790 .filter(|name| !placed.contains(name)),
791 );
792 order
793}
794
795fn conformance_key(module: &ResolvedModule, conformance: &Conformance) -> (String, String) {
799 let key = |owner: &str, name: &str| {
800 if owner == module.name {
801 name.to_string()
802 } else {
803 format!("{owner}.{name}")
804 }
805 };
806 (
807 key(&conformance.trait_module, &conformance.trait_name),
808 key(&conformance.type_module, &conformance.type_name),
809 )
810}
811
812fn qualified_name(name: &Arc<str>, module: &str) -> Arc<str> {
815 if name.contains('.') {
816 name.clone()
817 } else {
818 format!("{module}.{name}").into()
819 }
820}
821
822fn foreign_type(key: &str) -> Option<(&str, &str)> {
831 key.rsplit_once('.')
832}
833
834#[derive(Clone, Copy, PartialEq, Eq, Debug)]
842enum FieldUse {
843 Read,
844 Write,
845}
846
847impl FieldUse {
848 fn refused(self) -> &'static str {
850 match self {
851 FieldUse::Read => "read",
852 FieldUse::Write => "assigned",
853 }
854 }
855
856 fn correction(self) -> &'static str {
858 match self {
859 FieldUse::Read => "read the value through an exported method, such as",
860 FieldUse::Write => "change the value through an exported method, such as",
861 }
862 }
863}
864
865#[derive(Clone, Debug, Default)]
870struct ImportEnv {
871 structs: BTreeMap<String, StructSig>,
872 enums: BTreeMap<String, EnumSig>,
873 aliases: BTreeMap<String, (Vec<Arc<str>>, Ty)>,
874 functions: BTreeMap<String, FnSig>,
875 methods: BTreeMap<(String, String), FnSig>,
876 traits: BTreeMap<String, BTreeMap<String, FnSig>>,
877 conformances: BTreeSet<(String, String)>,
886}
887
888fn not_task_safe(ty: &Ty) -> Option<&Ty> {
896 match ty {
897 Ty::Vector(_) | Ty::Task(_) | Ty::Scope => Some(ty),
898 Ty::Shared(_) => None,
899 Ty::Array(inner) | Ty::Set(inner) | Ty::Option(inner) => not_task_safe(inner),
900 Ty::Map(key, value) | Ty::MapEntry(key, value) | Ty::Result(key, value) => {
901 not_task_safe(key).or_else(|| not_task_safe(value))
902 }
903 Ty::Struct(_, args) | Ty::Enum(_, args) => args.iter().find_map(not_task_safe),
904 _ => None,
907 }
908}
909
910fn qualify(ty: &Ty, module: &str) -> Ty {
918 let qualified = |name: &Arc<str>| qualified_name(name, module);
919 match ty {
920 Ty::Array(inner) => Ty::Array(Box::new(qualify(inner, module))),
921 Ty::Vector(inner) => Ty::Vector(Box::new(qualify(inner, module))),
922 Ty::Set(inner) => Ty::Set(Box::new(qualify(inner, module))),
923 Ty::Option(inner) => Ty::Option(Box::new(qualify(inner, module))),
924 Ty::Task(inner) => Ty::Task(Box::new(qualify(inner, module))),
925 Ty::Shared(inner) => Ty::Shared(Box::new(qualify(inner, module))),
926 Ty::Map(k, v) => Ty::Map(Box::new(qualify(k, module)), Box::new(qualify(v, module))),
927 Ty::MapEntry(k, v) => {
928 Ty::MapEntry(Box::new(qualify(k, module)), Box::new(qualify(v, module)))
929 }
930 Ty::Result(t, e) => Ty::Result(Box::new(qualify(t, module)), Box::new(qualify(e, module))),
931 Ty::Struct(name, args) => Ty::Struct(
932 qualified(name),
933 args.iter().map(|arg| qualify(arg, module)).collect(),
934 ),
935 Ty::Enum(name, args) => Ty::Enum(
936 qualified(name),
937 args.iter().map(|arg| qualify(arg, module)).collect(),
938 ),
939 Ty::Dyn(name) => Ty::Dyn(qualified(name)),
942 Ty::Fn(f) => Ty::func(
943 f.is_async,
944 f.params.iter().map(|p| qualify(p, module)).collect(),
945 qualify(&f.ret, module),
946 ),
947 other => other.clone(),
948 }
949}
950
951#[derive(Clone, Copy, Debug, PartialEq, Eq)]
962pub enum Unknown {
963 Recovery,
966 DynamicBoundary,
970 Unconstrained,
974 Var(u32),
986 Placeholder,
996}
997
998impl Unknown {
999 fn is_accounted_for(self) -> bool {
1011 !matches!(self, Unknown::Placeholder)
1012 }
1013}
1014
1015#[derive(Clone, Debug, PartialEq)]
1024pub enum Ty {
1025 Unknown(Unknown),
1027 Never,
1030 Any,
1056 Unit,
1057 Bool,
1058 Int,
1059 Float,
1060 Str,
1061 Duration,
1062 Error,
1063 Range,
1064 Array(Box<Ty>),
1065 Vector(Box<Ty>),
1066 Set(Box<Ty>),
1067 Map(Box<Ty>, Box<Ty>),
1068 MapEntry(Box<Ty>, Box<Ty>),
1071 Option(Box<Ty>),
1072 Result(Box<Ty>, Box<Ty>),
1073 Task(Box<Ty>),
1074 Shared(Box<Ty>),
1082 Scope,
1084 Struct(Arc<str>, Vec<Ty>),
1086 Enum(Arc<str>, Vec<Ty>),
1088 Fn(Arc<FnTy>),
1089 Param(Arc<str>),
1091 Dyn(Arc<str>),
1098 Host(Arc<str>),
1108}
1109
1110#[derive(Clone, Debug, PartialEq)]
1112pub struct FnTy {
1113 pub is_async: bool,
1114 pub params: Vec<Ty>,
1115 pub ret: Ty,
1116}
1117
1118impl Ty {
1119 fn func(is_async: bool, params: Vec<Ty>, ret: Ty) -> Ty {
1120 Ty::Fn(Arc::new(FnTy {
1121 is_async,
1122 params,
1123 ret,
1124 }))
1125 }
1126
1127 fn recovery() -> Ty {
1135 Ty::Unknown(Unknown::Recovery)
1136 }
1137
1138 fn abstention(&self) -> Option<Ty> {
1146 match self {
1147 Ty::Any => Some(Ty::Any),
1148 Ty::Unknown(kind @ (Unknown::Recovery | Unknown::DynamicBoundary)) => {
1149 Some(Ty::Unknown(*kind))
1150 }
1151 _ => None,
1152 }
1153 }
1154
1155 fn abstain(&self) -> Ty {
1172 match self {
1173 Ty::Unknown(Unknown::DynamicBoundary) => Ty::dynamic_boundary(),
1174 _ => Ty::recovery(),
1175 }
1176 }
1177
1178 fn dynamic_boundary() -> Ty {
1192 Ty::Unknown(Unknown::DynamicBoundary)
1193 }
1194
1195 fn unconstrained() -> Ty {
1211 Ty::Unknown(Unknown::Unconstrained)
1212 }
1213
1214 fn var(id: u32) -> Ty {
1218 Ty::Unknown(Unknown::Var(id))
1219 }
1220
1221 fn placeholder() -> Ty {
1232 Ty::Unknown(Unknown::Placeholder)
1233 }
1234
1235 fn is_wild(&self) -> bool {
1238 matches!(self, Ty::Unknown(_) | Ty::Never | Ty::Any)
1239 }
1240
1241 fn is_accounted_for(&self) -> bool {
1248 match self {
1249 Ty::Unknown(kind) => kind.is_accounted_for(),
1250 Ty::Never => true,
1251 Ty::Any => true,
1256 _ => false,
1257 }
1258 }
1259
1260 fn holds_placeholder(&self) -> bool {
1267 match self {
1268 Ty::Unknown(kind) => matches!(kind, Unknown::Placeholder),
1269 Ty::Array(inner)
1270 | Ty::Vector(inner)
1271 | Ty::Set(inner)
1272 | Ty::Option(inner)
1273 | Ty::Task(inner)
1274 | Ty::Shared(inner) => inner.holds_placeholder(),
1275 Ty::Map(k, v) | Ty::MapEntry(k, v) | Ty::Result(k, v) => {
1276 k.holds_placeholder() || v.holds_placeholder()
1277 }
1278 Ty::Struct(_, args) | Ty::Enum(_, args) => {
1279 args.iter().any(|arg| arg.holds_placeholder())
1280 }
1281 Ty::Fn(f) => f.params.iter().any(Ty::holds_placeholder) || f.ret.holds_placeholder(),
1282 _ => false,
1283 }
1284 }
1285
1286 fn each_var<F: FnMut(u32)>(&self, f: &mut F) {
1293 match self {
1294 Ty::Unknown(Unknown::Var(id)) => f(*id),
1295 Ty::Array(inner)
1296 | Ty::Vector(inner)
1297 | Ty::Set(inner)
1298 | Ty::Option(inner)
1299 | Ty::Task(inner)
1300 | Ty::Shared(inner) => inner.each_var(f),
1301 Ty::Map(k, v) | Ty::MapEntry(k, v) | Ty::Result(k, v) => {
1302 k.each_var(f);
1303 v.each_var(f);
1304 }
1305 Ty::Struct(_, args) | Ty::Enum(_, args) => {
1306 for arg in args {
1307 arg.each_var(f);
1308 }
1309 }
1310 Ty::Fn(func) => {
1311 for param in &func.params {
1312 param.each_var(f);
1313 }
1314 func.ret.each_var(f);
1315 }
1316 _ => {}
1317 }
1318 }
1319
1320 fn holds_var(&self) -> bool {
1326 let mut found = false;
1327 self.each_var(&mut |_| found = true);
1328 found
1329 }
1330
1331 fn vars(&self) -> Vec<u32> {
1333 let mut found = Vec::new();
1334 self.each_var(&mut |id| found.push(id));
1335 found
1336 }
1337
1338 fn resolved(&self, vars: &[TyVar]) -> Ty {
1356 match self {
1357 Ty::Unknown(Unknown::Var(id)) => vars
1358 .get(*id as usize)
1359 .and_then(|var| var.solved.as_ref())
1360 .map(|(ty, _)| ty.resolved(vars))
1361 .unwrap_or_else(|| self.clone()),
1362 Ty::Array(inner) => Ty::Array(Box::new(inner.resolved(vars))),
1363 Ty::Vector(inner) => Ty::Vector(Box::new(inner.resolved(vars))),
1364 Ty::Set(inner) => Ty::Set(Box::new(inner.resolved(vars))),
1365 Ty::Option(inner) => Ty::Option(Box::new(inner.resolved(vars))),
1366 Ty::Task(inner) => Ty::Task(Box::new(inner.resolved(vars))),
1367 Ty::Shared(inner) => Ty::Shared(Box::new(inner.resolved(vars))),
1368 Ty::Map(k, v) => Ty::Map(Box::new(k.resolved(vars)), Box::new(v.resolved(vars))),
1369 Ty::MapEntry(k, v) => {
1370 Ty::MapEntry(Box::new(k.resolved(vars)), Box::new(v.resolved(vars)))
1371 }
1372 Ty::Result(k, v) => Ty::Result(Box::new(k.resolved(vars)), Box::new(v.resolved(vars))),
1373 Ty::Struct(name, args) => Ty::Struct(
1374 name.clone(),
1375 args.iter().map(|a| a.resolved(vars)).collect(),
1376 ),
1377 Ty::Enum(name, args) => Ty::Enum(
1378 name.clone(),
1379 args.iter().map(|a| a.resolved(vars)).collect(),
1380 ),
1381 Ty::Fn(func) => Ty::func(
1382 func.is_async,
1383 func.params.iter().map(|p| p.resolved(vars)).collect(),
1384 func.ret.resolved(vars),
1385 ),
1386 other => other.clone(),
1387 }
1388 }
1389
1390 fn settled(&self, vars: &[TyVar]) -> Ty {
1398 match self {
1399 Ty::Unknown(Unknown::Var(id)) => vars
1400 .get(*id as usize)
1401 .and_then(|var| var.solved.as_ref())
1402 .map(|(ty, _)| ty.clone())
1403 .unwrap_or_else(Ty::unconstrained),
1404 Ty::Array(inner) => Ty::Array(Box::new(inner.settled(vars))),
1405 Ty::Vector(inner) => Ty::Vector(Box::new(inner.settled(vars))),
1406 Ty::Set(inner) => Ty::Set(Box::new(inner.settled(vars))),
1407 Ty::Option(inner) => Ty::Option(Box::new(inner.settled(vars))),
1408 Ty::Task(inner) => Ty::Task(Box::new(inner.settled(vars))),
1409 Ty::Shared(inner) => Ty::Shared(Box::new(inner.settled(vars))),
1410 Ty::Map(k, v) => Ty::Map(Box::new(k.settled(vars)), Box::new(v.settled(vars))),
1411 Ty::MapEntry(k, v) => {
1412 Ty::MapEntry(Box::new(k.settled(vars)), Box::new(v.settled(vars)))
1413 }
1414 Ty::Result(k, v) => Ty::Result(Box::new(k.settled(vars)), Box::new(v.settled(vars))),
1415 Ty::Struct(name, args) => {
1416 Ty::Struct(name.clone(), args.iter().map(|a| a.settled(vars)).collect())
1417 }
1418 Ty::Enum(name, args) => {
1419 Ty::Enum(name.clone(), args.iter().map(|a| a.settled(vars)).collect())
1420 }
1421 Ty::Fn(func) => Ty::func(
1422 func.is_async,
1423 func.params.iter().map(|p| p.settled(vars)).collect(),
1424 func.ret.settled(vars),
1425 ),
1426 other => other.clone(),
1427 }
1428 }
1429
1430 fn matches(&self, other: &Ty) -> bool {
1435 if self.is_wild() || other.is_wild() {
1436 return true;
1437 }
1438 match (self, other) {
1439 (Ty::Array(a), Ty::Array(b))
1440 | (Ty::Vector(a), Ty::Vector(b))
1441 | (Ty::Set(a), Ty::Set(b))
1442 | (Ty::Option(a), Ty::Option(b))
1443 | (Ty::Task(a), Ty::Task(b))
1444 | (Ty::Shared(a), Ty::Shared(b)) => a.matches(b),
1445 (Ty::Map(ak, av), Ty::Map(bk, bv))
1446 | (Ty::MapEntry(ak, av), Ty::MapEntry(bk, bv))
1447 | (Ty::Result(ak, av), Ty::Result(bk, bv)) => ak.matches(bk) && av.matches(bv),
1448 (Ty::Struct(a, aargs), Ty::Struct(b, bargs))
1449 | (Ty::Enum(a, aargs), Ty::Enum(b, bargs)) => {
1450 a == b
1451 && aargs.len() == bargs.len()
1452 && aargs.iter().zip(bargs).all(|(a, b)| a.matches(b))
1453 }
1454 (Ty::Fn(a), Ty::Fn(b)) => {
1455 a.is_async == b.is_async
1456 && a.params.len() == b.params.len()
1457 && a.params.iter().zip(&b.params).all(|(a, b)| a.matches(b))
1458 && a.ret.matches(&b.ret)
1459 }
1460 (Ty::Param(a), Ty::Param(b))
1461 | (Ty::Dyn(a), Ty::Dyn(b))
1462 | (Ty::Host(a), Ty::Host(b)) => a == b,
1463 (a, b) => std::mem::discriminant(a) == std::mem::discriminant(b),
1464 }
1465 }
1466
1467 fn join(&self, other: &Ty) -> Ty {
1477 match (self, other) {
1478 (Ty::Never, other) | (other, Ty::Never) => other.clone(),
1479 (Ty::Unknown(_) | Ty::Any, other) | (other, Ty::Unknown(_) | Ty::Any) => other.clone(),
1480 (Ty::Array(a), Ty::Array(b)) => Ty::Array(Box::new(a.join(b))),
1481 (Ty::Vector(a), Ty::Vector(b)) => Ty::Vector(Box::new(a.join(b))),
1482 (Ty::Set(a), Ty::Set(b)) => Ty::Set(Box::new(a.join(b))),
1483 (Ty::Option(a), Ty::Option(b)) => Ty::Option(Box::new(a.join(b))),
1484 (Ty::Task(a), Ty::Task(b)) => Ty::Task(Box::new(a.join(b))),
1485 (Ty::Shared(a), Ty::Shared(b)) => Ty::Shared(Box::new(a.join(b))),
1486 (Ty::Map(ak, av), Ty::Map(bk, bv)) => {
1487 Ty::Map(Box::new(ak.join(bk)), Box::new(av.join(bv)))
1488 }
1489 (Ty::MapEntry(ak, av), Ty::MapEntry(bk, bv)) => {
1490 Ty::MapEntry(Box::new(ak.join(bk)), Box::new(av.join(bv)))
1491 }
1492 (Ty::Result(ak, av), Ty::Result(bk, bv)) => {
1493 Ty::Result(Box::new(ak.join(bk)), Box::new(av.join(bv)))
1494 }
1495 (Ty::Struct(a, aargs), Ty::Struct(b, bargs))
1496 if a == b && aargs.len() == bargs.len() =>
1497 {
1498 Ty::Struct(
1499 a.clone(),
1500 aargs.iter().zip(bargs).map(|(a, b)| a.join(b)).collect(),
1501 )
1502 }
1503 (Ty::Enum(a, aargs), Ty::Enum(b, bargs)) if a == b && aargs.len() == bargs.len() => {
1504 Ty::Enum(
1505 a.clone(),
1506 aargs.iter().zip(bargs).map(|(a, b)| a.join(b)).collect(),
1507 )
1508 }
1509 (Ty::Fn(a), Ty::Fn(b))
1510 if a.is_async == b.is_async && a.params.len() == b.params.len() =>
1511 {
1512 Ty::func(
1513 a.is_async,
1514 a.params
1515 .iter()
1516 .zip(&b.params)
1517 .map(|(a, b)| a.join(b))
1518 .collect(),
1519 a.ret.join(&b.ret),
1520 )
1521 }
1522 _ => self.clone(),
1523 }
1524 }
1525
1526 pub fn instantiate(&self, generics: &[Arc<str>], args: &[Ty]) -> Ty {
1535 self.substitute(&substitution(generics, args))
1536 }
1537
1538 fn substitute(&self, subst: &BTreeMap<Arc<str>, Ty>) -> Ty {
1540 if subst.is_empty() {
1541 return self.clone();
1542 }
1543 match self {
1544 Ty::Param(name) => subst.get(name).cloned().unwrap_or_else(|| self.clone()),
1545 Ty::Array(inner) => Ty::Array(Box::new(inner.substitute(subst))),
1546 Ty::Vector(inner) => Ty::Vector(Box::new(inner.substitute(subst))),
1547 Ty::Set(inner) => Ty::Set(Box::new(inner.substitute(subst))),
1548 Ty::Option(inner) => Ty::Option(Box::new(inner.substitute(subst))),
1549 Ty::Task(inner) => Ty::Task(Box::new(inner.substitute(subst))),
1550 Ty::Shared(inner) => Ty::Shared(Box::new(inner.substitute(subst))),
1551 Ty::Map(k, v) => Ty::Map(Box::new(k.substitute(subst)), Box::new(v.substitute(subst))),
1552 Ty::MapEntry(k, v) => {
1553 Ty::MapEntry(Box::new(k.substitute(subst)), Box::new(v.substitute(subst)))
1554 }
1555 Ty::Result(t, e) => {
1556 Ty::Result(Box::new(t.substitute(subst)), Box::new(e.substitute(subst)))
1557 }
1558 Ty::Struct(name, args) => Ty::Struct(
1559 name.clone(),
1560 args.iter().map(|a| a.substitute(subst)).collect(),
1561 ),
1562 Ty::Enum(name, args) => Ty::Enum(
1563 name.clone(),
1564 args.iter().map(|a| a.substitute(subst)).collect(),
1565 ),
1566 Ty::Fn(f) => Ty::func(
1567 f.is_async,
1568 f.params.iter().map(|p| p.substitute(subst)).collect(),
1569 f.ret.substitute(subst),
1570 ),
1571 other => other.clone(),
1572 }
1573 }
1574}
1575
1576impl fmt::Display for Ty {
1579 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1580 match self {
1581 Ty::Unknown(_) => f.write_str("_"),
1584 Ty::Any => f.write_str("Any"),
1588 Ty::Never => f.write_str("!"),
1589 Ty::Unit => f.write_str("()"),
1590 Ty::Bool => f.write_str("Bool"),
1591 Ty::Int => f.write_str("Int"),
1592 Ty::Float => f.write_str("Float"),
1593 Ty::Str => f.write_str("String"),
1594 Ty::Duration => f.write_str("Duration"),
1595 Ty::Error => f.write_str("Error"),
1596 Ty::Range => f.write_str("Range"),
1597 Ty::Scope => f.write_str("Scope"),
1598 Ty::Array(inner) => write!(f, "Array<{inner}>"),
1599 Ty::Vector(inner) => write!(f, "Vector<{inner}>"),
1600 Ty::Set(inner) => write!(f, "Set<{inner}>"),
1601 Ty::Option(inner) => write!(f, "Option<{inner}>"),
1602 Ty::Task(inner) => write!(f, "Task<{inner}>"),
1603 Ty::Shared(inner) => write!(f, "Shared<{inner}>"),
1604 Ty::Map(k, v) => write!(f, "Map<{k}, {v}>"),
1605 Ty::MapEntry(k, v) => write!(f, "MapEntry<{k}, {v}>"),
1606 Ty::Result(t, e) => write!(f, "Result<{t}, {e}>"),
1607 Ty::Param(name) => f.write_str(name),
1608 Ty::Host(name) => f.write_str(name),
1609 Ty::Dyn(name) => write!(f, "dyn {name}"),
1610 Ty::Struct(name, args) | Ty::Enum(name, args) => {
1611 f.write_str(name)?;
1612 if !args.is_empty() {
1613 let args: Vec<String> = args.iter().map(Ty::to_string).collect();
1614 write!(f, "<{}>", args.join(", "))?;
1615 }
1616 Ok(())
1617 }
1618 Ty::Fn(func) => {
1619 if func.is_async {
1620 f.write_str("async ")?;
1621 }
1622 let params: Vec<String> = func.params.iter().map(Ty::to_string).collect();
1623 write!(f, "fn({})", params.join(", "))?;
1624 if func.ret != Ty::Unit {
1625 write!(f, " -> {}", func.ret)?;
1626 }
1627 Ok(())
1628 }
1629 }
1630 }
1631}
1632
1633#[derive(Clone, Debug)]
1638struct ParamSig {
1639 name: String,
1640 ty: Ty,
1641 variadic: bool,
1642 has_default: bool,
1643 is_var: bool,
1651 span: Span,
1652}
1653
1654#[derive(Clone, Debug)]
1657struct TraitBound {
1658 name: Arc<str>,
1659 span: Span,
1660}
1661
1662#[derive(Clone, Debug)]
1664struct FnSig {
1665 generics: Vec<Arc<str>>,
1667 bounds: BTreeMap<Arc<str>, Vec<TraitBound>>,
1671 params: Vec<ParamSig>,
1672 ret: Ty,
1673 ret_span: Span,
1674 is_async: bool,
1675 receiver: Option<Ty>,
1677 receiver_is_var: bool,
1680}
1681
1682impl FnSig {
1683 fn qualified(&self, module: &str) -> FnSig {
1686 FnSig {
1687 generics: self.generics.clone(),
1688 bounds: self
1689 .bounds
1690 .iter()
1691 .map(|(param, bounds)| {
1692 let bounds = bounds
1693 .iter()
1694 .map(|bound| TraitBound {
1695 name: qualified_name(&bound.name, module),
1696 span: bound.span,
1697 })
1698 .collect();
1699 (param.clone(), bounds)
1700 })
1701 .collect(),
1702 params: self
1703 .params
1704 .iter()
1705 .map(|param| ParamSig {
1706 ty: qualify(¶m.ty, module),
1707 ..param.clone()
1708 })
1709 .collect(),
1710 ret: qualify(&self.ret, module),
1711 ret_span: self.ret_span,
1712 is_async: self.is_async,
1713 receiver: self.receiver.as_ref().map(|ty| qualify(ty, module)),
1714 receiver_is_var: self.receiver_is_var,
1715 }
1716 }
1717
1718 fn as_value(&self) -> Ty {
1721 Ty::func(
1722 self.is_async,
1723 self.params.iter().map(|p| p.ty.clone()).collect(),
1724 self.ret.clone(),
1725 )
1726 }
1727}
1728
1729#[derive(Clone, Debug)]
1731struct StructSig {
1732 generics: Vec<Arc<str>>,
1733 fields: Vec<ParamSig>,
1734 opaque: bool,
1741}
1742
1743#[derive(Clone, Debug)]
1745struct EnumSig {
1746 generics: Vec<Arc<str>>,
1747 cases: Vec<CaseSig>,
1748}
1749
1750#[derive(Clone, Debug)]
1751struct CaseSig {
1752 name: String,
1753 payload: Vec<Ty>,
1754 span: Span,
1755}
1756
1757#[derive(Clone, Debug)]
1765struct LayoutMember {
1766 ty: Ty,
1769 label: String,
1771 span: Span,
1773}
1774
1775#[derive(Clone, Debug)]
1778struct LayoutStep {
1779 owner: String,
1781 member: String,
1783 reaches: String,
1785 span: Span,
1786}
1787
1788type LayoutParams = BTreeMap<String, BTreeSet<usize>>;
1797
1798#[derive(Debug, Default)]
1800struct InlineReach {
1801 declarations: Vec<String>,
1804 params: BTreeSet<Arc<str>>,
1806}
1807
1808fn inline_reach(ty: &Ty, params: &LayoutParams, out: &mut InlineReach) {
1827 match ty {
1828 Ty::Param(name) => {
1829 out.params.insert(name.clone());
1830 }
1831 Ty::Struct(name, args) | Ty::Enum(name, args) => {
1832 out.declarations.push(name.to_string());
1833 let Some(inline) = params.get(&**name) else {
1834 return;
1835 };
1836 for (position, arg) in args.iter().enumerate() {
1837 if inline.contains(&position) {
1838 inline_reach(arg, params, out);
1839 }
1840 }
1841 }
1842 Ty::Option(inner) => inline_reach(inner, params, out),
1843 Ty::Result(ok, err) => {
1844 inline_reach(ok, params, out);
1845 inline_reach(err, params, out);
1846 }
1847 Ty::MapEntry(key, value) => {
1848 inline_reach(key, params, out);
1849 inline_reach(value, params, out);
1850 }
1851 _ => {}
1852 }
1853}
1854
1855#[derive(Clone, Debug)]
1857struct Binding {
1858 ty: Ty,
1859 mutable: bool,
1868}
1869
1870#[derive(Debug)]
1881struct TyVar {
1882 owner: Option<Owned>,
1894 solved: Option<(Ty, Span)>,
1896 conflicted: bool,
1899 spoken_for: Option<(Ty, Span)>,
1915 at: Span,
1919 produced: Ty,
1923 abstained: Option<Ty>,
1938 probed: bool,
1945}
1946
1947#[derive(Clone, Debug)]
1949struct Owned {
1950 name: String,
1951 span: Span,
1954 ty: Ty,
1956}
1957
1958#[derive(Clone, Debug)]
1961struct Origin {
1962 span: Span,
1963 label: String,
1964}
1965
1966#[derive(Clone, Debug)]
1969struct Expected {
1970 ty: Ty,
1971 origin: Option<Origin>,
1972}
1973
1974impl Expected {
1975 fn new(ty: Ty, span: Span, label: impl Into<String>) -> Expected {
1976 Expected {
1977 ty,
1978 origin: Some(Origin {
1979 span,
1980 label: label.into(),
1981 }),
1982 }
1983 }
1984
1985 fn abstained(ty: Ty) -> Expected {
1993 Expected { ty, origin: None }
1994 }
1995}
1996
1997struct Checker<'a> {
2012 module: &'a ResolvedModule,
2013 program: &'a Program,
2016 schemas: &'a HostSchemas,
2020 diagnostics: Vec<Diagnostic>,
2021 functions: BTreeMap<String, FnSig>,
2022 methods: BTreeMap<(String, String), FnSig>,
2023 structs: BTreeMap<String, StructSig>,
2024 enums: BTreeMap<String, EnumSig>,
2025 aliases: BTreeMap<String, (Vec<Arc<str>>, Ty)>,
2028 expanding: Vec<String>,
2030 traits: BTreeMap<String, BTreeMap<String, FnSig>>,
2033 conformances: BTreeSet<(String, String)>,
2036 type_params: Vec<Arc<str>>,
2038 bounds: BTreeMap<Arc<str>, Vec<TraitBound>>,
2040 scopes: Vec<BTreeMap<String, Binding>>,
2041 capture_floor: usize,
2051 ret: Ty,
2054 ret_span: Span,
2055 assigned_place: Option<Span>,
2065 ret_stated: bool,
2074 probing: bool,
2077 body_mark: usize,
2087 facts: Facts,
2092 vars: Vec<TyVar>,
2100 open_facts: Vec<(cove_diag::FileId, ExprId)>,
2108 open_scopes: Vec<OpenScope>,
2115 open_lambdas: Vec<Vec<PendingTry>>,
2125}
2126
2127struct OpenScope {
2129 name: String,
2132 children: Vec<SpawnedChild>,
2133}
2134
2135struct SpawnedChild {
2145 span: Span,
2147 scope: String,
2149 binding: Option<String>,
2152 error: Ty,
2154 awaited: bool,
2163}
2164
2165struct PendingTry {
2177 span: Span,
2179 error: Option<Ty>,
2182}
2183
2184impl<'a> Checker<'a> {
2185 fn new(
2186 module: &'a ResolvedModule,
2187 program: &'a Program,
2188 schemas: &'a HostSchemas,
2189 ) -> Checker<'a> {
2190 Checker {
2191 module,
2192 program,
2193 schemas,
2194 diagnostics: Vec::new(),
2195 functions: BTreeMap::new(),
2196 methods: BTreeMap::new(),
2197 structs: BTreeMap::new(),
2198 enums: BTreeMap::new(),
2199 aliases: BTreeMap::new(),
2200 expanding: Vec::new(),
2201 traits: BTreeMap::new(),
2202 conformances: module
2203 .conformances
2204 .values()
2205 .map(|conformance| conformance_key(module, conformance))
2206 .collect(),
2207 type_params: Vec::new(),
2208 bounds: BTreeMap::new(),
2209 scopes: Vec::new(),
2210 capture_floor: 0,
2211 ret: Ty::placeholder(),
2214 ret_span: Span::new(cove_diag::FileId(0), 0, 0),
2215 assigned_place: None,
2216 ret_stated: false,
2217 probing: false,
2218 facts: Facts::default(),
2219 vars: Vec::new(),
2220 body_mark: 0,
2221 open_facts: Vec::new(),
2222 open_scopes: Vec::new(),
2223 open_lambdas: Vec::new(),
2224 }
2225 }
2226
2227 fn host_schema(&self, module: &str) -> Option<ModuleSchema> {
2250 self.schemas.module(module)
2251 }
2252
2253 fn host_declared_type(&self, qualified: &str) -> Option<&'static TypeSchema> {
2256 let (module, name) = qualified.split_once('.')?;
2257 self.host_schema(module)?.declared_type(name)
2258 }
2259
2260 fn host_resource(&self, qualified: &str) -> Option<&'static ResourceSchema> {
2262 let (module, name) = qualified.split_once('.')?;
2263 self.host_schema(module)?.resource(name)
2264 }
2265
2266 fn probe<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
2279 let mark = self.diagnostics.len();
2280 let outer = std::mem::replace(&mut self.probing, true);
2281 let found = f(self);
2282 self.probing = outer;
2283 self.diagnostics.truncate(mark);
2284 found
2285 }
2286
2287 fn key(&self, name: &str) -> String {
2296 match self.module.imports.get(name) {
2297 Some(owner) => format!("{owner}.{name}"),
2298 None => name.to_string(),
2299 }
2300 }
2301
2302 fn is_imported(&self, name: &str) -> bool {
2306 self.module.imports.contains_key(name)
2307 }
2308
2309 fn qualified_key(&mut self, head: &str, name: &str, span: Span) -> Option<String> {
2315 let owner_name = self.module.module_imports.get(head)?;
2316 let owner = self.program.modules.get(owner_name)?;
2317 let exported = match owner.exported(name) {
2318 Some(exported) => exported,
2319 None => {
2320 self.diagnostics.push(
2321 Diagnostic::error(
2322 UNKNOWN_MEMBER,
2323 format!("module `{owner_name}` declares no `{name}`"),
2324 )
2325 .at(span)
2326 .rule(
2327 "A qualified name reaches an exported declaration of the module it names.",
2328 )
2329 .help(format!(
2330 "module `{owner_name}` exports {}",
2331 list(&owner.exports())
2332 )),
2333 );
2334 return None;
2335 }
2336 };
2337 if !exported {
2338 self.diagnostics.push(
2339 Diagnostic::error(
2340 UNKNOWN_MEMBER,
2341 format!("`{name}` is declared by module `{owner_name}`, but is not exported"),
2342 )
2343 .at(span)
2344 .rule("An `export` declaration is public; other declarations are module-private.")
2345 .help(format!(
2346 "write `export` on `{name}` in module `{owner_name}`, or name something else"
2347 )),
2348 );
2349 return None;
2350 }
2351 Some(format!("{owner_name}.{name}"))
2352 }
2353
2354 fn exported_operations(
2370 &self,
2371 module: &str,
2372 type_name: &str,
2373 with_receiver: bool,
2374 ) -> Vec<String> {
2375 self.program
2376 .methods_of(module, type_name)
2377 .into_iter()
2378 .filter(|declared| {
2379 declared.module == module
2380 && declared.entry.exported
2381 && declared.entry.decl.receiver.is_some() == with_receiver
2382 })
2383 .map(|declared| {
2384 if with_receiver {
2385 format!("{}()", declared.name)
2386 } else {
2387 format!("{type_name}.{}()", declared.name)
2388 }
2389 })
2390 .collect()
2391 }
2392
2393 fn opaque_help(
2396 &self,
2397 module: &str,
2398 type_name: &str,
2399 with_receiver: bool,
2400 instead: &str,
2401 nothing: &str,
2402 ) -> String {
2403 let operations = self.exported_operations(module, type_name, with_receiver);
2404 if operations.is_empty() {
2405 format!(
2406 "module `{module}` exports no {nothing} for `{type_name}`, so ask it to export one"
2407 )
2408 } else {
2409 format!("{instead} {}", list(&operations))
2410 }
2411 }
2412
2413 fn reject_opaque_field(
2422 &mut self,
2423 key: &str,
2424 sig: &StructSig,
2425 field: &str,
2426 usage: FieldUse,
2427 span: Span,
2428 ) -> bool {
2429 if !sig.opaque {
2430 return false;
2431 }
2432 let Some((module, type_name)) = foreign_type(key) else {
2433 return false;
2434 };
2435 let help = self.opaque_help(module, type_name, true, usage.correction(), "method");
2436 self.diagnostics.push(
2437 Diagnostic::error(
2438 OPAQUE_FIELD,
2439 format!(
2440 "`{type_name}` is opaque here, so its field `{field}` cannot be {}",
2441 usage.refused()
2442 ),
2443 )
2444 .at(span)
2445 .rule(
2446 "An `export opaque struct` exports its name and its exported methods; its fields belong to the module that declares it.",
2447 )
2448 .help(help),
2449 );
2450 true
2451 }
2452
2453 fn reject_opaque_construction(&mut self, key: &str, sig: &StructSig, span: Span) -> bool {
2457 if !sig.opaque {
2458 return false;
2459 }
2460 let Some((module, type_name)) = foreign_type(key) else {
2461 return false;
2462 };
2463 let help = self.opaque_help(
2464 module,
2465 type_name,
2466 false,
2467 "build the value through an exported associated function, such as",
2468 "constructor",
2469 );
2470 self.diagnostics.push(
2471 Diagnostic::error(
2472 OPAQUE_CONSTRUCTION,
2473 format!("`{type_name}` is opaque here, so it cannot be built field by field"),
2474 )
2475 .at(span)
2476 .rule(
2477 "An `export opaque struct` does not export the labeled constructor its fields synthesize; only the module that declares it may write one.",
2478 )
2479 .help(help),
2480 );
2481 true
2482 }
2483
2484 fn import(&mut self, envs: &BTreeMap<&str, ImportEnv>) {
2493 for dependency in self.module.dependencies() {
2494 let Some(env) = envs.get(dependency) else {
2495 continue;
2496 };
2497 self.structs
2498 .extend(env.structs.iter().map(|(k, v)| (k.clone(), v.clone())));
2499 self.enums
2500 .extend(env.enums.iter().map(|(k, v)| (k.clone(), v.clone())));
2501 self.aliases
2502 .extend(env.aliases.iter().map(|(k, v)| (k.clone(), v.clone())));
2503 self.functions
2504 .extend(env.functions.iter().map(|(k, v)| (k.clone(), v.clone())));
2505 self.methods
2506 .extend(env.methods.iter().map(|(k, v)| (k.clone(), v.clone())));
2507 self.traits
2508 .extend(env.traits.iter().map(|(k, v)| (k.clone(), v.clone())));
2509 self.conformances.extend(env.conformances.iter().cloned());
2510 }
2511 }
2512
2513 fn export_env(&self) -> ImportEnv {
2516 let module = self.module.name.as_str();
2517 let key = |name: &String| {
2518 if name.contains('.') {
2519 name.clone()
2520 } else {
2521 format!("{module}.{name}")
2522 }
2523 };
2524 ImportEnv {
2525 structs: self
2526 .structs
2527 .iter()
2528 .map(|(name, sig)| {
2529 (
2530 key(name),
2531 StructSig {
2532 generics: sig.generics.clone(),
2533 fields: sig
2534 .fields
2535 .iter()
2536 .map(|field| ParamSig {
2537 ty: qualify(&field.ty, module),
2538 ..field.clone()
2539 })
2540 .collect(),
2541 opaque: sig.opaque,
2542 },
2543 )
2544 })
2545 .collect(),
2546 enums: self
2547 .enums
2548 .iter()
2549 .map(|(name, sig)| {
2550 (
2551 key(name),
2552 EnumSig {
2553 generics: sig.generics.clone(),
2554 cases: sig
2555 .cases
2556 .iter()
2557 .map(|case| CaseSig {
2558 payload: case
2559 .payload
2560 .iter()
2561 .map(|ty| qualify(ty, module))
2562 .collect(),
2563 ..case.clone()
2564 })
2565 .collect(),
2566 },
2567 )
2568 })
2569 .collect(),
2570 aliases: self
2571 .aliases
2572 .iter()
2573 .map(|(name, (generics, ty))| (key(name), (generics.clone(), qualify(ty, module))))
2574 .collect(),
2575 functions: self
2576 .functions
2577 .iter()
2578 .map(|(name, sig)| (key(name), sig.qualified(module)))
2579 .collect(),
2580 methods: self
2581 .methods
2582 .iter()
2583 .map(|((type_name, name), sig)| {
2584 ((key(type_name), name.clone()), sig.qualified(module))
2585 })
2586 .collect(),
2587 traits: self
2588 .traits
2589 .iter()
2590 .map(|(name, methods)| {
2591 let methods = methods
2592 .iter()
2593 .map(|(name, sig)| (name.clone(), sig.qualified(module)))
2594 .collect();
2595 (key(name), methods)
2596 })
2597 .collect(),
2598 conformances: self
2599 .conformances
2600 .iter()
2601 .map(|(trait_name, type_name)| (key(trait_name), key(type_name)))
2602 .collect(),
2603 }
2604 }
2605
2606 fn check_bodies(&mut self) {
2609 let fn_names: Vec<String> = self.module.functions.keys().cloned().collect();
2610 for name in fn_names {
2611 let decl = self.module.functions[&name].decl.clone();
2612 let sig = self.functions[&name].clone();
2613 self.check_body(&decl, &sig);
2614 }
2615 let method_keys: Vec<(String, String)> = self.module.methods.keys().cloned().collect();
2616 for key in method_keys {
2617 if self.module.methods[&key].from_trait_default.is_some() {
2620 continue;
2621 }
2622 let decl = self.module.methods[&key].decl.clone();
2623 let sig = self.methods[&(self.key(&key.0), key.1.clone())].clone();
2627 self.check_body(&decl, &sig);
2628 }
2629 self.check_trait_defaults();
2630 self.body_mark = self.diagnostics.len();
2634 self.finish_inference();
2635 }
2636
2637 fn check_trait_defaults(&mut self) {
2646 let trait_names: Vec<String> = self.module.traits.keys().cloned().collect();
2647 for trait_name in trait_names {
2648 let decl = self.module.traits[&trait_name].decl.clone();
2649 let self_param: Arc<str> = "Self".into();
2650 for method in &decl.methods {
2651 let sig = self.traits[&trait_name][&method.name.node].clone();
2652 self.type_params = vec![self_param.clone()];
2653 self.bounds = BTreeMap::from([(
2654 self_param.clone(),
2655 vec![TraitBound {
2656 name: trait_name.as_str().into(),
2657 span: decl.name.span,
2658 }],
2659 )]);
2660 self.ret = sig.ret.clone();
2661 self.ret_span = sig.ret_span;
2662 self.ret_stated = true;
2663 self.scopes.push(BTreeMap::new());
2664 if let Some(receiver) = method.receiver {
2665 self.declare("self", Ty::Param(self_param.clone()), receiver.is_var);
2666 }
2667 for param in &sig.params {
2668 let ty = if param.variadic {
2669 Ty::Array(Box::new(param.ty.clone()))
2670 } else {
2671 param.ty.clone()
2672 };
2673 self.declare(¶m.name, ty, param.is_var);
2674 }
2675 for (param, declared) in method.params.iter().zip(&sig.params) {
2676 if let Some(default) = ¶m.default {
2677 let expected = Expected::new(
2678 declared.ty.clone(),
2679 param.name.span,
2680 format!("this parameter is `{}`", declared.ty),
2681 );
2682 self.expr(default, Some(&expected));
2683 }
2684 }
2685 self.body_mark = self.diagnostics.len();
2686 if let Some(body) = &method.default {
2687 self.facts.record_signature(
2705 method.span.file,
2706 method.span,
2707 Signature {
2708 receiver: method.receiver.map(|_| Ty::Param(self_param.clone())),
2709 params: sig.params.iter().map(|param| param.ty.clone()).collect(),
2710 ret: sig.ret.clone(),
2711 },
2712 );
2713 let expected = Expected::new(
2714 sig.ret.clone(),
2715 sig.ret_span,
2716 if method.return_type.is_some() {
2717 format!("the declared return type is `{}`", sig.ret)
2718 } else {
2719 "this method declares no return type, so it returns `()`".to_string()
2720 },
2721 );
2722 self.block(body, Some(&expected));
2723 }
2724 self.finish_inference();
2725 self.scopes.pop();
2726 self.type_params.clear();
2727 self.bounds.clear();
2728 }
2729 }
2730 }
2731
2732 fn prepare(&mut self) {
2737 let alias_names: Vec<String> = self.module.aliases.keys().cloned().collect();
2738 for name in alias_names {
2739 self.alias(&name);
2740 }
2741
2742 let trait_names: Vec<String> = self.module.traits.keys().cloned().collect();
2745 for name in trait_names {
2746 let decl = self.module.traits[&name].decl.clone();
2747 let methods = decl
2748 .methods
2749 .iter()
2750 .map(|method| (method.name.node.clone(), self.trait_method_sig(method)))
2751 .collect();
2752 self.traits.insert(name, methods);
2753 }
2754
2755 let struct_names: Vec<String> = self.module.structs.keys().cloned().collect();
2756 for name in struct_names {
2757 let entry = &self.module.structs[&name];
2758 let (decl, opaque) = (entry.decl.clone(), entry.opaque);
2759 let sig = self.struct_sig(&decl, opaque);
2760 self.record_struct_signature(&decl, &sig);
2761 self.structs.insert(name, sig);
2762 }
2763
2764 let enum_names: Vec<String> = self.module.enums.keys().cloned().collect();
2765 for name in enum_names {
2766 let decl = self.module.enums[&name].decl.clone();
2767 let sig = self.enum_sig(&decl);
2768 self.record_case_signatures(&decl, &sig);
2769 self.enums.insert(name, sig);
2770 }
2771
2772 self.check_layout_cycles();
2775
2776 let fn_names: Vec<String> = self.module.functions.keys().cloned().collect();
2777 for name in fn_names {
2778 let decl = self.module.functions[&name].decl.clone();
2779 let sig = self.fn_sig(&decl, None);
2780 self.functions.insert(name, sig);
2781 }
2782
2783 let method_keys: Vec<(String, String)> = self.module.methods.keys().cloned().collect();
2788 for key in method_keys {
2789 let decl = self.module.methods[&key].decl.clone();
2790 let sig = self.fn_sig(&decl, Some(&key.0));
2791 self.methods.insert((self.key(&key.0), key.1), sig);
2792 }
2793
2794 self.check_conformance_signatures();
2795 }
2796
2797 fn trait_method_sig(&mut self, method: &TraitMethod) -> FnSig {
2805 let outer = std::mem::take(&mut self.type_params);
2806 self.check_variadic_shape(&method.params);
2807 let params = method
2808 .params
2809 .iter()
2810 .map(|param| self.param_sig(param))
2811 .collect::<Vec<_>>();
2812 let ret = match &method.return_type {
2813 Some(ty) => self.resolve(ty),
2814 None => Ty::Unit,
2815 };
2816 let ret_span = match &method.return_type {
2817 Some(ty) => ty.span,
2818 None => method.name.span,
2819 };
2820 self.type_params = outer;
2821 FnSig {
2822 generics: Vec::new(),
2823 bounds: BTreeMap::new(),
2824 params,
2825 ret,
2826 ret_span,
2827 is_async: method.is_async,
2828 receiver: method.receiver.map(|_| Ty::placeholder()),
2829 receiver_is_var: method.receiver.is_some_and(|receiver| receiver.is_var),
2830 }
2831 }
2832
2833 fn check_conformance_signatures(&mut self) {
2842 let conformances: Vec<(String, String, String, String)> = self
2846 .module
2847 .conformances
2848 .values()
2849 .map(|conformance| {
2850 let (trait_key, type_key) = conformance_key(self.module, conformance);
2851 (
2852 trait_key,
2853 type_key,
2854 conformance.trait_name.clone(),
2855 conformance.type_name.clone(),
2856 )
2857 })
2858 .collect();
2859 for (trait_key, type_key, trait_name, written_type) in conformances {
2860 let Some(entry) = self.trait_entry(&trait_key) else {
2861 continue;
2862 };
2863 let type_name = written_type;
2864 let trait_decl = entry.decl.clone();
2865 for method in &trait_decl.methods {
2866 let key = (type_key.clone(), method.name.node.clone());
2867 let Some(found) = self.methods.get(&key).cloned() else {
2868 continue;
2869 };
2870 let Some(declared) = self.traits[&trait_key].get(&method.name.node).cloned() else {
2871 continue;
2872 };
2873 let Some(reason) = signature_difference(&declared, &found) else {
2874 continue;
2875 };
2876 let span = self.module.methods[&(type_name.clone(), method.name.node.clone())]
2877 .decl
2878 .name
2879 .span;
2880 self.diagnostics.push(
2881 Diagnostic::error(
2882 CONFORMANCE_SIGNATURE,
2883 format!(
2884 "`{type_name}.{}` does not match the signature `{trait_name}` declares: {reason}",
2885 method.name.node
2886 ),
2887 )
2888 .at(span)
2889 .label(
2890 method.name.span,
2891 format!("`{trait_name}` declares {}", trait_signature(&declared, &method.name.node)),
2892 )
2893 .rule("A conformance's method has exactly the signature its trait declares, because a call through a bound or through `dyn Trait` is checked against the trait and dispatched to the conformance.")
2894 .help(format!(
2895 "write `{}`",
2896 trait_signature(&declared, &method.name.node)
2897 )),
2898 );
2899 }
2900 }
2901 }
2902
2903 fn check_body(&mut self, decl: &FnDecl, sig: &FnSig) {
2909 self.body_mark = self.diagnostics.len();
2910 self.record_signature(decl, sig);
2911 self.type_params = sig.generics.clone();
2912 self.bounds = sig.bounds.clone();
2913 self.ret = sig.ret.clone();
2914 self.ret_span = sig.ret_span;
2915 self.ret_stated = true;
2916 self.scopes.push(BTreeMap::new());
2917 if let Some(receiver) = &sig.receiver {
2918 let is_var = decl.receiver.is_some_and(|receiver| receiver.is_var);
2922 self.declare("self", receiver.clone(), is_var);
2923 }
2924 for param in &sig.params {
2925 let ty = if param.variadic {
2926 Ty::Array(Box::new(param.ty.clone()))
2927 } else {
2928 param.ty.clone()
2929 };
2930 self.declare(¶m.name, ty, param.is_var);
2931 }
2932 for (param, declared) in decl.params.iter().zip(&sig.params) {
2933 if let Some(default) = ¶m.default {
2934 let expected = Expected::new(
2935 declared.ty.clone(),
2936 param.name.span,
2937 format!("this parameter is `{}`", declared.ty),
2938 );
2939 self.expr(default, Some(&expected));
2940 }
2941 }
2942 let expected = Expected::new(
2943 sig.ret.clone(),
2944 sig.ret_span,
2945 if decl.return_type.is_some() {
2946 format!("the declared return type is `{}`", sig.ret)
2947 } else {
2948 "this function declares no return type, so it returns `()`".to_string()
2949 },
2950 );
2951 self.block(&decl.body, Some(&expected));
2952 self.finish_inference();
2955 self.scopes.pop();
2956 self.type_params.clear();
2957 self.bounds.clear();
2958 }
2959
2960 fn struct_sig(&mut self, decl: &StructDecl, opaque: bool) -> StructSig {
2963 let outer = std::mem::take(&mut self.type_params);
2964 self.reject_bounds(&decl.generics, "struct");
2965 let generics = self.enter_generics(&decl.generics);
2966 let fields = decl
2967 .fields
2968 .iter()
2969 .map(|field| ParamSig {
2970 name: field.name.node.clone(),
2971 ty: self.resolve(&field.ty),
2972 variadic: false,
2973 has_default: false,
2974 is_var: false,
2975 span: field.name.span,
2976 })
2977 .collect();
2978 self.type_params = outer;
2979 StructSig {
2980 generics,
2981 fields,
2982 opaque,
2983 }
2984 }
2985
2986 fn enum_sig(&mut self, decl: &EnumDecl) -> EnumSig {
2987 let outer = std::mem::take(&mut self.type_params);
2988 self.reject_bounds(&decl.generics, "enum");
2989 let generics = self.enter_generics(&decl.generics);
2990 let cases = decl
2991 .cases
2992 .iter()
2993 .map(|case| CaseSig {
2994 name: case.name.node.clone(),
2995 payload: case.payload.iter().map(|ty| self.resolve(ty)).collect(),
2996 span: case.name.span,
2997 })
2998 .collect();
2999 self.type_params = outer;
3000 EnumSig { generics, cases }
3001 }
3002
3003 fn check_layout_cycles(&mut self) {
3033 let visible: Vec<String> = self
3034 .structs
3035 .keys()
3036 .chain(self.enums.keys())
3037 .cloned()
3038 .collect();
3039 let params = self.layout_parameters(&visible);
3040 let declared: Vec<String> = self
3041 .module
3042 .structs
3043 .keys()
3044 .chain(self.module.enums.keys())
3045 .cloned()
3046 .collect();
3047 let mut finite: BTreeSet<String> = BTreeSet::new();
3050 let mut reported: BTreeSet<String> = BTreeSet::new();
3051 for name in &declared {
3052 if finite.contains(name) || reported.contains(name) {
3053 continue;
3054 }
3055 let mut path = vec![name.clone()];
3056 let mut steps: Vec<LayoutStep> = Vec::new();
3057 let found = self.layout_cycle(name, ¶ms, &mut path, &mut steps, &mut finite);
3058 let Some(cycle) = found else {
3059 finite.insert(name.clone());
3060 continue;
3061 };
3062 reported.extend(cycle.iter().map(|step| step.owner.clone()));
3063 let diagnostic = self.layout_cycle_diagnostic(&cycle);
3064 self.diagnostics.push(diagnostic);
3065 }
3066 }
3067
3068 fn layout_parameters(&self, visible: &[String]) -> LayoutParams {
3078 let mut params: LayoutParams = visible
3079 .iter()
3080 .map(|name| (name.clone(), BTreeSet::new()))
3081 .collect();
3082 loop {
3083 let mut changed = false;
3084 for name in visible {
3085 let generics = self.layout_generics(name);
3086 if generics.is_empty() {
3087 continue;
3088 }
3089 let mut reach = InlineReach::default();
3090 for member in self.layout_members(name) {
3091 inline_reach(&member.ty, ¶ms, &mut reach);
3092 }
3093 let held: BTreeSet<usize> = generics
3094 .iter()
3095 .enumerate()
3096 .filter(|(_, param)| reach.params.contains(*param))
3097 .map(|(position, _)| position)
3098 .collect();
3099 let entry = params.entry(name.clone()).or_default();
3100 if !held.is_subset(entry) {
3101 entry.extend(held);
3102 changed = true;
3103 }
3104 }
3105 if !changed {
3106 return params;
3107 }
3108 }
3109 }
3110
3111 fn layout_cycle(
3122 &self,
3123 key: &str,
3124 params: &LayoutParams,
3125 path: &mut Vec<String>,
3126 steps: &mut Vec<LayoutStep>,
3127 finite: &mut BTreeSet<String>,
3128 ) -> Option<Vec<LayoutStep>> {
3129 for member in self.layout_members(key) {
3130 let mut reach = InlineReach::default();
3131 inline_reach(&member.ty, params, &mut reach);
3132 for target in reach.declarations {
3133 if !self.module.structs.contains_key(&target)
3134 && !self.module.enums.contains_key(&target)
3135 {
3136 continue;
3137 }
3138 let step = LayoutStep {
3139 owner: key.to_string(),
3140 member: member.label.clone(),
3141 reaches: target.clone(),
3142 span: member.span,
3143 };
3144 if let Some(entered) = path.iter().position(|name| *name == target) {
3145 let mut cycle = steps[entered..].to_vec();
3146 cycle.push(step);
3147 return Some(cycle);
3148 }
3149 if finite.contains(&target) {
3150 continue;
3151 }
3152 path.push(target.clone());
3153 steps.push(step);
3154 let found = self.layout_cycle(&target, params, path, steps, finite);
3155 steps.pop();
3156 path.pop();
3157 if found.is_some() {
3158 return found;
3159 }
3160 finite.insert(target);
3161 }
3162 }
3163 None
3164 }
3165
3166 fn layout_generics(&self, key: &str) -> Vec<Arc<str>> {
3168 if let Some(sig) = self.structs.get(key) {
3169 return sig.generics.clone();
3170 }
3171 if let Some(sig) = self.enums.get(key) {
3172 return sig.generics.clone();
3173 }
3174 Vec::new()
3175 }
3176
3177 fn layout_members(&self, key: &str) -> Vec<LayoutMember> {
3184 let mut members = Vec::new();
3185 if let Some(sig) = self.structs.get(key) {
3186 let decl = self.module.structs.get(key).map(|entry| &entry.decl);
3187 for (position, field) in sig.fields.iter().enumerate() {
3188 members.push(LayoutMember {
3189 ty: field.ty.clone(),
3190 label: format!("field `{}`", field.name),
3191 span: decl
3192 .and_then(|decl| decl.fields.get(position))
3193 .map_or(field.span, |field| field.ty.span),
3194 });
3195 }
3196 }
3197 if let Some(sig) = self.enums.get(key) {
3198 let decl = self.module.enums.get(key).map(|entry| &entry.decl);
3199 for (position, case) in sig.cases.iter().enumerate() {
3200 for (index, payload) in case.payload.iter().enumerate() {
3201 members.push(LayoutMember {
3202 ty: payload.clone(),
3203 label: format!("case `{}`", case.name),
3204 span: decl
3205 .and_then(|decl| decl.cases.get(position))
3206 .and_then(|case| case.payload.get(index))
3207 .map_or(case.span, |payload| payload.span),
3208 });
3209 }
3210 }
3211 }
3212 members
3213 }
3214
3215 fn layout_cycle_diagnostic(&self, cycle: &[LayoutStep]) -> Diagnostic {
3222 let start = cycle[0].owner.clone();
3223 let closing = &cycle[cycle.len() - 1];
3224 let message = if cycle.len() == 1 {
3225 format!(
3226 "`{start}` contains itself by value, through {}",
3227 closing.member
3228 )
3229 } else {
3230 let mut names: Vec<String> = cycle
3231 .iter()
3232 .map(|step| format!("`{}`", step.owner))
3233 .collect();
3234 names.push(format!("`{start}`"));
3235 format!("`{start}` contains itself by value: {}", names.join(" -> "))
3236 };
3237 let mut diagnostic = Diagnostic::error(LAYOUT_CYCLE, message)
3238 .at(self.layout_declaration_span(&start).unwrap_or(closing.span));
3239 for step in cycle {
3240 diagnostic = diagnostic.label(
3241 step.span,
3242 format!(
3243 "{} puts `{}` inside `{}`",
3244 step.member, step.reaches, step.owner
3245 ),
3246 );
3247 }
3248 diagnostic.rule(LAYOUT_CYCLE_RULE).help(format!(
3249 "break the cycle by holding one of its steps behind a reference: `Array<{start}>`, `Vector<{start}>` and `Shared<{start}>` are each one word, so a cycle that passes through one has a finite width"
3250 ))
3251 }
3252
3253 fn layout_declaration_span(&self, name: &str) -> Option<Span> {
3255 if let Some(entry) = self.module.structs.get(name) {
3256 return Some(entry.decl.name.span);
3257 }
3258 Some(self.module.enums.get(name)?.decl.name.span)
3259 }
3260
3261 fn fn_sig(&mut self, decl: &FnDecl, receiver_type: Option<&str>) -> FnSig {
3267 let mut type_generics: Vec<GenericParam> = Vec::new();
3268 if let Some(type_name) = receiver_type {
3269 if let Some(owner) = self.declaring_module(type_name) {
3273 if let Some(entry) = owner.structs.get(type_name) {
3274 type_generics.extend(entry.decl.generics.iter().cloned());
3275 } else if let Some(entry) = owner.enums.get(type_name) {
3276 type_generics.extend(entry.decl.generics.iter().cloned());
3277 }
3278 }
3279 }
3280 let mut names = type_generics.clone();
3281 names.extend(decl.generics.iter().cloned());
3282 let outer = self.type_params.clone();
3283 let generics = self.enter_generics(&names);
3284 let bounds = self.bounds_of(&decl.generics);
3285 let owner_arity = type_generics.len();
3286
3287 for param in &decl.params {
3294 if param.ty.is_none() {
3295 self.diagnostics.push(
3296 Diagnostic::error(
3297 MISSING_PARAMETER_TYPE,
3298 format!("parameter `{}` has no declared type", param.name.node),
3299 )
3300 .at(param.span)
3301 .rule("A declaration's parameters are written: only a lambda's infer, from the expected type at its call site.")
3302 .help(format!("write `{}: <type>`", param.name.node)),
3303 );
3304 }
3305 }
3306 self.check_variadic_shape(&decl.params);
3307
3308 let params = decl
3309 .params
3310 .iter()
3311 .map(|param| self.param_sig(param))
3312 .collect::<Vec<_>>();
3313 let ret = match &decl.return_type {
3314 Some(ty) => self.resolve(ty),
3315 None => Ty::Unit,
3316 };
3317 let ret_span = match &decl.return_type {
3318 Some(ty) => ty.span,
3319 None => decl.name.span,
3320 };
3321 let receiver = receiver_type
3322 .filter(|_| decl.receiver.is_some())
3323 .map(|name| {
3324 let args: Vec<Ty> = generics
3325 .iter()
3326 .take(owner_arity)
3327 .map(|p| Ty::Param(p.clone()))
3328 .collect();
3329 self.nominal(name, args)
3330 });
3331 self.type_params = outer;
3332 FnSig {
3333 generics,
3334 bounds,
3335 params,
3336 ret,
3337 ret_span,
3338 is_async: decl.is_async,
3339 receiver,
3340 receiver_is_var: decl.receiver.is_some_and(|receiver| receiver.is_var),
3341 }
3342 }
3343
3344 fn param_sig(&mut self, param: &Param) -> ParamSig {
3348 let ty = match ¶m.ty {
3349 Some(ty) => self.resolve(ty),
3350 None => Ty::recovery(),
3351 };
3352 ParamSig {
3353 name: param.name.node.clone(),
3354 ty,
3355 variadic: param.variadic,
3356 has_default: param.default.is_some(),
3357 is_var: param.is_var && !param.variadic,
3361 span: param.span,
3362 }
3363 }
3364
3365 fn check_variadic_shape(&mut self, params: &[Param]) {
3395 for (index, param) in params.iter().enumerate() {
3396 if !param.variadic {
3397 continue;
3398 }
3399 if index + 1 != params.len() {
3400 self.diagnostics.push(
3401 Diagnostic::error(
3402 VARIADIC_POSITION,
3403 format!(
3404 "parameter `{}` is variadic, so it must be the last one",
3405 param.name.node
3406 ),
3407 )
3408 .at(param.span)
3409 .rule("A variadic parameter is the last one its declaration writes: it collects every argument the parameters before it did not take.")
3410 .help(format!(
3411 "move `{}` to the end of the parameter list",
3412 param.name.node
3413 )),
3414 );
3415 }
3416 if param.default.is_some() {
3417 self.diagnostics.push(
3418 Diagnostic::error(
3419 VARIADIC_DEFAULT,
3420 format!(
3421 "parameter `{}` is variadic, so it cannot have a default",
3422 param.name.node
3423 ),
3424 )
3425 .at(param.span)
3426 .rule("A variadic parameter given no arguments is an empty `Array<T>`, so there is nothing left for a default to answer.")
3427 .help(format!(
3428 "remove the `= ...`; a call that passes nothing already gives `{}` an empty array",
3429 param.name.node
3430 )),
3431 );
3432 }
3433 }
3434 }
3435
3436 fn enter_generics(&mut self, params: &[GenericParam]) -> Vec<Arc<str>> {
3440 let generics: Vec<Arc<str>> = params.iter().map(|p| p.name.node.as_str().into()).collect();
3441 self.type_params.extend(generics.iter().cloned());
3442 generics
3443 }
3444
3445 fn bounds_of(&mut self, params: &[GenericParam]) -> BTreeMap<Arc<str>, Vec<TraitBound>> {
3448 let mut bounds: BTreeMap<Arc<str>, Vec<TraitBound>> = BTreeMap::new();
3449 for param in params {
3450 let mut named: Vec<TraitBound> = Vec::new();
3451 for bound in ¶m.bounds {
3452 let Some(key) = self.trait_key(&bound.node) else {
3453 self.diagnostics
3454 .push(unknown_trait(&bound.node, bound.span));
3455 continue;
3456 };
3457 if named.iter().any(|b| *b.name == *key) {
3458 continue;
3459 }
3460 named.push(TraitBound {
3461 name: key.as_str().into(),
3462 span: bound.span,
3463 });
3464 }
3465 if !named.is_empty() {
3466 bounds.insert(param.name.node.as_str().into(), named);
3467 }
3468 }
3469 bounds
3470 }
3471
3472 fn reject_bounds(&mut self, params: &[GenericParam], what: &str) {
3480 for param in params {
3481 for bound in ¶m.bounds {
3482 self.diagnostics.push(
3483 Diagnostic::error(
3484 UNSUPPORTED_BOUND,
3485 format!(
3486 "a bound on a {what}'s type parameter is not checked in the MVP"
3487 ),
3488 )
3489 .at(bound.span)
3490 .rule("A bound is checked where its type parameter is instantiated, and only a call site instantiates one; a `struct`, `enum`, or `type` binds its arguments in a type instead.")
3491 .help(format!(
3492 "write `{}` here, and bound the type parameter of the functions that operate on it",
3493 param.name.node
3494 )),
3495 );
3496 }
3497 }
3498 }
3499
3500 fn nominal(&self, name: &str, args: Vec<Ty>) -> Ty {
3503 let Some(owner) = self.declaring_module(name) else {
3504 return Ty::recovery();
3505 };
3506 let key = self.key(name);
3507 if owner.structs.contains_key(name) {
3508 Ty::Struct(key.into(), args)
3509 } else if owner.enums.contains_key(name) {
3510 Ty::Enum(key.into(), args)
3511 } else {
3512 Ty::recovery()
3513 }
3514 }
3515
3516 fn declaring_module(&self, name: &str) -> Option<&'a ResolvedModule> {
3519 match self.module.imports.get(name) {
3520 Some(owner) if self.module.owner_of(name) != Some(&self.module.name) => {
3521 self.program.modules.get(owner)
3522 }
3523 _ => Some(self.module),
3524 }
3525 }
3526
3527 fn trait_entry(&self, key: &str) -> Option<&'a TraitEntry> {
3529 match key.rsplit_once('.') {
3530 Some((owner, name)) => self.program.modules.get(owner)?.traits.get(name),
3531 None => self.module.traits.get(key),
3532 }
3533 }
3534
3535 fn trait_key(&self, name: &str) -> Option<String> {
3538 let key = self.key(name);
3539 self.traits.contains_key(&key).then_some(key)
3540 }
3541
3542 fn resolve(&mut self, ty: &Type) -> Ty {
3547 match &ty.kind {
3548 TypeKind::Unit => Ty::Unit,
3549 TypeKind::Fn {
3550 is_async,
3551 params,
3552 return_type,
3553 } => {
3554 let params = params
3555 .iter()
3556 .map(|param| match ¶m.ty {
3557 Some(ty) => self.resolve(ty),
3558 None => Ty::placeholder(),
3562 })
3563 .collect();
3564 let ret = match return_type {
3565 Some(ty) => self.resolve(ty),
3566 None => Ty::Unit,
3567 };
3568 Ty::func(*is_async, params, ret)
3569 }
3570 TypeKind::Named { path, args } => self.resolve_named(path, args, ty.span),
3571 TypeKind::Dyn(name) => {
3572 let Some(key) = self.trait_key(&name.node) else {
3576 self.diagnostics.push(unknown_trait(&name.node, name.span));
3577 return Ty::recovery();
3578 };
3579 Ty::Dyn(key.as_str().into())
3580 }
3581 }
3582 }
3583
3584 fn resolve_named(&mut self, path: &[Ident], args: &[Type], span: Span) -> Ty {
3585 let arguments: Vec<Ty> = args.iter().map(|arg| self.resolve(arg)).collect();
3586 if path.len() > 1 {
3587 let head = &path[0].node;
3588 if path.len() == 2 && self.module.module_imports.contains_key(head.as_str()) {
3592 let Some(key) = self.qualified_key(head, &path[1].node, span) else {
3593 return Ty::recovery();
3594 };
3595 return self.foreign_type(&key, arguments, span);
3596 }
3597 if self.module.host_uses.contains(head.as_str()) {
3598 if path.len() == 2 {
3599 return self.host_named_type(head, &path[1].node, arguments.len(), span);
3600 }
3601 self.diagnostics
3605 .push(unchecked_host_type(&join_path(path), span));
3606 return Ty::dynamic_boundary();
3607 }
3608 self.diagnostics.push(
3609 Diagnostic::error(
3610 UNKNOWN_TYPE,
3611 format!("`{}` names no type this module can see", join_path(path)),
3612 )
3613 .at(span)
3614 .rule("A qualified type name reaches a host module, or a module of this package imported with `use`.")
3615 .help(format!(
3616 "add `use {}` if `{}` is a host module or a module of this package, or declare the type in this module",
3617 path[0].node,
3618 path[0].node
3619 )),
3620 );
3621 return Ty::recovery();
3622 }
3623
3624 let name = path[0].node.as_str();
3625 if let Some(param) = self.type_params.iter().find(|p| &***p == name).cloned() {
3626 self.check_type_arity(name, 0, arguments.len(), span);
3627 return Ty::Param(param);
3628 }
3629 if let Some(ty) = self.builtin_type(name, &arguments, span) {
3630 return ty;
3631 }
3632 if let Some(entry) = self.module.structs.get(name) {
3633 let declared = entry.decl.generics.len();
3634 self.check_type_arity(name, declared, arguments.len(), span);
3635 return Ty::Struct(name.into(), fit(arguments, declared));
3636 }
3637 if let Some(entry) = self.module.enums.get(name) {
3638 let declared = entry.decl.generics.len();
3639 self.check_type_arity(name, declared, arguments.len(), span);
3640 return Ty::Enum(name.into(), fit(arguments, declared));
3641 }
3642 if self.module.aliases.contains_key(name) {
3643 let (generics, ty) = self.alias(name);
3644 self.check_type_arity(name, generics.len(), arguments.len(), span);
3645 return expand_alias(generics, ty, arguments);
3646 }
3647 if self.is_imported(name) {
3648 let key = self.key(name);
3649 return self.foreign_type(&key, arguments, span);
3650 }
3651 self.diagnostics.push(
3652 Diagnostic::error(
3653 UNKNOWN_TYPE,
3654 format!("`{name}` names no type this module can see"),
3655 )
3656 .at(span)
3657 .rule("A module sees its own declarations, what it imports with `use`, and the builtins.")
3658 .help(format!(
3659 "declare `struct {name}`, `enum {name}`, or `type {name} = ...` in this module, or `use <module>.{name}` to import it; a type only a host knows is written `<module>.{name}` after a `use` of that module"
3660 )),
3661 );
3662 Ty::recovery()
3663 }
3664
3665 fn foreign_type(&mut self, key: &str, arguments: Vec<Ty>, span: Span) -> Ty {
3671 let written = key.rsplit('.').next().unwrap_or(key).to_string();
3672 if let Some(sig) = self.structs.get(key) {
3673 let declared = sig.generics.len();
3674 self.check_type_arity(&written, declared, arguments.len(), span);
3675 return Ty::Struct(key.into(), fit(arguments, declared));
3676 }
3677 if let Some(sig) = self.enums.get(key) {
3678 let declared = sig.generics.len();
3679 self.check_type_arity(&written, declared, arguments.len(), span);
3680 return Ty::Enum(key.into(), fit(arguments, declared));
3681 }
3682 if let Some((generics, ty)) = self.aliases.get(key).cloned() {
3683 self.check_type_arity(&written, generics.len(), arguments.len(), span);
3684 return expand_alias(generics, ty, arguments);
3685 }
3686 self.diagnostics.push(
3690 Diagnostic::error(UNKNOWN_TYPE, format!("`{written}` is not a type"))
3691 .at(span)
3692 .rule("A type is a struct, an enum, a type alias, a type parameter, or a builtin.")
3693 .help(format!(
3694 "`{written}` names something else the module exports; name a type instead"
3695 )),
3696 );
3697 Ty::recovery()
3698 }
3699
3700 fn builtin_type(&mut self, name: &str, args: &[Ty], span: Span) -> Option<Ty> {
3707 if name == SCOPE.name {
3711 return None;
3712 }
3713 let arity = cove_schema::builtin(name)?.parameters.len();
3714 self.check_type_arity(name, arity, args.len(), span);
3715 let first = args.first().cloned().unwrap_or(Ty::recovery());
3716 let second = args.get(1).cloned().unwrap_or(Ty::recovery());
3717 Some(match name {
3718 "Unit" => Ty::Unit,
3719 "Bool" => Ty::Bool,
3720 "Int" => Ty::Int,
3721 "Float" => Ty::Float,
3722 "String" => Ty::Str,
3723 "Duration" => Ty::Duration,
3724 "Error" => Ty::Error,
3725 "Range" => Ty::Range,
3726 "Array" => Ty::Array(Box::new(first)),
3727 "Vector" => Ty::Vector(Box::new(first)),
3728 "Set" => Ty::Set(Box::new(first)),
3729 "Option" => Ty::Option(Box::new(first)),
3730 "Task" => Ty::Task(Box::new(first)),
3731 "Shared" => Ty::Shared(Box::new(self.task_safe_argument(first, span))),
3732 "Map" => Ty::Map(Box::new(first), Box::new(second)),
3733 "MapEntry" => Ty::MapEntry(Box::new(first), Box::new(second)),
3734 _ => Ty::Result(Box::new(first), Box::new(second)),
3735 })
3736 }
3737
3738 fn check_type_arity(&mut self, name: &str, expected: usize, found: usize, span: Span) {
3739 if expected == found {
3740 return;
3741 }
3742 self.diagnostics.push(
3743 Diagnostic::error(
3744 TYPE_ARGUMENTS,
3745 format!("`{name}` takes {expected} type argument(s), but {found} were written"),
3746 )
3747 .at(span)
3748 .rule("A generic type is written with exactly the arguments its declaration binds.")
3749 .help(if expected == 0 {
3750 format!("write `{name}`")
3751 } else {
3752 format!(
3753 "write `{name}<{}>`",
3754 (0..expected).map(|_| "_").collect::<Vec<_>>().join(", ")
3755 )
3756 }),
3757 );
3758 }
3759
3760 fn task_safe_argument(&mut self, ty: Ty, span: Span) -> Ty {
3773 if let Some(offending) = not_task_safe(&ty) {
3774 let offending = offending.to_string();
3775 let message = if offending == ty.to_string() {
3776 format!("`Shared` cannot wrap a `{offending}`, which cannot cross a task boundary")
3777 } else {
3778 format!(
3779 "`Shared` cannot wrap `{ty}`: the `{offending}` in it cannot cross a task boundary"
3780 )
3781 };
3782 self.diagnostics.push(
3783 Diagnostic::error(TASK_SAFETY, message)
3784 .at(span)
3785 .rule(TASK_SAFETY_RULE)
3786 .help(if offending.starts_with("Vector") {
3787 "wrap an `Array` instead, or finish the vector with `freeze()` before wrapping it"
3788 .to_string()
3789 } else {
3790 format!("wrap a value that may cross a task boundary; a `{offending}` may not")
3791 }),
3792 );
3793 }
3794 ty
3795 }
3796
3797 fn alias(&mut self, name: &str) -> (Vec<Arc<str>>, Ty) {
3799 if let Some(cached) = self.aliases.get(name) {
3800 return cached.clone();
3801 }
3802 let Some(entry) = self.module.aliases.get(name) else {
3805 return (Vec::new(), Ty::placeholder());
3806 };
3807 let decl = entry.decl.clone();
3808 if self.expanding.iter().any(|n| n == name) {
3809 self.diagnostics.push(
3810 Diagnostic::error(ALIAS_CYCLE, format!("`{name}` expands to itself"))
3811 .at(decl.name.span)
3812 .rule("A type alias names an existing type; it cannot be defined in terms of itself.")
3813 .help(format!(
3814 "declare `struct {name}` or `enum {name}` instead, which may refer to itself through a field"
3815 )),
3816 );
3817 return (Vec::new(), Ty::recovery());
3818 }
3819 self.expanding.push(name.to_string());
3820 let outer = std::mem::take(&mut self.type_params);
3821 self.reject_bounds(&decl.generics, "type alias");
3822 let generics = self.enter_generics(&decl.generics);
3823 let ty = self.resolve(&decl.ty);
3824 self.type_params = outer;
3825 self.expanding.pop();
3826 let resolved = (generics, ty);
3827 if !self.probing {
3832 self.aliases.insert(name.to_string(), resolved.clone());
3833 }
3834 resolved
3835 }
3836
3837 fn bound(&self, ty: Ty) -> Ty {
3873 if self.vars.is_empty() {
3874 ty
3875 } else {
3876 ty.resolved(&self.vars)
3877 }
3878 }
3879
3880 fn declare(&mut self, name: &str, ty: Ty, mutable: bool) {
3881 debug_assert!(
3886 !ty.holds_placeholder(),
3887 "a placeholder unknown escaped into the type of `{name}`: `{ty}`"
3888 );
3889 if let Some(scope) = self.scopes.last_mut() {
3890 scope.insert(name.to_string(), Binding { ty, mutable });
3891 }
3892 }
3893
3894 fn lookup(&self, name: &str) -> Option<&Binding> {
3895 self.scopes.iter().rev().find_map(|scope| scope.get(name))
3896 }
3897
3898 fn writable(&self, name: &str) -> bool {
3906 self.scopes
3907 .iter()
3908 .enumerate()
3909 .rev()
3910 .find_map(|(depth, scope)| scope.get(name).map(|binding| (depth, binding)))
3911 .is_some_and(|(depth, binding)| binding.mutable && depth >= self.capture_floor)
3912 }
3913
3914 fn place_mutability(&self, expr: &Expr) -> Option<bool> {
3936 match &expr.kind {
3937 ExprKind::Ident(name) => self.lookup(name).map(|_| self.writable(name)),
3938 ExprKind::Field { base, .. } => self.place_mutability(base),
3939 _ => None,
3940 }
3941 }
3942
3943 fn var_arguments(&mut self, args: &[Arg]) {
3957 for arg in args.iter().filter(|arg| arg.is_var) {
3958 match self.place_mutability(&arg.value) {
3959 Some(true) => {}
3960 Some(false) => {
3961 let place = place_text(&arg.value);
3962 self.diagnostics.push(
3963 Diagnostic::error(
3964 READ_ONLY_PLACE,
3965 format!(
3966 "`{place}` is a read-only place, so it cannot be passed as `var`"
3967 ),
3968 )
3969 .at(arg.span)
3970 .rule("`let` creates a read-only place; `var` creates a mutable place.")
3971 .help(format!("declare it with `var {place}`")),
3972 );
3973 }
3974 None if Checker::not_a_place(&arg.value) => {
3975 self.diagnostics.push(
3976 Diagnostic::error(
3977 NOT_A_PLACE,
3978 "this expression is not a place, so it cannot be assigned or aliased",
3979 )
3980 .at(arg.value.span)
3981 .rule("Only variables and their struct fields are places.")
3982 .help("bind it with `var` first, then pass that binding"),
3983 );
3984 }
3985 None => {}
3986 }
3987 }
3988 }
3989
3990 fn mutating_receiver(&mut self, receiver: &Ty, method: &Ident, base: &Expr, span: Span) {
4001 let Some(needs_a_place) = self.mutating_method(receiver, &method.node) else {
4002 return;
4003 };
4004 match self.place_mutability(base) {
4005 Some(true) => {}
4006 Some(false) => {
4007 let place = place_text(base);
4008 self.diagnostics.push(
4009 Diagnostic::error(
4010 READ_ONLY_PLACE,
4011 format!(
4012 "`{}` takes a `var self` receiver, but `{place}` is a read-only place",
4013 method.node
4014 ),
4015 )
4016 .at(span)
4017 .rule("`let` creates a read-only place; `var` creates a mutable place.")
4018 .help(format!("declare it with `var {place}`")),
4019 );
4020 }
4021 None if needs_a_place && Checker::not_a_place(base) => {
4022 self.diagnostics.push(
4023 Diagnostic::error(
4024 NOT_A_PLACE,
4025 format!(
4026 "`{}` takes a `var self` receiver, but `{}` is not a place",
4027 method.node,
4028 place_text(base)
4029 ),
4030 )
4031 .at(span)
4032 .rule("A mutating receiver declares `var self` and mutates the caller's place.")
4033 .help("bind the value with `var` first, then call the method on that binding"),
4034 );
4035 }
4036 None => {}
4037 }
4038 }
4039
4040 fn mutating_method(&self, ty: &Ty, method: &str) -> Option<bool> {
4048 match ty {
4049 Ty::Unknown(_) | Ty::Any | Ty::Never | Ty::Host(_) => None,
4050 Ty::Struct(name, _) | Ty::Enum(name, _) => self
4051 .methods
4052 .get(&(name.to_string(), method.to_string()))
4053 .and_then(|sig| sig.receiver_is_var.then_some(true)),
4054 Ty::Param(param) => self
4059 .bound_method(param, method)
4060 .and_then(|(trait_name, _)| {
4061 self.mutating_trait_method(&trait_name, method)
4062 .then_some(true)
4063 }),
4064 Ty::Dyn(trait_name) => self
4065 .mutating_trait_method(trait_name, method)
4066 .then_some(true),
4067 _ => cove_schema::builtins::builtin(&builtin_name(ty))
4082 .and_then(|entry| entry.method(method))
4083 .filter(|declared| declared.mutating)
4084 .map(|_| method != "freeze"),
4085 }
4086 }
4087
4088 fn not_a_place(expr: &Expr) -> bool {
4100 !matches!(expr.kind, ExprKind::Ident(_) | ExprKind::Field { .. })
4101 }
4102
4103 fn block(&mut self, block: &Block, expected: Option<&Expected>) -> Ty {
4108 self.scopes.push(BTreeMap::new());
4109 for stmt in &block.statements {
4110 self.stmt(stmt);
4111 }
4112 let ty = match &block.tail {
4113 Some(tail) => self.expr(tail, expected),
4114 None => {
4115 let ty = Ty::Unit;
4116 if let Some(expected) = expected {
4117 self.expect(&ty, expected, block.span);
4118 }
4119 ty
4120 }
4121 };
4122 self.scopes.pop();
4123 ty
4124 }
4125
4126 fn stmt(&mut self, stmt: &Stmt) {
4127 match &stmt.kind {
4128 StmtKind::Let {
4129 is_var,
4130 name,
4131 ty,
4132 value,
4133 } => {
4134 let spawned_before = self.open_scopes.last().map_or(0, |o| o.children.len());
4139 let bound = match ty {
4140 Some(written) => {
4141 let declared = self.resolve(written);
4142 let expected = Expected::new(
4143 declared.clone(),
4144 written.span,
4145 format!("the declared type is `{declared}`"),
4146 );
4147 self.expr(value, Some(&expected));
4148 declared
4149 }
4150 None => {
4151 let inferred = self.expr(value, None);
4152 if inferred == Ty::Never {
4158 Ty::recovery()
4159 } else {
4160 inferred
4161 }
4162 }
4163 };
4164 if let Some(open) = self.open_scopes.last_mut() {
4169 for child in open.children.iter_mut().skip(spawned_before) {
4170 child.binding = Some(name.node.clone());
4171 }
4172 }
4173 self.attach(&bound, &name.node, name.span);
4174 self.declare(&name.node, bound, *is_var);
4175 }
4176 StmtKind::Expr(expr) => {
4177 self.expr(expr, None);
4178 }
4179 StmtKind::Item(item) => {
4180 if let ItemKind::Fn(decl) = &item.kind {
4182 let outer_params = self.type_params.clone();
4183 let sig = self.fn_sig(decl, None);
4184 self.record_signature(decl, &sig);
4193 self.declare(&decl.name.node, sig.as_value(), false);
4194 let outer_ret = std::mem::replace(&mut self.ret, sig.ret.clone());
4195 let outer_span = std::mem::replace(&mut self.ret_span, sig.ret_span);
4196 let outer_stated = std::mem::replace(&mut self.ret_stated, true);
4197 let outer_tries = std::mem::take(&mut self.open_lambdas);
4203 self.type_params.extend(sig.generics.iter().cloned());
4204 let outer_bounds = self.bounds.clone();
4205 self.bounds.extend(
4206 sig.bounds
4207 .iter()
4208 .map(|(name, bounds)| (name.clone(), bounds.clone())),
4209 );
4210 let outer_floor = std::mem::replace(&mut self.capture_floor, self.scopes.len());
4213 self.scopes.push(BTreeMap::new());
4214 for param in &sig.params {
4215 self.declare(¶m.name, param.ty.clone(), param.is_var);
4216 }
4217 let expected = Expected::new(
4218 sig.ret.clone(),
4219 sig.ret_span,
4220 format!("the declared return type is `{}`", sig.ret),
4221 );
4222 self.block(&decl.body, Some(&expected));
4223 self.open_lambdas = outer_tries;
4224 self.scopes.pop();
4225 self.capture_floor = outer_floor;
4226 self.bounds = outer_bounds;
4227 self.type_params = outer_params;
4228 self.ret_span = outer_span;
4229 self.ret = outer_ret;
4230 self.ret_stated = outer_stated;
4231 }
4232 }
4233 }
4234 }
4235
4236 fn expr(&mut self, expr: &Expr, expected: Option<&Expected>) -> Ty {
4254 let ty = self.expr_type(expr, expected);
4255 debug_assert!(
4256 !ty.holds_placeholder(),
4257 "a placeholder unknown escaped into the type of an expression at {:?}: `{ty}`",
4258 expr.span
4259 );
4260 self.facts.record_ty(expr.span.file, expr.id, &ty);
4261 if ty.holds_var() {
4266 self.open_facts.push((expr.span.file, expr.id));
4267 }
4268 ty
4269 }
4270
4271 fn expr_type(&mut self, expr: &Expr, expected: Option<&Expected>) -> Ty {
4272 let span = expr.span;
4273 let ty = match &expr.kind {
4274 ExprKind::Int(_) => Ty::Int,
4275 ExprKind::Float(_) => Ty::Float,
4276 ExprKind::Bool(_) => Ty::Bool,
4277 ExprKind::Duration(_) => Ty::Duration,
4278 ExprKind::Unit => Ty::Unit,
4279 ExprKind::Str(parts) => {
4280 for part in parts {
4281 if let StrPart::Interpolation(inner) = part {
4282 self.expr(inner, None);
4285 }
4286 }
4287 Ty::Str
4288 }
4289 ExprKind::Ident(name) => self.ident(name, span, expected),
4290 ExprKind::ArrayLit(items) => self.array_literal(items, span, expected),
4291 ExprKind::Field { base, name } => self.field(base, name, span),
4292 ExprKind::Call {
4293 callee,
4294 generics,
4295 args,
4296 trailing,
4297 } => self.call(
4298 expr.id,
4299 callee,
4300 generics,
4301 args,
4302 trailing.as_deref(),
4303 span,
4304 expected,
4305 ),
4306 ExprKind::Unary { op, operand } => self.unary(*op, operand, span),
4307 ExprKind::Binary { op, lhs, rhs } => self.binary(*op, lhs, rhs, span),
4308 ExprKind::Assign { op, target, value } => self.assign(*op, target, value, span),
4309 ExprKind::Try(inner) => self.try_expr(inner, span),
4310 ExprKind::Await(inner) => self.await_expr(inner, span),
4311 ExprKind::Block(block) => return self.block(block, expected),
4312 ExprKind::If {
4313 condition,
4314 then_branch,
4315 else_branch,
4316 } => {
4317 return self.if_expr(
4318 condition,
4319 then_branch,
4320 else_branch.as_deref(),
4321 span,
4322 expected,
4323 )
4324 }
4325 ExprKind::Match { scrutinee, arms } => {
4326 return self.match_expr(scrutinee, arms, span, expected)
4327 }
4328 ExprKind::For {
4329 binding,
4330 iterable,
4331 body,
4332 } => self.for_expr(binding, iterable, body),
4333 ExprKind::While { condition, body } => {
4334 self.condition(condition);
4335 self.block(body, None);
4336 Ty::Unit
4337 }
4338 ExprKind::Return(value) => {
4339 if self.ret_stated {
4340 let expected = Expected::new(
4341 self.ret.clone(),
4342 self.ret_span,
4343 format!("the declared return type is `{}`", self.ret),
4344 );
4345 match value {
4346 Some(value) => {
4347 self.expr(value, Some(&expected));
4348 }
4349 None => self.expect(&Ty::Unit, &expected, span),
4350 }
4351 } else {
4352 self.diagnostics.push(
4359 Diagnostic::error(
4360 LAMBDA_RETURN,
4361 "this function value uses `return`, but nothing says what it produces",
4362 )
4363 .at(span)
4364 .rule("A `return` is checked against a stated result type: a declaration writes one, and a function value takes one from the place that holds it.")
4365 .help("give this function value to a place that declares its type, as in `let handle: fn(Int) -> String = fn(n) { ... }`, or end the body with the value instead of returning it"),
4366 );
4367 if let Some(value) = value {
4368 self.expr(value, None);
4369 }
4370 }
4371 Ty::Never
4372 }
4373 ExprKind::Break(value) => {
4376 if let Some(value) = value {
4382 self.expr(value, None);
4383 }
4384 Ty::Never
4385 }
4386 ExprKind::Continue => Ty::Never,
4387 ExprKind::Lambda {
4388 is_async,
4389 params,
4390 body,
4391 } => return self.lambda(*is_async, params, body, span, expected),
4392 ExprKind::Scope { name, body } => {
4393 self.scopes.push(BTreeMap::new());
4394 self.declare(&name.node, Ty::Scope, false);
4395 self.open_scopes.push(OpenScope {
4396 name: name.node.clone(),
4397 children: Vec::new(),
4398 });
4399 let ty = self.block(body, expected);
4400 if let Some(open) = self.open_scopes.pop() {
4401 self.leaving_scope(open);
4402 }
4403 self.scopes.pop();
4404 return ty;
4405 }
4406 ExprKind::Range {
4407 start,
4408 end,
4409 inclusive_end: _,
4410 } => {
4411 let bound = Expected::new(Ty::Int, span, "a range runs between two `Int`s");
4412 self.expr(start, Some(&bound));
4413 self.expr(end, Some(&bound));
4414 Ty::Range
4415 }
4416 };
4417 if let Some(expected) = expected {
4418 self.expect(&ty, expected, span);
4419 }
4420 ty
4421 }
4422
4423 fn accounted_for(expected: Option<&Expected>) -> bool {
4437 expected.is_some_and(|e| e.ty.is_accounted_for())
4438 }
4439
4440 fn abstention_of(expected: Option<&Expected>) -> Ty {
4446 match expected.map(|e| &e.ty) {
4447 Some(Ty::Unknown(kind)) if kind.is_accounted_for() => Ty::Unknown(*kind),
4448 Some(Ty::Any) => Ty::Any,
4451 _ => Ty::recovery(),
4452 }
4453 }
4454
4455 fn expect(&mut self, found: &Ty, expected: &Expected, span: Span) {
4458 self.constrain(found, &expected.ty, span);
4464 if found.matches(&expected.ty) || coerces(found, &expected.ty, &self.view()) {
4465 return;
4466 }
4467 let mut diagnostic = match &expected.ty {
4471 Ty::Dyn(trait_name) if !matches!(found, Ty::Dyn(_)) => Diagnostic::error(
4472 MISMATCH,
4473 format!("`{found}` does not conform to `{trait_name}`, so it is not a `{}`", expected.ty),
4474 )
4475 .at(span)
4476 .rule("A concrete value becomes a `dyn Trait` value where one is expected, and that is the only implicit conversion in the language; it requires an explicit conformance.")
4477 .help(format!("write `impl {trait_name} for {found} {{ ... }}`")),
4478 _ => {
4479 let mut diagnostic = Diagnostic::error(
4480 MISMATCH,
4481 format!("expected `{}`, found `{found}`", expected.ty),
4482 )
4483 .at(span)
4484 .rule("Types are nominal and the only implicit conversion is to `dyn Trait`: a value must otherwise already have the type its place asks for.");
4485 if let Some(help) = conversion_help(&expected.ty, found) {
4486 diagnostic = diagnostic.help(help);
4487 }
4488 diagnostic
4489 }
4490 };
4491 if let Some(origin) = &expected.origin {
4492 diagnostic = diagnostic.label(origin.span, origin.label.clone());
4493 }
4494 self.diagnostics.push(diagnostic);
4495 }
4496
4497 fn ident(&mut self, name: &str, span: Span, expected: Option<&Expected>) -> Ty {
4500 if let Some(binding) = self.lookup(name) {
4501 return binding.ty.clone();
4502 }
4503 if name == NONE_CASE.name {
4504 return match expected.map(|e| &e.ty) {
4505 Some(Ty::Option(inner)) => Ty::Option(inner.clone()),
4506 _ => {
4509 if !Checker::accounted_for(expected) {
4510 self.diagnostics.push(unconstrained(
4511 "nothing says what this `None` is an `Option` of".to_string(),
4512 format!("write the type on the place that holds it, as in `let value: Option<Int> = {name}`"),
4513 span,
4514 ));
4515 }
4516 Ty::Option(Box::new(Ty::unconstrained()))
4517 }
4518 };
4519 }
4520 if let Some(sig) = self.functions.get(&self.key(name)) {
4521 return sig.as_value();
4522 }
4523 if let Some(module) = self.module.host_items.get(name).cloned() {
4529 return self.host_operation_value(&module, name, span);
4530 }
4531 if let Some(what) = self.namespace(name) {
4538 self.diagnostics.push(not_a_value(name, what, span));
4539 return Ty::recovery();
4540 }
4541 self.unresolved_name(name, span)
4542 }
4543
4544 fn host_operation_value(&mut self, module: &str, name: &str, span: Span) -> Ty {
4554 let shown = format!("{module}.{name}");
4555 let Some(schema) = self.host_schema(module) else {
4556 return Ty::dynamic_boundary();
4560 };
4561 let Some(operation) = schema.operation(name) else {
4562 if schema.declared_type(name).is_some() || schema.resource(name).is_some() {
4563 self.diagnostics
4564 .push(not_a_value(&shown, Namespace::HostType, span));
4565 return Ty::recovery();
4566 }
4567 self.diagnostics.push(
4568 Diagnostic::error(
4569 UNKNOWN_HOST_OPERATION,
4570 format!("host module `{module}` has no operation `{name}`"),
4571 )
4572 .at(span)
4573 .rule(HOST_SCHEMA_RULE)
4574 .help(format!(
4575 "`{module}` exposes {}",
4576 list(&operation_names(schema.operations))
4577 )),
4578 );
4579 return Ty::recovery();
4580 };
4581 if operation.variadic {
4582 self.diagnostics.push(
4589 Diagnostic::note(
4590 VARIADIC_AS_VALUE,
4591 format!(
4592 "`{shown}` is variadic, so this value has no function type here"
4593 ),
4594 )
4595 .at(span)
4596 .rule("A function type in Cove names a fixed list of parameters; a Host API operation may declare a variadic one, which no `fn` type can be written for.")
4597 .help(format!(
4598 "calling `{shown}` directly is checked against its schema; a call made through this value is checked by the boundary and by nothing here, so write `fn(value: {}) {{ {shown}(value) }}` to have one that is",
4599 operation
4600 .params
4601 .first()
4602 .map(host_ty)
4603 .unwrap_or(Ty::Unit)
4604 )),
4605 );
4606 return Ty::unconstrained();
4607 }
4608 Ty::func(
4609 false,
4610 operation.params.iter().map(host_ty).collect(),
4611 host_ty(&operation.result),
4612 )
4613 }
4614
4615 fn unresolved_name(&mut self, name: &str, span: Span) -> Ty {
4624 let (code, help) = if starts_uppercase(name) {
4625 (
4626 UNRESOLVED_NAME,
4627 format!(
4628 "declare `struct {name}` or `enum {name}` in this module, `use <module>.{name}` to import it, or `use <host>` and write `<host>.{name}`"
4629 ),
4630 )
4631 } else {
4632 (
4633 UNKNOWN_NAME,
4634 format!(
4635 "declare `let {name} = ...` before this expression, or `use <host>.{name}`"
4636 ),
4637 )
4638 };
4639 self.diagnostics.push(
4640 Diagnostic::error(code, format!("cannot find `{name}` in this scope"))
4641 .at(span)
4642 .rule("A name must be a local binding, a parameter, a declaration of this module, or something `use` imports.")
4643 .help(help),
4644 );
4645 Ty::recovery()
4646 }
4647
4648 fn namespace(&self, name: &str) -> Option<Namespace> {
4655 if self.module.structs.contains_key(name) {
4656 Some(Namespace::Struct)
4657 } else if self.module.enums.contains_key(name) {
4658 Some(Namespace::Enum)
4659 } else if self.is_imported(name) {
4660 Some(self.declared_shape(&self.key(name)))
4661 } else if cove_schema::is_builtin_type(name) || name == MAP_ENTRY.name {
4662 Some(Namespace::BuiltinType)
4663 } else if self.module.host_uses.contains(name) {
4664 Some(Namespace::HostModule)
4665 } else if self.module.module_imports.contains_key(name) {
4666 Some(Namespace::Module)
4667 } else {
4668 None
4669 }
4670 }
4671
4672 fn declared_shape(&self, key: &str) -> Namespace {
4679 if self.structs.contains_key(key) {
4680 Namespace::Struct
4681 } else if self.enums.contains_key(key) {
4682 Namespace::Enum
4683 } else {
4684 Namespace::Type
4685 }
4686 }
4687
4688 fn array_literal(&mut self, items: &[Expr], span: Span, expected: Option<&Expected>) -> Ty {
4689 let mut element_hint = match expected.map(|e| &e.ty) {
4690 Some(Ty::Array(inner)) => Some((**inner).clone()),
4691 _ => None,
4692 };
4693 if element_hint.is_none() && items.len() > 1 && !self.probing {
4699 let found = self.probe(|checker| {
4700 items.iter().fold(Ty::recovery(), |element, item| {
4701 let ty = checker.expr(item, None);
4702 element.join(&ty)
4703 })
4704 });
4705 if !found.is_wild() {
4706 element_hint = Some(found);
4707 }
4708 }
4709 if items.is_empty() && element_hint.is_none() && !Checker::accounted_for(expected) {
4710 self.diagnostics.push(unconstrained(
4715 "nothing says what this empty array holds".to_string(),
4716 "write the type on the place that holds it, as in `let items: Array<Int> = []`"
4717 .to_string(),
4718 span,
4719 ));
4720 }
4721 let mut element = element_hint
4722 .clone()
4723 .unwrap_or_else(|| match items.is_empty() {
4724 true => Ty::unconstrained(),
4725 false => Ty::recovery(),
4726 });
4727 for item in items {
4728 let hint = element_hint
4729 .clone()
4730 .map(|ty| {
4731 let label = format!("the array's element type is `{ty}`");
4732 Expected::new(ty, span, label)
4733 })
4734 .or_else(|| {
4735 (!element.is_wild()).then(|| {
4736 Expected::new(
4737 element.clone(),
4738 span,
4739 format!("the first element is `{element}`"),
4740 )
4741 })
4742 });
4743 let ty = self.expr(item, hint.as_ref());
4744 element = element.join(&ty);
4745 }
4746 Ty::Array(Box::new(element))
4747 }
4748
4749 fn field(&mut self, base: &Expr, name: &Ident, span: Span) -> Ty {
4752 if let ExprKind::Field {
4755 base: module,
4756 name: declared,
4757 } = &base.kind
4758 {
4759 if let ExprKind::Ident(head) = &module.kind {
4760 if self.lookup(head).is_none() && self.module.host_uses.contains(head.as_str()) {
4761 if let Some(ty) = self.host_enum_case(head, &declared.node, name, span) {
4762 return ty;
4763 }
4764 }
4765 }
4766 }
4767 if let ExprKind::Ident(head) = &base.kind {
4768 if self.lookup(head).is_none() {
4769 let key = self.key(head);
4770 if self.enums.contains_key(&key) {
4771 return self.enum_case(&key, name, &[], span);
4772 }
4773 if self.module.host_uses.contains(head.as_str()) {
4774 return self.host_operation_value(head, &name.node, span);
4780 }
4781 if self.module.module_imports.contains_key(head.as_str()) {
4782 let Some(key) = self.qualified_key(head, &name.node, span) else {
4783 return Ty::recovery();
4784 };
4785 return match self.functions.get(&key) {
4789 Some(sig) => sig.as_value(),
4790 None => {
4791 let shape = self.declared_shape(&key);
4792 self.diagnostics.push(not_a_value(
4793 &format!("{head}.{}", name.node),
4794 shape,
4795 span,
4796 ));
4797 Ty::recovery()
4798 }
4799 };
4800 }
4801 }
4802 }
4803 let base_ty = self.expr(base, None);
4804 self.field_of(&base_ty, name, span)
4805 }
4806
4807 fn field_of(&mut self, base_ty: &Ty, name: &Ident, span: Span) -> Ty {
4808 match base_ty {
4809 Ty::Unknown(_) => base_ty.abstain(),
4810 Ty::Any => Ty::Any,
4815 Ty::Struct(struct_name, args) => {
4816 let Some(sig) = self.structs.get(struct_name.as_ref()) else {
4819 return Ty::placeholder();
4820 };
4821 let sig = sig.clone();
4822 let subst = substitution(&sig.generics, args);
4823 let usage = if self.assigned_place == Some(span) {
4824 FieldUse::Write
4825 } else {
4826 FieldUse::Read
4827 };
4828 if self.reject_opaque_field(struct_name, &sig, &name.node, usage, span) {
4829 return Ty::recovery();
4830 }
4831 match sig.fields.iter().find(|f| f.name == name.node) {
4832 Some(field) => field.ty.substitute(&subst),
4833 None => {
4834 let known: Vec<String> =
4835 sig.fields.iter().map(|f| f.name.clone()).collect();
4836 self.diagnostics.push(
4837 Diagnostic::error(
4838 UNKNOWN_FIELD,
4839 format!("`{struct_name}` has no field `{}`", name.node),
4840 )
4841 .at(span)
4842 .rule("A struct's fields are exactly the ones its declaration lists.")
4843 .help(format!("`{struct_name}` declares {}", list(&known))),
4844 );
4845 Ty::recovery()
4846 }
4847 }
4848 }
4849 Ty::Host(declared) => {
4850 let declared = declared.clone();
4851 self.host_field(&declared, name, span)
4852 }
4853 Ty::MapEntry(_, _) | Ty::Error => self.builtin_field(base_ty, name, span),
4858 abstract_ty @ (Ty::Param(_) | Ty::Dyn(_)) => {
4862 self.diagnostics.push(
4863 Diagnostic::error(
4864 UNKNOWN_FIELD,
4865 format!("`{abstract_ty}` has no field `{}`", name.node),
4866 )
4867 .at(span)
4868 .rule("A trait declares methods, not fields, so a value reached only through a trait has no fields; conformance is explicit and never structural.")
4869 .help(format!(
4870 "declare `fn {}(self) -> ...` in the trait and call `{}()`",
4871 name.node, name.node
4872 )),
4873 );
4874 Ty::recovery()
4875 }
4876 other => {
4877 self.diagnostics.push(
4878 Diagnostic::error(
4879 UNKNOWN_FIELD,
4880 format!("`{other}` has no field `{}`", name.node),
4881 )
4882 .at(span)
4883 .rule("Only a struct has fields.")
4884 .help(format!(
4885 "`{other}` is not a struct; call a method such as `{}()` instead, if one exists",
4886 name.node
4887 )),
4888 );
4889 Ty::recovery()
4890 }
4891 }
4892 }
4893
4894 fn enum_case(&mut self, enum_name: &str, case: &Ident, args: &[Arg], span: Span) -> Ty {
4896 let Some(sig) = self.enums.get(enum_name).cloned() else {
4899 return Ty::placeholder();
4900 };
4901 let ty = Ty::Enum(
4902 enum_name.into(),
4903 sig.generics.iter().cloned().map(Ty::Param).collect(),
4904 );
4905 let Some(found) = sig.cases.iter().find(|c| c.name == case.node) else {
4906 let known: Vec<String> = sig.cases.iter().map(|c| c.name.clone()).collect();
4910 self.diagnostics.push(
4911 Diagnostic::error(
4912 UNKNOWN_CASE,
4913 format!("`{enum_name}` has no case `{}`", case.node),
4914 )
4915 .at(span)
4916 .rule("An enum's cases are exactly the ones its declaration lists.")
4917 .help(format!("`{enum_name}` declares {}", list(&known))),
4918 );
4919 return ty;
4920 };
4921 if found.payload.len() != args.len() {
4922 self.diagnostics.push(
4923 Diagnostic::error(
4924 PAYLOAD_ARITY,
4925 format!(
4926 "`{enum_name}.{}` carries {} value(s), but {} were given",
4927 case.node,
4928 found.payload.len(),
4929 args.len()
4930 ),
4931 )
4932 .at(span)
4933 .label(found.span, "declared here")
4934 .rule("An enum case carries exactly the payload its declaration writes.")
4935 .help(if found.payload.is_empty() {
4936 format!("write `{enum_name}.{}`", case.node)
4937 } else {
4938 format!(
4939 "write `{enum_name}.{}({})`",
4940 case.node,
4941 found
4942 .payload
4943 .iter()
4944 .map(Ty::to_string)
4945 .collect::<Vec<_>>()
4946 .join(", ")
4947 )
4948 }),
4949 );
4950 }
4951 let generic_set: BTreeSet<Arc<str>> = sig.generics.iter().cloned().collect();
4954 let mut subst: BTreeMap<Arc<str>, Ty> = BTreeMap::new();
4955 for (arg, payload) in args.iter().zip(&found.payload) {
4956 let hint = self.open(payload, &sig.generics, &subst);
4957 let expected = Expected::new(
4958 hint.clone(),
4959 found.span,
4960 format!("this case carries a `{hint}`"),
4961 );
4962 let found_ty = self.expr(&arg.value, Some(&expected));
4963 unify(payload, &found_ty, &generic_set, &mut subst, &self.view());
4964 }
4965 for arg in args.iter().skip(found.payload.len()) {
4966 self.expr(&arg.value, None);
4967 }
4968 self.open_result(&ty, &sig.generics, &subst, span)
4969 }
4970
4971 fn unary(&mut self, op: UnaryOp, operand: &Expr, span: Span) -> Ty {
4972 let ty = self.expr(operand, None);
4973 if ty.is_wild() {
4974 return ty;
4975 }
4976 match (op, &ty) {
4977 (UnaryOp::Not, Ty::Bool) => Ty::Bool,
4978 (UnaryOp::Neg, Ty::Int) => Ty::Int,
4979 (UnaryOp::Neg, Ty::Float) => Ty::Float,
4980 (UnaryOp::Neg, Ty::Duration) => Ty::Duration,
4981 _ => {
4982 let symbol = match op {
4983 UnaryOp::Not => "!",
4984 UnaryOp::Neg => "-",
4985 };
4986 self.diagnostics.push(
4987 Diagnostic::error(OPERATOR, format!("`{symbol}` is not defined for `{ty}`"))
4988 .at(span)
4989 .rule("There are no implicit numeric, string, or boolean conversions.")
4990 .help(match op {
4991 UnaryOp::Not => {
4992 "`!` negates a `Bool`; compare instead, as in `x == 0`".to_string()
4993 }
4994 UnaryOp::Neg => {
4995 "`-` negates an `Int`, a `Float`, or a `Duration`".to_string()
4996 }
4997 }),
4998 );
4999 Ty::recovery()
5000 }
5001 }
5002 }
5003
5004 fn binary(&mut self, op: BinaryOp, lhs: &Expr, rhs: &Expr, span: Span) -> Ty {
5005 let left = self.expr(lhs, None);
5006 let right = self.expr(rhs, None);
5007 self.binary_result(op, &left, &right, span)
5008 }
5009
5010 fn binary_result(&mut self, op: BinaryOp, left: &Ty, right: &Ty, span: Span) -> Ty {
5015 match op {
5016 BinaryOp::And | BinaryOp::Or => {
5017 let mut ok = true;
5018 for ty in [left, right] {
5019 if !ty.is_wild() && *ty != Ty::Bool {
5020 ok = false;
5021 }
5022 }
5023 if !ok {
5024 self.operator_error(op, left, right, span, "`&&` and `||` combine two `Bool`s");
5025 }
5026 Ty::Bool
5027 }
5028 BinaryOp::Eq | BinaryOp::Ne => {
5029 if !left.matches(right) {
5030 self.diagnostics.push(
5031 Diagnostic::error(
5032 OPERATOR,
5033 format!("cannot compare `{left}` with `{right}`"),
5034 )
5035 .at(span)
5036 .rule("`==` means value equality between values of the same type.")
5037 .help(format!(
5038 "convert one side explicitly so both are `{left}`, or compare values that already share a type"
5039 )),
5040 );
5041 }
5042 Ty::Bool
5043 }
5044 BinaryOp::Add | BinaryOp::Sub | BinaryOp::Mul | BinaryOp::Div | BinaryOp::Rem => {
5045 if left.is_wild() || right.is_wild() {
5046 return left.join(right);
5047 }
5048 if left != right {
5049 self.operator_error(
5050 op,
5051 left,
5052 right,
5053 span,
5054 "arithmetic combines two values of the same type",
5055 );
5056 return Ty::recovery();
5057 }
5058 match left {
5059 Ty::Int | Ty::Float => left.clone(),
5060 Ty::Duration if matches!(op, BinaryOp::Add | BinaryOp::Sub) => Ty::Duration,
5061 Ty::Str if op == BinaryOp::Add => {
5062 self.diagnostics.push(
5063 Diagnostic::error(OPERATOR, "`+` is not defined for `String`")
5064 .at(span)
5065 .rule("There are no implicit string conversions.")
5066 .help("use string interpolation, such as \"{left}{right}\""),
5067 );
5068 Ty::recovery()
5069 }
5070 _ => {
5071 self.operator_error(
5072 op,
5073 left,
5074 right,
5075 span,
5076 "arithmetic is defined for `Int`, `Float`, and (for `+` and `-`) `Duration`",
5077 );
5078 Ty::recovery()
5079 }
5080 }
5081 }
5082 BinaryOp::Is => {
5090 if !left.matches(right) {
5091 self.diagnostics.push(
5092 Diagnostic::error(
5093 OPERATOR,
5094 format!("cannot compare the identity of `{left}` with `{right}`"),
5095 )
5096 .at(span)
5097 .rule("`is` compares identity between values of the same type.")
5098 .help(format!(
5099 "convert one side explicitly so both are `{left}`, or compare values that already share a type"
5100 )),
5101 );
5102 return Ty::Bool;
5103 }
5104 if left.is_wild() || matches!(left, Ty::Vector(_)) {
5105 return Ty::Bool;
5106 }
5107 self.diagnostics.push(
5108 Diagnostic::error(
5109 OPERATOR,
5110 format!("identity is not available for `{left}`"),
5111 )
5112 .at(span)
5113 .rule("`==` means value equality. Identity, when available, is explicit.")
5114 .help(
5115 "`is` is defined for `Vector`; compare other values with `==`, or call `toArray()` for an independent copy",
5116 ),
5117 );
5118 Ty::Bool
5119 }
5120 BinaryOp::Lt | BinaryOp::Le | BinaryOp::Gt | BinaryOp::Ge => {
5121 if left.is_wild() || right.is_wild() {
5122 return Ty::Bool;
5123 }
5124 if left != right {
5125 self.operator_error(
5126 op,
5127 left,
5128 right,
5129 span,
5130 "an ordering compares two values of the same type",
5131 );
5132 } else if !matches!(left, Ty::Int | Ty::Float | Ty::Duration | Ty::Str) {
5133 self.operator_error(
5134 op,
5135 left,
5136 right,
5137 span,
5138 "`<`, `<=`, `>`, and `>=` are defined for `Int`, `Float`, `Duration`, and `String`",
5139 );
5140 }
5141 Ty::Bool
5142 }
5143 }
5144 }
5145
5146 fn operator_error(&mut self, op: BinaryOp, left: &Ty, right: &Ty, span: Span, help: &str) {
5147 let symbol = operator_symbol(op);
5148 self.diagnostics.push(
5149 Diagnostic::error(
5150 OPERATOR,
5151 format!("`{symbol}` is not defined for `{left}` and `{right}`"),
5152 )
5153 .at(span)
5154 .rule("There are no implicit numeric, string, or boolean conversions.")
5155 .help(help.to_string()),
5156 );
5157 }
5158
5159 fn assign(&mut self, op: Option<BinaryOp>, target: &Expr, value: &Expr, span: Span) -> Ty {
5160 if !matches!(target.kind, ExprKind::Ident(_) | ExprKind::Field { .. }) {
5161 self.diagnostics.push(
5162 Diagnostic::error(
5163 NOT_A_PLACE,
5164 "this expression is not a place, so it cannot be assigned",
5165 )
5166 .at(target.span)
5167 .rule("Only a binding or a field of one is a place.")
5168 .help("assign to a `var` binding, or to a field of one"),
5169 );
5170 self.expr(value, None);
5171 return Ty::Unit;
5172 }
5173 if self.place_mutability(target) == Some(false) {
5179 let place = place_text(target);
5180 self.diagnostics.push(
5181 Diagnostic::error(
5182 READ_ONLY_PLACE,
5183 format!("cannot assign to `{place}`, which is a read-only place"),
5184 )
5185 .at(span)
5186 .rule("`let` creates a read-only place; `var` creates a mutable place.")
5187 .help(format!(
5188 "declare it with `var {place}` to make it assignable"
5189 )),
5190 );
5191 }
5192 let outer = std::mem::replace(
5198 &mut self.assigned_place,
5199 matches!(target.kind, ExprKind::Field { .. }).then_some(target.span),
5200 );
5201 let target_ty = self.expr(target, None);
5202 self.assigned_place = outer;
5203 match op {
5204 None => {
5205 let expected = Expected::new(
5206 target_ty.clone(),
5207 target.span,
5208 format!("the assigned place is `{target_ty}`"),
5209 );
5210 self.expr(value, Some(&expected));
5211 }
5212 Some(op) => {
5213 let value_ty = self.expr(value, None);
5214 let result = self.binary_result(op, &target_ty, &value_ty, span);
5215 let expected = Expected::new(
5216 target_ty.clone(),
5217 target.span,
5218 format!("the assigned place is `{target_ty}`"),
5219 );
5220 self.expect(&result, &expected, span);
5221 }
5222 }
5223 Ty::Unit
5224 }
5225
5226 fn try_expr(&mut self, inner: &Expr, span: Span) -> Ty {
5228 let ty = self.expr(inner, None);
5229 match &ty {
5230 Ty::Unknown(_) | Ty::Never => ty.abstain(),
5231 Ty::Any => Ty::Any,
5232 Ty::Result(ok, error) => {
5233 let (ok, error) = ((**ok).clone(), (**error).clone());
5234 if let Ty::Result(_, ret_error) = self.ret.clone() {
5241 self.constrain(&error, &ret_error, span);
5242 }
5243 match self.ret.clone() {
5244 Ty::Unknown(_) | Ty::Any => self.defer_try(Some(error.clone()), span),
5253 Ty::Result(_, ret_error) if error.matches(&ret_error) => {}
5254 Ty::Result(_, ret_error) => self.diagnostics.push(
5255 Diagnostic::error(
5256 TRY_RETURN,
5257 format!(
5258 "`?` propagates `{error}`, but this function returns `{ret_error}` as its failure"
5259 ),
5260 )
5261 .at(span)
5262 .label(self.ret_span, format!("the declared failure type is `{ret_error}`"))
5263 .rule("`expr?` returns the error from the current function, so the two failure types must be the same.")
5264 .help(format!(
5265 "map the failure first, as in `expr.mapError(fn(error) {{ ... }})?`, or declare this function `-> Result<_, {error}>`"
5266 )),
5267 ),
5268 other => self.diagnostics.push(
5269 Diagnostic::error(
5270 TRY_RETURN,
5271 format!("`?` needs a function that returns a `Result`, but this one returns `{other}`"),
5272 )
5273 .at(span)
5274 .label(self.ret_span, format!("the declared return type is `{other}`"))
5275 .rule("`expr?` returns the error from the current function.")
5276 .help(format!("declare this function `-> Result<{other}, {error}>`, or handle the `Err` with `unwrapOr`")),
5277 ),
5278 }
5279 ok
5280 }
5281 Ty::Option(inner_ty) => {
5282 let inner_ty = (**inner_ty).clone();
5283 match self.ret.clone() {
5284 Ty::Option(_) => {}
5285 Ty::Unknown(_) | Ty::Any => self.defer_try(None, span),
5286 other => self.diagnostics.push(
5287 Diagnostic::error(
5288 TRY_RETURN,
5289 format!("`?` on an `Option` needs a function that returns an `Option`, but this one returns `{other}`"),
5290 )
5291 .at(span)
5292 .label(self.ret_span, format!("the declared return type is `{other}`"))
5293 .rule("`expr?` returns the missing value from the current function.")
5294 .help(format!("declare this function `-> Option<{other}>`, or handle the `None` with `unwrapOr`")),
5295 ),
5296 }
5297 inner_ty
5298 }
5299 Ty::Task(inner_ty) => {
5300 self.diagnostics.push(
5301 Diagnostic::error(
5302 TRY_OPERAND,
5303 format!(
5304 "`?` needs a `Result` or an `Option`, but found `Task<{inner_ty}>`"
5305 ),
5306 )
5307 .at(span)
5308 .rule("`expr?` returns the error from the current function.")
5309 .help("settle the task first, as in `task.await()?`"),
5310 );
5311 Ty::recovery()
5312 }
5313 other => {
5314 self.diagnostics.push(
5315 Diagnostic::error(
5316 TRY_OPERAND,
5317 format!("`?` needs a `Result` or an `Option`, but found `{other}`"),
5318 )
5319 .at(span)
5320 .rule("`expr?` returns the error from the current function.")
5321 .help(format!("`{other}` cannot fail, so drop the `?`")),
5322 );
5323 Ty::recovery()
5324 }
5325 }
5326 }
5327
5328 fn defer_try(&mut self, error: Option<Ty>, span: Span) {
5349 match &self.ret {
5350 Ty::Unknown(Unknown::Recovery | Unknown::DynamicBoundary) => return,
5351 ty if ty.is_wild() => {}
5352 _ => return,
5353 }
5354 if let Some(open) = self.open_lambdas.last_mut() {
5355 open.push(PendingTry { span, error });
5356 }
5357 }
5358
5359 fn settle_pending_tries(&mut self, pending: Vec<PendingTry>, produced: &Ty, span: Span) {
5372 if let Ty::Result(_, produced_error) = produced {
5378 for try_expr in &pending {
5379 if let Some(error) = &try_expr.error {
5380 self.constrain(error, produced_error, try_expr.span);
5381 }
5382 }
5383 }
5384 let produced = self.bound(produced.clone());
5385 if produced.is_wild() {
5386 return;
5387 }
5388 for PendingTry { span: at, error } in pending {
5389 let diagnostic = match (&error, &produced) {
5390 (Some(error), Ty::Result(_, produced_error)) if error.matches(produced_error) => {
5391 continue
5392 }
5393 (None, Ty::Option(_)) => continue,
5394 (Some(error), _) => Diagnostic::error(
5395 TRY_RETURN,
5396 format!(
5397 "`?` propagates `{error}`, but this function value produces `{produced}`"
5398 ),
5399 )
5400 .at(at)
5401 .label(
5402 span,
5403 format!("nothing declares what this function value produces, so its body's value does: `{produced}`"),
5404 )
5405 .rule(TRY_LAMBDA_RULE)
5406 .help(format!(
5407 "end the body with a `Result`, as in `Ok(...)`, so this function value produces `Result<{produced}, {error}>` and the `?` has an `Err` to return; then answer that failure where the value arrives"
5408 )),
5409 (None, _) => Diagnostic::error(
5410 TRY_RETURN,
5411 format!(
5412 "`?` on an `Option` returns `None`, but this function value produces `{produced}`"
5413 ),
5414 )
5415 .at(at)
5416 .label(
5417 span,
5418 format!("nothing declares what this function value produces, so its body's value does: `{produced}`"),
5419 )
5420 .rule(TRY_LAMBDA_RULE)
5421 .help(format!(
5422 "end the body with an `Option`, as in `Some(...)`, so this function value produces `Option<{produced}>` and the `?` has a `None` to return; then answer the missing value where it arrives"
5423 )),
5424 };
5425 self.diagnostics.push(diagnostic);
5426 }
5427 }
5428
5429 fn spawned(&mut self, receiver: &Expr, ty: &Ty, span: Span) {
5438 let Ty::Task(settled) = ty else { return };
5439 let Ty::Result(_, error) = &**settled else {
5440 return;
5441 };
5442 let error = (**error).clone();
5443 let Some(open) = self.open_scopes.last_mut() else {
5444 return;
5445 };
5446 let scope = match &receiver.kind {
5447 ExprKind::Ident(name) => name.clone(),
5448 _ => open.name.clone(),
5449 };
5450 open.children.push(SpawnedChild {
5451 span,
5452 scope,
5453 binding: None,
5454 error,
5455 awaited: false,
5456 });
5457 }
5458
5459 fn handle_awaited(&mut self, handle: &Expr) {
5470 let named = match &handle.kind {
5471 ExprKind::Ident(name) => Some(name.as_str()),
5472 _ => None,
5473 };
5474 let span = handle.span;
5475 for open in &mut self.open_scopes {
5476 for child in &mut open.children {
5477 let by_name = named.is_some() && child.binding.as_deref() == named;
5478 let by_span = child.span.file == span.file
5479 && child.span.start >= span.start
5480 && child.span.end <= span.end;
5481 if by_name || by_span {
5482 child.awaited = true;
5483 }
5484 }
5485 }
5486 }
5487
5488 fn leaving_scope(&mut self, open: OpenScope) {
5497 for child in open.children {
5498 if child.awaited {
5499 continue;
5500 }
5501 let subject = match &child.binding {
5502 Some(name) => format!("`{name}`"),
5503 None => "this task".to_string(),
5504 };
5505 let SpawnedChild {
5506 span, scope, error, ..
5507 } = child;
5508 if let Ty::Result(_, ret_error) = self.ret.clone() {
5515 self.constrain(&error, &ret_error, span);
5516 }
5517 let diagnostic = match self.ret.clone() {
5518 Ty::Unknown(_) | Ty::Any | Ty::Never => continue,
5521 Ty::Result(_, ret_error) if error.matches(&ret_error) => continue,
5522 Ty::Result(ret_ok, ret_error) => Diagnostic::error(
5523 SCOPE_CHILD_FAILURE,
5524 format!(
5525 "nothing awaits {subject}, so leaving `{scope}` propagates its `{error}`, but this function returns `{ret_error}` as its failure"
5526 ),
5527 )
5528 .at(span)
5529 .label(
5530 self.ret_span,
5531 format!("the declared failure type is `{ret_error}`"),
5532 )
5533 .rule(SCOPE_CHILD_RULE)
5534 .help(format!(
5535 "map the failure inside the task, as in `{scope}.spawn {{ ... .mapError(fn(error) {{ ... }}) }}`, or declare this function `-> Result<{ret_ok}, {error}>`"
5536 )),
5537 other => Diagnostic::error(
5538 SCOPE_CHILD_FAILURE,
5539 format!(
5540 "nothing awaits {subject}, so leaving `{scope}` propagates its `{error}`, but this function returns `{other}`"
5541 ),
5542 )
5543 .at(span)
5544 .label(
5545 self.ret_span,
5546 format!("the declared return type is `{other}`"),
5547 )
5548 .rule(SCOPE_CHILD_RULE)
5549 .help(format!(
5550 "declare this function `-> Result<{other}, {error}>`, or await {subject} and answer its `Err` here"
5551 )),
5552 };
5553 self.diagnostics.push(diagnostic);
5554 }
5555 }
5556
5557 fn await_expr(&mut self, inner: &Expr, span: Span) -> Ty {
5558 let ty = self.expr(inner, None);
5559 if matches!(ty, Ty::Task(_)) {
5560 self.handle_awaited(inner);
5561 }
5562 match &ty {
5563 Ty::Unknown(_) | Ty::Never => ty.abstain(),
5564 Ty::Any => Ty::Any,
5565 Ty::Task(inner_ty) => (**inner_ty).clone(),
5566 other => {
5567 self.diagnostics.push(
5568 Diagnostic::error(
5569 AWAIT_OPERAND,
5570 format!("`await` needs a task, but found `{other}`"),
5571 )
5572 .at(span)
5573 .rule("`await` settles a task. Only a task spawned into a scope, or one returned by an `async fn`, has a value to settle.")
5574 .help("call an `async fn`, or spawn the work into a task scope, and await that handle"),
5575 );
5576 Ty::recovery()
5577 }
5578 }
5579 }
5580
5581 fn condition(&mut self, condition: &Expr) -> Ty {
5582 let ty = self.expr(condition, None);
5583 if !ty.matches(&Ty::Bool) {
5584 self.diagnostics.push(
5585 Diagnostic::error(
5586 CONDITION,
5587 format!("a condition must be a `Bool`, but found `{ty}`"),
5588 )
5589 .at(condition.span)
5590 .rule("There are no implicit boolean conversions.")
5591 .help(condition_help(&ty)),
5592 );
5593 }
5594 Ty::Bool
5595 }
5596
5597 fn if_expr(
5603 &mut self,
5604 condition: &Expr,
5605 then_branch: &Block,
5606 else_branch: Option<&Expr>,
5607 span: Span,
5608 expected: Option<&Expected>,
5609 ) -> Ty {
5610 self.condition(condition);
5611 let Some(else_branch) = else_branch else {
5612 self.block(then_branch, None);
5613 if let Some(expected) = expected {
5614 self.expect(&Ty::Unit, expected, span);
5615 }
5616 return Ty::Unit;
5617 };
5618 let settled = match expected {
5625 Some(_) => None,
5626 None if self.probing => None,
5627 None => self
5628 .probe(|checker| {
5629 let then_ty = checker.block(then_branch, None);
5630 let else_ty = checker.expr(else_branch, None);
5631 then_ty
5632 .matches(&else_ty)
5633 .then(|| then_ty.join(&else_ty))
5634 .filter(|ty| !ty.is_wild())
5635 })
5636 .map(|ty| {
5637 let label = format!("both branches produce `{ty}`");
5638 Expected::new(ty, span, label)
5639 }),
5640 };
5641 let hint = expected.or(settled.as_ref());
5642 let then_ty = self.block(then_branch, hint);
5643 let else_ty = self.expr(else_branch, hint);
5644 if expected.is_none() && !then_ty.matches(&else_ty) {
5648 self.branches_disagree(then_branch.span, else_branch.span, &then_ty, &else_ty);
5649 }
5650 then_ty.join(&else_ty)
5651 }
5652
5653 fn branches_disagree(&mut self, first: Span, second: Span, first_ty: &Ty, second_ty: &Ty) {
5654 self.diagnostics.push(
5655 Diagnostic::error(
5656 BRANCHES,
5657 format!("this branch produces `{second_ty}`, but the other produces `{first_ty}`"),
5658 )
5659 .at(second)
5660 .label(first, format!("this branch produces `{first_ty}`"))
5661 .rule(
5662 "Every branch of an `if` or `match` used as an expression produces the same type.",
5663 )
5664 .help(format!(
5665 "make both branches produce `{first_ty}`, or bind them separately"
5666 )),
5667 );
5668 }
5669
5670 fn match_expr(
5671 &mut self,
5672 scrutinee: &Expr,
5673 arms: &[MatchArm],
5674 span: Span,
5675 expected: Option<&Expected>,
5676 ) -> Ty {
5677 let scrutinee_ty = self.expr(scrutinee, None);
5678 let mut result: Option<(Ty, Span)> = None;
5679 for arm in arms {
5680 self.scopes.push(BTreeMap::new());
5681 self.pattern(&arm.pattern, &scrutinee_ty);
5682 let ty = self.expr(&arm.body, expected);
5683 self.scopes.pop();
5684 result = Some(match result {
5685 None => (ty, arm.body.span),
5686 Some((previous, previous_span)) => {
5687 if expected.is_none() && !previous.matches(&ty) {
5688 self.branches_disagree(previous_span, arm.body.span, &previous, &ty);
5689 }
5690 (previous.join(&ty), previous_span)
5691 }
5692 });
5693 }
5694 let _ = span;
5695 match result {
5696 Some((ty, _)) => ty,
5697 None => Ty::Never,
5700 }
5701 }
5702
5703 fn pattern(&mut self, pattern: &Pattern, scrutinee: &Ty) {
5710 match &pattern.kind {
5711 PatternKind::Wildcard => {}
5712 PatternKind::Binding(name) => {
5713 let ty = self.bound(scrutinee.clone());
5714 self.declare(name, ty, false);
5715 }
5716 PatternKind::Literal(expr) => {
5717 let ty = self.expr(expr, None);
5718 if !ty.matches(scrutinee) {
5719 self.diagnostics.push(
5720 Diagnostic::error(
5721 PATTERN,
5722 format!(
5723 "this pattern matches `{ty}`, but the scrutinee is `{scrutinee}`"
5724 ),
5725 )
5726 .at(pattern.span)
5727 .rule("A pattern matches values of the scrutinee's type.")
5728 .help(format!(
5729 "write a `{scrutinee}` literal, or a binding such as `other`"
5730 )),
5731 );
5732 }
5733 }
5734 PatternKind::Variant { path, payload } => {
5735 self.variant_pattern(pattern.span, path, payload, scrutinee)
5736 }
5737 }
5738 }
5739
5740 fn variant_pattern(&mut self, span: Span, path: &[Ident], payload: &[Pattern], scrutinee: &Ty) {
5741 let case = path.last().expect("a variant path is never empty");
5742 let payload_types: Option<Vec<Ty>> = match scrutinee {
5743 Ty::Unknown(_) | Ty::Any | Ty::Never => None,
5744 Ty::Option(_) | Ty::Result(_, _) => builtin_case_payload(scrutinee, &case.node),
5751 Ty::Enum(name, args) => {
5752 if let [qualifier, _] = path {
5753 if self.key(&qualifier.node) != **name {
5754 self.diagnostics.push(
5755 Diagnostic::error(
5756 PATTERN,
5757 format!(
5758 "this pattern matches `{}`, but the scrutinee is `{name}`",
5759 qualifier.node
5760 ),
5761 )
5762 .at(span)
5763 .rule("A pattern matches values of the scrutinee's type.")
5764 .help(format!(
5765 "write a `{name}` case, such as `{name}.{}`",
5766 first_case_of(self.enums.get(name.as_ref()))
5767 )),
5768 );
5769 None
5770 } else {
5771 self.case_payload(name, &case.node, args)
5772 }
5773 } else {
5774 self.case_payload(name, &case.node, args)
5775 }
5776 }
5777 Ty::Host(declared) => match self.host_declared_type(declared) {
5781 Some(schema) if schema.cases.contains(&case.node.as_str()) => Some(Vec::new()),
5782 Some(schema) if schema.is_enum() => {
5783 let known: Vec<String> =
5784 schema.cases.iter().map(|c| (*c).to_string()).collect();
5785 self.diagnostics.push(
5786 Diagnostic::error(
5787 UNKNOWN_CASE,
5788 format!("`{declared}` has no case `{}`", case.node),
5789 )
5790 .at(span)
5791 .rule(HOST_SCHEMA_RULE)
5792 .help(format!("`{declared}` declares {}", list(&known))),
5793 );
5794 None
5795 }
5796 _ => {
5797 self.diagnostics.push(
5798 Diagnostic::error(
5799 PATTERN,
5800 format!(
5801 "`{declared}` has no cases, so it cannot be matched by `{}`",
5802 case.node
5803 ),
5804 )
5805 .at(span)
5806 .rule(HOST_SCHEMA_RULE)
5807 .help(format!(
5808 "match a `{declared}` with a binding, or read one of its fields"
5809 )),
5810 );
5811 None
5812 }
5813 },
5814 other => {
5815 self.diagnostics.push(
5816 Diagnostic::error(
5817 PATTERN,
5818 format!(
5819 "`{other}` has no cases, so it cannot be matched by `{}`",
5820 case.node
5821 ),
5822 )
5823 .at(span)
5824 .rule("A variant pattern matches an enum case.")
5825 .help(format!(
5826 "match a literal `{other}`, or bind the value with a name"
5827 )),
5828 );
5829 None
5830 }
5831 };
5832
5833 let Some(types) = payload_types else {
5834 for sub in payload {
5835 self.pattern(sub, &Ty::recovery());
5836 }
5837 return;
5838 };
5839 if types.len() != payload.len() {
5840 self.diagnostics.push(
5841 Diagnostic::error(
5842 PAYLOAD_ARITY,
5843 format!(
5844 "`{}` carries {} value(s), but this pattern binds {}",
5845 case.node,
5846 types.len(),
5847 payload.len()
5848 ),
5849 )
5850 .at(span)
5851 .rule("A pattern binds exactly the payload its case declares.")
5852 .help(if types.is_empty() {
5853 format!("write `{}`", case.node)
5854 } else {
5855 format!(
5856 "write `{}({})`",
5857 case.node,
5858 types.iter().map(|_| "value").collect::<Vec<_>>().join(", ")
5859 )
5860 }),
5861 );
5862 }
5863 for (sub, ty) in payload.iter().zip(types.iter()) {
5864 self.pattern(sub, ty);
5865 }
5866 for sub in payload.iter().skip(types.len()) {
5867 self.pattern(sub, &Ty::recovery());
5868 }
5869 }
5870
5871 fn case_payload(&mut self, name: &str, case: &str, args: &[Ty]) -> Option<Vec<Ty>> {
5875 let sig = self.enums.get(name)?;
5876 let subst = substitution(&sig.generics, args);
5877 let found = sig.cases.iter().find(|c| c.name == case)?;
5878 Some(
5879 found
5880 .payload
5881 .iter()
5882 .map(|ty| ty.substitute(&subst))
5883 .collect(),
5884 )
5885 }
5886
5887 fn for_expr(&mut self, binding: &Ident, iterable: &Expr, body: &Block) -> Ty {
5888 let ty = self.expr(iterable, None);
5889 let element = match &ty {
5890 Ty::Unknown(_) | Ty::Never => ty.abstain(),
5891 Ty::Any => Ty::Any,
5892 Ty::Array(inner) | Ty::Vector(inner) | Ty::Set(inner) => (**inner).clone(),
5893 Ty::Range => Ty::Int,
5894 Ty::Map(key, value) => Ty::MapEntry(key.clone(), value.clone()),
5897 other => {
5898 self.diagnostics.push(
5899 Diagnostic::error(
5900 ITERABLE,
5901 format!(
5902 "`for` iterates an `Array`, a `Vector`, a `Range`, a `Set`, or a `Map`, but found `{other}`"
5903 ),
5904 )
5905 .at(iterable.span)
5906 .rule("`for` iterates a sequence; iteration order is defined by each collection type.")
5907 .help(iterable_help(other)),
5908 );
5909 Ty::recovery()
5910 }
5911 };
5912 self.scopes.push(BTreeMap::new());
5913 let element = self.bound(element);
5914 self.declare(&binding.node, element, false);
5915 self.block(body, None);
5916 self.scopes.pop();
5917 Ty::Unit
5918 }
5919
5920 fn lambda(
5928 &mut self,
5929 is_async: bool,
5930 params: &[Param],
5931 body: &Block,
5932 span: Span,
5933 expected: Option<&Expected>,
5934 ) -> Ty {
5935 let hint = match expected.map(|e| &e.ty) {
5936 Some(Ty::Fn(func)) => match self.bound(Ty::Fn(func.clone())) {
5940 Ty::Fn(func) => Some(func),
5941 _ => Some(func.clone()),
5942 },
5943 _ => None,
5944 };
5945 let stated = expected.is_some();
5953 let stated_ret: Option<Ty> = match (hint.as_ref(), expected) {
5956 (Some(func), _) if !func.ret.holds_placeholder() => Some(func.ret.clone()),
5959 (Some(_), _) => None,
5965 (None, Some(_)) => Some(Checker::abstention_of(expected)),
5971 (None, None) => None,
5972 };
5973 if let Some(func) = &hint {
5974 if func.params.len() != params.len() {
5975 let help = if params.is_empty() {
5984 "a trailing closure can never declare a parameter — write it as an ordinary argument instead, as in `result.mapError(fn(error) { ... })`".to_string()
5985 } else {
5986 format!(
5987 "write `fn({}) {{ ... }}`",
5988 (0..func.params.len())
5989 .map(|i| format!("p{i}"))
5990 .collect::<Vec<_>>()
5991 .join(", ")
5992 )
5993 };
5994 self.diagnostics.push(
5995 Diagnostic::error(
5996 ARITY,
5997 format!(
5998 "this function takes {} parameter(s), but {} were expected here",
5999 params.len(),
6000 func.params.len()
6001 ),
6002 )
6003 .at(span)
6004 .rule("A function value has exactly the parameters the place that holds it declares.")
6005 .help(help),
6006 );
6007 }
6008 }
6009
6010 let mut param_types = Vec::with_capacity(params.len());
6011 let outer_floor = std::mem::replace(&mut self.capture_floor, self.scopes.len());
6015 self.scopes.push(BTreeMap::new());
6016 for (index, param) in params.iter().enumerate() {
6017 let ty = match ¶m.ty {
6018 Some(written) => self.resolve(written),
6019 None => match hint.as_ref().and_then(|f| f.params.get(index)) {
6026 Some(ty) => ty.clone(),
6027 None if stated => Checker::abstention_of(expected),
6028 None => {
6029 self.diagnostics.push(unconstrained(
6030 format!("nothing says what `{}` is", param.name.node),
6031 format!(
6032 "write the type, as in `{}: <type>`, or give this function value to a place that declares one",
6033 param.name.node
6034 ),
6035 param.span,
6036 ));
6037 Ty::recovery()
6038 }
6039 },
6040 };
6041 let ty = if param.variadic {
6066 self.diagnostics.push(
6067 Diagnostic::error(
6068 VARIADIC_LAMBDA,
6069 format!(
6070 "parameter `{}` is variadic, so it cannot be written on a function value",
6071 param.name.node
6072 ),
6073 )
6074 .at(param.span)
6075 .rule("A variadic parameter is written on a declaration: a function value has exactly the parameters its function type names, and a function type names a fixed list of them.")
6076 .help(format!(
6077 "remove the `...` and give `{}` an `Array` type, passing one at the call; or declare an `fn`, which a call reaches by name and can gather arguments for",
6078 param.name.node
6079 )),
6080 );
6081 Ty::recovery()
6082 } else {
6083 ty
6084 };
6085 param_types.push(ty.clone());
6086 self.declare(¶m.name.node, ty, param.is_var);
6087 }
6088
6089 let declared_ret = stated_ret.clone().filter(|ty| !ty.is_wild());
6092 let outer_ret = std::mem::replace(
6093 &mut self.ret,
6094 stated_ret.clone().unwrap_or_else(Ty::placeholder),
6095 );
6096 let outer_span = std::mem::replace(&mut self.ret_span, span);
6097 let outer_stated = std::mem::replace(&mut self.ret_stated, stated_ret.is_some());
6098 self.open_lambdas.push(Vec::new());
6099 let expected_body = stated_ret.clone().map(|ty| match ty.is_wild() {
6100 true => Expected::abstained(ty),
6104 false => {
6105 let label = format!("this function value produces `{ty}`");
6106 Expected::new(ty, span, label)
6107 }
6108 });
6109 let body_ty = self.block(body, expected_body.as_ref());
6110 let pending = self.open_lambdas.pop().unwrap_or_default();
6114 self.ret = outer_ret;
6115 self.ret_span = outer_span;
6116 self.ret_stated = outer_stated;
6117 self.scopes.pop();
6118 self.capture_floor = outer_floor;
6119
6120 let produced = match declared_ret {
6128 Some(declared) => declared.join(&body_ty),
6129 None => body_ty,
6130 };
6131 self.settle_pending_tries(pending, &produced, span);
6132 let value = Ty::func(is_async, param_types, produced);
6133 if let Some(expected) = expected {
6144 if !matches!(expected.ty, Ty::Fn(_)) && !expected.ty.is_wild() {
6145 self.expect(&value, expected, span);
6146 }
6147 }
6148 value
6149 }
6150
6151 #[allow(clippy::too_many_arguments)]
6161 fn call(
6162 &mut self,
6163 id: ExprId,
6164 callee: &Expr,
6165 generics: &[Type],
6166 args: &[Arg],
6167 trailing: Option<&Expr>,
6168 span: Span,
6169 expected: Option<&Expected>,
6170 ) -> Ty {
6171 self.var_arguments(args);
6174 match &callee.kind {
6175 ExprKind::Ident(name) if self.lookup(name).is_none() => {
6176 self.call_named(name, generics, args, trailing, span, callee.span, expected)
6177 }
6178 ExprKind::Field { base, name } => {
6179 if let ExprKind::Ident(head) = &base.kind {
6180 if self.lookup(head).is_none() {
6181 if let Some(ty) =
6182 self.call_qualified(id, head, name, args, trailing, span, expected)
6183 {
6184 return ty;
6185 }
6186 }
6187 }
6188 let receiver = self.expr(base, None);
6189 self.mutating_receiver(&receiver, name, base, span);
6190 if matches!(receiver, Ty::Task(_)) && name.node == "await" {
6196 self.handle_awaited(base);
6197 }
6198 let ty = self.method_call(id, &receiver, name, args, trailing, span);
6199 if matches!(receiver, Ty::Scope) && name.node == "spawn" {
6200 self.spawned(base, &ty, span);
6201 }
6202 ty
6203 }
6204 _ => {
6205 let callee_ty = self.expr(callee, None);
6206 self.call_value(&callee_ty, args, trailing, span, callee.span)
6207 }
6208 }
6209 }
6210
6211 #[allow(clippy::too_many_arguments)]
6213 fn call_named(
6214 &mut self,
6215 name: &str,
6216 generics: &[Type],
6217 args: &[Arg],
6218 trailing: Option<&Expr>,
6219 span: Span,
6220 callee_span: Span,
6221 expected: Option<&Expected>,
6222 ) -> Ty {
6223 let key = self.key(name);
6224 if let Some(sig) = self.functions.get(&key).cloned() {
6225 let explicit = generics.iter().map(|ty| self.resolve(ty)).collect();
6226 return self.call_signature(&sig, &format!("`{name}`"), explicit, args, trailing, span);
6227 }
6228 if let Some(sig) = self.structs.get(&key).cloned() {
6229 return self.struct_init(&key, &sig, args, trailing, span, expected);
6230 }
6231 if self.enums.contains_key(&key) {
6232 let cases = first_case_of(self.enums.get(&key));
6233 self.diagnostics.push(
6234 Diagnostic::error(NOT_CALLABLE, format!("`{name}` is an enum, not a function"))
6235 .at(callee_span)
6236 .rule("An enum value is one of its cases; the enum itself is not callable.")
6237 .help(format!("name a case, such as `{name}.{cases}`")),
6238 );
6239 self.check_args_freely(args, trailing);
6240 return Ty::recovery();
6241 }
6242 if let Some(module) = self.module.host_items.get(name).cloned() {
6246 return self.host_call(&module, name, args, trailing, span);
6247 }
6248 if name == MAP_ENTRY.name {
6249 return self.map_entry(args, trailing, span);
6250 }
6251 if let Some(ty) = self.assertion(name, args, trailing, span) {
6252 return ty;
6253 }
6254 if let Some(ty) = self.constructor(name, args, trailing, span, expected) {
6255 return ty;
6256 }
6257 if name == NONE_CASE.name {
6258 self.diagnostics.push(
6259 Diagnostic::error(NOT_CALLABLE, "`None` is a value, not a call")
6260 .at(callee_span)
6261 .rule("`None` is the empty case of `Option`, which carries nothing.")
6262 .help("write `None`"),
6263 );
6264 self.check_args_freely(args, trailing);
6265 return Ty::Option(Box::new(Ty::recovery()));
6266 }
6267 self.check_args_freely(args, trailing);
6268 self.unresolved_name(name, callee_span)
6269 }
6270
6271 fn assertion(
6283 &mut self,
6284 name: &str,
6285 args: &[Arg],
6286 trailing: Option<&Expr>,
6287 span: Span,
6288 ) -> Option<Ty> {
6289 let schema = free_builtin(name, FreeBuiltinKind::Assertion)?;
6290 let supplied: Vec<&Expr> = args.iter().map(|arg| &arg.value).chain(trailing).collect();
6291 let open = vec![Ty::unconstrained(); schema.generics.len()];
6297 let mut bindings = FreeBindings::new(schema, open);
6298 if supplied.len() == schema.arity() {
6299 self.free_arguments(schema, &supplied, &mut bindings, span);
6300 } else {
6301 self.diagnostics.push(
6305 free_arity(schema, supplied.len(), span)
6306 .rule(
6307 "`assert` checks one condition; `assertEqual` compares one pair of values.",
6308 )
6309 .help(format!(
6310 "write `{name}({})`",
6311 schema
6312 .params
6313 .iter()
6314 .map(|param| param.name)
6315 .collect::<Vec<_>>()
6316 .join(", ")
6317 )),
6318 );
6319 self.check_args_freely(args, trailing);
6320 }
6321 Some(bindings.open(&schema.result))
6322 }
6323
6324 fn constructor(
6330 &mut self,
6331 name: &str,
6332 args: &[Arg],
6333 trailing: Option<&Expr>,
6334 span: Span,
6335 expected: Option<&Expected>,
6336 ) -> Option<Ty> {
6337 let schema = free_builtin(name, FreeBuiltinKind::Constructor)?;
6338 let open: Vec<Ty> = schema
6339 .generics
6340 .iter()
6341 .map(|_| self.fresh_var(span))
6342 .collect();
6343 let mut bindings = FreeBindings::new(schema, open);
6344 let opened = bindings.open(&schema.result);
6345 self.produced(&opened);
6346 if let Some(hint) = expected.map(|e| &e.ty) {
6347 self.constrain(&opened, hint, span);
6353 bindings.read_off(&schema.result, hint, span);
6354 }
6355 let mut supplied: Vec<&Expr> = args.iter().map(|arg| &arg.value).collect();
6356 if let Some(trailing) = trailing {
6357 supplied.push(trailing);
6358 }
6359 if supplied.len() != schema.arity() {
6360 self.diagnostics.push(
6361 free_arity(schema, supplied.len(), span)
6362 .rule("A constructor carries exactly one value.")
6363 .help(format!("write `{name}(value)`")),
6364 );
6365 }
6366 self.free_arguments(schema, &supplied, &mut bindings, span);
6370 Some(bindings.open(&schema.result))
6371 }
6372
6373 fn free_arguments(
6381 &mut self,
6382 schema: &'static FreeBuiltinSchema,
6383 supplied: &[&Expr],
6384 bindings: &mut FreeBindings,
6385 span: Span,
6386 ) {
6387 for (index, value) in supplied.iter().enumerate() {
6388 let Some(param) = schema.params.get(index) else {
6389 self.expr(value, None);
6393 continue;
6394 };
6395 let declared = bindings.open(¶m.ty);
6396 if declared.is_wild() {
6397 let found = self.expr(value, None);
6398 self.constrain(&found, &declared, value.span);
6401 let found = if matches!(schema.result, BuiltinType::Shared(_)) {
6405 self.task_safe_argument(found, span)
6406 } else {
6407 found
6408 };
6409 bindings.bind(¶m.ty, found, value.span);
6410 } else {
6411 let reason = free_builtin_reason(schema, param, &declared);
6412 let expected = Expected::new(declared, bindings.origin(¶m.ty, span), reason);
6413 self.expr(value, Some(&expected));
6414 }
6415 }
6416 }
6417
6418 #[allow(clippy::too_many_arguments)]
6422 fn call_qualified(
6423 &mut self,
6424 id: ExprId,
6425 head: &str,
6426 name: &Ident,
6427 args: &[Arg],
6428 trailing: Option<&Expr>,
6429 span: Span,
6430 expected: Option<&Expected>,
6431 ) -> Option<Ty> {
6432 if self.module.host_uses.contains(head) {
6433 return Some(self.host_call(head, &name.node, args, trailing, span));
6434 }
6435 if self.module.module_imports.contains_key(head) {
6439 let Some(key) = self.qualified_key(head, &name.node, span) else {
6440 self.check_args_freely(args, trailing);
6441 return Some(Ty::recovery());
6442 };
6443 if let Some(sig) = self.functions.get(&key).cloned() {
6444 return Some(self.call_signature(
6445 &sig,
6446 &format!("`{head}.{}`", name.node),
6447 Vec::new(),
6448 args,
6449 trailing,
6450 span,
6451 ));
6452 }
6453 if let Some(sig) = self.structs.get(&key).cloned() {
6454 return Some(self.struct_init(&key, &sig, args, trailing, span, expected));
6455 }
6456 self.diagnostics.push(
6459 Diagnostic::error(
6460 NOT_CALLABLE,
6461 format!("`{head}.{}` is not a function", name.node),
6462 )
6463 .at(span)
6464 .rule("A qualified call reaches a function the named module exports, or a struct it declares.")
6465 .help(format!(
6466 "`{head}` exports `{}` as something else; name a case or a method of it instead",
6467 name.node
6468 )),
6469 );
6470 self.check_args_freely(args, trailing);
6471 return Some(Ty::recovery());
6472 }
6473 let key = self.key(head);
6474 if let Some(sig) = self.enums.get(&key).cloned() {
6475 let is_case = sig.cases.iter().any(|c| c.name == name.node);
6476 if !is_case {
6477 if let Some(sig) = self.methods.get(&(key.clone(), name.node.clone())).cloned() {
6478 self.record_target(id, span.file, &key, &name.node);
6479 self.check_receiver(&sig, &key, &name.node, span, false);
6480 return Some(self.call_signature(
6481 &sig,
6482 &format!("`{head}.{}`", name.node),
6483 Vec::new(),
6484 args,
6485 trailing,
6486 span,
6487 ));
6488 }
6489 }
6490 return Some(self.enum_case(&key, name, args, span));
6491 }
6492 if self.structs.contains_key(&key) {
6493 if let Some(sig) = self.methods.get(&(key.clone(), name.node.clone())).cloned() {
6494 self.record_target(id, span.file, &key, &name.node);
6495 self.check_receiver(&sig, &key, &name.node, span, false);
6496 return Some(self.call_signature(
6497 &sig,
6498 &format!("`{head}.{}`", name.node),
6499 Vec::new(),
6500 args,
6501 trailing,
6502 span,
6503 ));
6504 }
6505 let known = self.known_members(&key);
6506 self.diagnostics.push(
6507 Diagnostic::error(
6508 UNKNOWN_ASSOCIATED,
6509 format!("`{head}` has no associated function `{}`", name.node),
6510 )
6511 .at(span)
6512 .rule("An associated function is declared in the type's `impl` block.")
6513 .help(format!("`{head}` declares {known}")),
6514 );
6515 self.check_args_freely(args, trailing);
6516 return Some(Ty::recovery());
6517 }
6518 if cove_schema::is_builtin_type(head) {
6519 return Some(self.builtin_associated(head, name, args, trailing, span, expected));
6520 }
6521 None
6522 }
6523
6524 fn host_call(
6541 &mut self,
6542 module: &str,
6543 name: &str,
6544 args: &[Arg],
6545 trailing: Option<&Expr>,
6546 span: Span,
6547 ) -> Ty {
6548 let Some(schema) = self.host_schema(module) else {
6549 self.check_args_abstained(args, trailing, Ty::dynamic_boundary());
6566 return Ty::dynamic_boundary();
6567 };
6568 if let Some(operation) = schema.operation(name) {
6569 return self.call_host_operation(
6570 operation,
6571 &format!("{module}.{name}"),
6572 args,
6573 trailing,
6574 span,
6575 );
6576 }
6577 if let Some(declared) = schema.declared_type(name) {
6578 if !declared.is_enum() {
6579 return self.host_type_init(module, declared, args, trailing, span);
6580 }
6581 self.diagnostics.push(
6582 Diagnostic::error(
6583 NOT_CALLABLE,
6584 format!("`{module}.{name}` is a host enum, not a function"),
6585 )
6586 .at(span)
6587 .rule(HOST_SCHEMA_RULE)
6588 .help(format!(
6589 "name a case, such as `{module}.{name}.{}`",
6590 declared.cases.first().copied().unwrap_or("Case")
6591 )),
6592 );
6593 self.check_args_freely(args, trailing);
6594 return Ty::Host(format!("{module}.{name}").into());
6595 }
6596 if schema.resource(name).is_some() {
6597 self.diagnostics.push(
6598 Diagnostic::error(
6599 NOT_CALLABLE,
6600 format!("`{module}.{name}` is a host resource, not a function"),
6601 )
6602 .at(span)
6603 .rule("A host resource is opened by an operation of its module, which hands back a handle to it.")
6604 .help(format!(
6605 "call the operation that opens one, such as {}",
6606 list(&operation_names(schema.operations))
6607 )),
6608 );
6609 self.check_args_freely(args, trailing);
6610 return Ty::recovery();
6611 }
6612 self.diagnostics.push(
6613 Diagnostic::error(
6614 UNKNOWN_HOST_OPERATION,
6615 format!("host module `{module}` has no operation `{name}`"),
6616 )
6617 .at(span)
6618 .rule(HOST_SCHEMA_RULE)
6619 .help(format!(
6620 "`{module}` exposes {}",
6621 list(&operation_names(schema.operations))
6622 )),
6623 );
6624 self.check_args_freely(args, trailing);
6625 Ty::recovery()
6626 }
6627
6628 fn call_host_operation(
6635 &mut self,
6636 operation: &'static OperationSchema,
6637 shown: &str,
6638 args: &[Arg],
6639 trailing: Option<&Expr>,
6640 span: Span,
6641 ) -> Ty {
6642 let supplied = args.len() + usize::from(trailing.is_some());
6643 let spread = args.iter().any(|arg| arg.spread);
6647 if !spread && !operation.accepts(supplied) {
6648 self.diagnostics.push(
6649 Diagnostic::error(
6650 ARITY,
6651 format!(
6652 "`{shown}` takes {}, but {supplied} were given",
6653 operation.expected_arity()
6654 ),
6655 )
6656 .at(span)
6657 .rule(HOST_SCHEMA_RULE)
6658 .help(declared_signature(shown, operation)),
6659 );
6660 self.check_args_freely(args, trailing);
6661 return host_ty(&operation.result);
6665 }
6666 let last = operation.params.len().saturating_sub(1);
6667 let params: Vec<ParamSig> = operation
6668 .params
6669 .iter()
6670 .enumerate()
6671 .map(|(index, declared)| {
6672 let variadic = operation.variadic && index == last;
6676 ParamSig {
6677 name: format!("#{}{}", index + 1, if variadic { "..." } else { "" }),
6678 ty: host_ty(declared),
6679 variadic,
6680 has_default: false,
6681 is_var: false,
6682 span,
6683 }
6684 })
6685 .collect();
6686 self.match_arguments(
6687 ¶ms,
6688 &[],
6689 BTreeMap::new(),
6690 args,
6691 trailing,
6692 span,
6693 &format!("`{shown}`"),
6694 "argument",
6695 );
6696 self.host_result(operation, shown, span)
6697 }
6698
6699 fn host_result(&mut self, operation: &'static OperationSchema, shown: &str, span: Span) -> Ty {
6709 if contains_any(&operation.result) {
6710 self.diagnostics.push(
6711 Diagnostic::note(
6712 UNCONSTRAINED_RESULT,
6713 format!(
6714 "`{shown}` declares its result `{}`, so nothing here says what this call produced",
6715 operation.result
6716 ),
6717 )
6718 .at(span)
6719 .rule("A Host API operation declares `Any` where its meaning does not depend on the type of a value: the schema promises to carry the value, not to describe it.")
6720 .help(format!(
6721 "whatever the program does with the result of `{shown}` is checked at run time and by nothing here; {}",
6722 declared_signature(shown, operation)
6723 )),
6724 );
6725 }
6726 host_ty(&operation.result)
6727 }
6728
6729 fn host_type_init(
6737 &mut self,
6738 module: &str,
6739 declared: &'static TypeSchema,
6740 args: &[Arg],
6741 trailing: Option<&Expr>,
6742 span: Span,
6743 ) -> Ty {
6744 let params: Vec<ParamSig> = declared
6745 .fields
6746 .iter()
6747 .map(|field| ParamSig {
6748 name: field.name.to_string(),
6749 ty: host_ty(&field.ty),
6750 variadic: false,
6751 has_default: false,
6752 is_var: false,
6753 span,
6754 })
6755 .collect();
6756 self.match_arguments(
6757 ¶ms,
6758 &[],
6759 BTreeMap::new(),
6760 args,
6761 trailing,
6762 span,
6763 &format!("`{module}.{}`", declared.name),
6764 "the field",
6765 );
6766 Ty::Host(format!("{module}.{}", declared.name).into())
6767 }
6768
6769 fn host_named_type(&mut self, module: &str, name: &str, arguments: usize, span: Span) -> Ty {
6771 let qualified = format!("{module}.{name}");
6772 let Some(schema) = self.host_schema(module) else {
6773 self.diagnostics.push(unchecked_host_type(&qualified, span));
6774 return Ty::dynamic_boundary();
6775 };
6776 if !schema.declares_type(name) {
6777 let mut known: Vec<String> = schema
6778 .types
6779 .iter()
6780 .map(|declared| declared.name.to_string())
6781 .collect();
6782 known.extend(
6783 schema
6784 .resources
6785 .iter()
6786 .map(|resource| resource.name.to_string()),
6787 );
6788 self.diagnostics.push(
6789 Diagnostic::error(
6790 UNKNOWN_HOST_TYPE,
6791 format!("host module `{module}` declares no type `{name}`"),
6792 )
6793 .at(span)
6794 .rule(HOST_SCHEMA_RULE)
6795 .help(if known.is_empty() {
6796 format!("`{module}` declares no types of its own")
6797 } else {
6798 format!("`{module}` declares {}", list(&known))
6799 }),
6800 );
6801 return Ty::recovery();
6802 }
6803 self.check_type_arity(&qualified, 0, arguments, span);
6806 Ty::Host(qualified.into())
6807 }
6808
6809 fn host_enum_case(
6813 &mut self,
6814 module: &str,
6815 declared: &str,
6816 case: &Ident,
6817 span: Span,
6818 ) -> Option<Ty> {
6819 let schema = self.host_schema(module)?.declared_type(declared)?;
6820 if !schema.is_enum() {
6821 return None;
6822 }
6823 let qualified: Arc<str> = format!("{module}.{declared}").into();
6824 if !schema.cases.contains(&case.node.as_str()) {
6825 let known: Vec<String> = schema.cases.iter().map(|c| (*c).to_string()).collect();
6826 self.diagnostics.push(
6827 Diagnostic::error(
6828 UNKNOWN_CASE,
6829 format!("`{qualified}` has no case `{}`", case.node),
6830 )
6831 .at(span)
6832 .rule(HOST_SCHEMA_RULE)
6833 .help(format!("`{qualified}` declares {}", list(&known))),
6834 );
6835 }
6836 Some(Ty::Host(qualified))
6837 }
6838
6839 fn builtin_field(&mut self, base_ty: &Ty, name: &Ident, span: Span) -> Ty {
6848 let Some(schema) = builtin_schema_of(base_ty) else {
6851 return Ty::placeholder();
6852 };
6853 let bound = receiver_binding(schema, base_ty);
6854 match schema.field(&name.node) {
6855 Some(field) => builtin_ty(&field.ty, &bound, Some(base_ty)),
6856 None => {
6857 let known: Vec<String> = schema.fields.iter().map(|f| f.name.to_string()).collect();
6858 self.diagnostics.push(
6859 Diagnostic::error(
6860 UNKNOWN_FIELD,
6861 format!("`{}` has no field `{}`", schema.name, name.node),
6862 )
6863 .at(span)
6864 .rule("A builtin struct's fields are exactly the ones the language defines.")
6865 .help(format!(
6866 "`{}` declares {}",
6867 schema.name,
6868 list(&known)
6869 )),
6870 );
6871 Ty::recovery()
6872 }
6873 }
6874 }
6875
6876 fn host_field(&mut self, declared: &str, name: &Ident, span: Span) -> Ty {
6878 let Some(schema) = self.host_declared_type(declared) else {
6879 self.diagnostics.push(
6883 Diagnostic::error(
6884 UNKNOWN_FIELD,
6885 format!("`{declared}` has no field `{}`", name.node),
6886 )
6887 .at(span)
6888 .rule("A host resource is a name for something the host keeps, so it has operations rather than fields.")
6889 .help(format!("call an operation on it, such as `{}()`", name.node)),
6890 );
6891 return Ty::recovery();
6892 };
6893 match schema.fields.iter().find(|f| f.name == name.node) {
6894 Some(field) => {
6895 if contains_any(&field.ty) {
6901 self.diagnostics.push(
6902 Diagnostic::note(
6903 UNCONSTRAINED_FIELD,
6904 format!(
6905 "`{declared}` declares `{}` as `{}`, so nothing here says what this field holds",
6906 name.node, field.ty
6907 ),
6908 )
6909 .at(span)
6910 .rule("A Host API schema declares `Any` where its meaning does not depend on the type of a value: the schema promises to carry the value, not to describe it.")
6911 .help(format!(
6912 "whatever the program does with `{}.{}` is checked at run time and by nothing here",
6913 declared, name.node
6914 )),
6915 );
6916 }
6917 host_ty(&field.ty)
6918 }
6919 None => {
6920 let known: Vec<String> = schema.fields.iter().map(|f| f.name.to_string()).collect();
6921 self.diagnostics.push(
6922 Diagnostic::error(
6923 UNKNOWN_FIELD,
6924 format!("`{declared}` has no field `{}`", name.node),
6925 )
6926 .at(span)
6927 .rule(HOST_SCHEMA_RULE)
6928 .help(if known.is_empty() {
6929 format!("`{declared}` carries no fields")
6930 } else {
6931 format!("`{declared}` declares {}", list(&known))
6932 }),
6933 );
6934 Ty::recovery()
6935 }
6936 }
6937 }
6938
6939 fn host_method_call(
6942 &mut self,
6943 declared: &str,
6944 name: &Ident,
6945 args: &[Arg],
6946 trailing: Option<&Expr>,
6947 span: Span,
6948 ) -> Ty {
6949 if let Some(resource) = self.host_resource(declared) {
6950 if let Some(operation) = resource.operation(&name.node) {
6951 return self.call_host_operation(
6952 operation,
6953 &format!("{declared}.{}", name.node),
6954 args,
6955 trailing,
6956 span,
6957 );
6958 }
6959 self.diagnostics.push(
6960 Diagnostic::error(
6961 UNKNOWN_HOST_OPERATION,
6962 format!("`{declared}` has no operation `{}`", name.node),
6963 )
6964 .at(span)
6965 .rule(HOST_SCHEMA_RULE)
6966 .help(format!(
6967 "`{declared}` answers {}",
6968 list(&operation_names(resource.operations))
6969 )),
6970 );
6971 self.check_args_freely(args, trailing);
6972 return Ty::recovery();
6973 }
6974 self.diagnostics.push(
6975 Diagnostic::error(
6976 UNKNOWN_HOST_OPERATION,
6977 format!("`{declared}` has no operation `{}`", name.node),
6978 )
6979 .at(span)
6980 .rule("A host type that is plain data has fields; only a host resource answers operations.")
6981 .help(format!("read a field, such as `.{}`", name.node)),
6982 );
6983 self.check_args_freely(args, trailing);
6984 Ty::recovery()
6985 }
6986
6987 #[allow(clippy::too_many_arguments)]
6993 fn builtin_associated(
6994 &mut self,
6995 type_name: &str,
6996 name: &Ident,
6997 args: &[Arg],
6998 trailing: Option<&Expr>,
6999 span: Span,
7000 expected: Option<&Expected>,
7001 ) -> Ty {
7002 let declared = cove_schema::builtin(type_name)
7006 .and_then(|schema| schema.associated_function(&name.node));
7007 let Some(declared) = declared else {
7008 self.diagnostics.push(
7009 Diagnostic::error(
7010 UNKNOWN_ASSOCIATED,
7011 format!("`{type_name}` has no associated function `{}`", name.node),
7012 )
7013 .at(span)
7014 .rule(format!(
7015 "A builtin type's associated functions are {}.",
7016 builtin_associated_functions()
7017 ))
7018 .help(format!(
7019 "`{type_name}` has no `{}`; construct the value another way",
7020 name.node
7021 )),
7022 );
7023 self.check_args_freely(args, trailing);
7024 return Ty::recovery();
7025 };
7026 let sig = builtin_sig(declared, &[], &[], None);
7027 let what = format!("`{type_name}.{}`", name.node);
7028 let mut subst = self.builtin_arguments(&sig, &what, args, trailing, span);
7029 self.settle_from_expectation(&sig, &mut subst, expected);
7030 self.open_result(&sig.ret, &sig.generics, &subst, span)
7042 }
7043
7044 fn settle_from_expectation(
7062 &mut self,
7063 sig: &BuiltinSig,
7064 subst: &mut BTreeMap<Arc<str>, Ty>,
7065 expected: Option<&Expected>,
7066 ) {
7067 if sig.generics.iter().all(|g| subst.contains_key(g)) {
7068 return;
7069 }
7070 let Some(expected) = expected else {
7071 return;
7072 };
7073 let generics: BTreeSet<Arc<str>> = sig.generics.iter().cloned().collect();
7074 let mut found = subst.clone();
7075 if unify(&sig.ret, &expected.ty, &generics, &mut found, &self.view()) {
7076 *subst = found;
7077 }
7078 }
7079
7080 fn map_entry(&mut self, args: &[Arg], trailing: Option<&Expr>, span: Span) -> Ty {
7088 let bound = BTreeMap::new();
7091 let sig = BuiltinSig {
7092 generics: MAP_ENTRY
7093 .parameters
7094 .iter()
7095 .map(|name| Arc::from(*name))
7096 .collect(),
7097 params: MAP_ENTRY
7098 .fields
7099 .iter()
7100 .map(|field| (field.name, builtin_ty(&field.ty, &bound, None)))
7101 .collect(),
7102 variadic: false,
7103 ret: Ty::MapEntry(
7104 Box::new(Ty::Param("K".into())),
7105 Box::new(Ty::Param("V".into())),
7106 ),
7107 };
7108 self.call_builtin(&sig, "`MapEntry`", args, trailing, span)
7109 }
7110
7111 fn expected_arguments(name: &str, expected: Option<&Expected>) -> Vec<Ty> {
7115 match expected.map(|e| &e.ty) {
7116 Some(Ty::Struct(other, args)) if other.as_ref() == name => args.clone(),
7117 _ => Vec::new(),
7118 }
7119 }
7120
7121 #[allow(clippy::too_many_arguments)]
7124 fn struct_init(
7125 &mut self,
7126 name: &str,
7127 sig: &StructSig,
7128 args: &[Arg],
7129 trailing: Option<&Expr>,
7130 span: Span,
7131 expected: Option<&Expected>,
7132 ) -> Ty {
7133 if self.reject_opaque_construction(name, sig, span) {
7141 self.check_args_freely(args, trailing);
7142 return Ty::Struct(name.into(), vec![Ty::recovery(); sig.generics.len()]);
7143 }
7144 let generics: Vec<Arc<str>> = sig.generics.clone();
7145 let stated = Checker::expected_arguments(name, expected);
7146 let subst = self.match_arguments(
7147 &sig.fields,
7148 &generics,
7149 BTreeMap::new(),
7150 args,
7151 trailing,
7152 span,
7153 &format!("`{name}`"),
7154 "the field",
7155 );
7156 let arguments = generics
7157 .iter()
7158 .enumerate()
7159 .map(|(index, g)| {
7160 if let Some(ty) = subst.get(g) {
7164 return ty.clone();
7165 }
7166 if let Some(ty) = stated.get(index).filter(|ty| !ty.is_wild()) {
7167 return ty.clone();
7168 }
7169 if !Checker::accounted_for(expected) {
7175 self.diagnostics.push(unconstrained(
7176 format!("nothing says what `{g}` is in `{name}<{g}>`"),
7177 format!(
7178 "write the type on the place that holds it, as in `let value: {name}<Int> = ...`, or give the value to a place that declares one"
7179 ),
7180 span,
7181 ));
7182 }
7183 Ty::unconstrained()
7184 })
7185 .collect();
7186 Ty::Struct(name.into(), arguments)
7187 }
7188
7189 fn call_signature(
7192 &mut self,
7193 sig: &FnSig,
7194 what: &str,
7195 explicit: Vec<Ty>,
7196 args: &[Arg],
7197 trailing: Option<&Expr>,
7198 span: Span,
7199 ) -> Ty {
7200 let mut subst: BTreeMap<Arc<str>, Ty> = BTreeMap::new();
7201 for (param, ty) in sig.generics.iter().zip(explicit) {
7202 subst.insert(param.clone(), ty);
7203 }
7204 let subst = self.match_arguments(
7205 &sig.params,
7206 &sig.generics,
7207 subst,
7208 args,
7209 trailing,
7210 span,
7211 what,
7212 "the parameter",
7213 );
7214 self.check_bounds(sig, &subst, what, span);
7215 let ret = self.open_result(&sig.ret, &sig.generics, &subst, span);
7216 if sig.is_async {
7217 Ty::Task(Box::new(ret))
7220 } else {
7221 ret
7222 }
7223 }
7224
7225 fn call_value(
7227 &mut self,
7228 callee: &Ty,
7229 args: &[Arg],
7230 trailing: Option<&Expr>,
7231 span: Span,
7232 callee_span: Span,
7233 ) -> Ty {
7234 match callee {
7235 Ty::Unknown(_) | Ty::Never => {
7236 self.check_args_freely(args, trailing);
7237 callee.abstain()
7238 }
7239 Ty::Any => {
7240 self.check_args_freely(args, trailing);
7241 Ty::Any
7242 }
7243 Ty::Fn(func) => {
7244 let params: Vec<ParamSig> = func
7245 .params
7246 .iter()
7247 .enumerate()
7248 .map(|(index, ty)| ParamSig {
7249 name: format!("#{index}"),
7250 ty: ty.clone(),
7251 variadic: false,
7252 has_default: false,
7253 is_var: false,
7257 span: callee_span,
7258 })
7259 .collect();
7260 self.match_arguments(
7261 ¶ms,
7262 &[],
7263 BTreeMap::new(),
7264 args,
7265 trailing,
7266 span,
7267 "this function value",
7268 "the parameter",
7269 );
7270 if func.is_async {
7271 Ty::Task(Box::new(func.ret.clone()))
7272 } else {
7273 func.ret.clone()
7274 }
7275 }
7276 other => {
7277 self.diagnostics.push(
7278 Diagnostic::error(NOT_CALLABLE, format!("`{other}` is not a function"))
7279 .at(callee_span)
7280 .rule("Only a function value can be called.")
7281 .help(format!(
7282 "`{other}` is a value, not a function; remove the argument list"
7283 )),
7284 );
7285 self.check_args_freely(args, trailing);
7286 Ty::recovery()
7287 }
7288 }
7289 }
7290
7291 #[allow(clippy::too_many_arguments)]
7299 fn match_arguments(
7300 &mut self,
7301 params: &[ParamSig],
7302 generics: &[Arc<str>],
7303 mut subst: BTreeMap<Arc<str>, Ty>,
7304 args: &[Arg],
7305 trailing: Option<&Expr>,
7306 span: Span,
7307 what: &str,
7308 role: &str,
7309 ) -> BTreeMap<Arc<str>, Ty> {
7310 let variadic_last = params.last().is_some_and(|p| p.variadic);
7311 let mut slots: Vec<Option<&Arg>> = vec![None; params.len()];
7312 let mut rest: Vec<&Arg> = Vec::new();
7313 let mut next = 0usize;
7314 let mut labeled = false;
7315 let mut mislabeled = false;
7319 let generic_set: BTreeSet<Arc<str>> = generics.iter().cloned().collect();
7320
7321 for arg in args {
7322 match &arg.label {
7323 Some(label) => {
7324 labeled = true;
7325 match params.iter().position(|p| p.name == label.node) {
7326 Some(index) => {
7327 if index < next && slots[index].is_none() {
7334 let order: Vec<&str> =
7335 params.iter().map(|p| p.name.as_str()).collect();
7336 self.diagnostics.push(
7337 Diagnostic::error(
7338 LABEL_ORDER,
7339 format!(
7340 "{what} was given the label `{}` out of declaration order",
7341 label.node
7342 ),
7343 )
7344 .at(arg.span)
7345 .rule("Labeled arguments appear in declaration order, so argument order matches parameter order.")
7346 .help(format!(
7347 "write the arguments in this order: {}",
7348 order.join(", ")
7349 )),
7350 );
7351 }
7352 slots[index] = Some(arg);
7353 next = index + 1;
7354 }
7355 None => {
7356 let known: Vec<String> =
7357 params.iter().map(|p| p.name.clone()).collect();
7358 self.diagnostics.push(
7359 Diagnostic::error(
7360 UNKNOWN_LABEL,
7361 format!("{what} has no parameter labeled `{}`", label.node),
7362 )
7363 .at(arg.span)
7364 .rule("Argument labels are parameter names and part of the API contract.")
7365 .help(format!("known labels: {}", list(&known))),
7366 );
7367 self.expr(&arg.value, None);
7368 mislabeled = true;
7369 }
7370 }
7371 }
7372 None if labeled => {
7375 self.expr(&arg.value, None);
7376 }
7377 None if variadic_last && next + 1 >= params.len() => rest.push(arg),
7378 None if next < params.len() => {
7379 slots[next] = Some(arg);
7380 next += 1;
7381 }
7382 None => {
7383 self.diagnostics.push(
7384 Diagnostic::error(
7385 ARITY,
7386 format!(
7387 "{what} takes {} argument(s), but more were given",
7388 params.len()
7389 ),
7390 )
7391 .at(arg.span)
7392 .rule("A call passes exactly the arguments the declaration binds.")
7393 .help(format!(
7394 "{what} declares {}",
7395 list(¶ms.iter().map(|p| p.name.clone()).collect::<Vec<_>>())
7396 )),
7397 );
7398 self.expr(&arg.value, None);
7399 }
7400 }
7401 }
7402
7403 let trailing_slot = trailing.and_then(|_| {
7406 if variadic_last {
7407 None
7408 } else {
7409 slots.iter().position(Option::is_none)
7410 }
7411 });
7412 if let Some(trailing) = trailing {
7413 match trailing_slot.and_then(|index| params.get(index).map(|p| (index, p))) {
7414 Some((index, param)) => {
7415 let param = param.clone();
7416 let expected = param.ty.substitute(&subst);
7417 let hint = self.open(&expected, generics, &subst);
7418 let found = self.trailing_type(trailing, Some(&hint));
7419 self.check_argument(
7420 &found,
7421 &hint,
7422 &expected,
7423 trailing.span,
7424 ¶m,
7425 &generic_set,
7426 &mut subst,
7427 role,
7428 );
7429 slots[index] = None;
7430 }
7431 None => {
7432 let ty = self.trailing_type(trailing, None);
7433 if variadic_last {
7434 if let Some(param) = params.last() {
7435 let param = param.clone();
7436 let element = param.ty.substitute(&subst);
7437 let hint = self.open(&element, generics, &subst);
7438 self.check_argument(
7439 &ty,
7440 &hint,
7441 &element,
7442 trailing.span,
7443 ¶m,
7444 &generic_set,
7445 &mut subst,
7446 role,
7447 );
7448 }
7449 } else {
7450 self.diagnostics.push(
7451 Diagnostic::error(
7452 ARITY,
7453 format!(
7454 "{what} takes {} argument(s), but a trailing closure was given too",
7455 params.len()
7456 ),
7457 )
7458 .at(trailing.span)
7459 .rule("A trailing closure is the call's last argument.")
7460 .help("remove the trailing closure, or pass it in place of an argument"),
7461 );
7462 }
7463 }
7464 }
7465 }
7466
7467 for (index, param) in params.iter().enumerate() {
7468 if param.variadic {
7469 let element = param.ty.substitute(&subst);
7470 let mut supplied: Vec<&Arg> = rest.clone();
7471 if let Some(arg) = slots[index] {
7472 supplied.insert(0, arg);
7473 }
7474 for arg in supplied {
7475 self.variadic_argument(
7476 arg,
7477 &element,
7478 param,
7479 generics,
7480 &generic_set,
7481 &mut subst,
7482 role,
7483 );
7484 }
7485 continue;
7486 }
7487 let Some(arg) = slots[index] else {
7488 if !param.has_default && trailing_slot != Some(index) && !mislabeled {
7489 self.diagnostics.push(
7490 Diagnostic::error(
7491 MISSING_ARGUMENT,
7492 format!("{what} needs {role} `{}`", param.name),
7493 )
7494 .at(span)
7495 .label(param.span, format!("`{}` is `{}`", param.name, param.ty))
7496 .rule("A call passes every parameter that has no default.")
7497 .help(format!("pass `{}: <{}>`", param.name, param.ty)),
7498 );
7499 }
7500 continue;
7501 };
7502 let expected = param.ty.substitute(&subst);
7503 let hint = self.open(&expected, generics, &subst);
7504 let hint_expected = Expected::new(
7505 hint.clone(),
7506 param.span,
7507 format!("{role} `{}` is `{}`", param.name, param.ty),
7508 );
7509 let found = self.expr(&arg.value, Some(&hint_expected));
7510 self.check_argument(
7511 &found,
7512 &hint,
7513 &expected,
7514 arg.span,
7515 param,
7516 &generic_set,
7517 &mut subst,
7518 role,
7519 );
7520 }
7521 subst
7522 }
7523
7524 #[allow(clippy::too_many_arguments)]
7529 fn check_argument(
7530 &mut self,
7531 found: &Ty,
7532 hint: &Ty,
7533 expected: &Ty,
7534 span: Span,
7535 param: &ParamSig,
7536 generics: &BTreeSet<Arc<str>>,
7537 subst: &mut BTreeMap<Arc<str>, Ty>,
7538 role: &str,
7539 ) {
7540 self.constrain(found, hint, span);
7544 let resolved = self.bound(found.clone());
7559 let unified = unify(expected, &resolved, generics, subst, &self.view());
7560 if !unified && found.matches(hint) {
7561 let expected = expected.substitute(subst);
7562 self.report_argument(found, &expected, span, param, role);
7563 }
7564 }
7565
7566 fn open(&self, ty: &Ty, generics: &[Arc<str>], subst: &BTreeMap<Arc<str>, Ty>) -> Ty {
7577 if generics.is_empty() {
7578 return ty.clone();
7579 }
7580 let map: BTreeMap<Arc<str>, Ty> = generics
7581 .iter()
7582 .map(|g| {
7583 (
7584 g.clone(),
7585 subst.get(g).cloned().unwrap_or(Ty::unconstrained()),
7586 )
7587 })
7588 .collect();
7589 ty.substitute(&map)
7590 }
7591
7592 fn open_result(
7615 &mut self,
7616 ty: &Ty,
7617 generics: &[Arc<str>],
7618 subst: &BTreeMap<Arc<str>, Ty>,
7619 at: Span,
7620 ) -> Ty {
7621 if generics.is_empty() {
7622 return ty.clone();
7623 }
7624 let map: BTreeMap<Arc<str>, Ty> = generics
7625 .iter()
7626 .map(|generic| {
7627 let ty = match subst.get(generic) {
7628 Some(settled) => settled.clone(),
7629 None => self.fresh_var(at),
7630 };
7631 (generic.clone(), ty)
7632 })
7633 .collect();
7634 let opened = ty.substitute(&map);
7635 self.produced(&opened);
7636 opened
7637 }
7638
7639 fn produced(&mut self, ty: &Ty) {
7646 for id in ty.vars() {
7647 if let Some(var) = self.vars.get_mut(id as usize) {
7648 var.produced = ty.clone();
7649 }
7650 }
7651 }
7652
7653 fn fresh_var(&mut self, at: Span) -> Ty {
7655 self.vars.push(TyVar {
7656 owner: None,
7657 solved: None,
7658 conflicted: false,
7659 spoken_for: None,
7660 abstained: None,
7661 at,
7662 produced: Ty::Unknown(Unknown::Var(self.vars.len() as u32)),
7663 probed: self.probing,
7664 });
7665 Ty::var((self.vars.len() - 1) as u32)
7666 }
7667
7668 fn attach(&mut self, ty: &Ty, name: &str, span: Span) {
7678 if self.probing {
7684 return;
7685 }
7686 for id in ty.vars() {
7687 if let Some(var) = self.vars.get_mut(id as usize) {
7688 if var.owner.is_none() {
7689 var.owner = Some(Owned {
7690 name: name.to_string(),
7691 span,
7692 ty: ty.clone(),
7693 });
7694 }
7695 }
7696 }
7697 }
7698
7699 fn constrain(&mut self, found: &Ty, expected: &Ty, span: Span) {
7715 if self.vars.is_empty() || self.probing {
7719 return;
7720 }
7721 for (open, place) in [(found, expected), (expected, found)] {
7726 if let Some(abstention) = place.abstention() {
7727 for id in open.vars() {
7728 if let Some(var) = self.vars.get_mut(id as usize) {
7729 if var.abstained.is_none() {
7730 var.abstained = Some(abstention.clone());
7731 }
7732 }
7733 }
7734 }
7735 }
7736 match (found, expected) {
7737 (Ty::Unknown(Unknown::Var(id)), other) | (other, Ty::Unknown(Unknown::Var(id))) => {
7738 self.settle_var(*id, other, span)
7739 }
7740 (Ty::Array(a), Ty::Array(b))
7741 | (Ty::Vector(a), Ty::Vector(b))
7742 | (Ty::Set(a), Ty::Set(b))
7743 | (Ty::Option(a), Ty::Option(b))
7744 | (Ty::Task(a), Ty::Task(b))
7745 | (Ty::Shared(a), Ty::Shared(b)) => self.constrain(a, b, span),
7746 (Ty::Map(ak, av), Ty::Map(bk, bv))
7747 | (Ty::MapEntry(ak, av), Ty::MapEntry(bk, bv))
7748 | (Ty::Result(ak, av), Ty::Result(bk, bv)) => {
7749 self.constrain(ak, bk, span);
7750 self.constrain(av, bv, span);
7751 }
7752 (Ty::Struct(a, aargs), Ty::Struct(b, bargs))
7753 | (Ty::Enum(a, aargs), Ty::Enum(b, bargs))
7754 if a == b && aargs.len() == bargs.len() =>
7755 {
7756 for (a, b) in aargs.iter().zip(bargs) {
7757 self.constrain(a, b, span);
7758 }
7759 }
7760 (Ty::Fn(a), Ty::Fn(b))
7761 if a.is_async == b.is_async && a.params.len() == b.params.len() =>
7762 {
7763 for (a, b) in a.params.iter().zip(&b.params) {
7764 self.constrain(a, b, span);
7765 }
7766 self.constrain(&a.ret, &b.ret, span);
7767 }
7768 _ => {}
7769 }
7770 }
7771
7772 fn settle_var(&mut self, id: u32, ty: &Ty, span: Span) {
7782 if ty.is_wild() {
7783 return;
7784 }
7785 if ty.holds_var() {
7786 if let Some(var) = self.vars.get_mut(id as usize) {
7787 if var.spoken_for.is_none() {
7788 var.spoken_for = Some((ty.clone(), span));
7789 }
7790 }
7791 return;
7792 }
7793 let Some(var) = self.vars.get(id as usize) else {
7794 return;
7795 };
7796 let Some((settled, first)) = var.solved.clone() else {
7797 self.vars[id as usize].solved = Some((ty.clone(), span));
7798 return;
7799 };
7800 if settled.matches(ty) || var.conflicted {
7801 return;
7802 }
7803 let Some(owner) = var.owner.clone() else {
7808 self.vars[id as usize].conflicted = true;
7809 return;
7810 };
7811 self.vars[id as usize].conflicted = true;
7812 let holds = owner.ty.settled(&self.vars);
7818 self.vars[id as usize].solved = Some((ty.clone(), span));
7819 let needs = owner.ty.settled(&self.vars);
7820 self.vars[id as usize].solved = Some((settled, first));
7821 let name = owner.name;
7822 self.diagnostics.push(
7823 Diagnostic::error(
7824 INFERENCE_CONFLICT,
7825 format!("`{name}` was settled as `{holds}`, but this use needs `{needs}`"),
7826 )
7827 .at(span)
7828 .label(first, format!("settled as `{holds}` here"))
7829 .label(owner.span, format!("`{name}` is declared with no written type"))
7830 .rule("A binding whose initializer leaves a type open takes that type from its uses, and every use of it means the same type.")
7831 .help(format!(
7832 "write the type on the binding, as in `{name}: {holds}`, and correct whichever use disagrees"
7833 )),
7834 );
7835 }
7836
7837 fn finish_inference(&mut self) {
7860 for index in 0..self.vars.len() {
7865 if self.vars[index].solved.is_some() {
7866 continue;
7867 }
7868 if let Some(abstention) = self.vars[index].abstained.clone() {
7869 let at = self.vars[index].at;
7870 self.vars[index].solved = Some((abstention, at));
7871 }
7872 }
7873 let stopped = self.diagnostics[self.body_mark.min(self.diagnostics.len())..]
7878 .iter()
7879 .any(|item| item.severity == Severity::Error);
7880 let mut reported: BTreeSet<(cove_diag::FileId, u32)> = BTreeSet::new();
7883 for index in 0..self.vars.len() {
7884 let var = &self.vars[index];
7885 if stopped || var.probed || var.solved.is_some() || var.conflicted {
7889 continue;
7890 }
7891 let owner = var.owner.clone();
7892 let at = owner.as_ref().map_or(var.at, |owner| owner.span);
7893 if !reported.insert((at.file, at.start)) {
7894 continue;
7895 }
7896 if let Some((asked, use_span)) = var.spoken_for.clone() {
7897 let holds = asked.settled(&self.vars);
7898 let subject = match &owner {
7899 Some(owner) => format!("`{}`", owner.name),
7900 None => "this value".to_string(),
7901 };
7902 self.diagnostics.push(
7903 Diagnostic::error(
7904 RECURSIVE_TYPE,
7905 format!("this use makes {subject} hold a `{holds}`, which holds itself"),
7906 )
7907 .at(use_span)
7908 .label(at, format!("{subject} is declared with no written type"))
7909 .rule("A type the checker infers is a type the program could have written, and a type that contains itself is written by declaring one.")
7910 .help(
7911 "declare a struct or an enum for the thing that repeats and hold that, as in `struct Node { next: Vector<Node> }`",
7912 ),
7913 );
7914 continue;
7915 }
7916 match &owner {
7917 Some(owner) => {
7918 let shown = owner.ty.settled(&self.vars);
7919 let name = &owner.name;
7920 self.diagnostics.push(unconstrained(
7921 format!("nothing says what the `_` in `{name}: {shown}` is"),
7922 format!(
7923 "write the type on the binding, as in `{name}: {shown}` with the `_` filled in, or use `{name}` in a way that says what it holds"
7924 ),
7925 at,
7926 ));
7927 }
7928 None => {
7931 let shown = self.vars[index].produced.settled(&self.vars);
7932 self.diagnostics.push(unconstrained(
7933 format!("nothing says what the `_` in `{shown}` is"),
7934 format!(
7935 "bind this value first, with the type written, as in `let value: {shown} = ...` with the `_` filled in"
7936 ),
7937 at,
7938 ));
7939 }
7940 }
7941 }
7942 for (file, id) in std::mem::take(&mut self.open_facts) {
7943 let Some(ty) = self.facts.ty(file, id).cloned() else {
7944 continue;
7945 };
7946 let settled = ty.settled(&self.vars);
7947 debug_assert!(
7948 !settled.holds_var(),
7949 "an inference variable escaped into a fact: `{settled}`"
7950 );
7951 self.facts.record_ty(file, id, &settled);
7952 }
7953 self.vars.clear();
7954 }
7955
7956 #[allow(clippy::too_many_arguments)]
7960 fn variadic_argument(
7961 &mut self,
7962 arg: &Arg,
7963 element: &Ty,
7964 param: &ParamSig,
7965 generics: &[Arc<str>],
7966 generic_set: &BTreeSet<Arc<str>>,
7967 subst: &mut BTreeMap<Arc<str>, Ty>,
7968 role: &str,
7969 ) {
7970 if arg.spread {
7971 let ty = self.expr(&arg.value, None);
7972 let spread_element = match &ty {
7973 Ty::Array(inner) | Ty::Vector(inner) => (**inner).clone(),
7974 Ty::Unknown(_) | Ty::Never => ty.abstain(),
7975 Ty::Any => Ty::Any,
7976 other => {
7977 self.diagnostics.push(
7978 Diagnostic::error(
7979 MISMATCH,
7980 format!("`...` spreads an `Array` or a `Vector`, but found `{other}`"),
7981 )
7982 .at(arg.span)
7983 .label(param.span, format!("`{}` is variadic", param.name))
7984 .rule("A variadic parameter is an `Array<T>`, so a spread argument must be a sequence of `T`.")
7985 .help(format!("pass the value directly, as in `f(<{other}>)`")),
7986 );
7987 return;
7988 }
7989 };
7990 if !unify(element, &spread_element, generic_set, subst, &self.view()) {
7991 self.diagnostics.push(
7992 Diagnostic::error(
7993 MISMATCH,
7994 format!("expected `{element}`, found `{spread_element}`"),
7995 )
7996 .at(arg.span)
7997 .label(param.span, format!("`{}` is `{element}...`", param.name))
7998 .rule("A variadic parameter is an `Array<T>`; every spread element is a `T`.")
7999 .help(format!("spread a sequence of `{element}`")),
8000 );
8001 }
8002 return;
8003 }
8004 let hint = self.open(element, generics, subst);
8005 let hint_expected = Expected::new(
8006 hint.clone(),
8007 param.span,
8008 format!("{role} `{}` is `{}`", param.name, param.ty),
8009 );
8010 let found = self.expr(&arg.value, Some(&hint_expected));
8011 self.check_argument(
8012 &found,
8013 &hint,
8014 element,
8015 arg.span,
8016 param,
8017 generic_set,
8018 subst,
8019 role,
8020 );
8021 }
8022
8023 fn report_argument(
8024 &mut self,
8025 found: &Ty,
8026 expected: &Ty,
8027 span: Span,
8028 param: &ParamSig,
8029 role: &str,
8030 ) {
8031 if found.matches(expected) {
8032 return;
8033 }
8034 let mut diagnostic = Diagnostic::error(
8035 MISMATCH,
8036 format!("expected `{expected}`, found `{found}`"),
8037 )
8038 .at(span)
8039 .label(param.span, format!("{role} `{}` is `{}`", param.name, param.ty))
8040 .rule("Types are nominal and there are no implicit conversions: an argument must already have the parameter's type.");
8041 if let Some(help) = conversion_help(expected, found) {
8042 diagnostic = diagnostic.help(help);
8043 }
8044 self.diagnostics.push(diagnostic);
8045 }
8046
8047 fn trailing_type(&mut self, trailing: &Expr, expected: Option<&Ty>) -> Ty {
8050 match &trailing.kind {
8051 ExprKind::Block(block) => {
8052 let ret = match expected {
8053 Some(Ty::Fn(func)) => Some(func.ret.clone()),
8054 Some(ty) if ty.is_accounted_for() => Some(ty.clone()),
8061 _ => None,
8062 };
8063 let hint = ret.clone().map(|ty| match ty.is_wild() {
8064 true => Expected::abstained(ty),
8065 false => {
8066 let label = format!("the trailing closure produces `{ty}`");
8067 Expected::new(ty, trailing.span, label)
8068 }
8069 });
8070 let ty = self.block(block, hint.as_ref());
8071 Ty::func(
8072 false,
8073 Vec::new(),
8074 ret.filter(|ty| !ty.is_wild()).unwrap_or(ty),
8075 )
8076 }
8077 _ => {
8078 let hint = expected.cloned().map(|ty| match ty.is_accounted_for() {
8079 true => Expected::abstained(ty),
8080 false => {
8081 let label = format!("the trailing argument is `{ty}`");
8082 Expected::new(ty, trailing.span, label)
8083 }
8084 });
8085 self.expr(trailing, hint.as_ref())
8086 }
8087 }
8088 }
8089
8090 fn check_args_freely(&mut self, args: &[Arg], trailing: Option<&Expr>) {
8093 self.check_args_abstained(args, trailing, Ty::recovery());
8094 }
8095
8096 fn check_args_abstained(&mut self, args: &[Arg], trailing: Option<&Expr>, why: Ty) {
8111 let expected = Expected::abstained(why.clone());
8112 for arg in args {
8113 self.expr(&arg.value, Some(&expected));
8114 }
8115 if let Some(trailing) = trailing {
8116 self.trailing_type(trailing, Some(&why));
8117 }
8118 }
8119
8120 fn view(&self) -> ConformanceView<'_> {
8127 ConformanceView {
8128 declared: &self.conformances,
8129 bounds: &self.bounds,
8130 }
8131 }
8132
8133 fn check_bounds(
8139 &mut self,
8140 sig: &FnSig,
8141 subst: &BTreeMap<Arc<str>, Ty>,
8142 what: &str,
8143 span: Span,
8144 ) {
8145 for (param, bounds) in &sig.bounds {
8146 let Some(ty) = subst.get(param) else {
8147 continue;
8148 };
8149 if ty.is_wild() {
8150 continue;
8151 }
8152 for bound in bounds {
8153 if conforms(ty, &bound.name, &self.view()) {
8154 continue;
8155 }
8156 let (message, help) = if let Ty::Dyn(trait_name) = ty {
8159 (
8160 format!("`dyn {trait_name}` cannot be used as a type argument"),
8161 format!(
8162 "pass a concrete value that conforms to `{}`, or declare the parameter as `dyn {}` instead of `{param}`",
8163 bound.name, bound.name
8164 ),
8165 )
8166 } else {
8167 (
8168 format!("`{ty}` does not conform to `{}`", bound.name),
8169 format!("write `impl {} for {ty} {{ ... }}`", bound.name),
8170 )
8171 };
8172 self.diagnostics.push(
8173 Diagnostic::error(UNSATISFIED_BOUND, message)
8174 .at(span)
8175 .label(
8176 bound.span,
8177 format!("{what} requires `{param}: {}`", bound.name),
8178 )
8179 .rule("A type argument must conform to every trait its type parameter is bounded by, and conformance is explicit: only an `impl Trait for Type` block declares one.")
8180 .help(help),
8181 );
8182 }
8183 }
8184 }
8185
8186 fn mutating_trait_method(&self, trait_name: &str, method: &str) -> bool {
8189 self.trait_entry(trait_name)
8190 .and_then(|entry| entry.method(method))
8191 .and_then(|method| method.receiver)
8192 .is_some_and(|receiver| receiver.is_var)
8193 }
8194
8195 fn bound_method(&self, param: &str, method: &str) -> Option<(Arc<str>, FnSig)> {
8198 for bound in self.bounds.get(param)? {
8199 if let Some(sig) = self.traits.get(&*bound.name).and_then(|m| m.get(method)) {
8200 return Some((bound.name.clone(), sig.clone()));
8201 }
8202 }
8203 None
8204 }
8205
8206 fn param_method_call(
8212 &mut self,
8213 param: &Arc<str>,
8214 name: &Ident,
8215 args: &[Arg],
8216 trailing: Option<&Expr>,
8217 span: Span,
8218 ) -> Ty {
8219 if let Some((trait_name, sig)) = self.bound_method(param, &name.node) {
8220 return self.call_signature(
8221 &sig,
8222 &format!("`{trait_name}.{}`", name.node),
8223 Vec::new(),
8224 args,
8225 trailing,
8226 span,
8227 );
8228 }
8229 let diagnostic = match self.bounds.get(param) {
8230 None => Diagnostic::error(
8231 UNBOUNDED_PARAMETER,
8232 format!("`{param}` has no bound, so it has no method `{}`", name.node),
8233 )
8234 .rule("A method call on a type parameter resolves through the parameter's bounds; an unbounded parameter's values can only be moved, not inspected.")
8235 .help(format!(
8236 "bound the parameter, as in `<{param}: SomeTrait>`, and declare `{}` in that trait",
8237 name.node
8238 )),
8239 Some(bounds) => {
8240 let names: Vec<String> = bounds.iter().map(|b| b.name.to_string()).collect();
8241 Diagnostic::error(
8242 UNKNOWN_METHOD,
8243 format!(
8244 "no trait `{param}` is bounded by declares a method `{}`",
8245 name.node
8246 ),
8247 )
8248 .rule("A method call on a type parameter resolves through the parameter's bounds.")
8249 .help(format!(
8250 "`{param}` is bounded by {}; declare `{}` in one of them, or add another bound",
8251 list(&names),
8252 name.node
8253 ))
8254 }
8255 };
8256 self.diagnostics.push(diagnostic.at(span));
8257 self.check_args_freely(args, trailing);
8258 Ty::recovery()
8259 }
8260
8261 fn dyn_method_call(
8267 &mut self,
8268 trait_name: &Arc<str>,
8269 name: &Ident,
8270 args: &[Arg],
8271 trailing: Option<&Expr>,
8272 span: Span,
8273 ) -> Ty {
8274 let sig = self
8275 .traits
8276 .get(&**trait_name)
8277 .and_then(|methods| methods.get(&name.node))
8278 .cloned();
8279 let Some(sig) = sig else {
8280 let known: Vec<String> = self
8281 .traits
8282 .get(&**trait_name)
8283 .map(|methods| methods.keys().cloned().collect())
8284 .unwrap_or_default();
8285 self.diagnostics.push(
8286 Diagnostic::error(
8287 UNKNOWN_METHOD,
8288 format!("`{trait_name}` has no method `{}`", name.node),
8289 )
8290 .at(span)
8291 .rule("A call on a `dyn Trait` value reaches the trait's methods and nothing else: the concrete type is not known here.")
8292 .help(if known.is_empty() {
8293 format!("`{trait_name}` declares no methods")
8294 } else {
8295 format!("`{trait_name}` declares {}", list(&known))
8296 }),
8297 );
8298 self.check_args_freely(args, trailing);
8299 return Ty::recovery();
8300 };
8301 if sig.receiver.is_none() {
8302 self.diagnostics.push(
8303 Diagnostic::error(
8304 DYN_ASSOCIATED,
8305 format!(
8306 "`{trait_name}.{}` takes no `self`, so it cannot be called through `dyn {trait_name}`",
8307 name.node
8308 ),
8309 )
8310 .at(span)
8311 .rule("Only a trait method whose first parameter is `self` may be called through `dyn Trait`: an associated function has no receiver to dispatch on.")
8312 .help(format!(
8313 "call it on a concrete type, as in `SomeType.{}(...)`, or give it a `self` parameter",
8314 name.node
8315 )),
8316 );
8317 self.check_args_freely(args, trailing);
8318 return Ty::recovery();
8319 }
8320 if self.mutating_trait_method(trait_name, &name.node) {
8324 self.diagnostics.push(
8325 Diagnostic::error(
8326 DYN_MUTATING,
8327 format!(
8328 "`{trait_name}.{}` takes `var self`, so it cannot be called through `dyn {trait_name}`",
8329 name.node
8330 ),
8331 )
8332 .at(span)
8333 .rule("A concrete value becomes a `dyn Trait` value by conversion, and a conversion produces a value; a mutating receiver needs the caller's own place, which a converted value is not.")
8334 .help(format!(
8335 "call `{}` on the concrete value before converting it, or declare the method with `self`",
8336 name.node
8337 )),
8338 );
8339 self.check_args_freely(args, trailing);
8340 return Ty::recovery();
8341 }
8342 self.call_signature(
8343 &sig,
8344 &format!("`{trait_name}.{}`", name.node),
8345 Vec::new(),
8346 args,
8347 trailing,
8348 span,
8349 )
8350 }
8351
8352 fn record_signature(&mut self, decl: &FnDecl, sig: &FnSig) {
8387 self.facts.record_signature(
8388 decl.span.file,
8389 decl.span,
8390 Signature {
8391 receiver: sig.receiver.clone(),
8392 params: sig.params.iter().map(|param| param.ty.clone()).collect(),
8393 ret: sig.ret.clone(),
8394 },
8395 );
8396 }
8397
8398 fn record_struct_signature(&mut self, decl: &StructDecl, sig: &StructSig) {
8412 let ret = Ty::Struct(
8413 self.key(&decl.name.node).into(),
8414 sig.generics.iter().cloned().map(Ty::Param).collect(),
8415 );
8416 self.facts.record_signature(
8417 decl.span.file,
8418 decl.span,
8419 Signature {
8420 receiver: None,
8421 params: sig.fields.iter().map(|field| field.ty.clone()).collect(),
8422 ret,
8423 },
8424 );
8425 }
8426
8427 fn record_case_signatures(&mut self, decl: &EnumDecl, sig: &EnumSig) {
8435 let ret = Ty::Enum(
8436 self.key(&decl.name.node).into(),
8437 sig.generics.iter().cloned().map(Ty::Param).collect(),
8438 );
8439 for (case, declared) in decl.cases.iter().zip(&sig.cases) {
8440 self.facts.record_signature(
8441 case.span.file,
8442 case.span,
8443 Signature {
8444 receiver: None,
8445 params: declared.payload.clone(),
8446 ret: ret.clone(),
8447 },
8448 );
8449 }
8450 }
8451
8452 fn record_target(&mut self, id: ExprId, file: FileId, key: &str, method: &str) {
8453 let (module, type_name) = match key.rsplit_once('.') {
8454 Some((module, name)) => (module.to_string(), name.to_string()),
8455 None => (self.module.name.clone(), key.to_string()),
8456 };
8457 self.facts.record_target(
8458 file,
8459 id,
8460 MethodTarget {
8461 module,
8462 type_name,
8463 method: method.to_string(),
8464 },
8465 );
8466 }
8467
8468 fn method_call(
8469 &mut self,
8470 id: ExprId,
8471 receiver: &Ty,
8472 name: &Ident,
8473 args: &[Arg],
8474 trailing: Option<&Expr>,
8475 span: Span,
8476 ) -> Ty {
8477 match receiver {
8478 Ty::Unknown(_) | Ty::Never => {
8479 self.check_args_freely(args, trailing);
8480 return receiver.abstain();
8481 }
8482 Ty::Any => {
8486 self.check_args_freely(args, trailing);
8487 return Ty::Any;
8488 }
8489 Ty::Struct(type_name, type_args) | Ty::Enum(type_name, type_args) => {
8490 let key = (type_name.to_string(), name.node.clone());
8491 if let Some(sig) = self.methods.get(&key).cloned() {
8492 self.record_target(id, span.file, type_name, &name.node);
8493 self.check_receiver(&sig, type_name, &name.node, span, true);
8494 let generics = self.declared_generics(type_name);
8495 let subst = substitution(&generics, type_args);
8496 let sig = FnSig {
8497 generics: sig
8498 .generics
8499 .iter()
8500 .filter(|g| !generics.contains(g))
8501 .cloned()
8502 .collect(),
8503 params: sig
8504 .params
8505 .iter()
8506 .map(|p| ParamSig {
8507 ty: p.ty.substitute(&subst),
8508 ..p.clone()
8509 })
8510 .collect(),
8511 ret: sig.ret.substitute(&subst),
8512 ..sig
8513 };
8514 return self.call_signature(
8515 &sig,
8516 &format!("`{type_name}.{}`", name.node),
8517 Vec::new(),
8518 args,
8519 trailing,
8520 span,
8521 );
8522 }
8523 let known = self.known_members(type_name);
8524 self.diagnostics.push(
8525 Diagnostic::error(
8526 UNKNOWN_METHOD,
8527 format!("`{type_name}` has no method `{}`", name.node),
8528 )
8529 .at(span)
8530 .rule("A method is declared in its type's `impl` block.")
8531 .help(format!("`{type_name}` declares {known}")),
8532 );
8533 self.check_args_freely(args, trailing);
8534 return Ty::recovery();
8535 }
8536 Ty::Param(param) => {
8537 let param = param.clone();
8538 return self.param_method_call(¶m, name, args, trailing, span);
8539 }
8540 Ty::Dyn(trait_name) => {
8541 let trait_name = trait_name.clone();
8542 return self.dyn_method_call(&trait_name, name, args, trailing, span);
8543 }
8544 Ty::Host(declared) => {
8545 let declared = declared.clone();
8546 return self.host_method_call(&declared, name, args, trailing, span);
8547 }
8548 _ => {}
8549 }
8550
8551 if let (Ty::Result(ok, error), "mapError") = (receiver, name.node.as_str()) {
8552 return self.map_error(ok, error, args, trailing, span);
8553 }
8554
8555 if name.node == "snapshot"
8561 && matches!(
8562 receiver,
8563 Ty::Fn(_) | Ty::Task(_) | Ty::Scope | Ty::Shared(_)
8564 )
8565 {
8566 self.diagnostics
8567 .push(no_snapshot_conformance(receiver, span));
8568 self.check_args_freely(args, trailing);
8569 return Ty::recovery();
8570 }
8571
8572 match builtin_method(receiver, &name.node) {
8573 Some(sig) => {
8574 let what = format!("`{}.{}`", builtin_name(receiver), name.node);
8575 self.call_builtin(&sig, &what, args, trailing, span)
8576 }
8577 None => {
8578 self.diagnostics
8579 .push(unknown_builtin_method(receiver, &name.node, span));
8580 self.check_args_freely(args, trailing);
8581 Ty::recovery()
8582 }
8583 }
8584 }
8585
8586 fn map_error(
8593 &mut self,
8594 ok: &Ty,
8595 error: &Ty,
8596 args: &[Arg],
8597 trailing: Option<&Expr>,
8598 span: Span,
8599 ) -> Ty {
8600 let callback: Option<&Expr> = match (args.first(), trailing) {
8601 (Some(arg), None) => Some(&arg.value),
8602 (None, Some(trailing)) => Some(trailing),
8603 _ => None,
8604 };
8605 let count = args.len() + usize::from(trailing.is_some());
8606 if count != 1 {
8607 self.diagnostics.push(
8608 Diagnostic::error(
8609 ARITY,
8610 format!("`Result.mapError` takes 1 argument, but {count} were given"),
8611 )
8612 .at(span)
8613 .rule("`mapError` replaces a failure with the value its one callback produces.")
8614 .help("write `result.mapError(fn(error) { ... })`"),
8615 );
8616 }
8617 let Some(callback) = callback else {
8618 self.check_args_freely(args, trailing);
8619 return Ty::Result(Box::new(ok.clone()), Box::new(Ty::recovery()));
8620 };
8621 let expected = Ty::func(
8622 false,
8623 vec![error.clone()],
8624 Ty::placeholder(),
8628 );
8629 let found = self.trailing_type(callback, Some(&expected));
8630 let replacement = match &found {
8631 Ty::Fn(func) => func.ret.clone(),
8632 _ => Ty::recovery(),
8633 };
8634 Ty::Result(Box::new(ok.clone()), Box::new(replacement))
8635 }
8636
8637 fn call_builtin(
8640 &mut self,
8641 sig: &BuiltinSig,
8642 what: &str,
8643 args: &[Arg],
8644 trailing: Option<&Expr>,
8645 span: Span,
8646 ) -> Ty {
8647 let subst = self.builtin_arguments(sig, what, args, trailing, span);
8648 self.open_result(&sig.ret, &sig.generics, &subst, span)
8649 }
8650
8651 fn builtin_arguments(
8660 &mut self,
8661 sig: &BuiltinSig,
8662 what: &str,
8663 args: &[Arg],
8664 trailing: Option<&Expr>,
8665 span: Span,
8666 ) -> BTreeMap<Arc<str>, Ty> {
8667 let last = sig.params.len().saturating_sub(1);
8668 let params: Vec<ParamSig> = sig
8669 .params
8670 .iter()
8671 .enumerate()
8672 .map(|(index, (name, ty))| ParamSig {
8673 name: (*name).to_string(),
8674 ty: ty.clone(),
8675 variadic: sig.variadic && index == last,
8676 has_default: false,
8677 is_var: false,
8678 span,
8679 })
8680 .collect();
8681 self.match_arguments(
8682 ¶ms,
8683 &sig.generics,
8684 BTreeMap::new(),
8685 args,
8686 trailing,
8687 span,
8688 what,
8689 "the parameter",
8690 )
8691 }
8692
8693 fn check_receiver(
8700 &mut self,
8701 sig: &FnSig,
8702 type_name: &str,
8703 name: &str,
8704 span: Span,
8705 given: bool,
8706 ) {
8707 match (sig.receiver.is_some(), given) {
8708 (true, false) => self.diagnostics.push(
8709 Diagnostic::error(
8710 RECEIVER,
8711 format!("`{type_name}.{name}` is a method and needs a receiver"),
8712 )
8713 .at(span)
8714 .rule("A method is called on a value; only an associated function is called on its type.")
8715 .help(format!(
8716 "call it on a value, as in `value.{name}(...)`, or declare `fn {name}()` without `self`"
8717 )),
8718 ),
8719 (false, true) => self.diagnostics.push(
8720 Diagnostic::error(
8721 RECEIVER,
8722 format!("`{type_name}.{name}` takes no receiver"),
8723 )
8724 .at(span)
8725 .rule("An associated function is called on its type; only a method is called on a value.")
8726 .help(format!("write `{type_name}.{name}(...)`")),
8727 ),
8728 _ => {}
8729 }
8730 }
8731
8732 fn declared_generics(&self, name: &str) -> Vec<Arc<str>> {
8734 if let Some(sig) = self.structs.get(name) {
8735 return sig.generics.clone();
8736 }
8737 if let Some(sig) = self.enums.get(name) {
8738 return sig.generics.clone();
8739 }
8740 Vec::new()
8741 }
8742
8743 fn known_members(&self, type_name: &str) -> String {
8745 let mut names: Vec<String> = self
8746 .methods
8747 .keys()
8748 .filter(|(owner, _)| owner == type_name)
8749 .map(|(_, name)| name.clone())
8750 .collect();
8751 if let Some(sig) = self.enums.get(type_name) {
8752 names.extend(sig.cases.iter().map(|c| c.name.clone()));
8753 }
8754 if names.is_empty() {
8755 format!("no methods; declare one in `impl {type_name}`")
8756 } else {
8757 list(&names)
8758 }
8759 }
8760}
8761
8762fn check_entries(
8768 package: &Package,
8769 checked: &BTreeMap<&str, Checker<'_>>,
8770 diagnostics: &mut Vec<Diagnostic>,
8771) {
8772 for run in package.config.runs.values() {
8773 let Some((module_name, entry)) = run.entry_parts() else {
8774 continue;
8775 };
8776 let Some(checker) = checked.get(module_name) else {
8777 continue;
8778 };
8779 let Some(sig) = checker.functions.get(entry) else {
8780 continue;
8781 };
8782 let Some(function) = checker.module.functions.get(entry) else {
8783 continue;
8784 };
8785 let name_span = function.decl.name.span;
8786
8787 if sig.params.len() > 1 {
8788 diagnostics.push(
8789 Diagnostic::error(
8790 ENTRY,
8791 format!(
8792 "entry `{}` declares {} parameters",
8793 run.entry,
8794 sig.params.len()
8795 ),
8796 )
8797 .at(name_span)
8798 .rule("An entry function takes either no parameters or one `Array<String>` of process arguments.")
8799 .help(format!(
8800 "write `fn {entry}()` or `fn {entry}(args: Array<String>)`"
8801 )),
8802 );
8803 } else if let Some(param) = sig.params.first() {
8804 let expected = Ty::Array(Box::new(Ty::Str));
8805 if !param.ty.matches(&expected) {
8806 diagnostics.push(
8807 Diagnostic::error(
8808 ENTRY,
8809 format!(
8810 "entry `{}` takes `{}`, but the host passes `Array<String>`",
8811 run.entry, param.ty
8812 ),
8813 )
8814 .at(param.span)
8815 .rule("An entry function's one parameter is the process arguments, an `Array<String>`.")
8816 .help(format!("write `fn {entry}(args: Array<String>)`")),
8817 );
8818 }
8819 }
8820
8821 if !matches!(
8822 sig.ret,
8823 Ty::Unit | Ty::Result(_, _) | Ty::Unknown(_) | Ty::Any
8824 ) {
8825 diagnostics.push(
8826 Diagnostic::error(
8827 ENTRY,
8828 format!(
8829 "entry `{}` returns `{}`, which the host cannot report",
8830 run.entry, sig.ret
8831 ),
8832 )
8833 .at(sig.ret_span)
8834 .rule("The host reports an entry's failure through its `Err`, so an entry returns `()` or a `Result`.")
8835 .help(format!(
8836 "write `fn {entry}(...) -> Result<{}, Error>`",
8837 sig.ret
8838 )),
8839 );
8840 }
8841 }
8842}
8843
8844fn unify(
8853 param: &Ty,
8854 arg: &Ty,
8855 generics: &BTreeSet<Arc<str>>,
8856 subst: &mut BTreeMap<Arc<str>, Ty>,
8857 view: &ConformanceView<'_>,
8858) -> bool {
8859 if coerces(arg, param, view) {
8860 return true;
8861 }
8862 if let Ty::Param(name) = param {
8863 if generics.contains(name) {
8864 return match subst.get(name) {
8865 Some(bound) => bound.matches(arg),
8866 None => {
8867 if !arg.is_wild() {
8868 subst.insert(name.clone(), arg.clone());
8869 }
8870 true
8871 }
8872 };
8873 }
8874 }
8875 if param.is_wild() || arg.is_wild() {
8876 return true;
8877 }
8878 match (param, arg) {
8879 (Ty::Array(a), Ty::Array(b))
8880 | (Ty::Vector(a), Ty::Vector(b))
8881 | (Ty::Set(a), Ty::Set(b))
8882 | (Ty::Option(a), Ty::Option(b))
8883 | (Ty::Task(a), Ty::Task(b))
8884 | (Ty::Shared(a), Ty::Shared(b)) => unify(a, b, generics, subst, view),
8885 (Ty::Map(ak, av), Ty::Map(bk, bv))
8886 | (Ty::MapEntry(ak, av), Ty::MapEntry(bk, bv))
8887 | (Ty::Result(ak, av), Ty::Result(bk, bv)) => {
8888 unify(ak, bk, generics, subst, view) && unify(av, bv, generics, subst, view)
8889 }
8890 (Ty::Struct(a, aargs), Ty::Struct(b, bargs)) | (Ty::Enum(a, aargs), Ty::Enum(b, bargs)) => {
8891 a == b
8892 && aargs.len() == bargs.len()
8893 && aargs
8894 .iter()
8895 .zip(bargs)
8896 .all(|(a, b)| unify(a, b, generics, subst, view))
8897 }
8898 (Ty::Fn(a), Ty::Fn(b)) => {
8899 a.is_async == b.is_async
8900 && a.params.len() == b.params.len()
8901 && a.params
8902 .iter()
8903 .zip(&b.params)
8904 .all(|(a, b)| unify(a, b, generics, subst, view))
8905 && unify(&a.ret, &b.ret, generics, subst, view)
8906 }
8907 (param, arg) => param.matches(arg),
8908 }
8909}
8910
8911struct ConformanceView<'c> {
8918 declared: &'c BTreeSet<(String, String)>,
8919 bounds: &'c BTreeMap<Arc<str>, Vec<TraitBound>>,
8920}
8921
8922fn conforms(ty: &Ty, trait_name: &str, view: &ConformanceView<'_>) -> bool {
8928 match ty {
8929 Ty::Unknown(_) | Ty::Any | Ty::Never => true,
8930 Ty::Struct(name, _) | Ty::Enum(name, _) => view
8931 .declared
8932 .contains(&(trait_name.to_string(), name.to_string())),
8933 Ty::Param(name) => view
8936 .bounds
8937 .get(name)
8938 .is_some_and(|bounds| bounds.iter().any(|b| &*b.name == trait_name)),
8939 _ => false,
8942 }
8943}
8944
8945fn coerces(found: &Ty, expected: &Ty, view: &ConformanceView<'_>) -> bool {
8957 let Ty::Dyn(trait_name) = expected else {
8958 return false;
8959 };
8960 !matches!(found, Ty::Dyn(_)) && conforms(found, trait_name, view)
8961}
8962
8963fn unknown_trait(name: &str, span: Span) -> Diagnostic {
8965 Diagnostic::error(UNKNOWN_TRAIT, format!("`{name}` is not a trait"))
8966 .at(span)
8967 .rule("`dyn` and a type parameter's bound both name a trait the module declares; there are no module-to-module imports yet.")
8968 .help(format!(
8969 "declare `trait {name} {{ ... }}` in this module, or name a trait that exists"
8970 ))
8971}
8972
8973fn signature_difference(declared: &FnSig, found: &FnSig) -> Option<String> {
8976 if declared.receiver.is_some() != found.receiver.is_some() {
8977 return Some(if declared.receiver.is_some() {
8978 "it takes no `self`".to_string()
8979 } else {
8980 "it takes a `self` the trait does not declare".to_string()
8981 });
8982 }
8983 if declared.is_async != found.is_async {
8984 return Some(if declared.is_async {
8985 "it is not `async`".to_string()
8986 } else {
8987 "it is `async`".to_string()
8988 });
8989 }
8990 if declared.params.len() != found.params.len() {
8991 return Some(format!(
8992 "it takes {} parameter(s), not {}",
8993 found.params.len(),
8994 declared.params.len()
8995 ));
8996 }
8997 for (want, got) in declared.params.iter().zip(&found.params) {
8998 if want.name != got.name {
8999 return Some(format!(
9000 "its parameter `{}` is named `{}` in the trait",
9001 got.name, want.name
9002 ));
9003 }
9004 if !want.ty.matches(&got.ty) {
9005 return Some(format!(
9006 "its parameter `{}` is `{}`, not `{}`",
9007 got.name, got.ty, want.ty
9008 ));
9009 }
9010 }
9011 if !declared.ret.matches(&found.ret) {
9012 return Some(format!(
9013 "it returns `{}`, not `{}`",
9014 found.ret, declared.ret
9015 ));
9016 }
9017 None
9018}
9019
9020fn trait_signature(sig: &FnSig, name: &str) -> String {
9022 let mut out = String::new();
9023 if sig.is_async {
9024 out.push_str("async ");
9025 }
9026 out.push_str("fn ");
9027 out.push_str(name);
9028 out.push('(');
9029 let mut entries: Vec<String> = Vec::new();
9030 if sig.receiver.is_some() {
9031 entries.push("self".to_string());
9032 }
9033 entries.extend(
9034 sig.params
9035 .iter()
9036 .map(|param| format!("{}: {}", param.name, param.ty)),
9037 );
9038 out.push_str(&entries.join(", "));
9039 out.push(')');
9040 if sig.ret != Ty::Unit {
9041 out.push_str(&format!(" -> {}", sig.ret));
9042 }
9043 out
9044}
9045
9046fn substitution(generics: &[Arc<str>], args: &[Ty]) -> BTreeMap<Arc<str>, Ty> {
9053 generics
9054 .iter()
9055 .cloned()
9056 .zip(
9057 args.iter()
9058 .cloned()
9059 .chain(std::iter::repeat(Ty::recovery())),
9060 )
9061 .collect()
9062}
9063
9064fn expand_alias(generics: Vec<Arc<str>>, ty: Ty, arguments: Vec<Ty>) -> Ty {
9067 let subst = generics
9068 .into_iter()
9069 .zip(
9070 fit(arguments, 0)
9071 .into_iter()
9072 .chain(std::iter::repeat(Ty::recovery())),
9073 )
9074 .collect();
9075 ty.substitute(&subst)
9076}
9077
9078fn fit(mut args: Vec<Ty>, arity: usize) -> Vec<Ty> {
9081 args.truncate(arity);
9082 while args.len() < arity {
9083 args.push(Ty::recovery());
9084 }
9085 args
9086}
9087
9088struct BuiltinSig {
9092 generics: Vec<Arc<str>>,
9095 params: Vec<(&'static str, Ty)>,
9096 variadic: bool,
9099 ret: Ty,
9100}
9101
9102const HOST_SCHEMA_RULE: &str = "A Host API operation's argument, result, and error types come from its schema, which the compiler, the runtime, and the CLI all read.";
9110
9111fn host_ty(declared: &HostType) -> Ty {
9123 match declared {
9124 HostType::Unit => Ty::Unit,
9125 HostType::Bool => Ty::Bool,
9126 HostType::Int => Ty::Int,
9127 HostType::String => Ty::Str,
9128 HostType::Duration => Ty::Duration,
9129 HostType::Error => Ty::Error,
9130 HostType::Array(item) => Ty::Array(Box::new(host_ty(item))),
9131 HostType::Set(item) => Ty::Set(Box::new(host_ty(item))),
9132 HostType::Map(key, value) => Ty::Map(Box::new(host_ty(key)), Box::new(host_ty(value))),
9133 HostType::Option(some) => Ty::Option(Box::new(host_ty(some))),
9134 HostType::Result(ok, error) => Ty::Result(Box::new(host_ty(ok)), Box::new(host_ty(error))),
9135 HostType::Named(name) => Ty::Host((*name).into()),
9136 HostType::Any => Ty::Any,
9137 }
9138}
9139
9140fn place_text(expr: &Expr) -> String {
9149 match &expr.kind {
9150 ExprKind::Ident(name) => name.clone(),
9151 ExprKind::Field { base, name } => format!("{}.{}", place_text(base), name.node),
9152 _ => "this expression".to_string(),
9153 }
9154}
9155
9156fn declared_signature(shown: &str, operation: &OperationSchema) -> String {
9159 let owner = match shown.rsplit_once('.') {
9160 Some((owner, _)) => owner,
9161 None => shown,
9162 };
9163 format!(
9164 "the Host API schema declares `{owner}.{}`",
9165 operation.signature()
9166 )
9167}
9168
9169fn operation_names(operations: &'static [OperationSchema]) -> Vec<String> {
9172 operations
9173 .iter()
9174 .map(|entry| entry.name.to_string())
9175 .collect()
9176}
9177
9178#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9185enum Namespace {
9186 Struct,
9189 Enum,
9191 BuiltinType,
9194 HostType,
9196 Type,
9198 HostModule,
9200 Module,
9202}
9203
9204impl Namespace {
9205 fn what(self) -> &'static str {
9207 match self {
9208 Namespace::Struct => "a struct",
9209 Namespace::Enum => "an enum",
9210 Namespace::BuiltinType => "a builtin type",
9211 Namespace::HostType => "a host type",
9212 Namespace::Type => "a type",
9213 Namespace::HostModule => "a host module",
9214 Namespace::Module => "a module",
9215 }
9216 }
9217
9218 fn correction(self, name: &str) -> String {
9224 match self {
9225 Namespace::Struct => {
9226 format!("construct one, as in `{name}(field: value)`, or name a value instead")
9227 }
9228 Namespace::Enum => {
9229 format!("name one of its cases, as in `{name}.<case>`, or name a value instead")
9230 }
9231 Namespace::BuiltinType | Namespace::Type => format!(
9232 "call an associated function of it, as in `{name}.<name>(...)`, or name a value instead"
9233 ),
9234 Namespace::HostType => format!(
9235 "construct one, as in `{name}(field: value)`, or call the operation that answers one"
9236 ),
9237 Namespace::HostModule | Namespace::Module => {
9238 format!("name something in it, as in `{name}.<name>(...)`, or name a value instead")
9239 }
9240 }
9241 }
9242}
9243
9244fn not_a_value(name: &str, what: Namespace, span: Span) -> Diagnostic {
9254 let help = what.correction(name);
9255 Diagnostic::error(
9256 NOT_A_VALUE,
9257 format!("`{name}` is {}, not a value", what.what()),
9258 )
9259 .at(span)
9260 .rule("A value is a literal, a binding, a call, or a constructed struct or enum case. A type and a module are names other forms read; neither is a value on its own.")
9261 .help(help)
9262}
9263
9264fn unconstrained(message: String, help: String, span: Span) -> Diagnostic {
9280 Diagnostic::error(UNCONSTRAINED, message)
9281 .at(span)
9282 .rule("A type the checker infers is inferred from something written: a value, an annotation, or the type of the place the value is given to.")
9283 .help(help)
9284}
9285
9286fn contains_any(declared: &HostType) -> bool {
9288 match declared {
9289 HostType::Any => true,
9290 HostType::Array(inner) | HostType::Set(inner) | HostType::Option(inner) => {
9291 contains_any(inner)
9292 }
9293 HostType::Map(key, value) | HostType::Result(key, value) => {
9294 contains_any(key) || contains_any(value)
9295 }
9296 _ => false,
9297 }
9298}
9299
9300fn unchecked_host_type(shown: &str, span: Span) -> Diagnostic {
9308 Diagnostic::warning(
9309 HOST_TYPE,
9310 format!("`{shown}` comes from a host module no Host API schema describes, so values of it are unchecked"),
9311 )
9312 .at(span)
9313 .rule("A Host API's types come from its schema; the checker reads the shipped schemas and any an embedder supplies.")
9314 .help("the checker treats this type as unknown; every operation on it is left to the runtime, which holds the host to the schema it registered with")
9315}
9316
9317pub(crate) fn builtin_schema_of(receiver: &Ty) -> Option<&'static BuiltinSchema> {
9328 let name = match receiver {
9329 Ty::Unit => "Unit",
9330 Ty::Bool => "Bool",
9331 Ty::Int => "Int",
9332 Ty::Float => "Float",
9333 Ty::Str => "String",
9334 Ty::Duration => "Duration",
9335 Ty::Error => "Error",
9336 Ty::Range => "Range",
9337 Ty::Array(_) => "Array",
9338 Ty::Vector(_) => "Vector",
9339 Ty::Map(_, _) => "Map",
9340 Ty::MapEntry(_, _) => "MapEntry",
9341 Ty::Set(_) => "Set",
9342 Ty::Option(_) => "Option",
9343 Ty::Result(_, _) => "Result",
9344 Ty::Task(_) => "Task",
9345 Ty::Shared(_) => "Shared",
9346 Ty::Scope => "Scope",
9347 _ => return None,
9348 };
9349 cove_schema::builtin(name)
9350}
9351
9352fn receiver_arguments(receiver: &Ty) -> Vec<Ty> {
9358 match receiver {
9359 Ty::Array(item)
9360 | Ty::Vector(item)
9361 | Ty::Set(item)
9362 | Ty::Option(item)
9363 | Ty::Task(item)
9364 | Ty::Shared(item) => vec![(**item).clone()],
9365 Ty::Map(left, right) | Ty::MapEntry(left, right) | Ty::Result(left, right) => {
9366 vec![(**left).clone(), (**right).clone()]
9367 }
9368 _ => Vec::new(),
9369 }
9370}
9371
9372fn builtin_ty(declared: &BuiltinType, bound: &BTreeMap<&str, Ty>, receiver: Option<&Ty>) -> Ty {
9383 let nested = |inner: &BuiltinType| Box::new(builtin_ty(inner, bound, receiver));
9384 match declared {
9385 BuiltinType::Unit => Ty::Unit,
9386 BuiltinType::Bool => Ty::Bool,
9387 BuiltinType::Int => Ty::Int,
9388 BuiltinType::Float => Ty::Float,
9389 BuiltinType::String => Ty::Str,
9390 BuiltinType::Error => Ty::Error,
9391 BuiltinType::Duration => Ty::Duration,
9392 BuiltinType::Array(item) => Ty::Array(nested(item)),
9393 BuiltinType::Vector(item) => Ty::Vector(nested(item)),
9394 BuiltinType::Set(item) => Ty::Set(nested(item)),
9395 BuiltinType::Map(key, value) => Ty::Map(nested(key), nested(value)),
9396 BuiltinType::MapEntry(key, value) => Ty::MapEntry(nested(key), nested(value)),
9397 BuiltinType::Option(some) => Ty::Option(nested(some)),
9398 BuiltinType::Result(ok, error) => Ty::Result(nested(ok), nested(error)),
9399 BuiltinType::Task(inner) => Ty::Task(nested(inner)),
9400 BuiltinType::Shared(inner) => Ty::Shared(nested(inner)),
9401 BuiltinType::Fn(params, ret) => Ty::func(
9402 false,
9403 params
9404 .iter()
9405 .map(|param| builtin_ty(param, bound, receiver))
9406 .collect(),
9407 builtin_ty(ret, bound, receiver),
9408 ),
9409 BuiltinType::Param(name) => bound
9410 .get(name)
9411 .cloned()
9412 .unwrap_or_else(|| Ty::Param((*name).into())),
9413 BuiltinType::SelfType => receiver.cloned().unwrap_or(Ty::placeholder()),
9416 }
9417}
9418
9419fn free_builtin(name: &str, kind: FreeBuiltinKind) -> Option<&'static FreeBuiltinSchema> {
9426 cove_schema::free_builtin(name).filter(|schema| schema.kind == kind)
9427}
9428
9429struct FreeBindings {
9449 types: BTreeMap<&'static str, Ty>,
9450 origins: BTreeMap<&'static str, Span>,
9451}
9452
9453impl FreeBindings {
9454 fn new(schema: &'static FreeBuiltinSchema, open: Vec<Ty>) -> FreeBindings {
9466 FreeBindings {
9467 types: schema.generics.iter().copied().zip(open).collect(),
9468 origins: BTreeMap::new(),
9469 }
9470 }
9471
9472 fn open(&self, declared: &BuiltinType) -> Ty {
9474 builtin_ty(declared, &self.types, None)
9475 }
9476
9477 fn bind(&mut self, declared: &BuiltinType, ty: Ty, at: Span) {
9491 if ty.is_wild() {
9492 return;
9493 }
9494 if let BuiltinType::Param(name) = declared {
9495 self.types.insert(name, ty);
9496 self.origins.insert(name, at);
9497 }
9498 }
9499
9500 fn read_off(&mut self, declared: &BuiltinType, actual: &Ty, at: Span) {
9508 match (declared, actual) {
9509 (BuiltinType::Param(_), _) => self.bind(declared, actual.clone(), at),
9510 (BuiltinType::Array(inner), Ty::Array(ty))
9511 | (BuiltinType::Vector(inner), Ty::Vector(ty))
9512 | (BuiltinType::Set(inner), Ty::Set(ty))
9513 | (BuiltinType::Option(inner), Ty::Option(ty))
9514 | (BuiltinType::Task(inner), Ty::Task(ty))
9515 | (BuiltinType::Shared(inner), Ty::Shared(ty)) => self.read_off(inner, ty, at),
9516 (BuiltinType::Map(key, value), Ty::Map(left, right))
9517 | (BuiltinType::MapEntry(key, value), Ty::MapEntry(left, right))
9518 | (BuiltinType::Result(key, value), Ty::Result(left, right)) => {
9519 self.read_off(key, left, at);
9520 self.read_off(value, right, at);
9521 }
9522 _ => {}
9523 }
9524 }
9525
9526 fn origin(&self, declared: &BuiltinType, fallback: Span) -> Span {
9529 match declared {
9530 BuiltinType::Param(name) => self.origins.get(name).copied().unwrap_or(fallback),
9531 _ => fallback,
9532 }
9533 }
9534}
9535
9536fn free_arity(schema: &FreeBuiltinSchema, found: usize, span: Span) -> Diagnostic {
9542 Diagnostic::error(
9543 ARITY,
9544 match schema.kind {
9545 FreeBuiltinKind::Constructor => format!(
9546 "`{}` takes {} argument, but {found} were given",
9547 schema.name,
9548 schema.arity()
9549 ),
9550 FreeBuiltinKind::Assertion => format!(
9551 "`{}` takes {} argument(s), but {found} were given",
9552 schema.name,
9553 schema.arity()
9554 ),
9555 },
9556 )
9557 .at(span)
9558}
9559
9560fn free_builtin_reason(schema: &FreeBuiltinSchema, param: &ParamSchema, ty: &Ty) -> String {
9565 match schema.kind {
9566 FreeBuiltinKind::Constructor => format!("`{}` carries a `{ty}`", schema.name),
9567 FreeBuiltinKind::Assertion if schema.arity() == 1 => {
9571 format!("`{}` checks a `{ty}` {}", schema.name, param.name)
9572 }
9573 FreeBuiltinKind::Assertion => {
9574 format!("`{}` compares two values of one type", schema.name)
9575 }
9576 }
9577}
9578
9579fn builtin_sig(
9584 method: &'static MethodSchema,
9585 parameters: &'static [&'static str],
9586 arguments: &[Ty],
9587 receiver: Option<&Ty>,
9588) -> BuiltinSig {
9589 let bound: BTreeMap<&str, Ty> = parameters
9590 .iter()
9591 .copied()
9592 .zip(arguments.iter().cloned())
9593 .collect();
9594 BuiltinSig {
9595 generics: method
9596 .generics
9597 .iter()
9598 .map(|generic| Arc::from(*generic))
9599 .collect(),
9600 params: method
9601 .params
9602 .iter()
9603 .map(|param| (param.name, builtin_ty(¶m.ty, &bound, receiver)))
9604 .collect(),
9605 variadic: method.variadic,
9606 ret: builtin_ty(&method.result, &bound, receiver),
9607 }
9608}
9609
9610fn receiver_binding<'a>(schema: &'a BuiltinSchema, receiver: &Ty) -> BTreeMap<&'a str, Ty> {
9615 schema
9616 .parameters
9617 .iter()
9618 .copied()
9619 .zip(receiver_arguments(receiver))
9620 .collect()
9621}
9622
9623fn builtin_case_payload(scrutinee: &Ty, case: &str) -> Option<Vec<Ty>> {
9631 let schema = builtin_schema_of(scrutinee)?;
9632 let case = schema.case(case)?;
9633 let bound = receiver_binding(schema, scrutinee);
9634 Some(
9635 case.payload
9636 .iter()
9637 .map(|ty| builtin_ty(ty, &bound, Some(scrutinee)))
9638 .collect(),
9639 )
9640}
9641
9642fn builtin_method(receiver: &Ty, name: &str) -> Option<BuiltinSig> {
9649 let schema = builtin_schema_of(receiver)?;
9650 let method = schema.method(name)?;
9651 Some(builtin_sig(
9652 method,
9653 schema.parameters,
9654 &receiver_arguments(receiver),
9655 Some(receiver),
9656 ))
9657}
9658
9659fn builtin_associated_functions() -> String {
9662 let names: Vec<String> = cove_schema::builtins::builtins()
9663 .iter()
9664 .flat_map(|entry| {
9665 entry
9666 .associated
9667 .iter()
9668 .map(|method| format!("`{}.{}`", entry.name, method.name))
9669 })
9670 .collect();
9671 match names.split_last() {
9672 Some((last, [])) => last.clone(),
9673 Some((last, rest)) => format!("{}, and {last}", rest.join(", ")),
9674 None => "nothing".to_string(),
9675 }
9676}
9677
9678fn builtin_name(ty: &Ty) -> String {
9680 match ty {
9681 Ty::Array(_) => "Array".to_string(),
9682 Ty::Vector(_) => "Vector".to_string(),
9683 Ty::Option(_) => "Option".to_string(),
9684 Ty::Result(_, _) => "Result".to_string(),
9685 Ty::Task(_) => "Task".to_string(),
9686 Ty::Shared(_) => "Shared".to_string(),
9687 Ty::Map(_, _) => "Map".to_string(),
9688 Ty::Set(_) => "Set".to_string(),
9689 Ty::MapEntry(_, _) => "MapEntry".to_string(),
9690 other => other.to_string(),
9691 }
9692}
9693
9694fn no_snapshot_conformance(receiver: &Ty, span: Span) -> Diagnostic {
9697 let what = match receiver {
9698 Ty::Fn(_) => "closures",
9699 Ty::Task(_) => "tasks",
9700 Ty::Shared(_) => "synchronized values",
9701 _ => "task scopes",
9702 };
9703 Diagnostic::error(
9704 UNKNOWN_METHOD,
9705 format!("`{receiver}` does not implement `Snapshot`"),
9706 )
9707 .at(span)
9708 .rule(format!(
9709 "Closures, synchronized values, and Host resources do not implement `Snapshot` by default; {what} have no independent mutable graph to copy."
9710 ))
9711 .help("a struct or enum conforms explicitly with `impl Snapshot for Type`")
9712}
9713
9714fn unknown_builtin_method(receiver: &Ty, name: &str, span: Span) -> Diagnostic {
9715 let type_name = builtin_name(receiver);
9716 if name == "count" && cove_schema::builtins::declares_length(&type_name) {
9722 return Diagnostic::error(
9723 UNKNOWN_METHOD,
9724 format!("`{type_name}` has no method `count`; Cove spells the number of elements `length()`"),
9725 )
9726 .at(span)
9727 .rule("Every sequence reports its element count as `length()`; there is no `count()`.")
9728 .help("write `length()` instead of `count()`");
9729 }
9730 let known = builtin_methods_of(receiver);
9731 Diagnostic::error(
9732 UNKNOWN_METHOD,
9733 format!("`{type_name}` has no method `{name}`"),
9734 )
9735 .at(span)
9736 .rule("A builtin type's methods are exactly the ones the language defines.")
9737 .help(if known.is_empty() {
9738 format!("`{type_name}` has no methods")
9739 } else {
9740 format!("`{type_name}` has {}", list(&known))
9741 })
9742}
9743
9744fn builtin_methods_of(receiver: &Ty) -> Vec<String> {
9753 builtin_schema_of(receiver)
9754 .map(|schema| {
9755 schema
9756 .methods
9757 .iter()
9758 .map(|method| method.name.to_string())
9759 .collect()
9760 })
9761 .unwrap_or_default()
9762}
9763
9764fn operator_symbol(op: BinaryOp) -> &'static str {
9767 match op {
9768 BinaryOp::Add => "+",
9769 BinaryOp::Sub => "-",
9770 BinaryOp::Mul => "*",
9771 BinaryOp::Div => "/",
9772 BinaryOp::Rem => "%",
9773 BinaryOp::Eq => "==",
9774 BinaryOp::Ne => "!=",
9775 BinaryOp::Lt => "<",
9776 BinaryOp::Le => "<=",
9777 BinaryOp::Gt => ">",
9778 BinaryOp::Ge => ">=",
9779 BinaryOp::Is => "is",
9780 BinaryOp::And => "&&",
9781 BinaryOp::Or => "||",
9782 }
9783}
9784
9785fn starts_uppercase(name: &str) -> bool {
9786 name.chars().next().is_some_and(char::is_uppercase)
9787}
9788
9789fn join_path(path: &[Ident]) -> String {
9790 path.iter()
9791 .map(|segment| segment.node.as_str())
9792 .collect::<Vec<_>>()
9793 .join(".")
9794}
9795
9796fn list(items: &[String]) -> String {
9797 if items.is_empty() {
9798 return "nothing".to_string();
9799 }
9800 items
9801 .iter()
9802 .map(|item| format!("`{item}`"))
9803 .collect::<Vec<_>>()
9804 .join(", ")
9805}
9806
9807fn first_case_of(sig: Option<&EnumSig>) -> String {
9808 sig.and_then(|sig| sig.cases.first())
9809 .map(|case| case.name.clone())
9810 .unwrap_or_else(|| "Case".to_string())
9811}
9812
9813fn conversion_help(expected: &Ty, found: &Ty) -> Option<String> {
9815 Some(match (expected, found) {
9816 (Ty::Str, _) => {
9817 format!("interpolate the `{found}`, as in \"{{value}}\", to make a `String`")
9818 }
9819 (Ty::Int, Ty::Str) => {
9820 "parse the `String` with `Int.parse(text)`, which returns a `Result<Int, Error>`"
9821 .to_string()
9822 }
9823 (Ty::Option(inner), other) if inner.matches(other) => {
9824 format!("wrap it, as in `Some(value)`, to make an `Option<{inner}>`")
9825 }
9826 (Ty::Result(ok, _), other) if ok.matches(other) => {
9827 format!("wrap it, as in `Ok(value)`, to make a `Result<{ok}, _>`")
9828 }
9829 (other, Ty::Option(inner)) if other.matches(inner) => {
9830 format!(
9831 "unwrap it, as in `value.unwrapOr(<{other}>)`, which always produces a `{other}`"
9832 )
9833 }
9834 (other, Ty::Result(ok, _)) if other.matches(ok) => {
9835 format!(
9836 "unwrap it, as in `value.unwrapOr(<{other}>)`, which always produces a `{other}`"
9837 )
9838 }
9839 (Ty::Array(element), Ty::Vector(other)) if element.matches(other) => {
9840 "finish the vector, as in `vector.freeze()` or `vector.toArray()`".to_string()
9841 }
9842 (Ty::Float, Ty::Int) | (Ty::Int, Ty::Float) => {
9843 format!("write the literal as a `{expected}`; Cove converts nothing implicitly")
9844 }
9845 _ => return None,
9846 })
9847}
9848
9849fn condition_help(ty: &Ty) -> String {
9850 match ty {
9851 Ty::Option(_) => "compare it, as in `value.isSome()`".to_string(),
9852 Ty::Int | Ty::Float => format!("compare it, as in `value != 0`; a `{ty}` is not a `Bool`"),
9853 _ => format!("compare it, so the condition is a `Bool` rather than a `{ty}`"),
9854 }
9855}
9856
9857fn iterable_help(ty: &Ty) -> String {
9858 match ty {
9859 Ty::Option(inner) => {
9860 format!("match the `Option`, or write `for x in [value.unwrapOr(<{inner}>)]`")
9861 }
9862 Ty::Int => "write a range, as in `0..<n`".to_string(),
9863 Ty::MapEntry(_, _) => {
9864 "iterate the `Map` itself; `for` already binds each pair as a `MapEntry`".to_string()
9865 }
9866 _ => format!("build an `Array`, a `Vector`, or a `Range` from the `{ty}` first"),
9867 }
9868}
9869
9870#[cfg(test)]
9871mod tests {
9872 use super::*;
9873 use crate::config::Config;
9874 use crate::package::{Module, Unit};
9875 use crate::resolve::resolve;
9876 use cove_diag::{Severity, SourceMap};
9877 use std::path::{Path, PathBuf};
9878
9879 fn diagnostics_of(source: &str) -> Vec<Diagnostic> {
9882 diagnostics_with(source, Config::default())
9883 }
9884
9885 fn diagnostics_with(source: &str, config: Config) -> Vec<Diagnostic> {
9886 let mut sources = SourceMap::new();
9887 let path = PathBuf::from("main.cove");
9888 let file = sources.add(path.clone(), source);
9889 let ast = cove_syntax::parse_file(&sources, file).expect("test source parses");
9890 let mut modules = BTreeMap::new();
9891 modules.insert(
9892 "main".to_string(),
9893 Module {
9894 name: "main".to_string(),
9895 dir: PathBuf::from("main"),
9896 units: vec![Unit { file, path, ast }],
9897 },
9898 );
9899 let package = Package {
9900 root: PathBuf::new(),
9901 config,
9902 modules,
9903 };
9904 let program = resolve(&package).expect("test source resolves");
9905 check(&package, &program)
9906 }
9907
9908 fn diagnostics_of_modules(modules: &[(&str, &str)]) -> Vec<Diagnostic> {
9910 let mut sources = SourceMap::new();
9911 let mut map = BTreeMap::new();
9912 for (name, source) in modules {
9913 let path = PathBuf::from(format!("{name}.cove"));
9914 let file = sources.add(path.clone(), *source);
9915 let ast = cove_syntax::parse_file(&sources, file).expect("test source parses");
9916 map.insert(
9917 (*name).to_string(),
9918 Module {
9919 name: (*name).to_string(),
9920 dir: PathBuf::from(*name),
9921 units: vec![Unit { file, path, ast }],
9922 },
9923 );
9924 }
9925 let package = Package {
9926 root: PathBuf::new(),
9927 config: Config::default(),
9928 modules: map,
9929 };
9930 let program = resolve(&package).expect("test package resolves");
9931 check(&package, &program)
9932 }
9933
9934 #[track_caller]
9935 fn accepts_modules(modules: &[(&str, &str)]) {
9936 let errors: Vec<Diagnostic> = diagnostics_of_modules(modules)
9937 .into_iter()
9938 .filter(|d| d.severity == Severity::Error)
9939 .collect();
9940 assert!(
9941 errors.is_empty(),
9942 "expected no errors, found: {}",
9943 errors
9944 .iter()
9945 .map(|d| format!("{}: {}", d.code, d.message))
9946 .collect::<Vec<_>>()
9947 .join("; ")
9948 );
9949 }
9950
9951 #[track_caller]
9952 fn rejects_modules(modules: &[(&str, &str)]) -> Diagnostic {
9953 let mut errors: Vec<Diagnostic> = diagnostics_of_modules(modules)
9954 .into_iter()
9955 .filter(|d| d.severity == Severity::Error)
9956 .collect();
9957 assert_eq!(
9958 errors.len(),
9959 1,
9960 "expected exactly one error, found: {}",
9961 errors
9962 .iter()
9963 .map(|d| format!("{}: {}", d.code, d.message))
9964 .collect::<Vec<_>>()
9965 .join("; ")
9966 );
9967 errors.remove(0)
9968 }
9969
9970 fn errors_of(source: &str) -> Vec<Diagnostic> {
9971 diagnostics_of(source)
9972 .into_iter()
9973 .filter(|d| d.severity == Severity::Error)
9974 .collect()
9975 }
9976
9977 fn warnings_of(source: &str) -> Vec<Diagnostic> {
9978 diagnostics_of(source)
9979 .into_iter()
9980 .filter(|d| d.severity == Severity::Warning)
9981 .collect()
9982 }
9983
9984 fn notes_of(source: &str) -> Vec<Diagnostic> {
9985 diagnostics_of(source)
9986 .into_iter()
9987 .filter(|d| d.severity == Severity::Note)
9988 .collect()
9989 }
9990
9991 #[track_caller]
9993 fn warns(source: &str) -> Diagnostic {
9994 accepts(source);
9995 let mut warnings = warnings_of(source);
9996 assert_eq!(
9997 warnings.len(),
9998 1,
9999 "expected exactly one warning, found: {}",
10000 warnings
10001 .iter()
10002 .map(|d| format!("{}: {}", d.code, d.message))
10003 .collect::<Vec<_>>()
10004 .join("; ")
10005 );
10006 warnings.remove(0)
10007 }
10008
10009 #[track_caller]
10012 fn accepts(source: &str) {
10013 let errors = errors_of(source);
10014 assert!(
10015 errors.is_empty(),
10016 "expected no errors, found: {}",
10017 errors
10018 .iter()
10019 .map(|d| format!("{}: {}", d.code, d.message))
10020 .collect::<Vec<_>>()
10021 .join("; ")
10022 );
10023 }
10024
10025 #[track_caller]
10027 fn rejects(source: &str) -> Diagnostic {
10028 let mut errors = errors_of(source);
10029 assert_eq!(
10030 errors.len(),
10031 1,
10032 "expected exactly one error, found: {}",
10033 errors
10034 .iter()
10035 .map(|d| format!("{}: {}", d.code, d.message))
10036 .collect::<Vec<_>>()
10037 .join("; ")
10038 );
10039 errors.remove(0)
10040 }
10041
10042 #[test]
10043 fn accepts_a_test_of_the_shape_the_runner_calls() {
10044 accepts("test fn passes() -> Result<Unit, Error> {\n Ok(())\n}\n");
10045 }
10046
10047 #[test]
10048 fn rejects_a_test_that_declares_a_parameter() {
10049 let error = rejects("test fn passes(n: Int) -> Result<Unit, Error> {\n Ok(())\n}\n");
10050 assert_eq!(error.code, TEST);
10051 assert!(error.message.contains("declares 1 parameter(s)"));
10052 assert_eq!(
10053 error.help.as_deref(),
10054 Some("write `test fn passes() -> Result<Unit, Error>`")
10055 );
10056 }
10057
10058 #[test]
10059 fn rejects_a_test_that_returns_something_else() {
10060 for source in [
10061 "test fn passes() -> Int {\n 1\n}\n",
10062 "test fn passes() {\n}\n",
10063 "test fn passes() -> Result<Int, Error> {\n Ok(1)\n}\n",
10064 ] {
10065 let error = rejects(source);
10066 assert_eq!(error.code, TEST, "{source}");
10067 assert!(
10068 error
10069 .message
10070 .contains("a test returns `Result<Unit, Error>`"),
10071 "{}",
10072 error.message
10073 );
10074 }
10075 }
10076
10077 #[test]
10078 fn rejects_an_async_test() {
10079 let error = rejects("test async fn passes() -> Result<Unit, Error> {\n Ok(())\n}\n");
10080 assert_eq!(error.code, TEST);
10081 assert!(error.message.contains("is `async`"));
10082 }
10083
10084 #[test]
10085 fn assert_takes_a_bool_and_produces_a_result() {
10086 accepts("test fn passes() -> Result<Unit, Error> {\n assert(1 == 1)?\n Ok(())\n}\n");
10087 let error =
10088 rejects("test fn passes() -> Result<Unit, Error> {\n assert(1)?\n Ok(())\n}\n");
10089 assert_eq!(error.code, MISMATCH);
10090 }
10091
10092 #[test]
10093 fn assert_equal_compares_two_values_of_one_type() {
10094 accepts(
10095 "test fn passes() -> Result<Unit, Error> {\n assertEqual(1 + 1, 2)?\n Ok(())\n}\n",
10096 );
10097 let error = rejects(
10098 "test fn passes() -> Result<Unit, Error> {\n assertEqual(1, \"1\")?\n Ok(())\n}\n",
10099 );
10100 assert_eq!(error.code, MISMATCH);
10101 }
10102
10103 #[test]
10104 fn an_assertion_takes_the_number_of_arguments_it_declares() {
10105 let error =
10106 rejects("test fn passes() -> Result<Unit, Error> {\n assert()?\n Ok(())\n}\n");
10107 assert_eq!(error.code, ARITY);
10108 let error =
10109 rejects("test fn passes() -> Result<Unit, Error> {\n assertEqual(1)?\n Ok(())\n}\n");
10110 assert_eq!(error.code, ARITY);
10111 }
10112
10113 #[test]
10114 fn a_declaration_of_the_same_name_wins_over_the_assertion_builtin() {
10115 accepts(
10118 "fn assert(message: String) -> Result<Unit, Error> {\n Ok(())\n}\n\n test fn passes() -> Result<Unit, Error> {\n assert(\"anything\")?\n Ok(())\n}\n",
10119 );
10120 }
10121
10122 fn in_main(body: &str) -> String {
10124 format!(
10125 "use console.println\n\nexport fn main() -> Result<Unit, Error> {{\n{body}\n Ok(())\n}}\n"
10126 )
10127 }
10128
10129 #[track_caller]
10130 fn accepts_body(body: &str) {
10131 accepts(&in_main(body));
10132 }
10133
10134 #[track_caller]
10135 fn rejects_body(body: &str) -> Diagnostic {
10136 rejects(&in_main(body))
10137 }
10138
10139 #[test]
10142 fn accepts_the_card_s_greeting_program() {
10143 accepts(
10144 "\
10145use console.println
10146
10147export fn greet(name: String) -> String {
10148 \"Hello, {name}!\"
10149}
10150
10151export fn main(args: Array<String>) -> Result<Unit, Error> {
10152 let name = args.get(0).unwrapOr(\"world\")
10153 console.println(greet(name))?
10154 Ok(())
10155}
10156",
10157 );
10158 }
10159
10160 #[test]
10161 fn infers_a_let_from_its_initializer() {
10162 accepts_body(" let n = 1\n let doubled = n * 2\n println(\"{doubled}\")?");
10163 }
10164
10165 #[test]
10166 fn checks_a_written_let_annotation() {
10167 accepts_body(" let n: Int = 1\n println(\"{n}\")?");
10168 let error = rejects_body(" let n: Int = \"one\"");
10169 assert_eq!(error.code, MISMATCH);
10170 assert_eq!(error.message, "expected `Int`, found `String`");
10171 assert_eq!(error.rule.unwrap(), "Types are nominal and the only implicit conversion is to `dyn Trait`: a value must otherwise already have the type its place asks for.");
10172 assert_eq!(
10173 error.help.unwrap(),
10174 "parse the `String` with `Int.parse(text)`, which returns a `Result<Int, Error>`"
10175 );
10176 }
10177
10178 #[test]
10179 fn an_array_literal_takes_its_element_type_from_its_elements() {
10180 accepts_body(" let items = [1, 2]\n let first: Option<Int> = items.get(0)");
10181 let error = rejects_body(" let items = [1, \"two\"]");
10182 assert_eq!(error.code, MISMATCH);
10183 assert_eq!(error.message, "expected `Int`, found `String`");
10184 }
10185
10186 #[test]
10187 fn an_empty_array_literal_has_no_element_type_to_infer() {
10188 let error =
10193 rejects_body(" let empty = []\n println(\"{empty.length()} {empty.isEmpty()}\")?");
10194 assert_eq!(error.code, UNCONSTRAINED);
10195 }
10196
10197 #[test]
10198 fn a_later_method_call_settles_what_an_empty_collection_holds() {
10199 accepts(
10205 "\
10206fn build(text: String) -> Array<String> {
10207 var log = Vector.of()
10208 log.push(text)
10209 log.freeze()
10210}
10211",
10212 );
10213 let error = rejects(
10214 "\
10215fn build(text: String) -> Array<Int> {
10216 var log = Vector.of()
10217 log.push(text)
10218 log.freeze()
10219}
10220",
10221 );
10222 assert_eq!(error.code, INFERENCE_CONFLICT);
10226 assert_eq!(
10227 error.message,
10228 "`log` was settled as `Vector<String>`, but this use needs `Vector<Int>`"
10229 );
10230 }
10231
10232 #[test]
10233 fn every_empty_collection_takes_its_type_from_its_uses() {
10234 accepts(
10239 "\
10240fn build(text: String, n: Int) -> Int {
10241 var names = Set.of()
10242 names = names.inserted(text)
10243 var counts = Map.of()
10244 counts = counts.inserted(text, n)
10245 names.length() + counts.length()
10246}
10247",
10248 );
10249 accepts(
10254 "\
10255fn empty<T>() -> Vector<T> {
10256 Vector.of()
10257}
10258
10259fn build(text: String) -> Array<String> {
10260 var log = empty()
10261 log.push(text)
10262 log.toArray()
10263}
10264",
10265 );
10266 }
10267
10268 #[test]
10269 fn an_argument_position_settles_what_a_binding_holds() {
10270 accepts(
10271 "\
10272fn count(lines: Vector<String>) -> Int {
10273 lines.length()
10274}
10275
10276fn build(text: String) -> Int {
10277 var log = Vector.of()
10278 count(log)
10279 log.push(text)
10280 count(log)
10281}
10282",
10283 );
10284 let error = rejects(
10285 "\
10286fn count(lines: Vector<String>) -> Int {
10287 lines.length()
10288}
10289
10290fn build(n: Int) -> Int {
10291 var log = Vector.of()
10292 count(log)
10293 log.push(n)
10294 0
10295}
10296",
10297 );
10298 assert_eq!(error.code, INFERENCE_CONFLICT);
10299 assert_eq!(
10300 error.message,
10301 "`log` was settled as `Vector<String>`, but this use needs `Vector<Int>`"
10302 );
10303 }
10304
10305 #[test]
10308 fn a_generic_parameter_is_bound_from_an_argument_a_binding_only_just_settled() {
10309 accepts(
10318 "\
10319fn myFilter<T>(items: Array<T>, keep: fn(item: T) -> Bool) -> Array<T> {
10320 var out = Vector.of()
10321 for item in items {
10322 if keep(item) {
10323 out.push(item)
10324 }
10325 }
10326 out.freeze()
10327}
10328
10329fn build() -> Int {
10330 var v = Vector.of()
10331 v.push(1)
10332 v.push(2)
10333 let items = v.freeze()
10334 let result = myFilter(items, fn(item) { item % 2 == 0 })
10335 result.length()
10336}
10337",
10338 );
10339 }
10340
10341 #[test]
10342 fn an_annotated_closure_parameter_still_settles_a_generic_call_alone() {
10343 accepts(
10347 "\
10348fn myFilter<T>(items: Array<T>, keep: fn(item: T) -> Bool) -> Array<T> {
10349 var out = Vector.of()
10350 for item in items {
10351 if keep(item) {
10352 out.push(item)
10353 }
10354 }
10355 out.freeze()
10356}
10357
10358fn build() -> Int {
10359 var v = Vector.of()
10360 v.push(1)
10361 v.push(2)
10362 let items = v.freeze()
10363 let result = myFilter(items, fn(item: Int) { item % 2 == 0 })
10364 result.length()
10365}
10366",
10367 );
10368 }
10369
10370 #[test]
10371 fn a_generic_call_still_unconstrained_reads_as_before() {
10372 let error = rejects(
10378 "\
10379fn myFilter<T>(items: Array<T>, keep: fn(item: T) -> Bool) -> Array<T> {
10380 var out = Vector.of()
10381 for item in items {
10382 if keep(item) {
10383 out.push(item)
10384 }
10385 }
10386 out.freeze()
10387}
10388
10389fn build() -> Int {
10390 let items = Vector.of().freeze()
10391 let result = myFilter(items, fn(item: Int) { item == item })
10392 result.length()
10393}
10394",
10395 );
10396 assert_eq!(error.code, UNCONSTRAINED);
10397 assert_eq!(
10398 error.message,
10399 "nothing says what the `_` in `items: Array<_>` is"
10400 );
10401 }
10402
10403 #[test]
10404 fn two_type_parameters_are_settled_from_different_arguments() {
10405 accepts(
10411 "\
10412fn myMap<T, U>(items: Array<T>, transform: fn(item: T) -> U) -> Array<U> {
10413 var out = Vector.of()
10414 for item in items {
10415 out.push(transform(item))
10416 }
10417 out.freeze()
10418}
10419
10420fn build() -> Int {
10421 var v = Vector.of()
10422 v.push(1)
10423 v.push(2)
10424 let items = v.freeze()
10425 let result = myMap(items, fn(item) { \"{item}\" })
10426 result.length()
10427}
10428",
10429 );
10430 }
10431
10432 #[test]
10433 fn an_assignment_settles_what_a_binding_holds() {
10434 accepts(
10439 "\
10440fn build(lines: Vector<String>) -> Array<String> {
10441 var log = Vector.of()
10442 log = lines
10443 log.toArray()
10444}
10445",
10446 );
10447 let error = rejects(
10448 "\
10449fn build(lines: Vector<String>) -> Array<Int> {
10450 var log = Vector.of()
10451 log = lines
10452 log.freeze()
10453}
10454",
10455 );
10456 assert_eq!(error.code, INFERENCE_CONFLICT);
10457 assert_eq!(
10458 error.message,
10459 "`log` was settled as `Vector<String>`, but this use needs `Vector<Int>`"
10460 );
10461 }
10462
10463 #[test]
10464 fn two_uses_that_disagree_are_an_error_naming_both() {
10465 let error = rejects(
10466 "\
10467fn build(text: String, n: Int) -> Int {
10468 var log = Vector.of()
10469 log.push(text)
10470 log.push(n)
10471 log.length()
10472}
10473",
10474 );
10475 assert_eq!(error.code, INFERENCE_CONFLICT);
10476 assert_eq!(
10477 error.message,
10478 "`log` was settled as `Vector<String>`, but this use needs `Vector<Int>`"
10479 );
10480 assert_eq!(error.rule.unwrap(), "A binding whose initializer leaves a type open takes that type from its uses, and every use of it means the same type.");
10481 assert_eq!(
10482 error.help.unwrap(),
10483 "write the type on the binding, as in `log: Vector<String>`, and correct whichever use disagrees"
10484 );
10485 assert_eq!(error.labels.len(), 2);
10487 }
10488
10489 #[test]
10490 fn a_third_use_does_not_report_the_same_disagreement_again() {
10491 let errors = errors_of(
10492 "\
10493fn build(text: String, n: Int) -> Int {
10494 var log = Vector.of()
10495 log.push(text)
10496 log.push(n)
10497 log.push(n)
10498 log.length()
10499}
10500",
10501 );
10502 assert_eq!(errors.len(), 1, "found: {errors:?}");
10503 }
10504
10505 #[test]
10506 fn a_binding_nothing_settles_asks_for_the_annotation() {
10507 let error = rejects(
10508 "\
10509fn build() -> Int {
10510 var log = Vector.of()
10511 log.length()
10512}
10513",
10514 );
10515 assert_eq!(error.code, UNCONSTRAINED);
10516 assert_eq!(
10517 error.message,
10518 "nothing says what the `_` in `log: Vector<_>` is"
10519 );
10520 assert_eq!(error.rule.unwrap(), "A type the checker infers is inferred from something written: a value, an annotation, or the type of the place the value is given to.");
10521 assert_eq!(
10522 error.help.unwrap(),
10523 "write the type on the binding, as in `log: Vector<_>` with the `_` filled in, or use `log` in a way that says what it holds"
10524 );
10525 }
10526
10527 #[test]
10530 fn a_value_no_binding_holds_is_asked_where_it_is_written() {
10531 let error = rejects(
10532 "\
10533fn build() -> Int {
10534 Vector.of().length()
10535}
10536",
10537 );
10538 assert_eq!(error.code, UNCONSTRAINED);
10539 assert_eq!(error.message, "nothing says what the `_` in `Vector<_>` is");
10540 assert_eq!(
10541 error.help.unwrap(),
10542 "bind this value first, with the type written, as in `let value: Vector<_> = ...` with the `_` filled in"
10543 );
10544 }
10545
10546 #[test]
10547 fn a_written_annotation_settles_it_without_any_use() {
10548 assert!(warnings_of(
10551 "\
10552fn build() -> Int {
10553 var log: Vector<String> = Vector.of()
10554 log.length()
10555}
10556"
10557 )
10558 .is_empty());
10559 }
10560
10561 #[test]
10562 fn inference_does_not_reach_across_a_declaration() {
10563 accepts(
10567 "\
10568fn first(text: String) -> Int {
10569 var log = Vector.of()
10570 log.push(text)
10571 log.length()
10572}
10573
10574fn second(n: Int) -> Int {
10575 var log = Vector.of()
10576 log.push(n)
10577 log.length()
10578}
10579",
10580 );
10581 assert!(warnings_of(
10582 "\
10583fn first(text: String) -> Int {
10584 var log = Vector.of()
10585 log.push(text)
10586 log.length()
10587}
10588
10589fn second(n: Int) -> Int {
10590 var log = Vector.of()
10591 log.push(n)
10592 log.length()
10593}
10594"
10595 )
10596 .is_empty());
10597 }
10598
10599 #[test]
10600 fn a_vector_that_holds_itself_is_refused() {
10601 let error = rejects(
10612 "\
10613fn churn() {
10614 var v = Vector.of()
10615 v.push(v)
10616}
10617",
10618 );
10619 assert_eq!(error.code, RECURSIVE_TYPE);
10620 assert_eq!(
10621 error.message,
10622 "this use makes `v` hold a `Vector<_>`, which holds itself"
10623 );
10624 assert_eq!(error.rule.unwrap(), "A type the checker infers is a type the program could have written, and a type that contains itself is written by declaring one.");
10625 assert_eq!(
10626 error.help.unwrap(),
10627 "declare a struct or an enum for the thing that repeats and hold that, as in `struct Node { next: Vector<Node> }`"
10628 );
10629 }
10630
10631 #[test]
10634 fn a_cycle_through_a_declared_type_is_accepted() {
10635 accepts(
10636 "\
10637struct Node {
10638 next: Vector<Node>
10639}
10640
10641fn churn() {
10642 var v: Vector<Node> = Vector.of()
10643 v.push(Node(next: v))
10644}
10645",
10646 );
10647 }
10648
10649 #[test]
10650 fn a_use_inside_a_nested_block_settles_the_binding() {
10651 accepts(
10655 "\
10656fn build(lines: Array<String>) -> Array<String> {
10657 var log = Vector.of()
10658 for line in lines {
10659 log.push(line)
10660 }
10661 log.freeze()
10662}
10663",
10664 );
10665 }
10666
10667 #[test]
10668 fn a_vector_grows_and_freezes_into_an_array() {
10669 accepts(
10670 "\
10671fn build(upTo: Int) -> Array<Int> {
10672 var building = Vector.of(1)
10673 for n in 1..upTo {
10674 building.push(n)
10675 }
10676 building.freeze()
10677}
10678",
10679 );
10680 let error = rejects(
10681 "\
10682fn build() -> Array<String> {
10683 var building = Vector.of(1)
10684 building.freeze()
10685}
10686",
10687 );
10688 assert_eq!(error.code, MISMATCH);
10689 assert_eq!(
10690 error.message,
10691 "expected `Array<String>`, found `Array<Int>`"
10692 );
10693 }
10694
10695 #[test]
10696 fn snapshot_returns_the_receiver_s_own_type_for_every_builtin_value() {
10697 accepts_body(
10698 "\
10699 let n: Int = 1.snapshot()
10700 let s: String = \"a\".snapshot()
10701 let arr: Array<Int> = [1, 2].snapshot()
10702 var v: Vector<Int> = Vector.of(1).snapshot()
10703 println(\"{n} {s} {arr} {v}\")?
10704",
10705 );
10706 }
10707
10708 #[test]
10709 fn rejects_snapshot_on_a_closure() {
10710 let error = rejects_body(
10711 "\
10712 let handler = fn(x: Int) { x }
10713 println(\"{handler.snapshot()}\")?
10714",
10715 );
10716 assert_eq!(error.code, UNKNOWN_METHOD);
10717 assert_eq!(
10718 error.message,
10719 "`fn(Int) -> Int` does not implement `Snapshot`"
10720 );
10721 assert!(error.rule.unwrap().contains("Closures"));
10722 }
10723
10724 #[test]
10725 fn a_vector_push_takes_the_element_type() {
10726 let error = rejects(
10727 "\
10728fn build() -> Array<Int> {
10729 var building = Vector.of(1)
10730 building.push(\"two\")
10731 building.freeze()
10732}
10733",
10734 );
10735 assert_eq!(error.code, MISMATCH);
10736 assert_eq!(error.message, "expected `Int`, found `String`");
10737 }
10738
10739 #[test]
10744 fn unwrap_or_takes_the_type_inside_on_both_option_and_result() {
10745 accepts_body(
10746 " let found: Int = [1].get(0).unwrapOr(0)\n\
10747 \x20 let parsed: Int = Int.parse(\"1\").unwrapOr(0)\n\
10748 \x20 let mapped: Int = Int.parse(\"1\").mapError(fn(error) { \"bad\" }).unwrapOr(0)",
10749 );
10750 let error = rejects_body(" Int.parse(\"1\").unwrapOr(\"zero\")");
10751 assert_eq!(error.code, MISMATCH);
10752 assert_eq!(error.message, "expected `Int`, found `String`");
10753 let error = rejects_body(" let n: String = Int.parse(\"1\").unwrapOr(0)");
10754 assert_eq!(error.code, MISMATCH);
10755 assert_eq!(error.message, "expected `String`, found `Int`");
10756 }
10757
10758 #[test]
10761 fn a_result_where_its_ok_type_belongs_is_told_about_unwrap_or() {
10762 let error = rejects_body(" let n: Int = Int.parse(\"1\")");
10763 assert_eq!(error.code, MISMATCH);
10764 assert_eq!(
10765 error.help.unwrap(),
10766 "unwrap it, as in `value.unwrapOr(<Int>)`, which always produces a `Int`"
10767 );
10768 }
10769
10770 #[test]
10774 fn int_parse_and_parse_radix_are_two_signatures() {
10775 accepts_body(
10776 " let decimal: Result<Int, Error> = Int.parse(\"12\")\n\
10777 \x20 let hex: Result<Int, Error> = Int.parseRadix(\"ff\", 16)",
10778 );
10779 let error = rejects_body(" Int.parse(\"ff\", 16)");
10780 assert_eq!(error.code, ARITY);
10781 let error = rejects_body(" Int.parseRadix(\"ff\")");
10782 assert_eq!(error.code, MISSING_ARGUMENT);
10783 let error = rejects_body(" Int.parseRadix(\"ff\", \"16\")");
10784 assert_eq!(error.code, MISMATCH);
10785 assert_eq!(error.message, "expected `Int`, found `String`");
10786 }
10787
10788 #[test]
10792 fn from_code_point_answers_a_result_of_string() {
10793 accepts_body(
10794 " let character: Result<String, Error> = String.fromCodePoint(65)\n\
10795 \x20 let letter: String = String.fromCodePoint(65).unwrapOr(\"?\")",
10796 );
10797 let error = rejects_body(" String.fromCodePoint(\"A\")");
10798 assert_eq!(error.code, MISMATCH);
10799 assert_eq!(error.message, "expected `Int`, found `String`");
10800 let error = rejects_body(" let letter: String = String.fromCodePoint(65)");
10801 assert_eq!(error.code, MISMATCH);
10802 assert_eq!(
10803 error.message,
10804 "expected `String`, found `Result<String, Error>`"
10805 );
10806 }
10807
10808 #[test]
10809 fn checks_every_map_operation() {
10810 accepts_body(
10811 " let ages = Map.of(MapEntry(key: \"Alice\", value: 30))\n\
10812 \x20 let found: Option<Int> = ages.get(\"Alice\")\n\
10813 \x20 let has: Bool = ages.contains(\"Bob\")\n\
10814 \x20 let n: Int = ages.length()\n\
10815 \x20 let empty: Bool = ages.isEmpty()\n\
10816 \x20 let names: Array<String> = ages.keys()\n\
10817 \x20 let numbers: Array<Int> = ages.values()\n\
10818 \x20 let more: Map<String, Int> = ages.inserted(\"Carol\", 41)\n\
10819 \x20 let fewer: Map<String, Int> = ages.removed(\"Alice\")",
10820 );
10821 let error =
10822 rejects_body(" let ages = Map.of(MapEntry(key: \"Alice\", value: 30))\n ages.get(1)");
10823 assert_eq!(error.code, MISMATCH);
10824 assert_eq!(error.message, "expected `String`, found `Int`");
10825 }
10826
10827 #[test]
10828 fn checks_every_set_operation() {
10829 accepts_body(
10830 " let tags = Set.of(\"a\", \"b\")\n\
10831 \x20 let has: Bool = tags.contains(\"a\")\n\
10832 \x20 let n: Int = tags.length()\n\
10833 \x20 let empty: Bool = tags.isEmpty()\n\
10834 \x20 let items: Array<String> = tags.toArray()\n\
10835 \x20 let bigger: Set<String> = tags.inserted(\"c\")\n\
10836 \x20 let smaller: Set<String> = tags.removed(\"a\")",
10837 );
10838 let error = rejects_body(" let tags = Set.of(\"a\")\n tags.contains(1)");
10839 assert_eq!(error.code, MISMATCH);
10840 assert_eq!(error.message, "expected `String`, found `Int`");
10841 }
10842
10843 #[test]
10844 fn map_of_collects_map_entries() {
10845 let error = rejects_body(" let ages = Map.of(1)");
10846 assert_eq!(error.code, MISMATCH);
10847 assert_eq!(error.message, "expected `MapEntry<_, _>`, found `Int`");
10848
10849 let error = rejects_body(
10850 " let ages = Map.of(MapEntry(key: \"a\", value: 1), MapEntry(key: 2, value: 3))",
10851 );
10852 assert_eq!(error.code, MISMATCH);
10853 assert_eq!(
10854 error.message,
10855 "expected `MapEntry<String, Int>`, found `MapEntry<Int, Int>`"
10856 );
10857 }
10858
10859 #[test]
10860 fn a_map_entry_carries_a_key_and_a_value() {
10861 accepts_body(
10862 " let entry = MapEntry(key: \"a\", value: 1)\n\
10863 \x20 let key: String = entry.key\n\
10864 \x20 let value: Int = entry.value",
10865 );
10866 let error = rejects_body(" let entry = MapEntry(key: \"a\", value: 1)\n entry.other");
10867 assert_eq!(error.code, UNKNOWN_FIELD);
10868 assert_eq!(error.message, "`MapEntry` has no field `other`");
10869 assert_eq!(
10870 error.rule.unwrap(),
10871 "A builtin struct's fields are exactly the ones the language defines."
10872 );
10873 assert_eq!(error.help.unwrap(), "`MapEntry` declares `key`, `value`");
10874 }
10875
10876 #[test]
10881 fn an_error_carries_a_message() {
10882 accepts_body(" let message: String = Error(\"boom\").message");
10883 accepts_body(
10884 " let outcome: Result<Int, Error> = Err(Error(\"boom\"))\n\
10885 \x20 match outcome {\n\
10886 \x20 Ok(n) => n,\n\
10887 \x20 Err(failure) => failure.message.length()\n\
10888 \x20 }",
10889 );
10890 let error = rejects_body(" let code = Error(\"boom\").code");
10891 assert_eq!(error.code, UNKNOWN_FIELD);
10892 assert_eq!(error.message, "`Error` has no field `code`");
10893 assert_eq!(
10894 error.rule.unwrap(),
10895 "A builtin struct's fields are exactly the ones the language defines."
10896 );
10897 assert_eq!(error.help.unwrap(), "`Error` declares `message`");
10898 }
10899
10900 #[test]
10903 fn an_error_s_message_is_a_string() {
10904 let error = rejects_body(" let code: Int = Error(\"boom\").message");
10905 assert_eq!(error.code, MISMATCH);
10906 assert_eq!(error.message, "expected `Int`, found `String`");
10907 }
10908
10909 #[test]
10910 fn a_map_iterates_map_entries_and_a_set_its_elements() {
10911 accepts_body(
10912 " let ages = Map.of(MapEntry(key: \"a\", value: 1))\n\
10913 \x20 for entry in ages {\n\
10914 \x20 let key: String = entry.key\n\
10915 \x20 let value: Int = entry.value\n\
10916 \x20 }\n\
10917 \x20 for tag in Set.of(\"a\") {\n\
10918 \x20 let element: String = tag\n\
10919 \x20 }",
10920 );
10921 let error = rejects_body(
10922 " let ages = Map.of(MapEntry(key: \"a\", value: 1))\n\
10923 \x20 for entry in ages {\n\
10924 \x20 let key: Int = entry.key\n\
10925 \x20 }",
10926 );
10927 assert_eq!(error.code, MISMATCH);
10928 assert_eq!(error.message, "expected `Int`, found `String`");
10929 }
10930
10931 #[test]
10932 fn checks_every_range_operation() {
10933 accepts_body(
10934 " let range = 0..<3\n\
10935 \x20 let n: Int = range.length()\n\
10936 \x20 let empty: Bool = range.isEmpty()\n\
10937 \x20 let has: Bool = range.contains(1)",
10938 );
10939 let error = rejects_body(" let range = 0..<3\n range.contains(\"one\")");
10940 assert_eq!(error.code, MISMATCH);
10941 assert_eq!(error.message, "expected `Int`, found `String`");
10942 }
10943
10944 #[test]
10945 fn checks_struct_initialization_and_field_access() {
10946 accepts(
10947 "\
10948struct Point { x: Int, y: Int }
10949
10950fn sum(point: Point) -> Int {
10951 point.x + point.y
10952}
10953
10954fn origin() -> Point {
10955 Point(x: 0, y: 0)
10956}
10957",
10958 );
10959 }
10960
10961 #[test]
10962 fn rejects_a_struct_field_of_the_wrong_type() {
10963 let error = rejects(
10964 "\
10965struct Point { x: Int, y: Int }
10966
10967fn origin() -> Point {
10968 Point(x: 0, y: \"zero\")
10969}
10970",
10971 );
10972 assert_eq!(error.code, MISMATCH);
10973 assert_eq!(error.message, "expected `Int`, found `String`");
10974 assert_eq!(error.labels[0].message, "the field `y` is `Int`");
10975 }
10976
10977 #[test]
10978 fn rejects_a_missing_struct_field() {
10979 let error = rejects(
10980 "\
10981struct Point { x: Int, y: Int }
10982
10983fn origin() -> Point {
10984 Point(x: 0)
10985}
10986",
10987 );
10988 assert_eq!(error.code, MISSING_ARGUMENT);
10989 assert_eq!(error.message, "`Point` needs the field `y`");
10990 assert_eq!(
10991 error.rule.unwrap(),
10992 "A call passes every parameter that has no default."
10993 );
10994 assert_eq!(error.help.unwrap(), "pass `y: <Int>`");
10995 }
10996
10997 #[test]
10998 fn rejects_a_field_the_struct_does_not_declare() {
10999 let error = rejects(
11000 "\
11001struct Point { x: Int, y: Int }
11002
11003fn z(point: Point) -> Int {
11004 point.z
11005}
11006",
11007 );
11008 assert_eq!(error.code, UNKNOWN_FIELD);
11009 assert_eq!(error.message, "`Point` has no field `z`");
11010 assert_eq!(
11011 error.rule.unwrap(),
11012 "A struct's fields are exactly the ones its declaration lists."
11013 );
11014 assert_eq!(error.help.unwrap(), "`Point` declares `x`, `y`");
11015 }
11016
11017 #[test]
11018 fn checks_enum_construction_and_match_payloads() {
11019 accepts(
11020 "\
11021enum Status {
11022 Pending
11023 Active(Int)
11024}
11025
11026fn describe(status: Status) -> String {
11027 match status {
11028 Status.Pending => \"pending\"
11029 Status.Active(since) => \"active since {since}\"
11030 }
11031}
11032
11033fn active() -> Status {
11034 Status.Active(7)
11035}
11036",
11037 );
11038 }
11039
11040 #[test]
11041 fn rejects_an_enum_payload_of_the_wrong_type() {
11042 let error = rejects(
11043 "\
11044enum Status {
11045 Active(Int)
11046}
11047
11048fn active() -> Status {
11049 Status.Active(\"now\")
11050}
11051",
11052 );
11053 assert_eq!(error.code, MISMATCH);
11054 assert_eq!(error.message, "expected `Int`, found `String`");
11055 }
11056
11057 #[test]
11058 fn rejects_an_enum_payload_of_the_wrong_arity() {
11059 let error = rejects(
11060 "\
11061enum Status {
11062 Active(Int)
11063}
11064
11065fn active() -> Status {
11066 Status.Active(1, 2)
11067}
11068",
11069 );
11070 assert_eq!(error.code, PAYLOAD_ARITY);
11071 assert_eq!(
11072 error.message,
11073 "`Status.Active` carries 1 value(s), but 2 were given"
11074 );
11075 assert_eq!(
11076 error.rule.unwrap(),
11077 "An enum case carries exactly the payload its declaration writes."
11078 );
11079 assert_eq!(error.help.unwrap(), "write `Status.Active(Int)`");
11080 }
11081
11082 #[test]
11083 fn rejects_a_case_the_enum_does_not_declare() {
11084 let error = rejects(
11085 "\
11086enum Status {
11087 Pending
11088}
11089
11090fn active() -> Status {
11091 Status.Active
11092}
11093",
11094 );
11095 assert_eq!(error.code, UNKNOWN_CASE);
11096 assert_eq!(error.message, "`Status` has no case `Active`");
11097 assert_eq!(
11098 error.rule.unwrap(),
11099 "An enum's cases are exactly the ones its declaration lists."
11100 );
11101 assert_eq!(error.help.unwrap(), "`Status` declares `Pending`");
11102 }
11103
11104 #[test]
11105 fn rejects_a_pattern_from_another_enum() {
11106 let error = rejects(
11107 "\
11108enum Suit {
11109 Hearts
11110}
11111
11112enum Card {
11113 Blank
11114}
11115
11116fn name(card: Card) -> String {
11117 match card {
11118 Suit.Hearts => \"hearts\"
11119 _ => \"other\"
11120 }
11121}
11122",
11123 );
11124 assert_eq!(error.code, PATTERN);
11125 assert_eq!(
11126 error.message,
11127 "this pattern matches `Suit`, but the scrutinee is `Card`"
11128 );
11129 assert_eq!(
11130 error.rule.unwrap(),
11131 "A pattern matches values of the scrutinee's type."
11132 );
11133 assert_eq!(
11134 error.help.unwrap(),
11135 "write a `Card` case, such as `Card.Blank`"
11136 );
11137 }
11138
11139 #[test]
11140 fn rejects_a_literal_pattern_of_another_type() {
11141 let error = rejects(
11142 "\
11143fn name(n: Int) -> String {
11144 match n {
11145 \"one\" => \"one\"
11146 _ => \"other\"
11147 }
11148}
11149",
11150 );
11151 assert_eq!(error.code, PATTERN);
11152 assert_eq!(
11153 error.message,
11154 "this pattern matches `String`, but the scrutinee is `Int`"
11155 );
11156 assert_eq!(
11157 error.help.unwrap(),
11158 "write a `Int` literal, or a binding such as `other`"
11159 );
11160 }
11161
11162 #[test]
11163 fn checks_methods_and_associated_functions() {
11164 accepts(
11165 "\
11166struct Counter { hits: Int }
11167
11168impl Counter {
11169 fn start() -> Counter {
11170 Counter(hits: 0)
11171 }
11172
11173 fn hit(var self) {
11174 self.hits += 1
11175 }
11176
11177 fn describe(self) -> String {
11178 \"{self.hits}\"
11179 }
11180}
11181
11182fn run() -> String {
11183 var counter = Counter.start()
11184 counter.hit()
11185 counter.describe()
11186}
11187",
11188 );
11189 }
11190
11191 #[test]
11192 fn a_method_needs_a_receiver_and_an_associated_function_takes_none() {
11193 let source = "\
11194struct Counter { hits: Int }
11195
11196impl Counter {
11197 fn start() -> Counter {
11198 Counter(hits: 0)
11199 }
11200
11201 fn describe(self) -> String {
11202 \"{self.hits}\"
11203 }
11204}
11205";
11206 let error = rejects(&format!(
11207 "{source}\nfn run() -> String {{\n Counter.describe()\n}}\n"
11208 ));
11209 assert_eq!(error.code, RECEIVER);
11210 assert_eq!(
11211 error.message,
11212 "`Counter.describe` is a method and needs a receiver"
11213 );
11214 assert_eq!(
11215 error.rule.unwrap(),
11216 "A method is called on a value; only an associated function is called on its type."
11217 );
11218 assert_eq!(
11219 error.help.unwrap(),
11220 "call it on a value, as in `value.describe(...)`, or declare `fn describe()` without `self`"
11221 );
11222
11223 let error = rejects(&format!(
11224 "{source}\nfn run(counter: Counter) -> Counter {{\n counter.start()\n}}\n"
11225 ));
11226 assert_eq!(error.code, RECEIVER);
11227 assert_eq!(error.message, "`Counter.start` takes no receiver");
11228 assert_eq!(error.help.unwrap(), "write `Counter.start(...)`");
11229 }
11230
11231 #[test]
11232 fn rejects_a_method_the_type_does_not_declare() {
11233 let error = rejects(
11234 "\
11235struct Counter { hits: Int }
11236
11237impl Counter {
11238 fn describe(self) -> String {
11239 \"{self.hits}\"
11240 }
11241}
11242
11243fn run(counter: Counter) -> String {
11244 counter.report()
11245}
11246",
11247 );
11248 assert_eq!(error.code, UNKNOWN_METHOD);
11249 assert_eq!(error.message, "`Counter` has no method `report`");
11250 assert_eq!(
11251 error.rule.unwrap(),
11252 "A method is declared in its type's `impl` block."
11253 );
11254 assert_eq!(error.help.unwrap(), "`Counter` declares `describe`");
11255 }
11256
11257 #[test]
11258 fn rejects_an_associated_function_the_type_does_not_declare() {
11259 let error = rejects(
11260 "\
11261struct Counter { hits: Int }
11262
11263fn run() -> Counter {
11264 Counter.start()
11265}
11266",
11267 );
11268 assert_eq!(error.code, UNKNOWN_ASSOCIATED);
11269 assert_eq!(
11270 error.message,
11271 "`Counter` has no associated function `start`"
11272 );
11273 assert_eq!(
11274 error.help.unwrap(),
11275 "`Counter` declares no methods; declare one in `impl Counter`"
11276 );
11277 }
11278
11279 #[test]
11280 fn count_is_spelled_length() {
11281 let error = rejects_body(" let items = [1]\n println(\"{items.count()}\")?");
11282 assert_eq!(error.code, UNKNOWN_METHOD);
11283 assert_eq!(
11284 error.message,
11285 "`Array` has no method `count`; Cove spells the number of elements `length()`"
11286 );
11287 assert_eq!(
11288 error.rule.unwrap(),
11289 "Every sequence reports its element count as `length()`; there is no `count()`."
11290 );
11291 assert_eq!(error.help.unwrap(), "write `length()` instead of `count()`");
11292 }
11293
11294 #[test]
11298 fn every_sequence_is_told_that_count_is_spelled_length() {
11299 let receivers = [
11300 ("Array", "let items = [1]\n items"),
11301 ("Vector", "var items = Vector.of(1)\n items"),
11302 ("String", "let text = \"ab\"\n text"),
11303 ("Range", "let span = 0..<3\n span"),
11304 (
11305 "Map",
11306 "let ages = Map.of(MapEntry(key: \"a\", value: 1))\n ages",
11307 ),
11308 ("Set", "let seen = Set.of(1)\n seen"),
11309 ];
11310 for (type_name, receiver) in receivers {
11311 let error = rejects_body(&format!(" {receiver}.count()"));
11312 assert_eq!(error.code, UNKNOWN_METHOD, "{type_name}");
11313 assert_eq!(
11314 error.message,
11315 format!(
11316 "`{type_name}` has no method `count`; Cove spells the number of elements `length()`"
11317 )
11318 );
11319 assert_eq!(
11320 error.help.unwrap(),
11321 "write `length()` instead of `count()`",
11322 "{type_name}"
11323 );
11324 }
11325 }
11326
11327 #[test]
11337 fn a_callbacks_parameters_come_from_the_element_type() {
11338 accepts_body(
11339 " let words = [\"a\", \"bb\"]\n \
11340 let lengths = words.map(fn(w) { w.length() })\n \
11341 let long = words.filter(fn(w) { w.length() > 1 })\n \
11342 let total = words.fold(0, fn(t, w) { t + w.length() })\n \
11343 let ordered = words.sorted(by: fn(a, b) { a < b })",
11344 );
11345 let error = rejects_body(" let words = [\"a\"]\n let n = words.map(fn(w) { w + 1 })");
11346 assert_eq!(error.code, OPERATOR);
11347 assert_eq!(error.message, "`+` is not defined for `String` and `Int`");
11348 }
11349
11350 #[test]
11353 fn a_walk_answers_an_array_of_what_its_callback_produced() {
11354 for (receiver, answer) in [
11355 ("let items = [1, 2]", "Array<String>"),
11356 ("var items = Vector.of(1, 2)", "Array<String>"),
11357 ] {
11358 let error = rejects_body(&format!(
11359 " {receiver}\n let n: Int = items.map(fn(v) {{ \"{{v}}\" }})"
11360 ));
11361 assert_eq!(error.code, MISMATCH);
11362 assert_eq!(error.message, format!("expected `Int`, found `{answer}`"));
11363 }
11364 let error = rejects_body(
11365 " let items = [1, 2]\n let n: Int = items.sorted(by: fn(a, b) { a < b })",
11366 );
11367 assert_eq!(error.message, "expected `Int`, found `Array<Int>`");
11368 let error = rejects_body(
11369 " var items = Vector.of(1, 2)\n let n: Int = items.filter(fn(v) { v > 1 })",
11370 );
11371 assert_eq!(error.message, "expected `Int`, found `Array<Int>`");
11372 }
11373
11374 #[test]
11377 fn folds_accumulator_is_the_type_its_initial_value_has() {
11378 accepts_body(
11379 " let items = [1, 2]\n let text = items.fold(\"\", fn(t, n) { \"{t}{n}\" })",
11380 );
11381 let error =
11382 rejects_body(" let items = [1, 2]\n let n = items.fold(0, fn(t, v) { \"{t}\" })");
11383 assert_eq!(error.code, MISMATCH);
11384 assert_eq!(error.message, "expected `Int`, found `String`");
11385 }
11386
11387 #[test]
11390 fn a_callback_takes_the_parameters_its_builtin_declares() {
11391 let error =
11392 rejects_body(" let items = [2, 1]\n let n = items.sorted(by: fn(a) { true })");
11393 assert_eq!(error.code, ARITY);
11394 assert_eq!(
11395 error.message,
11396 "this function takes 1 parameter(s), but 2 were expected here"
11397 );
11398 let error = rejects_body(" let items = [2, 1]\n let n = items.map(fn(a, b) { a })");
11399 assert_eq!(error.code, ARITY);
11400 assert_eq!(
11401 error.message,
11402 "this function takes 2 parameter(s), but 1 were expected here"
11403 );
11404 }
11405
11406 #[test]
11410 fn a_predicate_callback_must_answer_a_bool() {
11411 let error = rejects_body(" let items = [1, 2]\n let n = items.filter(fn(v) { v })");
11412 assert_eq!(error.code, MISMATCH);
11413 assert_eq!(error.message, "expected `Bool`, found `Int`");
11414 let error =
11415 rejects_body(" let items = [2, 1]\n let n = items.sorted(by: fn(a, b) { a - b })");
11416 assert_eq!(error.code, MISMATCH);
11417 assert_eq!(error.message, "expected `Bool`, found `Int`");
11418 }
11419
11420 #[test]
11430 fn a_sequence_answers_membership_position_and_a_part_of_itself() {
11431 for receiver in ["let items = [1, 2]", "var items = Vector.of(1, 2)"] {
11432 accepts_body(&format!(
11433 " {receiver}\n \
11434 let held: Bool = items.contains(1)\n \
11435 let at: Option<Int> = items.indexOf(2)\n \
11436 let first: Array<Int> = items.slice(0, 1)"
11437 ));
11438 let error = rejects_body(&format!(" {receiver}\n let n = items.contains(\"1\")"));
11439 assert_eq!(error.code, MISMATCH);
11440 assert_eq!(error.message, "expected `Int`, found `String`");
11441 let error = rejects_body(&format!(" {receiver}\n let n: Int = items.indexOf(1)"));
11442 assert_eq!(error.message, "expected `Int`, found `Option<Int>`");
11443 let error = rejects_body(&format!(" {receiver}\n let n = items.slice(0)"));
11444 assert_eq!(error.code, MISSING_ARGUMENT);
11445 }
11446 }
11447
11448 #[test]
11455 fn an_unordered_collection_answers_membership_and_not_a_position() {
11456 accepts_body(" let seen = Set.of(1, 2)\n let held: Bool = seen.contains(1)");
11457 accepts_body(
11458 " let seen = Set.of(1, 2)\n let at: Option<Int> = seen.toArray().indexOf(1)",
11459 );
11460 let error = rejects_body(" let seen = Set.of(1, 2)\n let n = seen.indexOf(1)");
11461 assert_eq!(error.code, UNKNOWN_METHOD);
11462 assert_eq!(error.message, "`Set` has no method `indexOf`");
11463 let error = rejects_body(
11464 " let ages = Map.of(MapEntry(key: \"a\", value: 1))\n let n = ages.slice(0, 1)",
11465 );
11466 assert_eq!(error.message, "`Map` has no method `slice`");
11467 }
11468
11469 #[test]
11472 fn a_vector_replaces_an_element_with_one_of_its_own_type() {
11473 accepts_body(" var items = Vector.of(1, 2)\n let was: Option<Int> = items.set(0, 9)");
11474 let error = rejects_body(" var items = Vector.of(1, 2)\n let n = items.set(0, \"9\")");
11475 assert_eq!(error.code, MISMATCH);
11476 assert_eq!(error.message, "expected `Int`, found `String`");
11477 let error = rejects_body(" var items = Vector.of(1, 2)\n let n = items.set(\"0\", 9)");
11478 assert_eq!(error.message, "expected `Int`, found `String`");
11479 let error = rejects_body(" var items = Vector.of(1, 2)\n let n: Int = items.set(0, 9)");
11480 assert_eq!(error.message, "expected `Int`, found `Option<Int>`");
11481 for receiver in ["var items = [1, 2]", "let items = [1, 2]"] {
11487 let error = rejects_body(&format!(" {receiver}\n let n = items.set(0, 9)"));
11488 assert_eq!(error.code, UNKNOWN_METHOD);
11489 assert_eq!(error.message, "`Array` has no method `set`");
11490 }
11491 }
11492
11493 #[test]
11497 fn a_vector_answers_what_it_took_out() {
11498 accepts_body(
11499 " var items = Vector.of(1, 2)\n \
11500 let last: Option<Int> = items.pop()\n \
11501 let first: Option<Int> = items.remove(0)",
11502 );
11503 let error = rejects_body(" var items = Vector.of(1, 2)\n let n: Int = items.pop()");
11504 assert_eq!(error.code, MISMATCH);
11505 assert_eq!(error.message, "expected `Int`, found `Option<Int>`");
11506 let error = rejects_body(" var items = Vector.of(1, 2)\n let n = items.remove(\"0\")");
11507 assert_eq!(error.message, "expected `Int`, found `String`");
11508 for receiver in ["let items = [1, 2]", "var items = [1, 2]"] {
11512 let error = rejects_body(&format!(" {receiver}\n let n = items.pop()"));
11513 assert_eq!(error.code, UNKNOWN_METHOD);
11514 assert_eq!(error.message, "`Array` has no method `pop`");
11515 }
11516 let error = rejects_body(" var items = Vector.of(1, 2)\n let n = items.removed(0)");
11517 assert_eq!(error.message, "`Vector` has no method `removed`");
11518 let error = rejects_body(" var items = Vector.of(1, 2)\n items.clear()");
11521 assert_eq!(error.message, "`Vector` has no method `clear`");
11522 }
11523
11524 #[test]
11527 fn rejects_a_removal_on_a_read_only_place_and_on_no_place() {
11528 for call in ["pop()", "remove(0)"] {
11529 let error = rejects(&format!(
11530 "fn run() -> Int {{\n let items = Vector.of(1)\n let n = items.{call}\n 0\n}}\n"
11531 ));
11532 assert_eq!(error.code, READ_ONLY_PLACE);
11533 assert!(
11534 error.message.ends_with("but `items` is a read-only place"),
11535 "{}",
11536 error.message
11537 );
11538 let error = rejects(&format!(
11539 "fn run() -> Int {{\n let n = Vector.of(1).{call}\n 0\n}}\n"
11540 ));
11541 assert_eq!(error.code, NOT_A_PLACE);
11542 }
11543 }
11544
11545 #[test]
11548 fn an_array_answers_a_growable_copy_of_itself() {
11549 accepts_body(" let items = [1, 2]\n var building: Vector<Int> = items.toVector()");
11550 let error = rejects_body(" let items = [1, 2]\n let n: Array<Int> = items.toVector()");
11551 assert_eq!(error.code, MISMATCH);
11552 assert_eq!(error.message, "expected `Array<Int>`, found `Vector<Int>`");
11553 let error = rejects_body(" var items = Vector.of(1, 2)\n let n = items.toVector()");
11554 assert_eq!(error.code, UNKNOWN_METHOD);
11555 assert_eq!(error.message, "`Vector` has no method `toVector`");
11556 }
11557
11558 #[test]
11561 fn rejects_set_on_a_read_only_place_and_on_no_place() {
11562 let error = rejects(
11563 "fn run() -> Int {\n let items = Vector.of(1)\n items.set(0, 2)\n items.length()\n}\n",
11564 );
11565 assert_eq!(error.code, READ_ONLY_PLACE);
11566 assert_eq!(
11567 error.message,
11568 "`set` takes a `var self` receiver, but `items` is a read-only place"
11569 );
11570 assert_eq!(error.help.unwrap(), "declare it with `var items`");
11571 let error = rejects("fn run() -> Int {\n Vector.of(1).set(0, 2)\n 0\n}\n");
11572 assert_eq!(error.code, NOT_A_PLACE);
11573 assert_eq!(
11574 error.message,
11575 "`set` takes a `var self` receiver, but `this expression` is not a place"
11576 );
11577 }
11578
11579 #[test]
11584 fn a_duration_is_built_from_a_count_and_read_back_as_one() {
11585 accepts_body(
11586 " let timeout: Duration = Duration.millis(250)\n \
11587 let whole: Duration = Duration.nanos(1) + Duration.micros(1) + \
11588 Duration.seconds(1) + Duration.minutes(1) + Duration.hours(1)\n \
11589 let back: Int = timeout.millis()\n \
11590 let coarse: Int = whole.seconds()",
11591 );
11592 let error = rejects_body(" let d = Duration.millis(1s)");
11595 assert_eq!(error.code, MISMATCH);
11596 assert_eq!(error.message, "expected `Int`, found `Duration`");
11597 let error = rejects_body(" let n: Int = Duration.seconds(1)");
11598 assert_eq!(error.message, "expected `Int`, found `Duration`");
11599 let error = rejects_body(" let d = 1s\n let n: Duration = d.seconds()");
11600 assert_eq!(error.message, "expected `Duration`, found `Int`");
11601 }
11602
11603 #[test]
11605 fn a_duration_has_only_the_units_a_literal_is_written_in() {
11606 let error = rejects_body(" let d = Duration.weeks(1)");
11607 assert_eq!(error.code, UNKNOWN_ASSOCIATED);
11608 assert_eq!(
11609 error.message,
11610 "`Duration` has no associated function `weeks`"
11611 );
11612 let error = rejects_body(" let d = 1s\n let n = d.weeks()");
11613 assert_eq!(error.code, UNKNOWN_METHOD);
11614 assert_eq!(error.message, "`Duration` has no method `weeks`");
11615 assert_eq!(
11616 error.help.unwrap(),
11617 "`Duration` has `nanos`, `micros`, `millis`, `seconds`, `minutes`, `hours`, `snapshot`"
11618 );
11619 }
11620
11621 #[test]
11623 fn only_a_sequence_walks_with_a_closure() {
11624 let error = rejects_body(" let ages = Set.of(1, 2)\n let n = ages.map(fn(v) { v })");
11625 assert_eq!(error.code, UNKNOWN_METHOD);
11626 assert_eq!(error.message, "`Set` has no method `map`");
11627 }
11628
11629 #[test]
11632 fn a_receiver_that_has_no_length_is_not_taught_the_spelling() {
11633 let error = rejects_body(" let value = Some(1)\n let n = value.count()");
11634 assert_eq!(error.code, UNKNOWN_METHOD);
11635 assert_eq!(error.message, "`Option` has no method `count`");
11636 }
11637
11638 #[test]
11639 fn rejects_a_builtin_method_that_does_not_exist() {
11640 let error = rejects_body(" println(\"{\"text\".scream()}\")?");
11641 assert_eq!(error.code, UNKNOWN_METHOD);
11642 assert_eq!(error.message, "`String` has no method `scream`");
11643 assert_eq!(
11644 error.help.unwrap(),
11645 "`String` has `length`, `isEmpty`, `words`, `chars`, `split`, `join`, `slice`, \
11646 `trim`, `contains`, `startsWith`, `endsWith`, `indexOf`, `replace`, `toUpper`, \
11647 `toLower`, `byteLength`, `byteAt`, `codePointAtByte`, `sliceBytes`, \
11648 `snapshot`"
11649 );
11650 }
11651
11652 #[test]
11658 fn the_methods_a_diagnostic_lists_are_the_ones_the_table_declares() {
11659 let error = rejects_body(" let outcome = Ok(1)\n println(\"{outcome.unwrap()}\")?");
11660 assert_eq!(error.code, UNKNOWN_METHOD);
11661 assert_eq!(error.message, "`Result` has no method `unwrap`");
11662 assert_eq!(
11663 error.help.unwrap(),
11664 "`Result` has `isOk`, `isError`, `unwrapOr`, `mapError`"
11665 );
11666 }
11667
11668 #[test]
11671 fn a_shared_is_told_that_lock_is_what_it_has() {
11672 let error = rejects_body(" let counts = Shared(1)\n let value = counts.get()");
11673 assert_eq!(error.help.unwrap(), "`Shared` has `lock`");
11674 }
11675
11676 #[test]
11679 fn an_unknown_associated_function_names_the_ones_that_exist() {
11680 let error = rejects_body(" let items = Array.of(1)");
11681 assert_eq!(error.code, UNKNOWN_ASSOCIATED);
11682 assert_eq!(error.message, "`Array` has no associated function `of`");
11683 assert_eq!(
11684 error.rule.unwrap(),
11685 "A builtin type's associated functions are `Vector.of`, `Map.of`, `Set.of`, `String.fromCodePoint`, `Int.parse`, `Int.parseRadix`, `Float.parse`, `Duration.nanos`, `Duration.micros`, `Duration.millis`, `Duration.seconds`, `Duration.minutes`, and `Duration.hours`."
11686 );
11687 }
11688
11689 #[test]
11692 fn checks_an_argument_against_its_parameter() {
11693 let error = rejects(
11694 "\
11695fn greet(name: String) -> String {
11696 name
11697}
11698
11699fn run() -> String {
11700 greet(42)
11701}
11702",
11703 );
11704 assert_eq!(error.code, MISMATCH);
11705 assert_eq!(error.message, "expected `String`, found `Int`");
11706 assert_eq!(error.labels[0].message, "the parameter `name` is `String`");
11707 assert_eq!(
11708 error.help.unwrap(),
11709 "interpolate the `Int`, as in \"{value}\", to make a `String`"
11710 );
11711 }
11712
11713 #[test]
11714 fn rejects_too_many_arguments() {
11715 let error = rejects(
11716 "\
11717fn greet(name: String) -> String {
11718 name
11719}
11720
11721fn run() -> String {
11722 greet(\"a\", \"b\")
11723}
11724",
11725 );
11726 assert_eq!(error.code, ARITY);
11727 assert_eq!(
11728 error.message,
11729 "`greet` takes 1 argument(s), but more were given"
11730 );
11731 assert_eq!(
11732 error.rule.unwrap(),
11733 "A call passes exactly the arguments the declaration binds."
11734 );
11735 assert_eq!(error.help.unwrap(), "`greet` declares `name`");
11736 }
11737
11738 #[test]
11739 fn rejects_a_label_that_names_no_parameter() {
11740 let error = rejects(
11741 "\
11742fn between(low: Int, high: Int) -> Int {
11743 high - low
11744}
11745
11746fn run() -> Int {
11747 between(low: 1, top: 2)
11748}
11749",
11750 );
11751 assert_eq!(error.code, UNKNOWN_LABEL);
11752 assert_eq!(error.message, "`between` has no parameter labeled `top`");
11753 assert_eq!(
11754 error.rule.unwrap(),
11755 "Argument labels are parameter names and part of the API contract."
11756 );
11757 assert_eq!(error.help.unwrap(), "known labels: `low`, `high`");
11758 }
11759
11760 #[test]
11761 fn labels_bind_arguments_to_the_parameters_they_name() {
11762 accepts(
11763 "\
11764fn between(low: Int, high: Int) -> Int {
11765 high - low
11766}
11767
11768fn run() -> Int {
11769 between(low: 1, high: 2)
11770}
11771",
11772 );
11773 }
11774
11775 #[test]
11776 fn rejects_labels_that_stand_out_of_declaration_order() {
11777 let error = rejects(
11778 "\
11779fn between(low: Int, high: Int) -> Int {
11780 high - low
11781}
11782
11783fn run() -> Int {
11784 between(high: 2, low: 1)
11785}
11786",
11787 );
11788 assert_eq!(error.code, LABEL_ORDER);
11789 assert_eq!(
11790 error.message,
11791 "`between` was given the label `low` out of declaration order"
11792 );
11793 assert_eq!(
11794 error.rule.unwrap(),
11795 "Labeled arguments appear in declaration order, so argument order matches parameter order."
11796 );
11797 assert_eq!(
11798 error.help.unwrap(),
11799 "write the arguments in this order: low, high"
11800 );
11801 }
11802
11803 #[test]
11807 fn rejects_a_struct_initializer_whose_labels_are_out_of_order() {
11808 let error = rejects(
11809 "\
11810struct Point {
11811 x: Int
11812 y: Int
11813}
11814
11815fn run() -> Point {
11816 Point(y: 20, x: 10)
11817}
11818",
11819 );
11820 assert_eq!(error.code, LABEL_ORDER);
11821 assert_eq!(
11822 error.message,
11823 "`Point` was given the label `x` out of declaration order"
11824 );
11825 }
11826
11827 #[test]
11830 fn a_label_written_twice_is_one_diagnostic() {
11831 let error = rejects(
11832 "\
11833fn between(low: Int, high: Int) -> Int {
11834 high - low
11835}
11836
11837fn run() -> Int {
11838 between(low: 1, low: 2)
11839}
11840",
11841 );
11842 assert_eq!(error.code, MISSING_ARGUMENT);
11843 }
11844
11845 #[test]
11853 fn rejects_an_assignment_to_a_let_binding() {
11854 let error = rejects("fn run() -> Int {\n let x = 1\n x = 2\n x\n}\n");
11855 assert_eq!(error.code, READ_ONLY_PLACE);
11856 assert_eq!(
11857 error.message,
11858 "cannot assign to `x`, which is a read-only place"
11859 );
11860 assert_eq!(
11861 error.rule.unwrap(),
11862 "`let` creates a read-only place; `var` creates a mutable place."
11863 );
11864 assert_eq!(
11865 error.help.unwrap(),
11866 "declare it with `var x` to make it assignable"
11867 );
11868 }
11869
11870 #[test]
11873 fn rejects_an_assignment_to_a_parameter_that_is_not_var() {
11874 let error = rejects("fn run(n: Int) -> Int {\n n = 2\n n\n}\n");
11875 assert_eq!(error.code, READ_ONLY_PLACE);
11876 assert_eq!(
11877 error.message,
11878 "cannot assign to `n`, which is a read-only place"
11879 );
11880 }
11881
11882 #[test]
11885 fn rejects_an_assignment_to_a_field_of_a_let_binding() {
11886 let error = rejects(
11887 "struct P {\n x: Int\n}\n\nfn run() -> Int {\n let p = P(x: 1)\n p.x = 2\n p.x\n}\n",
11888 );
11889 assert_eq!(error.code, READ_ONLY_PLACE);
11890 assert_eq!(
11891 error.message,
11892 "cannot assign to `p.x`, which is a read-only place"
11893 );
11894 }
11895
11896 #[test]
11899 fn accepts_a_write_to_a_var_binding_and_to_its_fields() {
11900 accepts(
11901 "struct P {\n x: Int\n}\n\nfn bump(var n: Int) {\n n += 1\n}\n\nfn run() -> Int {\n var p = P(x: 1)\n p.x = 2\n var n = 0\n n += 1\n bump(var n)\n p.x + n\n}\n",
11902 );
11903 }
11904
11905 #[test]
11909 fn rejects_an_assignment_to_a_captured_var_binding() {
11910 let error = rejects(
11911 "fn run() -> Int {\n var count = 0\n let bump = fn() {\n count = count + 1\n }\n count\n}\n",
11912 );
11913 assert_eq!(error.code, READ_ONLY_PLACE);
11914 assert_eq!(
11915 error.message,
11916 "cannot assign to `count`, which is a read-only place"
11917 );
11918 }
11919
11920 #[test]
11922 fn rejects_an_assignment_to_a_var_captured_by_a_local_fn() {
11923 let error = rejects(
11924 "fn run() -> Int {\n var count = 0\n fn bump() {\n count = count + 1\n }\n count\n}\n",
11925 );
11926 assert_eq!(error.code, READ_ONLY_PLACE);
11927 }
11928
11929 #[test]
11930 fn rejects_a_var_argument_that_is_a_read_only_place() {
11931 let error = rejects(
11932 "fn bump(var n: Int) {\n n += 1\n}\n\nfn run() -> Int {\n let total = 1\n bump(var total)\n total\n}\n",
11933 );
11934 assert_eq!(error.code, READ_ONLY_PLACE);
11935 assert_eq!(
11936 error.message,
11937 "`total` is a read-only place, so it cannot be passed as `var`"
11938 );
11939 }
11940
11941 #[test]
11942 fn rejects_a_var_argument_that_is_not_a_place() {
11943 let error = rejects(
11944 "fn bump(var n: Int) {\n n += 1\n}\n\nfn run() -> Int {\n bump(var 1 + 2)\n 0\n}\n",
11945 );
11946 assert_eq!(error.code, NOT_A_PLACE);
11947 assert_eq!(
11948 error.message,
11949 "this expression is not a place, so it cannot be assigned or aliased"
11950 );
11951 assert_eq!(
11952 error.rule.unwrap(),
11953 "Only variables and their struct fields are places."
11954 );
11955 }
11956
11957 #[test]
11958 fn rejects_push_on_a_read_only_place() {
11959 let error = rejects(
11960 "fn run() -> Int {\n let items = Vector.of(1)\n items.push(2)\n items.length()\n}\n",
11961 );
11962 assert_eq!(error.code, READ_ONLY_PLACE);
11963 assert_eq!(
11964 error.message,
11965 "`push` takes a `var self` receiver, but `items` is a read-only place"
11966 );
11967 assert_eq!(error.help.unwrap(), "declare it with `var items`");
11968 }
11969
11970 #[test]
11971 fn rejects_push_on_a_receiver_that_is_not_a_place() {
11972 let error = rejects("fn run() -> () {\n Vector.of(1).push(2)\n}\n");
11973 assert_eq!(error.code, NOT_A_PLACE);
11974 assert_eq!(
11975 error.message,
11976 "`push` takes a `var self` receiver, but `this expression` is not a place"
11977 );
11978 assert_eq!(
11979 error.rule.unwrap(),
11980 "A mutating receiver declares `var self` and mutates the caller's place."
11981 );
11982 }
11983
11984 #[test]
11989 fn freeze_needs_a_writable_place_only_when_it_has_one() {
11990 accepts("fn run() -> Int {\n Vector.of(1).freeze().length()\n}\n");
11991 let error =
11992 rejects("fn run() -> Int {\n let v = Vector.of(1)\n v.freeze().length()\n}\n");
11993 assert_eq!(error.code, READ_ONLY_PLACE);
11994 assert_eq!(
11995 error.message,
11996 "`freeze` takes a `var self` receiver, but `v` is a read-only place"
11997 );
11998 }
11999
12000 #[test]
12003 fn rejects_a_var_self_method_on_a_read_only_place() {
12004 let error = rejects(
12005 "struct Counter {\n value: Int\n}\n\nimpl Counter {\n fn bump(var self) {\n self.value += 1\n }\n}\n\nfn run() -> Int {\n let counter = Counter(value: 1)\n counter.bump()\n counter.value\n}\n",
12006 );
12007 assert_eq!(error.code, READ_ONLY_PLACE);
12008 assert_eq!(
12009 error.message,
12010 "`bump` takes a `var self` receiver, but `counter` is a read-only place"
12011 );
12012 }
12013
12014 #[test]
12018 fn rejects_a_mutating_method_on_a_lock_closures_copy() {
12019 let source = "struct Counter {\n value: Int\n}\n\nimpl Counter {\n fn bump(var self) {\n self.value += 1\n }\n}\n\nfn run() -> () {\n let shared = Shared(Counter(value: 0))\n shared.lock(fn(value) {\n value.bump()\n })\n}\n";
12020 let error = rejects(source);
12021 assert_eq!(error.code, READ_ONLY_PLACE);
12022 assert_eq!(
12023 error.message,
12024 "`bump` takes a `var self` receiver, but `value` is a read-only place"
12025 );
12026 accepts(&source.replace("fn(value)", "fn(var value)"));
12027 }
12028
12029 #[test]
12033 fn abstains_about_a_mutating_method_on_an_unknown_receiver() {
12034 accepts(
12035 "use unknownhost.open\n\nfn run() -> () {\n let handle = open()\n handle.push(1)\n}\n",
12036 );
12037 }
12038
12039 #[test]
12040 fn a_parameter_with_a_default_may_be_omitted() {
12041 accepts(
12042 "\
12043fn measure(value: Int, unit: String = \"m\") -> String {
12044 \"{value}{unit}\"
12045}
12046
12047fn run() -> String {
12048 measure(3)
12049}
12050",
12051 );
12052 let error = rejects(
12053 "\
12054fn measure(value: Int, unit: String = 1) -> String {
12055 \"{value}{unit}\"
12056}
12057",
12058 );
12059 assert_eq!(error.code, MISMATCH);
12060 assert_eq!(error.message, "expected `String`, found `Int`");
12061 }
12062
12063 #[test]
12064 fn checks_a_variadic_parameter_and_its_spread() {
12065 accepts(
12066 "\
12067fn joinAll(separator: String, items: String...) -> Int {
12068 items.length()
12069}
12070
12071fn run() -> Int {
12072 let ready = [\"x\"]
12073 joinAll(\"-\", \"a\", ...ready)
12074}
12075",
12076 );
12077 let error = rejects(
12078 "\
12079fn joinAll(items: String...) -> Int {
12080 items.length()
12081}
12082
12083fn run() -> Int {
12084 joinAll(\"a\", 2)
12085}
12086",
12087 );
12088 assert_eq!(error.code, MISMATCH);
12089 assert_eq!(error.message, "expected `String`, found `Int`");
12090 }
12091
12092 #[test]
12093 fn a_declaration_parameter_without_a_type_is_refused() {
12094 let error = rejects(
12097 "\
12098fn double(x) -> Int {
12099 x + x
12100}
12101",
12102 );
12103 assert_eq!(error.code, MISSING_PARAMETER_TYPE);
12104 assert_eq!(error.message, "parameter `x` has no declared type");
12105 assert_eq!(
12106 error.rule.unwrap(),
12107 "A declaration's parameters are written: only a lambda's infer, from the expected type at its call site."
12108 );
12109 assert_eq!(error.help.unwrap(), "write `x: <type>`");
12110 }
12111
12112 #[test]
12113 fn a_lambda_parameter_without_a_type_still_infers() {
12114 let error = rejects_body(" let double = fn(n) { n + n }\n println(\"{double(1)}\")?");
12123 assert_eq!(error.code, UNCONSTRAINED);
12124 }
12125
12126 #[test]
12127 fn rejects_a_spread_of_the_wrong_element_type() {
12128 let error = rejects(
12129 "\
12130fn joinAll(items: String...) -> Int {
12131 items.length()
12132}
12133
12134fn run() -> Int {
12135 joinAll(...[1, 2])
12136}
12137",
12138 );
12139 assert_eq!(error.code, MISMATCH);
12140 assert_eq!(error.message, "expected `String`, found `Int`");
12141 assert_eq!(
12142 error.rule.unwrap(),
12143 "A variadic parameter is an `Array<T>`; every spread element is a `T`."
12144 );
12145 assert_eq!(error.help.unwrap(), "spread a sequence of `String`");
12146 }
12147
12148 #[test]
12149 fn rejects_calling_something_that_is_not_a_function() {
12150 let error = rejects_body(" let n = 1\n println(\"{n(2)}\")?");
12151 assert_eq!(error.code, NOT_CALLABLE);
12152 assert_eq!(error.message, "`Int` is not a function");
12153 assert_eq!(error.rule.unwrap(), "Only a function value can be called.");
12154 }
12155
12156 #[test]
12157 fn rejects_calling_an_enum_rather_than_a_case() {
12158 let error = rejects(
12159 "\
12160enum Status {
12161 Pending
12162}
12163
12164fn run() -> Status {
12165 Status(1)
12166}
12167",
12168 );
12169 assert_eq!(error.code, NOT_CALLABLE);
12170 assert_eq!(error.message, "`Status` is an enum, not a function");
12171 assert_eq!(error.help.unwrap(), "name a case, such as `Status.Pending`");
12172 }
12173
12174 const TRAITS: &str = "\
12179/// Renders itself.
12180trait Display {
12181 /// The full form.
12182 fn describe(self) -> String
12183
12184 /// A short form, defaulting to the full one.
12185 fn label(self) -> String { self.describe() }
12186}
12187
12188/// A booking.
12189struct Booking(id: Int)
12190
12191/// A receipt.
12192struct Receipt(total: Int)
12193
12194/// Conforms to nothing.
12195struct Ticket(seat: Int)
12196
12197impl Display for Booking {
12198 fn describe(self) -> String { \"booking\" }
12199 fn label(self) -> String { \"#\" }
12200}
12201
12202impl Display for Receipt {
12203 fn describe(self) -> String { \"receipt\" }
12204}
12205";
12206
12207 fn with_traits(source: &str) -> String {
12208 format!("{TRAITS}\n{source}")
12209 }
12210
12211 #[track_caller]
12212 fn accepts_with_traits(source: &str) {
12213 accepts(&with_traits(source));
12214 }
12215
12216 #[track_caller]
12217 fn rejects_with_traits(source: &str) -> Diagnostic {
12218 rejects(&with_traits(source))
12219 }
12220
12221 #[test]
12222 fn a_bound_makes_the_trait_s_methods_callable_on_a_type_parameter() {
12223 accepts_with_traits(
12224 "fn render<T: Display>(value: T) -> String {\n \"{value.label()}: {value.describe()}\"\n}\n\nfn run() -> String {\n render(Booking(id: 1))\n}\n",
12225 );
12226 }
12227
12228 #[test]
12229 fn rejects_a_type_argument_that_does_not_conform_to_the_bound() {
12230 let error = rejects_with_traits(
12231 "fn render<T: Display>(value: T) -> String {\n value.describe()\n}\n\nfn run() -> String {\n render(Ticket(seat: 1))\n}\n",
12232 );
12233 assert_eq!(error.code, UNSATISFIED_BOUND);
12234 assert_eq!(error.message, "`Ticket` does not conform to `Display`");
12235 assert_eq!(error.labels[0].message, "`render` requires `T: Display`");
12236 assert_eq!(
12237 error.help.as_deref(),
12238 Some("write `impl Display for Ticket { ... }`")
12239 );
12240 }
12241
12242 #[test]
12243 fn several_bounds_are_all_checked_and_all_searched_for_a_method() {
12244 let source = "\
12245/// Names itself.
12246trait Named {
12247 /// The name.
12248 fn name(self) -> String
12249}
12250
12251/// Weighs itself.
12252trait Weighed {
12253 /// The weight.
12254 fn weight(self) -> Int
12255}
12256
12257/// A crate.
12258struct Crate(label: String, kilos: Int)
12259
12260/// A pebble, which is named but not weighed.
12261struct Pebble(label: String)
12262
12263impl Named for Crate {
12264 fn name(self) -> String { self.label }
12265}
12266
12267impl Weighed for Crate {
12268 fn weight(self) -> Int { self.kilos }
12269}
12270
12271impl Named for Pebble {
12272 fn name(self) -> String { self.label }
12273}
12274
12275fn tag<T: Named + Weighed>(item: T) -> String {
12276 \"{item.name()}({item.weight()})\"
12277}
12278
12279fn ok() -> String {
12280 tag(Crate(label: \"a\", kilos: 3))
12281}
12282";
12283 accepts(source);
12284 let error = rejects(&format!(
12285 "{source}\nfn bad() -> String {{\n tag(Pebble(label: \"b\"))\n}}\n"
12286 ));
12287 assert_eq!(error.code, UNSATISFIED_BOUND);
12288 assert_eq!(error.message, "`Pebble` does not conform to `Weighed`");
12289 }
12290
12291 #[test]
12292 fn rejects_a_method_call_on_an_unbounded_type_parameter() {
12293 let error =
12294 rejects_with_traits("fn render<T>(value: T) -> String {\n value.describe()\n}\n");
12295 assert_eq!(error.code, UNBOUNDED_PARAMETER);
12296 assert_eq!(
12297 error.message,
12298 "`T` has no bound, so it has no method `describe`"
12299 );
12300 }
12301
12302 #[test]
12303 fn rejects_a_method_no_bound_of_the_parameter_declares() {
12304 let error =
12305 rejects_with_traits("fn render<T: Display>(value: T) -> Int {\n value.total()\n}\n");
12306 assert_eq!(error.code, UNKNOWN_METHOD);
12307 assert_eq!(
12308 error.message,
12309 "no trait `T` is bounded by declares a method `total`"
12310 );
12311 }
12312
12313 #[test]
12314 fn one_bounded_function_may_call_another() {
12315 accepts_with_traits(
12316 "fn render<T: Display>(value: T) -> String {\n value.describe()\n}\n\nfn shout<U: Display>(value: U) -> String {\n render(value)\n}\n",
12317 );
12318 }
12319
12320 #[test]
12321 fn a_conforming_value_is_accepted_where_dyn_is_expected() {
12322 accepts_with_traits(
12323 "fn show(value: dyn Display) -> String {\n value.describe()\n}\n\nfn run() -> String {\n show(Booking(id: 1))\n}\n",
12324 );
12325 }
12326
12327 #[test]
12328 fn rejects_a_value_that_does_not_conform_where_dyn_is_expected() {
12329 let error = rejects_with_traits(
12330 "fn show(value: dyn Display) -> String {\n value.describe()\n}\n\nfn run() -> String {\n show(Ticket(seat: 1))\n}\n",
12331 );
12332 assert_eq!(error.code, MISMATCH);
12333 assert_eq!(
12334 error.message,
12335 "`Ticket` does not conform to `Display`, so it is not a `dyn Display`"
12336 );
12337 }
12338
12339 #[test]
12340 fn an_array_of_dyn_mixes_conforming_types_element_by_element() {
12341 accepts_with_traits(
12345 "fn run() -> Array<dyn Display> {\n [Booking(id: 1), Receipt(total: 2)]\n}\n",
12346 );
12347 let error = rejects_with_traits(
12348 "fn run(bookings: Array<Booking>) -> Array<dyn Display> {\n bookings\n}\n",
12349 );
12350 assert_eq!(error.code, MISMATCH);
12351 assert_eq!(
12352 error.message,
12353 "expected `Array<dyn Display>`, found `Array<Booking>`"
12354 );
12355 }
12356
12357 #[test]
12358 fn dyn_is_not_a_type_parameter_and_satisfies_no_bound() {
12359 let error = rejects_with_traits(
12360 "fn render<T: Display>(value: T) -> String {\n value.describe()\n}\n\nfn run(value: dyn Display) -> String {\n render(value)\n}\n",
12361 );
12362 assert_eq!(error.code, UNSATISFIED_BOUND);
12363 assert_eq!(
12364 error.message,
12365 "`dyn Display` cannot be used as a type argument"
12366 );
12367 }
12368
12369 #[test]
12370 fn a_dyn_value_does_not_convert_back_to_its_concrete_type() {
12371 let error = rejects_with_traits("fn run(value: dyn Display) -> Booking {\n value\n}\n");
12372 assert_eq!(error.code, MISMATCH);
12373 assert_eq!(error.message, "expected `Booking`, found `dyn Display`");
12374 }
12375
12376 #[test]
12377 fn only_the_trait_s_methods_are_reachable_through_dyn() {
12378 let source = format!(
12379 "{TRAITS}\nimpl Booking {{\n /// The identifier.\n fn id(self) -> Int {{ self.id }}\n}}\n\nfn run(value: dyn Display) -> Int {{\n value.id()\n}}\n"
12380 );
12381 let error = rejects(&source);
12382 assert_eq!(error.code, UNKNOWN_METHOD);
12383 assert_eq!(error.message, "`Display` has no method `id`");
12384 assert_eq!(
12385 error.help.as_deref(),
12386 Some("`Display` declares `describe`, `label`")
12387 );
12388 }
12389
12390 #[test]
12391 fn an_associated_function_is_not_callable_through_dyn() {
12392 let source = "\
12393/// Renders itself.
12394trait Display {
12395 /// The full form.
12396 fn describe(self) -> String
12397
12398 /// Builds one.
12399 fn blank() -> Int
12400}
12401
12402/// A booking.
12403struct Booking(id: Int)
12404
12405impl Display for Booking {
12406 fn describe(self) -> String { \"booking\" }
12407 fn blank() -> Int { 0 }
12408}
12409
12410fn run(value: dyn Display) -> Int {
12411 value.blank()
12412}
12413";
12414 let error = rejects(source);
12415 assert_eq!(error.code, DYN_ASSOCIATED);
12416 assert_eq!(
12417 error.message,
12418 "`Display.blank` takes no `self`, so it cannot be called through `dyn Display`"
12419 );
12420 }
12421
12422 #[test]
12423 fn a_mutating_method_is_not_callable_through_dyn() {
12424 let source = "\
12425/// Counts.
12426trait Bump {
12427 /// Adds one.
12428 fn bump(var self)
12429}
12430
12431/// A counter.
12432struct Counter(hits: Int)
12433
12434impl Bump for Counter {
12435 fn bump(var self) { self.hits += 1 }
12436}
12437
12438fn run(var value: dyn Bump) {
12439 value.bump()
12440}
12441";
12442 let error = rejects(source);
12443 assert_eq!(error.code, DYN_MUTATING);
12444 assert_eq!(
12445 error.message,
12446 "`Bump.bump` takes `var self`, so it cannot be called through `dyn Bump`"
12447 );
12448 }
12449
12450 #[test]
12451 fn a_mutating_method_is_callable_through_a_bound() {
12452 accepts(
12455 "\
12456/// Counts.
12457trait Bump {
12458 /// Adds one.
12459 fn bump(var self)
12460}
12461
12462/// A counter.
12463struct Counter(hits: Int)
12464
12465impl Bump for Counter {
12466 fn bump(var self) { self.hits += 1 }
12467}
12468
12469fn run<T: Bump>(var value: T) {
12470 value.bump()
12471}
12472",
12473 );
12474 }
12475
12476 #[test]
12477 fn a_trait_method_call_is_checked_against_the_trait_s_signature() {
12478 let error = rejects_with_traits(
12479 "fn render<T: Display>(value: T) -> String {\n value.describe(1)\n}\n",
12480 );
12481 assert_eq!(error.code, ARITY);
12482 }
12483
12484 #[test]
12485 fn rejects_a_conformance_whose_method_has_the_wrong_signature() {
12486 let source = "\
12487/// Renders itself.
12488trait Display {
12489 /// The full form.
12490 fn describe(self) -> String
12491}
12492
12493/// A booking.
12494struct Booking(id: Int)
12495
12496impl Display for Booking {
12497 fn describe(self) -> Int { 1 }
12498}
12499";
12500 let error = rejects(source);
12501 assert_eq!(error.code, CONFORMANCE_SIGNATURE);
12502 assert_eq!(
12503 error.message,
12504 "`Booking.describe` does not match the signature `Display` declares: it returns `Int`, not `String`"
12505 );
12506 assert_eq!(
12507 error.help.as_deref(),
12508 Some("write `fn describe(self) -> String`")
12509 );
12510 }
12511
12512 #[test]
12513 fn a_default_body_sees_its_trait_and_nothing_of_the_conforming_type() {
12514 let source = "\
12518/// Renders itself.
12519trait Summary {
12520 /// The tag.
12521 fn tag(self) -> Int
12522
12523 /// A line, which reaches for a field no trait declares.
12524 fn line(self) -> String { \"{self.id}\" }
12525}
12526
12527/// A booking.
12528struct Booking(id: Int)
12529
12530impl Summary for Booking {
12531 fn tag(self) -> Int { self.id }
12532}
12533";
12534 let error = rejects(source);
12535 assert_eq!(error.code, UNKNOWN_FIELD);
12536 assert_eq!(error.message, "`Self` has no field `id`");
12537 }
12538
12539 #[test]
12540 fn a_default_body_may_call_the_trait_s_own_methods() {
12541 accepts_with_traits("fn run(value: Booking) -> String {\n value.label()\n}\n");
12542 }
12543
12544 #[test]
12545 fn a_default_body_is_reported_once_however_many_types_conform() {
12546 let source = "\
12547/// Renders itself.
12548trait Summary {
12549 /// The tag.
12550 fn tag(self) -> Int
12551
12552 /// A line whose body does not type-check.
12553 fn line(self) -> String { self.tag() }
12554}
12555
12556/// A booking.
12557struct Booking(id: Int)
12558
12559/// A receipt.
12560struct Receipt(cents: Int)
12561
12562impl Summary for Booking {
12563 fn tag(self) -> Int { self.id }
12564}
12565
12566impl Summary for Receipt {
12567 fn tag(self) -> Int { self.cents }
12568}
12569";
12570 let error = rejects(source);
12571 assert_eq!(error.code, MISMATCH);
12572 assert_eq!(error.message, "expected `String`, found `Int`");
12573 }
12574
12575 #[test]
12576 fn rejects_a_dyn_or_a_bound_that_names_no_trait() {
12577 let error = rejects("fn run(value: dyn Missing) {\n}\n");
12578 assert_eq!(error.code, UNKNOWN_TRAIT);
12579 let error = rejects("fn run<T: Missing>(value: T) {\n}\n");
12580 assert_eq!(error.code, UNKNOWN_TRAIT);
12581 }
12582
12583 #[test]
12584 fn rejects_a_bound_where_the_mvp_never_checks_one() {
12585 let source = with_traits("struct Box<T: Display>(value: T)\n");
12586 let error = rejects(&source);
12587 assert_eq!(error.code, UNSUPPORTED_BOUND);
12588 assert_eq!(
12589 error.message,
12590 "a bound on a struct's type parameter is not checked in the MVP"
12591 );
12592 }
12593
12594 #[test]
12597 fn unifies_a_type_parameter_at_the_call_site() {
12598 accepts(
12599 "\
12600fn first<T>(items: Array<T>, fallback: T) -> T {
12601 items.get(0).unwrapOr(fallback)
12602}
12603
12604fn run() -> Int {
12605 first([1, 2], 0)
12606}
12607",
12608 );
12609 }
12610
12611 #[test]
12612 fn rejects_a_type_parameter_used_at_two_types() {
12613 let error = rejects(
12614 "\
12615fn pair<T>(left: T, right: T) -> T {
12616 left
12617}
12618
12619fn run() -> Int {
12620 pair(1, \"two\")
12621}
12622",
12623 );
12624 assert_eq!(error.code, MISMATCH);
12625 assert_eq!(error.message, "expected `Int`, found `String`");
12626 }
12627
12628 #[test]
12629 fn substitutes_a_type_parameter_into_the_result() {
12630 let error = rejects(
12631 "\
12632fn identity<T>(value: T) -> T {
12633 value
12634}
12635
12636fn run() -> String {
12637 identity(1)
12638}
12639",
12640 );
12641 assert_eq!(error.code, MISMATCH);
12642 assert_eq!(error.message, "expected `String`, found `Int`");
12643 }
12644
12645 #[test]
12646 fn checks_a_generic_struct_s_fields_through_its_arguments() {
12647 accepts(
12648 "\
12649struct Box<T> { value: T }
12650
12651fn unwrap(box: Box<Int>) -> Int {
12652 box.value
12653}
12654",
12655 );
12656 let error = rejects(
12657 "\
12658struct Box<T> { value: T }
12659
12660fn unwrap(box: Box<String>) -> Int {
12661 box.value
12662}
12663",
12664 );
12665 assert_eq!(error.code, MISMATCH);
12666 assert_eq!(error.message, "expected `Int`, found `String`");
12667 }
12668
12669 #[test]
12670 fn a_generic_enum_takes_its_arguments_from_its_payload() {
12671 accepts(
12672 "\
12673enum Slot<T> {
12674 Full(T)
12675 Empty
12676}
12677
12678fn run() -> Slot<Int> {
12679 Slot.Full(1)
12680}
12681",
12682 );
12683 let error = rejects(
12684 "\
12685enum Slot<T> {
12686 Full(T)
12687 Empty
12688}
12689
12690fn run() -> Slot<Int> {
12691 Slot.Full(\"one\")
12692}
12693",
12694 );
12695 assert_eq!(error.code, MISMATCH);
12696 assert_eq!(error.message, "expected `Slot<Int>`, found `Slot<String>`");
12697 }
12698
12699 #[test]
12700 fn a_generic_type_s_method_sees_its_arguments() {
12701 accepts(
12702 "\
12703struct Slot<T> { value: T }
12704
12705impl Slot {
12706 fn get(self) -> T {
12707 self.value
12708 }
12709}
12710
12711fn run(slot: Slot<Int>) -> Int {
12712 slot.get()
12713}
12714",
12715 );
12716 let error = rejects(
12717 "\
12718struct Slot<T> { value: T }
12719
12720impl Slot {
12721 fn get(self) -> T {
12722 self.value
12723 }
12724}
12725
12726fn run(slot: Slot<String>) -> Int {
12727 slot.get()
12728}
12729",
12730 );
12731 assert_eq!(error.code, MISMATCH);
12732 assert_eq!(error.message, "expected `Int`, found `String`");
12733 }
12734
12735 #[test]
12736 fn rejects_the_wrong_number_of_type_arguments() {
12737 let error = rejects("fn run(items: Array<Int, String>) -> Int {\n 1\n}\n");
12738 assert_eq!(error.code, TYPE_ARGUMENTS);
12739 assert_eq!(
12740 error.message,
12741 "`Array` takes 1 type argument(s), but 2 were written"
12742 );
12743 assert_eq!(
12744 error.rule.unwrap(),
12745 "A generic type is written with exactly the arguments its declaration binds."
12746 );
12747 assert_eq!(error.help.unwrap(), "write `Array<_>`");
12748 }
12749
12750 #[test]
12753 fn a_lambda_takes_its_parameter_types_from_the_expected_type() {
12754 accepts(
12755 "\
12756fn apply(value: Int, transform: fn(Int) -> Int) -> Int {
12757 transform(value)
12758}
12759
12760fn run() -> Int {
12761 apply(5, fn(n) { n + 1 })
12762}
12763",
12764 );
12765 }
12766
12767 #[test]
12768 fn rejects_a_lambda_whose_result_does_not_fit() {
12769 let error = rejects(
12770 "\
12771fn apply(value: Int, transform: fn(Int) -> Int) -> Int {
12772 transform(value)
12773}
12774
12775fn run() -> Int {
12776 apply(5, fn(n) { \"{n}\" })
12777}
12778",
12779 );
12780 assert_eq!(error.code, MISMATCH);
12781 assert_eq!(error.message, "expected `Int`, found `String`");
12782 }
12783
12784 #[test]
12785 fn rejects_a_lambda_with_the_wrong_number_of_parameters() {
12786 let error = rejects(
12787 "\
12788fn apply(transform: fn(Int) -> Int) -> Int {
12789 transform(1)
12790}
12791
12792fn run() -> Int {
12793 apply(fn(a, b) { a })
12794}
12795",
12796 );
12797 assert_eq!(error.code, ARITY);
12798 assert_eq!(
12799 error.message,
12800 "this function takes 2 parameter(s), but 1 were expected here"
12801 );
12802 assert_eq!(error.help.unwrap(), "write `fn(p0) { ... }`");
12803 }
12804
12805 #[test]
12806 fn a_lambda_with_no_expected_type_infers_nothing_about_its_parameters() {
12807 let error = rejects_body(" let double = fn(n) { n * 2 }\n println(\"{double(4)}\")?");
12812 assert_eq!(error.code, UNCONSTRAINED);
12813 }
12814
12815 #[test]
12816 fn checks_a_function_value_s_arguments() {
12817 let error = rejects(
12818 "\
12819fn apply(transform: fn(Int) -> Int) -> Int {
12820 transform(\"one\")
12821}
12822",
12823 );
12824 assert_eq!(error.code, MISMATCH);
12825 assert_eq!(error.message, "expected `Int`, found `String`");
12826 }
12827
12828 #[test]
12831 fn rejects_mixed_arithmetic() {
12832 let error = rejects_body(" println(\"{1 + 1.0}\")?");
12833 assert_eq!(error.code, OPERATOR);
12834 assert_eq!(error.message, "`+` is not defined for `Int` and `Float`");
12835 assert_eq!(
12836 error.rule.unwrap(),
12837 "There are no implicit numeric, string, or boolean conversions."
12838 );
12839 assert_eq!(
12840 error.help.unwrap(),
12841 "arithmetic combines two values of the same type"
12842 );
12843 }
12844
12845 #[test]
12846 fn rejects_mixed_equality() {
12847 let error = rejects_body(" println(\"{1 == \"1\"}\")?");
12848 assert_eq!(error.code, OPERATOR);
12849 assert_eq!(error.message, "cannot compare `Int` with `String`");
12850 assert_eq!(
12851 error.rule.unwrap(),
12852 "`==` means value equality between values of the same type."
12853 );
12854 assert_eq!(
12855 error.help.unwrap(),
12856 "convert one side explicitly so both are `Int`, or compare values that already share a type"
12857 );
12858 }
12859
12860 #[test]
12861 fn is_compares_the_identity_of_two_vectors() {
12862 accepts_body(
12863 "\
12864 var a = Vector.of(1, 2)
12865 var b = a
12866 println(\"{a is b}\")?
12867",
12868 );
12869 }
12870
12871 #[test]
12872 fn rejects_is_between_different_types() {
12873 let error = rejects_body(" println(\"{Vector.of(1) is Vector.of(\"x\")}\")?");
12874 assert_eq!(error.code, OPERATOR);
12875 assert_eq!(
12876 error.message,
12877 "cannot compare the identity of `Vector<Int>` with `Vector<String>`"
12878 );
12879 assert_eq!(
12880 error.rule.unwrap(),
12881 "`is` compares identity between values of the same type."
12882 );
12883 }
12884
12885 #[test]
12886 fn rejects_is_on_a_value_type() {
12887 let error = rejects_body(" println(\"{1 is 1}\")?");
12888 assert_eq!(error.code, OPERATOR);
12889 assert_eq!(error.message, "identity is not available for `Int`");
12890 assert_eq!(
12891 error.rule.unwrap(),
12892 "`==` means value equality. Identity, when available, is explicit."
12893 );
12894 }
12895
12896 #[test]
12897 fn rejects_adding_two_strings() {
12898 let error = rejects_body(" println(\"{\"a\" + \"b\"}\")?");
12899 assert_eq!(error.code, OPERATOR);
12900 assert_eq!(error.message, "`+` is not defined for `String`");
12901 assert_eq!(
12902 error.rule.unwrap(),
12903 "There are no implicit string conversions."
12904 );
12905 assert_eq!(
12906 error.help.unwrap(),
12907 "use string interpolation, such as \"{left}{right}\""
12908 );
12909 }
12910
12911 #[test]
12912 fn accepts_duration_arithmetic_and_comparison() {
12913 accepts_body(" println(\"{1s + 500ms} {1s > 999ms}\")?");
12914 let error = rejects_body(" println(\"{1s * 2s}\")?");
12915 assert_eq!(error.code, OPERATOR);
12916 assert_eq!(
12917 error.message,
12918 "`*` is not defined for `Duration` and `Duration`"
12919 );
12920 }
12921
12922 #[test]
12923 fn rejects_negating_a_string() {
12924 let error = rejects_body(" println(\"{-\"a\"}\")?");
12925 assert_eq!(error.code, OPERATOR);
12926 assert_eq!(error.message, "`-` is not defined for `String`");
12927 assert_eq!(
12928 error.help.unwrap(),
12929 "`-` negates an `Int`, a `Float`, or a `Duration`"
12930 );
12931 }
12932
12933 #[test]
12934 fn rejects_a_non_bool_operand_of_and() {
12935 let error = rejects_body(" println(\"{1 && true}\")?");
12936 assert_eq!(error.code, OPERATOR);
12937 assert_eq!(error.message, "`&&` is not defined for `Int` and `Bool`");
12938 assert_eq!(error.help.unwrap(), "`&&` and `||` combine two `Bool`s");
12939 }
12940
12941 #[test]
12942 fn rejects_ordering_two_bools() {
12943 let error = rejects_body(" println(\"{true < false}\")?");
12944 assert_eq!(error.code, OPERATOR);
12945 assert_eq!(error.message, "`<` is not defined for `Bool` and `Bool`");
12946 }
12947
12948 #[test]
12949 fn rejects_a_non_bool_condition() {
12950 let error = rejects_body(" if 1 {\n println(\"never\")?\n }");
12951 assert_eq!(error.code, CONDITION);
12952 assert_eq!(
12953 error.message,
12954 "a condition must be a `Bool`, but found `Int`"
12955 );
12956 assert_eq!(
12957 error.rule.unwrap(),
12958 "There are no implicit boolean conversions."
12959 );
12960 assert_eq!(
12961 error.help.unwrap(),
12962 "compare it, as in `value != 0`; a `Int` is not a `Bool`"
12963 );
12964 }
12965
12966 #[test]
12969 fn an_if_with_no_else_is_a_statement() {
12970 accepts_body(" var seen = 0\n if true {\n seen = 1\n }\n println(\"{seen}\")?");
12971 let error = rejects_body(" let n: Int = if true { 1 }");
12974 assert_eq!(error.code, MISMATCH);
12975 assert_eq!(error.message, "expected `Int`, found `()`");
12976 }
12977
12978 #[test]
12979 fn if_branches_must_agree() {
12980 accepts_body(" let n = if true { 1 } else { 2 }\n println(\"{n}\")?");
12981 let error = rejects_body(" let n = if true { 1 } else { \"two\" }");
12982 assert_eq!(error.code, BRANCHES);
12983 assert_eq!(
12984 error.message,
12985 "this branch produces `String`, but the other produces `Int`"
12986 );
12987 assert_eq!(
12988 error.rule.unwrap(),
12989 "Every branch of an `if` or `match` used as an expression produces the same type."
12990 );
12991 assert_eq!(
12992 error.help.unwrap(),
12993 "make both branches produce `Int`, or bind them separately"
12994 );
12995 }
12996
12997 #[test]
12998 fn every_loop_is_unit_and_a_break_operand_is_discarded() {
12999 accepts_body(" let ran = for value in [1, 2] {\n value\n }\n println(\"{ran}\")?");
13005 accepts_body(
13006 " let ran = for value in [1, 2] {\n break value\n }\n println(\"{ran}\")?",
13007 );
13008 accepts_body(
13009 " var seen = 0\n let ran = while seen < 2 {\n seen += 1\n break seen\n }\n println(\"{ran}\")?",
13010 );
13011 accepts_body(" let ran = while true {\n break 1\n }\n println(\"{ran}\")?");
13014 accepts_body(
13017 " let ran = while true {\n if true {\n break 1\n }\n break \"two\"\n }\n println(\"{ran}\")?",
13018 );
13019 let error = rejects_body(" let n: Int = while true {\n break 1\n }");
13022 assert_eq!(error.code, MISMATCH);
13023 assert_eq!(error.message, "expected `Int`, found `()`");
13024 let error = rejects_body(" let n: Int = for value in [1, 2] {\n break value\n }");
13025 assert_eq!(error.code, MISMATCH);
13026 let error = rejects_body(" for value in [1, 2] {\n break value + \"a\"\n }");
13028 assert_eq!(error.code, OPERATOR);
13029 }
13030
13031 #[test]
13032 fn match_arms_must_agree() {
13033 let error = rejects(
13034 "\
13035fn name(n: Int) -> String {
13036 let value = match n {
13037 0 => \"zero\"
13038 _ => 1
13039 }
13040 \"{value}\"
13041}
13042",
13043 );
13044 assert_eq!(error.code, BRANCHES);
13045 assert_eq!(
13046 error.message,
13047 "this branch produces `Int`, but the other produces `String`"
13048 );
13049 }
13050
13051 #[test]
13052 fn a_return_never_disagrees_with_a_branch() {
13053 accepts(
13054 "\
13055fn label(n: Int) -> String {
13056 match n {
13057 0 => return \"zero\"
13058 _ => \"other\"
13059 }
13060}
13061",
13062 );
13063 }
13064
13065 #[test]
13066 fn checks_return_against_the_declared_return_type() {
13067 let error = rejects(
13068 "\
13069fn label(n: Int) -> String {
13070 if n == 0 {
13071 return 0
13072 }
13073 \"other\"
13074}
13075",
13076 );
13077 assert_eq!(error.code, MISMATCH);
13078 assert_eq!(error.message, "expected `String`, found `Int`");
13079 assert_eq!(
13080 error.labels[0].message,
13081 "the declared return type is `String`"
13082 );
13083 }
13084
13085 #[test]
13086 fn a_block_s_value_is_its_tail() {
13087 accepts_body(" let n = {\n let base = 1\n base + 1\n }\n println(\"{n}\")?");
13088 accepts_body(" let nothing = { }\n println(\"{nothing}\")?");
13089 }
13090
13091 #[test]
13092 fn a_function_with_no_return_type_returns_unit() {
13093 accepts(
13094 "\
13095fn record(var log: Vector<String>, entry: String) {
13096 log.push(entry)
13097}
13098",
13099 );
13100 let error = rejects("fn total() {\n 1\n}\n");
13101 assert_eq!(error.code, MISMATCH);
13102 assert_eq!(error.message, "expected `()`, found `Int`");
13103 assert_eq!(
13104 error.labels[0].message,
13105 "this function declares no return type, so it returns `()`"
13106 );
13107 }
13108
13109 #[test]
13110 fn checks_a_for_loop_s_iterable_and_binding() {
13111 accepts(
13112 "\
13113fn total(items: Array<Int>) -> Int {
13114 var sum = 0
13115 for item in items {
13116 sum += item
13117 }
13118 sum
13119}
13120",
13121 );
13122 let error = rejects(
13123 "\
13124fn total(items: Array<String>) -> Int {
13125 var sum = 0
13126 for item in items {
13127 sum += item
13128 }
13129 sum
13130}
13131",
13132 );
13133 assert_eq!(error.code, OPERATOR);
13134 assert_eq!(error.message, "`+` is not defined for `Int` and `String`");
13135 }
13136
13137 #[test]
13138 fn rejects_iterating_something_that_is_not_a_sequence() {
13139 let error = rejects_body(" for n in 1 {\n println(\"{n}\")?\n }");
13140 assert_eq!(error.code, ITERABLE);
13141 assert_eq!(
13142 error.message,
13143 "`for` iterates an `Array`, a `Vector`, a `Range`, a `Set`, or a `Map`, but found `Int`"
13144 );
13145 assert_eq!(
13146 error.rule.unwrap(),
13147 "`for` iterates a sequence; iteration order is defined by each collection type."
13148 );
13149 assert_eq!(error.help.unwrap(), "write a range, as in `0..<n`");
13150 }
13151
13152 #[test]
13153 fn checks_an_assignment_against_the_place_s_type() {
13154 let error = rejects_body(" var n = 1\n n = \"one\"");
13155 assert_eq!(error.code, MISMATCH);
13156 assert_eq!(error.message, "expected `Int`, found `String`");
13157 }
13158
13159 #[test]
13160 fn checks_a_compound_assignment_with_the_operator_s_rule() {
13161 accepts_body(" var n = 1\n n += 2\n println(\"{n}\")?");
13162 let error = rejects_body(" var n = 1\n n += 1.0");
13163 assert_eq!(error.code, OPERATOR);
13164 assert_eq!(error.message, "`+` is not defined for `Int` and `Float`");
13165 }
13166
13167 #[test]
13168 fn a_range_takes_two_ints() {
13169 accepts_body(" let range = 0..<3\n println(\"{range.length()}\")?");
13170 let error = rejects_body(" let range = 0..<\"three\"");
13171 assert_eq!(error.code, MISMATCH);
13172 assert_eq!(error.message, "expected `Int`, found `String`");
13173 }
13174
13175 #[test]
13178 fn checks_the_question_mark_against_the_enclosing_return_type() {
13179 accepts(
13180 "\
13181fn double(text: String) -> Result<Int, Error> {
13182 let value = Int.parse(text)?
13183 Ok(value * 2)
13184}
13185",
13186 );
13187 }
13188
13189 #[test]
13190 fn rejects_the_question_mark_on_a_value_that_cannot_fail() {
13191 let error = rejects(
13192 "\
13193fn length(text: String) -> Result<Int, Error> {
13194 let n = text.length()?
13195 Ok(n)
13196}
13197",
13198 );
13199 assert_eq!(error.code, TRY_OPERAND);
13200 assert_eq!(
13201 error.message,
13202 "`?` needs a `Result` or an `Option`, but found `Int`"
13203 );
13204 assert_eq!(
13205 error.rule.unwrap(),
13206 "`expr?` returns the error from the current function."
13207 );
13208 assert_eq!(error.help.unwrap(), "`Int` cannot fail, so drop the `?`");
13209 }
13210
13211 #[test]
13212 fn rejects_the_question_mark_when_the_failure_types_differ() {
13213 let error = rejects(
13214 "\
13215enum ParseError {
13216 NotANumber
13217}
13218
13219fn double(text: String) -> Result<Int, ParseError> {
13220 let value = Int.parse(text)?
13221 Ok(value * 2)
13222}
13223",
13224 );
13225 assert_eq!(error.code, TRY_RETURN);
13226 assert_eq!(
13227 error.message,
13228 "`?` propagates `Error`, but this function returns `ParseError` as its failure"
13229 );
13230 assert_eq!(
13231 error.rule.unwrap(),
13232 "`expr?` returns the error from the current function, so the two failure types must be the same."
13233 );
13234 assert_eq!(
13235 error.help.unwrap(),
13236 "map the failure first, as in `expr.mapError(fn(error) { ... })?`, or declare this function `-> Result<_, Error>`"
13237 );
13238 }
13239
13240 #[test]
13241 fn rejects_the_question_mark_in_a_function_that_cannot_fail() {
13242 let error = rejects(
13243 "\
13244fn double(text: String) -> Int {
13245 Int.parse(text)? * 2
13246}
13247",
13248 );
13249 assert_eq!(error.code, TRY_RETURN);
13250 assert_eq!(
13251 error.message,
13252 "`?` needs a function that returns a `Result`, but this one returns `Int`"
13253 );
13254 assert_eq!(
13255 error.help.unwrap(),
13256 "declare this function `-> Result<Int, Error>`, or handle the `Err` with `unwrapOr`"
13257 );
13258 }
13259
13260 #[test]
13267 fn rejects_the_question_mark_in_a_body_a_schema_declared_any() {
13268 let error = rejects(
13269 "\
13270use clock
13271
13272struct Dashboard {
13273 panel: String
13274}
13275
13276fn panelOf(name: String) -> Result<String, Error> {
13277 Ok(name)
13278}
13279
13280export fn load() -> Result<Dashboard, Error> {
13281 let result = clock.timeout(1s) {
13282 Dashboard(panel: panelOf(\"bookings\")?)
13283 }?
13284 Ok(result)
13285}
13286",
13287 );
13288 assert_eq!(error.code, TRY_RETURN);
13289 assert_eq!(
13290 error.message,
13291 "`?` propagates `Error`, but this function value produces `Dashboard`"
13292 );
13293 assert_eq!(error.rule.unwrap(), TRY_LAMBDA_RULE);
13294 assert_eq!(
13295 error.help.unwrap(),
13296 "end the body with a `Result`, as in `Ok(...)`, so this function value produces `Result<Dashboard, Error>` and the `?` has an `Err` to return; then answer that failure where the value arrives"
13297 );
13298 }
13299
13300 #[test]
13304 fn a_body_that_ends_with_a_result_carries_its_own_failure() {
13305 accepts(
13306 "\
13307use clock
13308
13309fn panelOf(name: String) -> Result<String, Error> {
13310 Ok(name)
13311}
13312
13313export fn load() -> Result<String, Error> {
13314 let result = clock.timeout(1s) {
13315 Ok(panelOf(\"bookings\")?)
13316 }?
13317 result
13318}
13319",
13320 );
13321 }
13322
13323 #[test]
13327 fn rejects_the_question_mark_in_a_lambda_nothing_types() {
13328 let error = rejects(
13329 "\
13330fn run() -> Int {
13331 let parse = fn(text: String) {
13332 Int.parse(text)?
13333 }
13334 1
13335}
13336",
13337 );
13338 assert_eq!(error.code, TRY_RETURN);
13339 assert_eq!(
13340 error.message,
13341 "`?` propagates `Error`, but this function value produces `Int`"
13342 );
13343 }
13344
13345 #[test]
13350 fn rejects_the_question_mark_in_a_spawned_body() {
13351 let error = rejects(
13352 "\
13353fn work() -> Result<Int, Error> {
13354 Ok(1)
13355}
13356
13357export async fn run() -> Result<Int, Error> {
13358 scope tasks {
13359 let job = tasks.spawn { work()? }
13360 let value = await job
13361 Ok(value)
13362 }
13363}
13364",
13365 );
13366 assert_eq!(error.code, TRY_RETURN);
13367 assert_eq!(
13368 error.message,
13369 "`?` propagates `Error`, but this function value produces `Int`"
13370 );
13371 }
13372
13373 #[test]
13376 fn rejects_the_option_question_mark_in_a_lambda_nothing_types() {
13377 let error = rejects(
13378 "\
13379fn run() -> Int {
13380 let first = fn(text: String) {
13381 text.words().get(0)?
13382 }
13383 1
13384}
13385",
13386 );
13387 assert_eq!(error.code, TRY_RETURN);
13388 assert_eq!(
13389 error.message,
13390 "`?` on an `Option` returns `None`, but this function value produces `String`"
13391 );
13392 assert_eq!(
13393 error.help.unwrap(),
13394 "end the body with an `Option`, as in `Some(...)`, so this function value produces `Option<String>` and the `?` has a `None` to return; then answer the missing value where it arrives"
13395 );
13396 }
13397
13398 #[test]
13402 fn a_written_function_type_is_checked_where_the_question_mark_is() {
13403 accepts(
13404 "\
13405fn run() -> Int {
13406 let parse: fn(String) -> Result<Int, Error> = fn(text) {
13407 Ok(Int.parse(text)?)
13408 }
13409 1
13410}
13411",
13412 );
13413 let error = rejects(
13414 "\
13415fn run() -> Int {
13416 let parse: fn(String) -> Int = fn(text) {
13417 Int.parse(text)?
13418 }
13419 1
13420}
13421",
13422 );
13423 assert_eq!(error.code, TRY_RETURN);
13424 assert_eq!(
13425 error.message,
13426 "`?` needs a function that returns a `Result`, but this one returns `Int`"
13427 );
13428 }
13429
13430 #[test]
13435 fn a_callback_whose_result_the_expectation_leaves_open_is_left_alone() {
13436 accepts(
13437 "\
13438enum ParseError {
13439 NotANumber
13440}
13441
13442fn parse(text: String) -> Result<Int, ParseError> {
13443 Int.parse(text).mapError(fn(cause) {
13444 ParseError.NotANumber
13445 })
13446}
13447",
13448 );
13449 }
13450
13451 #[test]
13452 fn the_question_mark_unwraps_an_option_inside_an_option() {
13453 accepts(
13454 "\
13455fn shout(text: String) -> Option<String> {
13456 let word = text.words().get(0)?
13457 Some(\"{word}!\")
13458}
13459",
13460 );
13461 let error = rejects(
13462 "\
13463fn shout(text: String) -> String {
13464 let word = text.words().get(0)?
13465 \"{word}!\"
13466}
13467",
13468 );
13469 assert_eq!(error.code, TRY_RETURN);
13470 assert_eq!(
13471 error.message,
13472 "`?` on an `Option` needs a function that returns an `Option`, but this one returns `String`"
13473 );
13474 }
13475
13476 #[test]
13477 fn map_error_replaces_the_failure_type() {
13478 accepts(
13479 "\
13480enum ParseError {
13481 NotANumber(String)
13482}
13483
13484fn parseOrFail(text: String) -> Result<Int, ParseError> {
13485 Int.parse(text).mapError(fn(error) { ParseError.NotANumber(text) })
13486}
13487",
13488 );
13489 let error = rejects(
13490 "\
13491enum ParseError {
13492 NotANumber(String)
13493}
13494
13495fn parseOrFail(text: String) -> Result<Int, ParseError> {
13496 Int.parse(text).mapError(fn(error) { text })
13497}
13498",
13499 );
13500 assert_eq!(error.code, MISMATCH);
13501 assert_eq!(
13502 error.message,
13503 "expected `Result<Int, ParseError>`, found `Result<Int, String>`"
13504 );
13505 }
13506
13507 #[test]
13508 fn map_error_also_takes_a_callback_of_the_error() {
13509 accepts(
13510 "\
13511fn keep(text: String) -> Result<Int, Error> {
13512 Int.parse(text).mapError(fn(error) { error })
13513}
13514",
13515 );
13516 }
13517
13518 #[test]
13519 fn calling_an_async_function_produces_a_task_that_await_settles() {
13520 accepts(
13521 "\
13522async fn load() -> Int {
13523 1
13524}
13525
13526async fn run() -> Int {
13527 await load()
13528}
13529",
13530 );
13531 let error = rejects(
13532 "\
13533async fn load() -> Int {
13534 1
13535}
13536
13537async fn run() -> Int {
13538 load()
13539}
13540",
13541 );
13542 assert_eq!(error.code, MISMATCH);
13543 assert_eq!(error.message, "expected `Int`, found `Task<Int>`");
13544 }
13545
13546 #[test]
13547 fn rejects_awaiting_something_that_is_not_a_task() {
13548 let error = rejects_body(" let n = await 1");
13549 assert_eq!(error.code, AWAIT_OPERAND);
13550 assert_eq!(error.message, "`await` needs a task, but found `Int`");
13551 assert_eq!(
13552 error.help.unwrap(),
13553 "call an `async fn`, or spawn the work into a task scope, and await that handle"
13554 );
13555 }
13556
13557 #[test]
13558 fn rejects_the_question_mark_on_a_task() {
13559 let error = rejects(
13560 "\
13561async fn load() -> Result<Int, Error> {
13562 Ok(1)
13563}
13564
13565async fn run() -> Result<Int, Error> {
13566 let value = load()?
13567 Ok(value)
13568}
13569",
13570 );
13571 assert_eq!(error.code, TRY_OPERAND);
13572 assert_eq!(
13573 error.message,
13574 "`?` needs a `Result` or an `Option`, but found `Task<Result<Int, Error>>`"
13575 );
13576 assert_eq!(
13577 error.help.unwrap(),
13578 "settle the task first, as in `task.await()?`"
13579 );
13580 }
13581
13582 #[test]
13583 fn a_scope_spawns_tasks_that_carry_the_block_s_value() {
13584 accepts(
13585 "\
13586async fn run() -> Result<Int, Error> {
13587 scope tasks {
13588 let first = tasks.spawn { 1 }
13589 let value = await first
13590 Ok(value)
13591 }
13592}
13593",
13594 );
13595 }
13596
13597 #[test]
13602 fn a_scope_of_unit_children_is_at_home_in_a_unit_function() {
13603 accepts(
13604 "\
13605fn counting(turns: Shared<Int>, stop: Bool) {
13606 scope work {
13607 let task = work.spawn {
13608 var at = 0
13609 while at < 10 {
13610 turns.lock(fn(var it) {
13611 it += 1
13612 })
13613 at += 1
13614 }
13615 }
13616 if stop {
13617 task.cancel()
13618 } else {
13619 task.await()
13620 }
13621 }
13622}
13623",
13624 );
13625 }
13626
13627 #[test]
13628 fn an_unawaited_failing_child_needs_a_function_that_can_return_its_failure() {
13629 let error = rejects(
13630 "\
13631fn work() -> Result<Int, Error> {
13632 Ok(1)
13633}
13634
13635fn run() {
13636 scope tasks {
13637 let job = tasks.spawn { work() }
13638 }
13639}
13640",
13641 );
13642 assert_eq!(error.code, SCOPE_CHILD_FAILURE);
13643 assert_eq!(
13644 error.message,
13645 "nothing awaits `job`, so leaving `tasks` propagates its `Error`, but this function returns `()`"
13646 );
13647 assert_eq!(
13648 error.help.unwrap(),
13649 "declare this function `-> Result<(), Error>`, or await `job` and answer its `Err` here"
13650 );
13651 }
13652
13653 #[test]
13657 fn awaiting_a_failing_child_leaves_the_enclosing_function_alone() {
13658 accepts(
13659 "\
13660fn work() -> Result<Int, Error> {
13661 Ok(1)
13662}
13663
13664fn run() -> Int {
13665 scope tasks {
13666 let job = tasks.spawn { work() }
13667 job.await().unwrapOr(0)
13668 }
13669}
13670",
13671 );
13672 accepts(
13673 "\
13674fn work() -> Result<Int, Error> {
13675 Ok(1)
13676}
13677
13678fn run() -> Int {
13679 scope tasks {
13680 (await tasks.spawn { work() }).unwrapOr(0)
13681 }
13682}
13683",
13684 );
13685 }
13686
13687 #[test]
13692 fn cancelling_a_failing_child_does_not_settle_it() {
13693 let error = rejects(
13694 "\
13695fn work() -> Result<Int, Error> {
13696 Ok(1)
13697}
13698
13699fn run() -> Int {
13700 scope tasks {
13701 let job = tasks.spawn { work() }
13702 job.cancel()
13703 0
13704 }
13705}
13706",
13707 );
13708 assert_eq!(error.code, SCOPE_CHILD_FAILURE);
13709 assert_eq!(
13710 error.message,
13711 "nothing awaits `job`, so leaving `tasks` propagates its `Error`, but this function returns `Int`"
13712 );
13713 }
13714
13715 #[test]
13720 fn a_result_returning_function_carries_its_children_s_failures() {
13721 accepts(
13722 "\
13723fn work() -> Result<Int, Error> {
13724 Ok(1)
13725}
13726
13727fn run() -> Result<Unit, Error> {
13728 scope tasks {
13729 let job = tasks.spawn { work() }
13730 job.cancel()
13731 }
13732 Ok(())
13733}
13734",
13735 );
13736 }
13737
13738 #[test]
13743 fn failures_of_several_children_must_fit_one_declared_failure() {
13744 let error = rejects(
13745 "\
13746struct Wrong {
13747 why: String
13748}
13749
13750fn fails() -> Result<Int, Wrong> {
13751 Err(Wrong(why: \"no\"))
13752}
13753
13754fn run() -> Result<Unit, Error> {
13755 scope tasks {
13756 let job = tasks.spawn { fails() }
13757 }
13758 Ok(())
13759}
13760",
13761 );
13762 assert_eq!(error.code, SCOPE_CHILD_FAILURE);
13763 assert_eq!(
13764 error.message,
13765 "nothing awaits `job`, so leaving `tasks` propagates its `Wrong`, but this function returns `Error` as its failure"
13766 );
13767 assert_eq!(
13768 error.help.unwrap(),
13769 "map the failure inside the task, as in `tasks.spawn { ... .mapError(fn(error) { ... }) }`, or declare this function `-> Result<(), Wrong>`"
13770 );
13771 }
13772
13773 #[test]
13776 fn a_child_awaited_inside_a_nested_scope_is_awaited() {
13777 accepts(
13778 "\
13779fn work() -> Result<Int, Error> {
13780 Ok(1)
13781}
13782
13783fn run() -> Int {
13784 scope outer {
13785 let job = outer.spawn { work() }
13786 scope inner {
13787 job.await().unwrapOr(0)
13788 }
13789 }
13790}
13791",
13792 );
13793 }
13794
13795 const METRICS: &str = "\
13800struct Metrics {
13801 requests: Int
13802 failures: Int
13803}
13804
13805impl Metrics {
13806 fn record(var self, failed: Bool) {
13807 self.requests += 1
13808 if failed {
13809 self.failures += 1
13810 }
13811 }
13812}
13813";
13814
13815 #[test]
13816 fn a_lock_gives_its_closure_the_wrapped_type_and_carries_its_result() {
13817 accepts(&format!(
13818 "{METRICS}
13819fn run() -> Int {{
13820 let metrics = Shared(Metrics(requests: 0, failures: 0))
13821 metrics.lock(fn(var value) {{
13822 value.record(true)
13823 }})
13824 metrics.lock(fn(value) {{
13825 value.requests
13826 }})
13827}}
13828"
13829 ));
13830 }
13831
13832 #[test]
13833 fn a_lock_result_has_the_closure_s_type() {
13834 let error = rejects(&format!(
13835 "{METRICS}
13836fn run() -> String {{
13837 let metrics = Shared(Metrics(requests: 0, failures: 0))
13838 metrics.lock(fn(value) {{
13839 value.requests
13840 }})
13841}}
13842"
13843 ));
13844 assert_eq!(error.message, "expected `String`, found `Int`");
13845 }
13846
13847 #[test]
13850 fn a_lock_closure_takes_the_wrapped_type() {
13851 let error = rejects(&format!(
13852 "{METRICS}
13853fn run() -> Int {{
13854 let metrics = Shared(Metrics(requests: 0, failures: 0))
13855 metrics.lock(fn(value) {{
13856 value.attempts
13857 }})
13858}}
13859"
13860 ));
13861 assert_eq!(error.code, UNKNOWN_FIELD);
13862 assert_eq!(error.message, "`Metrics` has no field `attempts`");
13863 }
13864
13865 #[test]
13868 fn a_shared_vector_is_refused_where_the_type_is_written() {
13869 let error = rejects_body(" let counts: Shared<Vector<Int>> = Shared(Vector.of(1))");
13870 assert_eq!(error.code, TASK_SAFETY);
13871 assert_eq!(
13872 error.message,
13873 "`Shared` cannot wrap a `Vector<Int>`, which cannot cross a task boundary"
13874 );
13875 assert!(error.rule.unwrap().contains("A vector cannot cross"));
13876 }
13877
13878 #[test]
13879 fn a_shared_vector_is_refused_where_it_is_constructed() {
13880 let error = rejects_body(" let counts = Shared(Vector.of(1))");
13881 assert_eq!(error.code, TASK_SAFETY);
13882 }
13883
13884 #[test]
13885 fn a_shared_of_an_array_of_vectors_names_the_vector() {
13886 let error = rejects_body(" let counts: Shared<Array<Vector<Int>>> = Shared([])");
13887 assert_eq!(
13888 error.message,
13889 "`Shared` cannot wrap `Array<Vector<Int>>`: the `Vector<Int>` in it cannot cross a task boundary"
13890 );
13891 }
13892
13893 #[test]
13894 fn a_shared_does_not_conform_to_snapshot() {
13895 let error = rejects_body(" let counts = Shared(1)\n let copy = counts.snapshot()");
13896 assert_eq!(error.message, "`Shared<Int>` does not implement `Snapshot`");
13897 assert!(error.rule.unwrap().contains("synchronized values"));
13898 }
13899
13900 #[test]
13901 fn a_shared_has_no_operation_but_lock() {
13902 let error = rejects_body(" let counts = Shared(1)\n let value = counts.get()");
13903 assert_eq!(error.code, UNKNOWN_METHOD);
13904 assert_eq!(error.message, "`Shared` has no method `get`");
13905 }
13906
13907 #[test]
13910 fn expands_a_type_alias() {
13911 accepts(
13912 "\
13913type Transform = fn(Int) -> Int
13914
13915fn apply(value: Int, transform: Transform) -> Int {
13916 transform(value)
13917}
13918
13919fn run() -> Int {
13920 apply(1, fn(n) { n + 1 })
13921}
13922",
13923 );
13924 let error = rejects(
13925 "\
13926type Transform = fn(Int) -> Int
13927
13928fn apply(value: Int, transform: Transform) -> Int {
13929 transform(value)
13930}
13931
13932fn run() -> Int {
13933 apply(1, fn(n) { \"{n}\" })
13934}
13935",
13936 );
13937 assert_eq!(error.code, MISMATCH);
13938 assert_eq!(error.message, "expected `Int`, found `String`");
13939 }
13940
13941 #[test]
13942 fn rejects_a_type_alias_that_expands_to_itself() {
13943 let error = rejects("type Loop = Loop\n\nfn run(value: Loop) {\n}\n");
13944 assert_eq!(error.code, ALIAS_CYCLE);
13945 assert_eq!(error.message, "`Loop` expands to itself");
13946 assert_eq!(
13947 error.rule.unwrap(),
13948 "A type alias names an existing type; it cannot be defined in terms of itself."
13949 );
13950 }
13951
13952 #[test]
13961 fn rejects_a_struct_that_contains_itself() {
13962 let error = rejects("struct Node {\n value: Int,\n next: Node,\n}\n");
13963 assert_eq!(error.code, LAYOUT_CYCLE);
13964 assert_eq!(
13965 error.message,
13966 "`Node` contains itself by value, through field `next`"
13967 );
13968 assert_eq!(error.rule.unwrap(), LAYOUT_CYCLE_RULE);
13969 assert_eq!(
13970 error.help.unwrap(),
13971 "break the cycle by holding one of its steps behind a reference: `Array<Node>`, `Vector<Node>` and `Shared<Node>` are each one word, so a cycle that passes through one has a finite width"
13972 );
13973 }
13974
13975 #[test]
13976 fn rejects_a_struct_that_contains_itself_through_an_option() {
13977 let error = rejects("struct Node {\n value: Int,\n next: Option<Node>,\n}\n");
13978 assert_eq!(error.code, LAYOUT_CYCLE);
13979 assert_eq!(
13980 error.message,
13981 "`Node` contains itself by value, through field `next`"
13982 );
13983 }
13984
13985 #[test]
13986 fn rejects_an_enum_whose_case_carries_itself() {
13987 let error = rejects("enum List {\n Empty,\n Cons(Int, List),\n}\n");
13988 assert_eq!(error.code, LAYOUT_CYCLE);
13989 assert_eq!(
13990 error.message,
13991 "`List` contains itself by value, through case `Cons`"
13992 );
13993 }
13994
13995 #[test]
13996 fn rejects_two_structs_that_contain_each_other() {
13997 let error = rejects("struct A {\n b: B,\n}\n\nstruct B {\n a: A,\n}\n");
13998 assert_eq!(error.code, LAYOUT_CYCLE);
13999 assert_eq!(
14000 error.message,
14001 "`A` contains itself by value: `A` -> `B` -> `A`"
14002 );
14003 let labels: Vec<&str> = error
14004 .labels
14005 .iter()
14006 .map(|label| label.message.as_str())
14007 .collect();
14008 assert_eq!(
14009 labels,
14010 vec![
14011 "field `b` puts `B` inside `A`",
14012 "field `a` puts `A` inside `B`"
14013 ]
14014 );
14015 }
14016
14017 #[test]
14018 fn rejects_a_three_step_cycle() {
14019 let error = rejects(
14020 "struct A {\n b: B,\n}\n\nstruct B {\n c: C,\n}\n\nstruct C {\n a: Option<A>,\n}\n",
14021 );
14022 assert_eq!(error.code, LAYOUT_CYCLE);
14023 assert_eq!(
14024 error.message,
14025 "`A` contains itself by value: `A` -> `B` -> `C` -> `A`"
14026 );
14027 }
14028
14029 #[test]
14030 fn a_cycle_is_reported_once_however_many_declarations_it_runs_through() {
14031 let errors = errors_of("struct A {\n b: B,\n}\n\nstruct B {\n a: A,\n}\n");
14032 assert_eq!(errors.len(), 1);
14033 }
14034
14035 #[test]
14036 fn a_struct_holds_itself_through_a_vector() {
14037 accepts("struct Node {\n label: String,\n peers: Vector<Node>,\n}\n");
14038 }
14039
14040 #[test]
14041 fn an_enum_holds_itself_through_an_array_and_a_map() {
14042 accepts("enum Json {\n Null,\n Items(Array<Json>),\n Fields(Map<String, Json>),\n}\n");
14043 }
14044
14045 #[test]
14046 fn a_cycle_through_a_closure_or_a_trait_object_is_a_reference() {
14047 accepts("trait Render {\n fn render(self) -> String\n}\n\nstruct Node {\n child: dyn Render,\n make: fn() -> Node,\n}\n");
14048 }
14049
14050 #[test]
14051 fn a_generic_declaration_that_holds_its_parameter_carries_the_cycle() {
14052 let error =
14053 rejects("struct Cell<T> {\n it: T,\n}\n\nstruct Loop {\n cell: Cell<Loop>,\n}\n");
14054 assert_eq!(error.code, LAYOUT_CYCLE);
14055 assert_eq!(
14056 error.message,
14057 "`Loop` contains itself by value, through field `cell`"
14058 );
14059 }
14060
14061 #[test]
14062 fn a_generic_declaration_that_holds_its_parameter_behind_a_reference_does_not() {
14063 accepts(
14064 "struct Holder<T> {\n it: Vector<T>,\n}\n\nstruct Node {\n held: Holder<Node>,\n}\n",
14065 );
14066 }
14067
14068 #[test]
14069 fn a_cycle_through_an_imported_generic_is_still_a_cycle() {
14070 let error = rejects_modules(&[
14075 ("cell", "/// A box.\nexport struct Cell<T> {\n it: T,\n}\n"),
14076 (
14077 "app",
14078 "use cell.Cell\n\nstruct Loop {\n cell: Cell<Loop>,\n}\n",
14079 ),
14080 ]);
14081 assert_eq!(error.code, LAYOUT_CYCLE);
14082 assert_eq!(
14083 error.message,
14084 "`Loop` contains itself by value, through field `cell`"
14085 );
14086 }
14087
14088 #[test]
14089 fn nesting_one_generic_inside_itself_is_finite() {
14090 accepts("struct Cell<T> {\n it: T,\n}\n\nstruct Twice {\n it: Cell<Cell<Int>>,\n}\n");
14091 }
14092
14093 #[test]
14102 fn a_host_call_produces_the_type_its_schema_declares() {
14103 accepts(
14106 "\
14107use console.println
14108use env.get
14109use documents
14110
14111export fn main() -> Result<Unit, Error> {
14112 let port: String = env.get(\"PORT\").unwrapOr(\"8080\")
14113 let note: String = documents.read(\"input\")?
14114 println(\"{port} {note}\")?
14115 Ok(())
14116}
14117",
14118 );
14119 }
14120
14121 #[test]
14122 fn a_host_call_s_result_is_checked_where_it_is_used() {
14123 let error = rejects(
14124 "\
14125use env.get
14126
14127export fn main() -> Int {
14128 get(\"PORT\").unwrapOr(\"8080\") + 1
14129}
14130",
14131 );
14132 assert_eq!(error.code, OPERATOR);
14133 assert_eq!(error.message, "`+` is not defined for `String` and `Int`");
14134 }
14135
14136 #[test]
14137 fn an_argument_a_host_operation_does_not_declare_is_rejected_at_the_call() {
14138 let error = rejects(
14139 "\
14140use documents
14141
14142export fn main() -> Result<Unit, Error> {
14143 documents.read(1)?
14144 Ok(())
14145}
14146",
14147 );
14148 assert_eq!(error.code, MISMATCH);
14149 assert_eq!(error.message, "expected `String`, found `Int`");
14150 assert_eq!(
14151 error.rule.unwrap(),
14152 "Types are nominal and the only implicit conversion is to `dyn Trait`: a value must otherwise already have the type its place asks for."
14153 );
14154 }
14155
14156 #[test]
14158 fn a_host_call_with_the_wrong_number_of_arguments_is_rejected_at_the_call() {
14159 let error = rejects(
14160 "\
14161use documents
14162
14163export fn main() -> Result<Unit, Error> {
14164 documents.read(\"input\", \"extra\")?
14165 Ok(())
14166}
14167",
14168 );
14169 assert_eq!(error.code, ARITY);
14170 assert_eq!(
14171 error.message,
14172 "`documents.read` takes 1 argument, but 2 were given"
14173 );
14174 assert_eq!(
14175 error.help.unwrap(),
14176 "the Host API schema declares `documents.read(String) -> Result<String, Error>`",
14177 "the boundary's diagnostic for the same mistake quotes it word for word"
14178 );
14179 }
14180
14181 #[test]
14185 fn a_variadic_host_operation_checks_every_argument() {
14186 accepts(
14187 "\
14188use console
14189
14190export fn main() -> Result<Unit, Error> {
14191 console.println()?
14192 console.println(\"one\", \"two\", \"three\")?
14193 Ok(())
14194}
14195",
14196 );
14197
14198 let error = rejects(
14199 "\
14200use console
14201
14202export fn main() -> Result<Unit, Error> {
14203 console.println(\"one\", 2)?
14204 Ok(())
14205}
14206",
14207 );
14208 assert_eq!(error.code, MISMATCH);
14209 assert_eq!(error.message, "expected `String`, found `Int`");
14210 }
14211
14212 #[test]
14216 fn a_parameter_declared_any_accepts_whatever_it_is_given() {
14217 accepts(
14218 "\
14219use clock
14220
14221export fn main() -> Result<Unit, Error> {
14222 clock.timeout(500ms) {
14223 1
14224 }?
14225 clock.every(60s, fn() {
14226 Ok(())
14227 })?
14228 Ok(())
14229}
14230",
14231 );
14232 }
14233
14234 #[test]
14235 fn an_operation_the_schema_does_not_declare_is_rejected() {
14236 let error = rejects(
14237 "\
14238use documents
14239
14240export fn main() -> Result<Unit, Error> {
14241 documents.write(\"input\", \"text\")?
14242 Ok(())
14243}
14244",
14245 );
14246 assert_eq!(error.code, UNKNOWN_HOST_OPERATION);
14247 assert_eq!(
14248 error.message,
14249 "host module `documents` has no operation `write`"
14250 );
14251 assert_eq!(error.help.unwrap(), "`documents` exposes `read`");
14252 }
14253
14254 #[test]
14257 fn a_host_type_is_a_type() {
14258 accepts(
14259 "\
14260use http
14261
14262/// Answers one request.
14263export fn health(request: http.Request) -> http.Response {
14264 http.json(200, request.path)
14265}
14266",
14267 );
14268
14269 let error = rejects(
14270 "\
14271use http
14272
14273export fn health(request: http.Request) -> Int {
14274 http.json(200, \"ok\")
14275}
14276",
14277 );
14278 assert_eq!(error.code, MISMATCH);
14279 assert_eq!(error.message, "expected `Int`, found `http.Response`");
14280 }
14281
14282 #[test]
14285 fn a_host_type_s_fields_are_typed_by_the_schema() {
14286 let error = rejects(
14287 "\
14288use http
14289
14290export fn path(request: http.Request) -> Int {
14291 request.path
14292}
14293",
14294 );
14295 assert_eq!(error.code, MISMATCH);
14296 assert_eq!(error.message, "expected `Int`, found `String`");
14297
14298 let missing = rejects(
14299 "\
14300use http
14301
14302export fn path(request: http.Request) -> String {
14303 request.query
14304}
14305",
14306 );
14307 assert_eq!(missing.code, UNKNOWN_FIELD);
14308 assert_eq!(missing.message, "`http.Request` has no field `query`");
14309 assert_eq!(
14310 missing.help.unwrap(),
14311 "`http.Request` declares `method`, `path`, `body`"
14312 );
14313 }
14314
14315 #[test]
14318 fn a_host_type_is_initialized_with_the_fields_the_schema_declares() {
14319 accepts(
14320 "\
14321use http
14322
14323/// Answers one request.
14324fn health(request: http.Request) -> http.Response {
14325 http.json(200, \"ok\")
14326}
14327
14328/// The one route this program serves.
14329export fn routes() -> Array<http.Route> {
14330 [http.Route(method: http.Method.Get, path: \"/health\", handler: health)]
14331}
14332",
14333 );
14334
14335 let error = rejects(
14336 "\
14337use http
14338
14339export fn routes() -> Array<http.Route> {
14340 [http.Route(method: http.Method.Get, path: 8080, handler: 1)]
14341}
14342",
14343 );
14344 assert_eq!(error.code, MISMATCH);
14345 assert_eq!(error.message, "expected `String`, found `Int`");
14346 }
14347
14348 #[test]
14349 fn a_case_the_host_enum_does_not_declare_is_rejected() {
14350 let error = rejects(
14351 "\
14352use http
14353
14354export fn method() -> http.Method {
14355 http.Method.Delete
14356}
14357",
14358 );
14359 assert_eq!(error.code, UNKNOWN_CASE);
14360 assert_eq!(error.message, "`http.Method` has no case `Delete`");
14361 assert_eq!(error.help.unwrap(), "`http.Method` declares `Get`, `Post`");
14362 }
14363
14364 #[test]
14367 fn an_operation_on_a_host_resource_is_checked_against_its_kind() {
14368 accepts(
14369 "\
14370use http
14371use console.println
14372
14373export fn main() -> Result<Unit, Error> {
14374 let server = http.listen(8080)?
14375 println(\"listening on :{server.port()}\")?
14376 server.close()?
14377 Ok(())
14378}
14379",
14380 );
14381
14382 let error = rejects(
14383 "\
14384use http
14385
14386export fn main() -> Result<Unit, Error> {
14387 let server = http.listen(8080)?
14388 server.handle(\"routes\")?
14389 Ok(())
14390}
14391",
14392 );
14393 assert_eq!(error.code, MISMATCH);
14394 assert_eq!(
14395 error.message,
14396 "expected `Array<http.Route>`, found `String`"
14397 );
14398 }
14399
14400 #[test]
14401 fn an_operation_a_host_resource_does_not_declare_is_rejected() {
14402 let error = rejects(
14403 "\
14404use http
14405
14406export fn main() -> Result<Unit, Error> {
14407 let server = http.listen(8080)?
14408 server.stop()?
14409 Ok(())
14410}
14411",
14412 );
14413 assert_eq!(error.code, UNKNOWN_HOST_OPERATION);
14414 assert_eq!(error.message, "`http.Server` has no operation `stop`");
14415 assert_eq!(
14416 error.help.unwrap(),
14417 "`http.Server` answers `port`, `handle`, `close`"
14418 );
14419 }
14420
14421 #[test]
14422 fn a_type_a_host_module_does_not_declare_is_rejected() {
14423 let error = rejects(
14424 "\
14425use http
14426
14427export fn handle(request: http.Payload) -> Int {
14428 1
14429}
14430",
14431 );
14432 assert_eq!(error.code, UNKNOWN_HOST_TYPE);
14433 assert_eq!(
14434 error.message,
14435 "host module `http` declares no type `Payload`"
14436 );
14437 assert_eq!(
14438 error.help.unwrap(),
14439 "`http` declares `Method`, `Request`, `Response`, `Route`, `Server`"
14440 );
14441 }
14442
14443 #[test]
14465 fn a_call_into_a_host_module_with_no_schema_is_not_reported_at_the_call() {
14466 let source = "\
14467use console.println
14468use sensors
14469
14470export fn main() -> Result<Unit, Error> {
14471 let value = sensors.read(\"pressure\")
14472 println(\"{value + 1}\")?
14473 Ok(())
14474}
14475";
14476 accepts(source);
14477 assert!(warnings_of(source).is_empty());
14478 assert!(notes_of(source).is_empty());
14479 }
14480
14481 #[test]
14489 fn a_callback_into_a_host_module_with_no_schema_is_not_reported() {
14490 let source = "\
14491use sensors
14492
14493fn run() -> Int {
14494 sensors.watch(fn(reading) { return 1 })
14495 1
14496}
14497";
14498 accepts(source);
14499 assert!(
14500 warnings_of(source).is_empty(),
14501 "{:?}",
14502 warnings_of(source)
14503 .iter()
14504 .map(|d| d.code.clone())
14505 .collect::<Vec<_>>()
14506 );
14507 }
14508
14509 #[test]
14510 fn a_member_of_a_host_module_with_no_schema_read_as_a_value_is_not_reported() {
14511 let source = "\
14512use sensors
14513
14514fn run() -> Int {
14515 let reading = sensors.latest
14516 1
14517}
14518";
14519 accepts(source);
14520 assert!(warnings_of(source).is_empty());
14521 }
14522
14523 #[test]
14524 fn a_type_from_a_host_module_with_no_schema_warns_rather_than_failing() {
14525 let warning = warns(
14526 "\
14527use sensors
14528
14529fn handle(reading: sensors.Reading) -> Int {
14530 1
14531}
14532",
14533 );
14534 assert_eq!(warning.code, HOST_TYPE);
14535 assert_eq!(
14536 warning.message,
14537 "`sensors.Reading` comes from a host module no Host API schema describes, so values of it are unchecked"
14538 );
14539 assert_eq!(
14540 warning.rule.as_deref().unwrap(),
14541 "A Host API's types come from its schema; the checker reads the shipped schemas and any an embedder supplies."
14542 );
14543 }
14544
14545 #[test]
14553 fn a_host_operation_read_as_a_value_has_the_type_its_schema_declares() {
14554 let source = "\
14555use console.println
14556use http
14557
14558export fn main() -> Result<Unit, Error> {
14559 let get = http.fetch
14560 let body = get(\"https://example.com\")?
14561 println(\"{body}\")?
14562 Ok(())
14563}
14564";
14565 accepts(source);
14566 assert!(warnings_of(source).is_empty());
14567 assert!(notes_of(source).is_empty());
14568 }
14569
14570 #[test]
14572 fn a_call_through_a_host_operation_value_is_checked() {
14573 let error = rejects(
14574 "\
14575use http
14576
14577fn run() -> Int {
14578 let get = http.fetch
14579 get(1)
14580 1
14581}
14582",
14583 );
14584 assert_eq!(error.code, MISMATCH);
14585 }
14586
14587 #[test]
14591 fn a_variadic_host_operation_used_as_a_value_is_noted() {
14592 let source = "\
14593use console
14594
14595fn run() -> Int {
14596 let write = console.println
14597 1
14598}
14599";
14600 accepts(source);
14601 assert!(warnings_of(source).is_empty());
14602 let notes = notes_of(source);
14603 assert_eq!(notes.len(), 1);
14604 assert_eq!(notes[0].code, VARIADIC_AS_VALUE);
14605 assert_eq!(
14606 notes[0].message,
14607 "`console.println` is variadic, so this value has no function type here"
14608 );
14609 }
14610
14611 #[test]
14614 fn a_host_type_read_as_a_value_is_an_error() {
14615 let error = rejects(
14616 "\
14617use http
14618
14619fn run() -> Int {
14620 let route = http.Route
14621 1
14622}
14623",
14624 );
14625 assert_eq!(error.code, NOT_A_VALUE);
14626 assert_eq!(error.message, "`http.Route` is a host type, not a value");
14627 assert_eq!(
14628 error.help.unwrap(),
14629 "construct one, as in `http.Route(field: value)`, or call the operation that answers one"
14630 );
14631 }
14632
14633 #[test]
14639 fn a_host_result_declared_any_is_noted_at_the_call() {
14640 let source = "\
14641use clock
14642
14643export fn main() -> Result<Unit, Error> {
14644 let value = clock.timeout(1s) {
14645 1
14646 }?
14647 Ok(())
14648}
14649";
14650 accepts(source);
14651 assert!(warnings_of(source).is_empty());
14652 let notes = notes_of(source);
14653 assert_eq!(notes.len(), 1);
14654 assert_eq!(notes[0].code, UNCONSTRAINED_RESULT);
14655 assert_eq!(
14656 notes[0].message,
14657 "`clock.timeout` declares its result `Result<Any, Error>`, so nothing here says what this call produced"
14658 );
14659 assert_eq!(
14660 notes[0].help.as_deref().unwrap(),
14661 "whatever the program does with the result of `clock.timeout` is checked at run time and by nothing here; the Host API schema declares `clock.timeout(Duration, Any) -> Result<Any, Error>`"
14662 );
14663 }
14664
14665 #[test]
14669 fn a_parameter_declared_any_is_not_noted() {
14670 let source = "\
14671use clock
14672
14673export fn main() -> Result<Unit, Error> {
14674 clock.every(1s) {
14675 1
14676 }?
14677 Ok(())
14678}
14679";
14680 accepts(source);
14681 assert!(notes_of(source).is_empty());
14682 assert!(warnings_of(source).is_empty());
14683 }
14684
14685 #[test]
14689 fn what_an_any_result_is_used_for_is_not_checked() {
14690 accepts(
14691 "\
14692use clock
14693
14694export fn main() -> Result<Unit, Error> {
14695 let value = clock.timeout(1s) {
14696 1
14697 }?
14698 let text: String = value
14699 Ok(())
14700}
14701",
14702 );
14703 }
14704
14705 #[test]
14710 fn a_host_field_declared_any_is_noted_where_it_is_read() {
14711 let source = "\
14712use http
14713
14714fn readHandler(route: http.Route) -> Int {
14715 let handler = route.handler
14716 1
14717}
14718";
14719 accepts(source);
14720 assert!(warnings_of(source).is_empty());
14721 let notes = notes_of(source);
14722 assert_eq!(notes.len(), 1);
14723 assert_eq!(notes[0].code, UNCONSTRAINED_FIELD);
14724 assert_eq!(
14725 notes[0].message,
14726 "`http.Route` declares `handler` as `Any`, so nothing here says what this field holds"
14727 );
14728 }
14729
14730 #[test]
14733 fn an_arity_error_on_an_any_result_operation_is_not_also_noted() {
14734 let source = "\
14735use clock
14736
14737export fn main() -> Result<Unit, Error> {
14738 clock.timeout(1s, 2, 3)?
14739 Ok(())
14740}
14741";
14742 let error = rejects(source);
14743 assert_eq!(error.code, ARITY);
14744 assert!(notes_of(source).is_empty());
14745 }
14746
14747 #[test]
14750 fn a_capitalized_name_no_module_declares_is_an_error() {
14751 let error = rejects("fn run() -> Int {\n Sensor(1)\n 1\n}\n");
14752 assert_eq!(error.code, UNRESOLVED_NAME);
14753 assert_eq!(error.message, "cannot find `Sensor` in this scope");
14754 assert_eq!(
14755 error.help.unwrap(),
14756 "declare `struct Sensor` or `enum Sensor` in this module, `use <module>.Sensor` to import it, or `use <host>` and write `<host>.Sensor`"
14757 );
14758 }
14759
14760 #[test]
14761 fn a_lowercase_name_nothing_declares_is_an_error() {
14762 let error = rejects("fn run() -> Int {\n total\n}\n");
14763 assert_eq!(error.code, UNKNOWN_NAME);
14764 assert_eq!(error.message, "cannot find `total` in this scope");
14765 assert_eq!(
14766 error.rule.unwrap(),
14767 "A name must be a local binding, a parameter, a declaration of this module, or something `use` imports."
14768 );
14769 assert_eq!(
14770 error.help.unwrap(),
14771 "declare `let total = ...` before this expression, or `use <host>.total`"
14772 );
14773 }
14774
14775 #[test]
14776 fn an_unknown_type_name_is_an_error() {
14777 let error = rejects("fn run(value: Missing) -> Int {\n 1\n}\n");
14778 assert_eq!(error.code, UNKNOWN_TYPE);
14779 assert_eq!(error.message, "`Missing` names no type this module can see");
14780 }
14781
14782 #[test]
14783 fn a_type_used_as_a_value_is_an_error() {
14784 let error = rejects(
14785 "\
14786struct Counter { hits: Int }
14787
14788fn run() -> Int {
14789 Counter
14790 1
14791}
14792",
14793 );
14794 assert_eq!(error.code, NOT_A_VALUE);
14795 assert_eq!(error.message, "`Counter` is a struct, not a value");
14796 assert_eq!(
14797 error.help.unwrap(),
14798 "construct one, as in `Counter(field: value)`, or name a value instead"
14799 );
14800 }
14801
14802 #[test]
14803 fn a_host_module_used_as_a_value_is_an_error() {
14804 let error = rejects(
14805 "\
14806use console
14807
14808fn run() -> Int {
14809 console
14810 1
14811}
14812",
14813 );
14814 assert_eq!(error.code, NOT_A_VALUE);
14815 assert_eq!(error.message, "`console` is a host module, not a value");
14816 }
14817
14818 #[test]
14822 fn a_return_in_a_function_value_nothing_expects_is_an_error() {
14823 let error = rejects_body(" let double = fn(n: Int) { return n * 2 }\n double(4)");
14824 assert_eq!(error.code, LAMBDA_RETURN);
14825 assert_eq!(
14826 error.message,
14827 "this function value uses `return`, but nothing says what it produces"
14828 );
14829 assert_eq!(
14830 error.rule.unwrap(),
14831 "A `return` is checked against a stated result type: a declaration writes one, and a function value takes one from the place that holds it."
14832 );
14833 }
14834
14835 #[test]
14836 fn a_return_in_a_function_value_the_place_types_is_checked() {
14837 accepts_body(" let double: fn(Int) -> Int = fn(n) { return n * 2 }\n double(4)");
14838 let error =
14839 rejects_body(" let double: fn(Int) -> Int = fn(n) { return \"two\" }\n double(4)");
14840 assert_eq!(error.code, MISMATCH);
14841 assert_eq!(error.message, "expected `Int`, found `String`");
14842 }
14843
14844 #[test]
14847 fn a_return_inside_a_body_a_schema_declared_any_is_not_reported() {
14848 accepts(
14849 "\
14850use clock
14851
14852export fn main() -> Result<Unit, Error> {
14853 clock.every(1s) {
14854 return 1
14855 }?
14856 Ok(())
14857}
14858",
14859 );
14860 }
14861
14862 #[test]
14863 fn a_lambda_parameter_with_no_expected_type_is_refused() {
14864 let error = rejects(&in_main(
14865 " let double = fn(n) { n * 2 }\n println(\"{double(4)}\")?",
14866 ));
14867 assert_eq!(error.code, UNCONSTRAINED);
14868 assert_eq!(error.message, "nothing says what `n` is");
14869 assert_eq!(
14870 error.help.unwrap(),
14871 "write the type, as in `n: <type>`, or give this function value to a place that declares one"
14872 );
14873 }
14874
14875 #[test]
14876 fn an_empty_array_literal_with_no_expected_type_is_refused() {
14877 let error = rejects(&in_main(
14878 " let empty = []\n println(\"{empty.length()} {empty.isEmpty()}\")?",
14879 ));
14880 assert_eq!(error.code, UNCONSTRAINED);
14881 assert_eq!(error.message, "nothing says what this empty array holds");
14882 assert_eq!(
14883 error.help.unwrap(),
14884 "write the type on the place that holds it, as in `let items: Array<Int> = []`"
14885 );
14886 }
14887
14888 #[test]
14889 fn an_empty_array_literal_the_place_types_does_not_warn() {
14890 accepts_body(" let empty: Array<Int> = []\n println(\"{empty.length()}\")?");
14891 assert!(warnings_of(&in_main(
14892 " let empty: Array<Int> = []\n println(\"{empty.length()}\")?"
14893 ))
14894 .is_empty());
14895 }
14896
14897 #[test]
14898 fn a_bare_none_with_no_expected_type_is_refused() {
14899 let error = rejects(&in_main(
14900 " let missing = None\n println(\"{missing.isNone()}\")?",
14901 ));
14902 assert_eq!(error.code, UNCONSTRAINED);
14903 assert_eq!(
14904 error.message,
14905 "nothing says what this `None` is an `Option` of"
14906 );
14907 }
14908
14909 #[test]
14910 fn a_none_the_place_types_does_not_warn() {
14911 assert!(warnings_of(&in_main(
14912 " let missing: Option<Int> = None\n println(\"{missing.isNone()}\")?"
14913 ))
14914 .is_empty());
14915 }
14916
14917 #[test]
14927 fn an_empty_collection_literal_settles_from_a_declared_return_type() {
14928 for source in [
14929 "fn f() -> Vector<Int> {\n Vector.of()\n}\n",
14930 "fn f() -> Set<Int> {\n Set.of()\n}\n",
14931 "fn f() -> Map<String, Int> {\n Map.of()\n}\n",
14932 ] {
14933 accepts(source);
14934 assert!(
14935 warnings_of(source).is_empty(),
14936 "the return type says what it holds: {source}"
14937 );
14938 }
14939 }
14940
14941 #[test]
14942 fn an_empty_collection_literal_settles_from_a_let_annotation() {
14943 accepts_body(" let empty: Vector<Int> = Vector.of()\n println(\"{empty.length()}\")?");
14944 assert!(warnings_of(&in_main(
14945 " let empty: Vector<Int> = Vector.of()\n println(\"{empty.length()}\")?"
14946 ))
14947 .is_empty());
14948 let error = rejects_body(" var empty: Vector<Int> = Vector.of()\n empty.push(\"one\")");
14951 assert_eq!(error.message, "expected `Int`, found `String`");
14952 }
14953
14954 #[test]
14955 fn an_empty_collection_literal_settles_from_a_parameter_s_default() {
14956 let source = "\
14957fn count(items: Vector<Int> = Vector.of()) -> Int {
14958 items.length()
14959}
14960
14961fn run() -> Int {
14962 count()
14963}
14964";
14965 accepts(source);
14966 assert!(warnings_of(source).is_empty());
14967 }
14968
14969 #[test]
14970 fn an_empty_collection_literal_settles_from_the_argument_position() {
14971 let source = "\
14972fn count(items: Set<Int>) -> Int {
14973 items.length()
14974}
14975
14976fn run() -> Int {
14977 count(Set.of())
14978}
14979";
14980 accepts(source);
14981 assert!(warnings_of(source).is_empty());
14982 }
14983
14984 #[test]
14985 fn an_empty_collection_literal_settles_from_a_struct_field() {
14986 let source = "\
14987struct Basket {
14988 items: Vector<Int>
14989}
14990
14991fn empty() -> Basket {
14992 Basket(items: Vector.of())
14993}
14994";
14995 accepts(source);
14996 assert!(warnings_of(source).is_empty());
14997 }
14998
14999 #[test]
15004 fn a_collection_literal_with_items_still_reads_them_and_not_the_place() {
15005 accepts_body(" let items = Vector.of(1, 2)\n println(\"{items.length()}\")?");
15006 assert!(warnings_of(&in_main(
15007 " let items = Vector.of(1, 2)\n println(\"{items.length()}\")?"
15008 ))
15009 .is_empty());
15010 let error = rejects("fn f() -> Vector<String> {\n Vector.of(1, 2)\n}\n");
15011 assert_eq!(
15012 error.message,
15013 "expected `Vector<String>`, found `Vector<Int>`"
15014 );
15015 }
15016
15017 #[test]
15020 fn an_error_inside_an_unchecked_call_is_still_reported() {
15021 let error = rejects(
15024 "\
15025use console.println
15026
15027export fn main() -> Result<Unit, Error> {
15028 println(1 + 1.0)?
15029 Ok(())
15030}
15031",
15032 );
15033 assert_eq!(error.code, OPERATOR);
15034 }
15035
15036 #[test]
15040 fn a_recovery_unknown_is_reported_once_however_far_it_spreads() {
15041 let error = rejects(
15042 "\
15043fn run(value: Missing) -> Int {
15044 value.field.other().length() + 1
15045}
15046",
15047 );
15048 assert_eq!(error.code, UNKNOWN_TYPE);
15049 }
15050
15051 #[test]
15055 fn the_arguments_of_a_rejected_call_are_not_reported_again() {
15056 let source = "\
15057fn run() -> Int {
15058 missing([], None, fn(n) { n })
15059 1
15060}
15061";
15062 let error = rejects(source);
15063 assert_eq!(error.code, UNKNOWN_NAME);
15064 assert!(warnings_of(source).is_empty());
15065 }
15066
15067 #[test]
15072 fn a_gap_in_the_value_of_a_body_a_schema_declared_any_is_not_reported() {
15073 let source = "\
15074use clock
15075
15076export fn main() -> Result<Unit, Error> {
15077 clock.timeout(1s) {
15078 []
15079 }?
15080 Ok(())
15081}
15082";
15083 accepts(source);
15084 assert!(warnings_of(source).is_empty());
15085 }
15086
15087 #[test]
15098 fn a_struct_type_parameter_nothing_settles_is_reported() {
15099 let source = "\
15100struct Tagged<T> { n: Int }
15101
15102fn needsString(t: Tagged<String>) -> Int { t.n }
15103
15104fn run() -> Int {
15105 let p = Tagged(n: 1)
15106 needsString(p)
15107}
15108";
15109 let error = rejects(source);
15110 assert_eq!(error.code, UNCONSTRAINED);
15111 assert_eq!(error.message, "nothing says what `T` is in `Tagged<T>`");
15112 }
15113
15114 #[test]
15117 fn a_struct_type_parameter_the_place_states_is_settled() {
15118 for body in [
15119 " let p: Tagged<String> = Tagged(n: 1)\n needsString(p)",
15120 " needsString(Tagged(n: 1))",
15121 ] {
15122 let source = format!(
15123 "\
15124struct Tagged<T> {{ n: Int }}
15125
15126fn needsString(t: Tagged<String>) -> Int {{ t.n }}
15127
15128fn run() -> Int {{
15129{body}
15130}}
15131"
15132 );
15133 accepts(&source);
15134 assert!(warnings_of(&source).is_empty(), "{source}");
15135 }
15136 }
15137
15138 #[test]
15144 fn a_return_in_a_map_error_callback_is_reported() {
15145 let error = rejects(
15146 "\
15147fn attempt() -> Result<Int, Error> { Ok(1) }
15148
15149fn run() -> Int {
15150 let r = attempt().mapError(fn(error) { return 42 })
15151 match r {
15152 Ok(v) => v
15153 Err(e) => e.length()
15154 }
15155}
15156",
15157 );
15158 assert_eq!(error.code, LAMBDA_RETURN);
15159 }
15160
15161 #[test]
15164 fn a_map_error_callback_that_ends_with_its_value_types_the_failure() {
15165 let error = rejects(
15166 "\
15167fn attempt() -> Result<Int, Error> { Ok(1) }
15168
15169fn run() -> Int {
15170 let r = attempt().mapError(fn(error) { 42 })
15171 match r {
15172 Ok(v) => v
15173 Err(e) => e.length()
15174 }
15175}
15176",
15177 );
15178 assert_eq!(error.code, UNKNOWN_METHOD);
15179 }
15180
15181 #[test]
15188 fn a_function_value_given_to_a_place_that_is_not_one_is_a_mismatch() {
15189 for source in [
15190 "fn run() -> Int {\n let x: Int = fn(n: Int) { n }\n x\n}\n",
15191 "fn run() -> Int {\n let x: Int = fn(n: Int) { return n }\n x\n}\n",
15192 ] {
15193 let error = rejects(source);
15194 assert_eq!(error.code, MISMATCH, "{source}");
15195 }
15196 }
15197
15198 #[test]
15202 fn only_a_placeholder_answers_that_it_must_not_escape() {
15203 assert!(Ty::placeholder().holds_placeholder());
15204 assert!(!Ty::recovery().holds_placeholder());
15205 assert!(!Ty::dynamic_boundary().holds_placeholder());
15206 assert!(!Ty::unconstrained().holds_placeholder());
15207 assert!(Ty::Array(Box::new(Ty::Option(Box::new(Ty::placeholder())))).holds_placeholder());
15210 assert!(Ty::func(false, vec![Ty::Int], Ty::placeholder()).holds_placeholder());
15211 assert!(Ty::recovery().is_accounted_for());
15214 assert!(Ty::dynamic_boundary().is_accounted_for());
15215 assert!(Ty::unconstrained().is_accounted_for());
15216 assert!(!Ty::placeholder().is_accounted_for());
15217 }
15218
15219 #[test]
15225 fn an_empty_array_a_sibling_settles_is_not_reported() {
15226 let source = "\
15227fn run() -> Int {
15228 let rows = [[], [1]]
15229 rows.length()
15230}
15231";
15232 accepts(source);
15233 assert!(warnings_of(source).is_empty());
15234 let error = rejects(
15237 "\
15238fn run() -> Int {
15239 let rows = [[], [1]]
15240 let first: Array<String> = rows[0]
15241 1
15242}
15243",
15244 );
15245 assert_eq!(error.code, MISMATCH);
15246 }
15247
15248 #[test]
15249 fn a_none_a_sibling_settles_is_not_reported() {
15250 let source = "\
15251fn run() -> Int {
15252 let values = [None, Some(1)]
15253 values.length()
15254}
15255";
15256 accepts(source);
15257 assert!(warnings_of(source).is_empty());
15258 }
15259
15260 #[test]
15261 fn a_none_the_other_branch_settles_is_not_reported() {
15262 let source = "\
15263fn run() -> Int {
15264 let value = if true { None } else { Some(1) }
15265 value.unwrapOr(0)
15266}
15267";
15268 accepts(source);
15269 assert!(warnings_of(source).is_empty());
15270 }
15271
15272 #[test]
15276 fn branches_that_disagree_are_still_reported_once() {
15277 let error = rejects(
15278 "\
15279fn run() -> Int {
15280 let value = if true { 1 } else { \"two\" }
15281 1
15282}
15283",
15284 );
15285 assert_eq!(error.code, BRANCHES);
15286 }
15287
15288 fn config_with_entry(entry: &str) -> Config {
15291 let mut runs = BTreeMap::new();
15292 runs.insert(
15293 "run".to_string(),
15294 crate::config::RunConfig {
15295 entry: entry.to_string(),
15296 allow: Vec::new(),
15297 fuel: None,
15298 deadline: None,
15299 max_host_calls: None,
15300 max_tasks: None,
15301 trace: None,
15302 generates: None,
15303 },
15304 );
15305 Config {
15306 runs,
15307 ..Config::default()
15308 }
15309 }
15310
15311 #[track_caller]
15312 fn entry_errors(source: &str) -> Vec<Diagnostic> {
15313 diagnostics_with(source, config_with_entry("main.main"))
15314 .into_iter()
15315 .filter(|d| d.severity == Severity::Error)
15316 .collect()
15317 }
15318
15319 #[test]
15320 fn accepts_both_entry_shapes() {
15321 assert!(
15322 entry_errors("export fn main() -> Result<Unit, Error> {\n Ok(())\n}\n").is_empty()
15323 );
15324 assert!(entry_errors(
15325 "export fn main(args: Array<String>) -> Result<Unit, Error> {\n Ok(())\n}\n"
15326 )
15327 .is_empty());
15328 assert!(entry_errors("export fn main() {\n}\n").is_empty());
15329 }
15330
15331 #[test]
15332 fn rejects_an_entry_with_two_parameters() {
15333 let errors = entry_errors(
15334 "export fn main(args: Array<String>, extra: Int) -> Result<Unit, Error> {\n Ok(())\n}\n",
15335 );
15336 assert_eq!(errors.len(), 1);
15337 assert_eq!(errors[0].code, ENTRY);
15338 assert_eq!(errors[0].message, "entry `main.main` declares 2 parameters");
15339 assert_eq!(
15340 errors[0].rule.as_deref().unwrap(),
15341 "An entry function takes either no parameters or one `Array<String>` of process arguments."
15342 );
15343 assert_eq!(
15344 errors[0].help.as_deref().unwrap(),
15345 "write `fn main()` or `fn main(args: Array<String>)`"
15346 );
15347 }
15348
15349 #[test]
15350 fn rejects_an_entry_whose_parameter_is_not_the_process_arguments() {
15351 let errors =
15352 entry_errors("export fn main(count: Int) -> Result<Unit, Error> {\n Ok(())\n}\n");
15353 assert_eq!(errors.len(), 1);
15354 assert_eq!(errors[0].code, ENTRY);
15355 assert_eq!(
15356 errors[0].message,
15357 "entry `main.main` takes `Int`, but the host passes `Array<String>`"
15358 );
15359 assert_eq!(
15360 errors[0].help.as_deref().unwrap(),
15361 "write `fn main(args: Array<String>)`"
15362 );
15363 }
15364
15365 #[test]
15366 fn rejects_an_entry_whose_result_the_host_cannot_report() {
15367 let errors = entry_errors("export fn main() -> Int {\n 1\n}\n");
15368 assert_eq!(errors.len(), 1);
15369 assert_eq!(errors[0].code, ENTRY);
15370 assert_eq!(
15371 errors[0].message,
15372 "entry `main.main` returns `Int`, which the host cannot report"
15373 );
15374 assert_eq!(
15375 errors[0].rule.as_deref().unwrap(),
15376 "The host reports an entry's failure through its `Err`, so an entry returns `()` or a `Result`."
15377 );
15378 }
15379
15380 #[test]
15396 fn every_program_in_the_repository_checks() {
15397 let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
15398 let mut packages = vec![root.join("examples"), root.join("tests/e2e")];
15399 let mut nested: Vec<PathBuf> = std::fs::read_dir(root.join("tests/e2e"))
15400 .expect("the end-to-end suite exists")
15401 .filter_map(|entry| entry.ok().map(|entry| entry.path()))
15402 .filter(|path| path.join("cove.toml").is_file())
15403 .collect();
15404 nested.sort();
15405 packages.append(&mut nested);
15406 assert!(
15407 packages.len() > 8,
15408 "expected to find every package, found {packages:?}"
15409 );
15410
15411 let mut failures = Vec::new();
15412 for directory in &packages {
15413 let name = directory
15414 .file_name()
15415 .expect("a package directory has a name")
15416 .to_string_lossy()
15417 .into_owned();
15418 let must_fail =
15419 name.starts_with("fail_") || matches!(name.as_str(), "fn_labels" | "type_struct");
15420
15421 let mut sources = SourceMap::new();
15422 let package = match crate::package::load(directory, &mut sources) {
15423 Ok(package) => package,
15424 Err(diagnostics) => {
15425 if !must_fail {
15426 failures.push(format!(
15427 "{name}: does not load: {}",
15428 render_all(&sources, &diagnostics)
15429 ));
15430 }
15431 continue;
15432 }
15433 };
15434 let program = match resolve(&package) {
15435 Ok(program) => program,
15436 Err(diagnostics) => {
15437 if !must_fail {
15438 failures.push(format!(
15439 "{name}: does not resolve: {}",
15440 render_all(&sources, &diagnostics)
15441 ));
15442 }
15443 continue;
15444 }
15445 };
15446 let errors: Vec<Diagnostic> = check(&package, &program)
15447 .into_iter()
15448 .filter(|d| d.severity == Severity::Error)
15449 .collect();
15450 match (must_fail, errors.is_empty()) {
15451 (false, false) => failures.push(format!(
15452 "{name}: does not type-check: {}",
15453 render_all(&sources, &errors)
15454 )),
15455 (true, true) => {
15456 failures.push(format!("{name}: was expected to fail, but it checks"))
15457 }
15458 _ => {}
15459 }
15460 }
15461 assert!(failures.is_empty(), "{}", failures.join("\n"));
15462 }
15463
15464 const LEVELS: &str = "\
15467/// Supported logging levels.
15468export enum LogLevel {
15469 Debug
15470 Info
15471}
15472
15473/// Validated configuration.
15474export struct Config {
15475 port: Int
15476 level: LogLevel
15477}
15478
15479/// A pair of ports.
15480export struct Pair<T> {
15481 first: T
15482 second: T
15483}
15484
15485/// The shape a handler has.
15486export type Handler = fn(Int) -> String
15487
15488impl Config {
15489 /// The port, as text.
15490 export fn describe(self) -> String {
15491 \"{self.port}\"
15492 }
15493}
15494
15495/// Loads configuration.
15496export fn load() -> Config {
15497 Config(port: 8080, level: LogLevel.Debug)
15498}
15499
15500fn secret() -> Int {
15501 1
15502}
15503";
15504
15505 #[test]
15506 fn the_checker_sees_an_imported_struct_s_fields() {
15507 accepts_modules(&[
15508 ("levels", LEVELS),
15509 (
15510 "app",
15511 "use levels.load\n\n/// Entry point.\nexport fn main() -> Int {\n load().port\n}\n",
15512 ),
15513 ]);
15514 }
15515
15516 #[test]
15517 fn a_field_an_imported_struct_does_not_declare_is_rejected() {
15518 let error = rejects_modules(&[
15519 ("levels", LEVELS),
15520 (
15521 "app",
15522 "use levels.load\n\n/// Entry point.\nexport fn main() -> Int {\n load().host\n}\n",
15523 ),
15524 ]);
15525 assert_eq!(error.code, UNKNOWN_FIELD);
15526 assert!(error.message.contains("levels.Config"));
15528 }
15529
15530 #[test]
15531 fn an_imported_struct_s_field_keeps_its_type() {
15532 let error = rejects_modules(&[
15533 ("levels", LEVELS),
15534 (
15535 "app",
15536 "use levels.load\n\n/// Entry point.\nexport fn main() -> String {\n load().port\n}\n",
15537 ),
15538 ]);
15539 assert_eq!(error.code, MISMATCH);
15540 assert!(error.message.contains("Int"));
15541 }
15542
15543 #[test]
15544 fn an_imported_struct_is_initialized_and_checked_like_a_declared_one() {
15545 accepts_modules(&[
15546 ("levels", LEVELS),
15547 (
15548 "app",
15549 "use levels.Config\nuse levels.LogLevel\n\n/// Entry point.\nexport fn main() -> Config {\n Config(port: 1, level: LogLevel.Info)\n}\n",
15550 ),
15551 ]);
15552 let error = rejects_modules(&[
15553 ("levels", LEVELS),
15554 (
15555 "app",
15556 "use levels.Config\nuse levels.LogLevel\n\n/// Entry point.\nexport fn main() -> Config {\n Config(port: \"1\", level: LogLevel.Info)\n}\n",
15557 ),
15558 ]);
15559 assert_eq!(error.code, MISMATCH);
15560 }
15561
15562 #[test]
15563 fn an_imported_function_s_arguments_are_checked() {
15564 let error = rejects_modules(&[
15565 (
15566 "greet",
15567 "/// Greets by name.\nexport fn greeting(name: String) -> String {\n name\n}\n",
15568 ),
15569 (
15570 "app",
15571 "use greet.greeting\n\n/// Entry point.\nexport fn main() -> String {\n greeting(1)\n}\n",
15572 ),
15573 ]);
15574 assert_eq!(error.code, MISMATCH);
15575 }
15576
15577 #[test]
15578 fn an_imported_method_is_reached_through_the_value_s_type() {
15579 accepts_modules(&[
15580 ("levels", LEVELS),
15581 (
15582 "app",
15583 "use levels.load\n\n/// Entry point.\nexport fn main() -> String {\n load().describe()\n}\n",
15584 ),
15585 ]);
15586 }
15587
15588 #[test]
15589 fn an_imported_enum_s_cases_are_checked() {
15590 accepts_modules(&[
15591 ("levels", LEVELS),
15592 (
15593 "app",
15594 "use levels.LogLevel\n\n/// Entry point.\nexport fn main(level: LogLevel) -> String {\n match level {\n LogLevel.Debug => \"d\"\n LogLevel.Info => \"i\"\n }\n}\n",
15595 ),
15596 ]);
15597 let error = rejects_modules(&[
15598 ("levels", LEVELS),
15599 (
15600 "app",
15601 "use levels.LogLevel\n\n/// Entry point.\nexport fn main() -> LogLevel {\n LogLevel.Bogus\n}\n",
15602 ),
15603 ]);
15604 assert_eq!(error.code, UNKNOWN_CASE);
15605 }
15606
15607 #[test]
15608 fn an_imported_generic_type_keeps_its_arity_and_arguments() {
15609 accepts_modules(&[
15610 ("levels", LEVELS),
15611 (
15612 "app",
15613 "use levels.Pair\n\n/// Entry point.\nexport fn main() -> Int {\n Pair(first: 1, second: 2).first\n}\n",
15614 ),
15615 ]);
15616 let error = rejects_modules(&[
15617 ("levels", LEVELS),
15618 (
15619 "app",
15620 "use levels.Pair\n\n/// Entry point.\nexport fn main() -> Pair<Int> {\n Pair(first: 1, second: \"2\")\n}\n",
15621 ),
15622 ]);
15623 assert_eq!(error.code, MISMATCH);
15624 }
15625
15626 const OPAQUE: &str = "\
15629/// A token, whose representation is this module's own business.
15630export opaque struct Token {
15631 raw: String
15632}
15633
15634/// Reads the representation from the module that declares it.
15635fn rawOf(token: Token) -> String {
15636 token.raw
15637}
15638
15639impl Token {
15640 /// Builds a token.
15641 export fn of(raw: String) -> Token {
15642 Token(raw: raw)
15643 }
15644
15645 /// The token as text.
15646 export fn text(self) -> String {
15647 rawOf(self)
15648 }
15649}
15650";
15651
15652 const OPAQUE_REPRESENTATION_CHANGED: &str = "\
15655/// A token, whose representation is this module's own business.
15656export opaque struct Token {
15657 scheme: String
15658 body: String
15659}
15660
15661impl Token {
15662 /// Builds a token.
15663 export fn of(raw: String) -> Token {
15664 Token(scheme: \"bearer\", body: raw)
15665 }
15666
15667 /// The token as text.
15668 export fn text(self) -> String {
15669 self.body
15670 }
15671}
15672";
15673
15674 #[test]
15678 fn the_declaring_module_builds_and_inspects_an_opaque_type() {
15679 accepts_modules(&[("auth", OPAQUE)]);
15680 }
15681
15682 #[test]
15683 fn another_module_may_not_build_an_opaque_type_field_by_field() {
15684 for caller in [
15685 "use auth.Token\n\n/// Entry point.\nexport fn main() -> Token {\n Token(raw: \"t\")\n}\n",
15686 "use auth\n\n/// Entry point.\nexport fn main() -> auth.Token {\n auth.Token(raw: \"t\")\n}\n",
15687 ] {
15688 let error = rejects_modules(&[("auth", OPAQUE), ("app", caller)]);
15689 assert_eq!(error.code, OPAQUE_CONSTRUCTION, "{caller}");
15690 assert!(error
15692 .help
15693 .as_ref()
15694 .expect("the diagnostic offers a correction")
15695 .contains("Token.of()"));
15696 }
15697 }
15698
15699 #[test]
15700 fn another_module_may_not_read_an_opaque_type_s_field() {
15701 let error = rejects_modules(&[
15702 ("auth", OPAQUE),
15703 (
15704 "app",
15705 "use auth.Token\n\n/// Entry point.\nexport fn main(token: Token) -> String {\n token.raw\n}\n",
15706 ),
15707 ]);
15708 assert_eq!(error.code, OPAQUE_FIELD);
15709 assert!(error
15710 .help
15711 .as_ref()
15712 .expect("the diagnostic offers a correction")
15713 .contains("text()"));
15714 }
15715
15716 #[test]
15719 fn another_module_may_not_assign_an_opaque_type_s_field() {
15720 let error = rejects_modules(&[
15721 ("auth", OPAQUE),
15722 (
15723 "app",
15724 "use auth.Token\n\n/// Entry point.\nexport fn main(var token: Token) {\n token.raw = \"other\"\n}\n",
15725 ),
15726 ]);
15727 assert_eq!(error.code, OPAQUE_FIELD);
15728 assert!(
15731 error.message.contains("cannot be assigned"),
15732 "{}",
15733 error.message
15734 );
15735 let help = error.help.expect("the diagnostic offers a correction");
15736 assert!(help.starts_with("change the value"), "{help}");
15737 }
15738
15739 #[test]
15742 fn a_refused_construction_does_not_disclose_the_fields() {
15743 for call in ["Token(bogus: 1)", "Token()", "Token(scheme: \"bearer\")"] {
15744 let caller = format!(
15745 "use auth.Token\n\n/// Entry point.\nexport fn main() -> Token {{\n {call}\n}}\n"
15746 );
15747 let error = rejects_modules(&[
15751 ("auth", OPAQUE_REPRESENTATION_CHANGED),
15752 ("app", caller.as_str()),
15753 ]);
15754 assert_eq!(error.code, OPAQUE_CONSTRUCTION, "{call}");
15755 for hidden in ["scheme", "body"] {
15756 let rendered = format!(
15757 "{}{}",
15758 error.message,
15759 error.help.clone().unwrap_or_default()
15760 );
15761 assert!(!rendered.contains(hidden), "{call} disclosed `{hidden}`");
15762 }
15763 }
15764 }
15765
15766 #[test]
15771 fn the_help_names_only_the_declaring_module_s_methods() {
15772 let error = rejects_modules(&[
15773 ("auth", OPAQUE),
15774 (
15775 "app",
15776 "use auth.Token\n\n/// A thing with a text form.\ntrait Show {\n /// Shows it.\n fn show(self) -> String\n}\n\nimpl Show for Token {\n fn show(self) -> String { self.raw }\n}\n",
15777 ),
15778 ]);
15779 assert_eq!(error.code, OPAQUE_FIELD);
15780 let help = error.help.expect("the diagnostic offers a correction");
15781 assert!(help.contains("text()"), "{help}");
15782 assert!(!help.contains("show()"), "{help}");
15783 }
15784
15785 #[test]
15788 fn another_module_uses_an_opaque_type_through_its_exported_operations() {
15789 accepts_modules(&[
15790 ("auth", OPAQUE),
15791 (
15792 "app",
15793 "use auth.Token\n\n/// Entry point.\nexport fn main() -> String {\n Token.of(\"t\").text()\n}\n",
15794 ),
15795 ]);
15796 }
15797
15798 #[test]
15801 fn an_opaque_type_s_representation_may_change_without_touching_its_callers() {
15802 let caller = "use auth.Token\n\n/// Entry point.\nexport fn main() -> String {\n Token.of(\"t\").text()\n}\n";
15803 accepts_modules(&[("auth", OPAQUE), ("app", caller)]);
15804 accepts_modules(&[("auth", OPAQUE_REPRESENTATION_CHANGED), ("app", caller)]);
15805 }
15806
15807 #[test]
15810 fn a_plain_exported_struct_still_exposes_its_representation() {
15811 accepts_modules(&[
15812 ("levels", LEVELS),
15813 (
15814 "app",
15815 "use levels.Config\nuse levels.LogLevel\n\n/// Entry point.\nexport fn main() -> Int {\n Config(port: 1, level: LogLevel.Info).port\n}\n",
15816 ),
15817 ]);
15818 }
15819
15820 #[test]
15821 fn an_imported_type_alias_expands() {
15822 accepts_modules(&[
15823 ("levels", LEVELS),
15824 (
15825 "app",
15826 "use levels.Handler\n\n/// Entry point.\nexport fn main(handler: Handler) -> String {\n handler(1)\n}\n",
15827 ),
15828 ]);
15829 }
15830
15831 #[test]
15834 fn a_module_imported_whole_is_named_qualified() {
15835 accepts_modules(&[
15836 ("levels", LEVELS),
15837 (
15838 "app",
15839 "use levels\n\n/// Entry point.\nexport fn main() -> levels.Config {\n levels.load()\n}\n",
15840 ),
15841 ]);
15842 let error = rejects_modules(&[
15843 ("levels", LEVELS),
15844 (
15845 "app",
15846 "use levels\n\n/// Entry point.\nexport fn main() -> Int {\n levels.load()\n}\n",
15847 ),
15848 ]);
15849 assert_eq!(error.code, MISMATCH);
15850 }
15851
15852 #[test]
15853 fn a_qualified_name_a_module_does_not_export_is_rejected() {
15854 let error = rejects_modules(&[
15855 ("levels", LEVELS),
15856 (
15857 "app",
15858 "use levels\n\n/// Entry point.\nexport fn main() -> Int {\n levels.secret()\n}\n",
15859 ),
15860 ]);
15861 assert_eq!(error.code, UNKNOWN_MEMBER);
15862 assert!(error.message.contains("not exported"));
15863 }
15864
15865 #[test]
15866 fn a_qualified_name_a_module_does_not_declare_is_rejected() {
15867 let error = rejects_modules(&[
15868 ("levels", LEVELS),
15869 (
15870 "app",
15871 "use levels\n\n/// Entry point.\nexport fn main() -> Int {\n levels.missing()\n}\n",
15872 ),
15873 ]);
15874 assert_eq!(error.code, UNKNOWN_MEMBER);
15875 assert!(error.message.contains("declares no `missing`"));
15876 }
15877
15878 #[test]
15881 fn two_modules_declaring_one_name_are_different_types() {
15882 let error = rejects_modules(&[
15883 ("levels", LEVELS),
15884 (
15885 "app",
15886 "use levels.load\n\n/// This module's own `Config`.\nexport struct Config {\n port: Int\n}\n\n\
15887 /// Entry point.\nexport fn main() -> Config {\n load()\n}\n",
15888 ),
15889 ]);
15890 assert_eq!(error.code, MISMATCH);
15891 assert!(error.message.contains("levels.Config"));
15892 }
15893
15894 #[test]
15897 fn a_type_reached_without_importing_it_still_has_its_fields() {
15898 accepts_modules(&[
15899 ("levels", LEVELS),
15900 (
15901 "app",
15902 "use levels.load\n\n/// Entry point.\nexport fn main() -> String {\n load().describe()\n}\n",
15903 ),
15904 ]);
15905 let error = rejects_modules(&[
15906 ("levels", LEVELS),
15907 (
15908 "app",
15909 "use levels.load\n\n/// Entry point.\nexport fn main() -> String {\n load().level\n}\n",
15910 ),
15911 ]);
15912 assert_eq!(error.code, MISMATCH);
15913 assert!(error.message.contains("levels.LogLevel"));
15914 }
15915
15916 #[test]
15919 fn a_type_keeps_one_identity_through_two_imports() {
15920 accepts_modules(&[
15921 ("levels", LEVELS),
15922 (
15923 "middle",
15924 "use levels.load\nuse levels.Config\n\n/// Reloads.\nexport fn reload() -> Config {\n load()\n}\n",
15925 ),
15926 (
15927 "app",
15928 "use middle.reload\nuse levels.Config\n\n/// Entry point.\nexport fn main() -> Config {\n reload()\n}\n",
15929 ),
15930 ]);
15931 }
15932
15933 #[test]
15936 fn a_type_keeps_one_identity_through_a_diamond() {
15937 accepts_modules(&[
15938 ("levels", LEVELS),
15939 (
15940 "left",
15941 "use levels.load\nuse levels.Config\n\n/// Loads.\nexport fn fromLeft() -> Config {\n load()\n}\n",
15942 ),
15943 (
15944 "right",
15945 "use levels.load\nuse levels.Config\n\n/// Loads.\nexport fn fromRight() -> Config {\n load()\n}\n",
15946 ),
15947 (
15948 "app",
15949 "use left.fromLeft\nuse right.fromRight\n\n/// Entry point.\nexport fn main() -> Int {\n fromLeft().port + fromRight().port\n}\n",
15950 ),
15951 ]);
15952 }
15953
15954 const DISPLAY: &str = "\
15957/// Renders itself.
15958export trait Display {
15959 /// The full form.
15960 fn describe(self) -> String
15961
15962 /// A short form, defaulting to the full one.
15963 fn label(self) -> String { self.describe() }
15964}
15965
15966/// Renders anything that conforms.
15967export fn render<T: Display>(value: T) -> String {
15968 value.label()
15969}
15970";
15971
15972 const BOOKING: &str = "\
15973/// A booking.
15974export struct Booking {
15975 id: Int
15976}
15977";
15978
15979 #[test]
15982 fn a_bound_is_satisfied_by_a_conformance_to_an_imported_trait() {
15983 let booking = format!(
15984 "use display.Display\n\n{BOOKING}\nimpl Display for Booking {{\n \
15985 /// The full form.\n fn describe(self) -> String {{\n \"b\"\n }}\n}}\n"
15986 );
15987 accepts_modules(&[
15988 ("display", DISPLAY),
15989 ("booking", &booking),
15990 (
15991 "app",
15992 "use display.render\nuse booking.Booking\n\n\
15993 /// Entry point.\nexport fn main() -> String {\n render(Booking(id: 1))\n}\n",
15994 ),
15995 ]);
15996 }
15997
15998 #[test]
16001 fn a_bound_is_satisfied_by_a_conformance_to_an_imported_type() {
16002 let display = format!(
16003 "use booking.Booking\n\n{DISPLAY}\nimpl Display for Booking {{\n \
16004 /// The full form.\n fn describe(self) -> String {{\n \"b\"\n }}\n}}\n"
16005 );
16006 accepts_modules(&[
16007 ("booking", BOOKING),
16008 ("display", &display),
16009 (
16010 "app",
16011 "use display.render\nuse booking.Booking\n\n\
16012 /// Entry point.\nexport fn main() -> String {\n render(Booking(id: 1))\n}\n",
16013 ),
16014 ]);
16015 }
16016
16017 #[test]
16020 fn a_conformance_method_declared_elsewhere_is_a_method_of_the_type() {
16021 let display = format!(
16022 "use booking.Booking\n\n{DISPLAY}\nimpl Display for Booking {{\n \
16023 /// The full form.\n fn describe(self) -> String {{\n \"b\"\n }}\n}}\n"
16024 );
16025 accepts_modules(&[
16026 ("booking", BOOKING),
16027 ("display", &display),
16028 (
16029 "app",
16030 "use display.Display\nuse booking.Booking\n\n\
16031 /// Entry point.\nexport fn main(value: Booking) -> String {\n value.describe()\n}\n",
16032 ),
16033 ]);
16034 }
16035
16036 #[test]
16037 fn a_type_that_conforms_nowhere_does_not_satisfy_an_imported_bound() {
16038 let error = rejects_modules(&[
16039 ("display", DISPLAY),
16040 ("booking", BOOKING),
16041 (
16042 "app",
16043 "use display.render\nuse booking.Booking\n\n\
16044 /// Entry point.\nexport fn main() -> String {\n render(Booking(id: 1))\n}\n",
16045 ),
16046 ]);
16047 assert_eq!(error.code, UNSATISFIED_BOUND);
16048 assert!(error.message.contains("booking.Booking"));
16049 assert!(error.message.contains("display.Display"));
16050 }
16051
16052 #[test]
16055 fn dyn_names_an_imported_trait() {
16056 let booking = format!(
16057 "use display.Display\n\n{BOOKING}\nimpl Display for Booking {{\n \
16058 /// The full form.\n fn describe(self) -> String {{\n \"b\"\n }}\n}}\n"
16059 );
16060 accepts_modules(&[
16061 ("display", DISPLAY),
16062 ("booking", &booking),
16063 (
16064 "app",
16065 "use display.Display\nuse booking.Booking\n\n\
16066 /// Entry point.\nexport fn main() -> String {\n \
16067 let shown: dyn Display = Booking(id: 1)\n shown.label()\n}\n",
16068 ),
16069 ]);
16070 }
16071
16072 #[test]
16073 fn a_trait_neither_declared_nor_imported_is_not_a_trait() {
16074 let error = rejects_modules(&[
16075 ("display", DISPLAY),
16076 (
16077 "app",
16078 "/// Entry point.\nexport fn main() -> Int {\n 1\n}\n\n\
16079 /// Renders.\nfn show<T: Display>(value: T) -> String {\n \"x\"\n}\n",
16080 ),
16081 ]);
16082 assert_eq!(error.code, UNKNOWN_TRAIT);
16083 }
16084
16085 #[test]
16088 fn two_modules_declaring_one_trait_name_are_different_traits() {
16089 let booking = format!(
16090 "use display.Display\n\n{BOOKING}\nimpl Display for Booking {{\n \
16091 /// The full form.\n fn describe(self) -> String {{\n \"b\"\n }}\n}}\n"
16092 );
16093 let error = rejects_modules(&[
16094 ("display", DISPLAY),
16095 ("booking", &booking),
16096 (
16097 "app",
16098 "use booking.Booking\n\n\
16099 /// This module's own `Display`, unrelated to `display`'s.\n\
16100 trait Display {\n /// The full form.\n fn describe(self) -> String\n}\n\n\
16101 /// Entry point.\nexport fn main() -> Int {\n \
16102 let shown: dyn Display = Booking(id: 1)\n 1\n}\n",
16103 ),
16104 ]);
16105 assert_eq!(error.code, MISMATCH);
16106 assert!(error.message.contains("dyn Display"));
16107 }
16108
16109 #[test]
16112 fn a_conformance_to_an_imported_trait_must_match_its_signature() {
16113 let booking = format!(
16114 "use display.Display\n\n{BOOKING}\nimpl Display for Booking {{\n \
16115 /// The full form.\n fn describe(self) -> Int {{\n 1\n }}\n}}\n"
16116 );
16117 let error = rejects_modules(&[("display", DISPLAY), ("booking", &booking)]);
16118 assert_eq!(error.code, CONFORMANCE_SIGNATURE);
16119 }
16120
16121 #[test]
16122 fn a_name_neither_declared_nor_imported_is_still_unresolved() {
16123 let error = rejects_modules(&[
16124 (
16125 "greet",
16126 "/// Greets.\nexport fn greeting() -> String {\n \"hi\"\n}\n",
16127 ),
16128 (
16129 "app",
16130 "/// Entry point.\nexport fn main() -> String {\n greeting()\n}\n",
16131 ),
16132 ]);
16133 assert_eq!(error.code, UNKNOWN_NAME);
16134 }
16135
16136 fn render_all(sources: &SourceMap, diagnostics: &[Diagnostic]) -> String {
16137 diagnostics
16138 .iter()
16139 .map(|d| cove_diag::render(sources, d))
16140 .collect::<Vec<_>>()
16141 .join("")
16142 }
16143}