1use std::cell::RefCell;
28use std::collections::BTreeSet;
29use std::rc::Rc;
30use std::sync::Arc;
31use std::time::Duration;
32
33use cove_diag::{SourceMap, Span};
34use cove_schema::builtins::{FreeBuiltinKind, MAP_ENTRY, NONE_CASE, OPTION, RESULT};
35use cove_sema::resolve::{Program, ResolvedModule};
36use cove_syntax::ast::{
37 Arg, BinaryOp, Block, EnumDecl, Expr, ExprKind, FnDecl, Ident, ItemKind, Param, Pattern,
38 PatternKind, Receiver, StmtKind, StrPart, StructDecl, Type, TypeKind, UnaryOp,
39};
40
41use crate::budget::{Budget, Cancellation, Meter, Stopped};
42use crate::builtins::{self, Callable};
43use crate::error::RuntimeError;
44use crate::heap::{Collection, Heap, HeapStats, SlotRoots};
45use crate::host::{HostRegistry, Reentry, ResourceHandle};
46use crate::runtime::{Runtime, ENTRY_TASK};
47use crate::schema::TypeSchema;
48use crate::task::{self, ChildFailure, Task, TaskOutcome, TaskScope, Tasking, Transfer};
49use crate::trace::{RunOutcome, Timing, TraceEvent};
50use crate::value::{
51 Closure, ClosureBody, DynValue, EnumValue, HostFnValue, RangeBounds, Repr, StructValue, Value,
52};
53use crate::wallclock::Instant;
54
55pub(crate) const MAX_CALL_DEPTH: usize = 256;
72
73#[cfg(debug_assertions)]
102const STACK_PER_FRAME: usize = 136 * 1024;
103
104#[cfg(not(debug_assertions))]
107const STACK_PER_FRAME: usize = 10 * 1024;
108
109#[cfg(debug_assertions)]
121const STACK_PER_REENTRY: usize = 164 * 1024;
122
123#[cfg(not(debug_assertions))]
126const STACK_PER_REENTRY: usize = 17 * 1024;
127
128const STACK_MARGIN: usize = 3;
137
138pub const STACK_SIZE: usize =
179 STACK_MARGIN * (MAX_CALL_DEPTH * STACK_PER_FRAME + MAX_REENTRY_DEPTH * STACK_PER_REENTRY);
180
181pub fn on_cove_stack<T: Send>(body: impl FnOnce() -> T + Send) -> std::io::Result<T> {
202 std::thread::scope(|scope| {
203 let thread = std::thread::Builder::new()
204 .name("cove entry".to_string())
205 .stack_size(STACK_SIZE)
206 .spawn_scoped(scope, body)?;
207 match thread.join() {
208 Ok(value) => Ok(value),
209 Err(panic) => std::panic::resume_unwind(panic),
210 }
211 })
212}
213
214pub(crate) const MAX_REENTRY_DEPTH: usize = 8;
238
239pub const SAFEPOINT_FUEL: u64 = 10;
250
251enum Control {
253 Error(RuntimeError),
254 Return(Value),
256 Break,
260 Continue,
262}
263
264impl From<RuntimeError> for Control {
265 fn from(error: RuntimeError) -> Self {
266 Control::Error(error)
267 }
268}
269
270type Eval = Result<Value, Control>;
271
272fn finish(result: Eval) -> Result<Value, RuntimeError> {
279 match result {
280 Ok(value) => Ok(value),
281 Err(Control::Return(value)) => Ok(value),
282 Err(Control::Error(error)) => Err(error),
283 Err(Control::Break) => {
284 unreachable!("`break` outside a loop is rejected before execution")
285 }
286 Err(Control::Continue) => {
287 unreachable!("`continue` outside a loop is rejected before execution")
288 }
289 }
290}
291
292#[derive(Clone)]
304struct Place {
305 slot: Rc<RefCell<Value>>,
306 steps: Vec<Rc<str>>,
307}
308
309impl Place {
310 fn binding(value: Value) -> Place {
311 Place {
312 slot: Rc::new(RefCell::new(value)),
313 steps: Vec::new(),
314 }
315 }
316
317 fn field(&self, name: Rc<str>) -> Place {
318 let mut steps = self.steps.clone();
319 steps.push(name);
320 Place {
321 slot: self.slot.clone(),
322 steps,
323 }
324 }
325
326 fn with_ref<R>(&self, span: Span, f: impl FnOnce(&Value) -> R) -> Result<R, RuntimeError> {
327 let root = self.slot.borrow();
328 let mut current: &Value = &root;
329 for step in &self.steps {
330 match current {
331 Value(Repr::Struct(value)) => {
332 current = value
333 .get(step)
334 .ok_or_else(|| no_field(&value.type_name, step, span))?;
335 }
336 other => return Err(not_a_struct(other, step, span)),
337 }
338 }
339 Ok(f(current))
340 }
341
342 fn with_mut<R>(&self, span: Span, f: impl FnOnce(&mut Value) -> R) -> Result<R, RuntimeError> {
343 let mut root = self.slot.borrow_mut();
344 let mut current: &mut Value = &mut root;
345 for step in &self.steps {
346 match current {
347 Value(Repr::Struct(value)) => {
348 let type_name = value.type_name.clone();
349 current = Rc::make_mut(value)
355 .get_mut(step)
356 .ok_or_else(|| no_field(&type_name, step, span))?;
357 }
358 other => return Err(not_a_struct(other, step, span)),
359 }
360 }
361 Ok(f(current))
362 }
363
364 fn read(&self, span: Span) -> Result<Value, RuntimeError> {
366 self.with_ref(span, Value::clone)
367 }
368
369 fn write(&self, span: Span, value: Value) -> Result<(), RuntimeError> {
370 self.with_mut(span, |slot| *slot = value)
371 }
372}
373
374struct Env {
412 module: Rc<str>,
413 captures: Vec<(Rc<str>, Place)>,
416 frame: Vec<(Rc<str>, Place)>,
418 marks: Vec<(usize, usize)>,
421 roots: Rc<RefCell<SlotRoots>>,
422 base: usize,
424}
425
426impl Env {
427 fn new(module: Rc<str>, roots: Rc<RefCell<SlotRoots>>) -> Env {
428 let base = roots.borrow().len();
429 Env {
430 module,
431 captures: Vec::new(),
432 frame: Vec::new(),
433 marks: Vec::new(),
434 roots,
435 base,
436 }
437 }
438
439 fn push(&mut self) {
440 let roots_mark = self.roots.borrow().len();
441 self.marks.push((self.frame.len(), roots_mark));
442 }
443
444 fn pop(&mut self) {
445 if let Some((frame_mark, roots_mark)) = self.marks.pop() {
446 self.frame.truncate(frame_mark);
447 self.roots.borrow_mut().truncate(roots_mark);
448 }
449 }
450
451 fn declare(&mut self, name: Rc<str>, place: Place) {
453 self.roots.borrow_mut().push(place.slot.clone());
454 self.frame.push((name, place));
455 }
456
457 fn declare_capture(&mut self, name: Rc<str>, place: Place) {
462 self.roots.borrow_mut().push(place.slot.clone());
463 self.captures.push((name, place));
464 }
465
466 fn lookup(&self, name: &str) -> Option<&Place> {
467 self.frame
468 .iter()
469 .rev()
470 .chain(self.captures.iter().rev())
471 .find(|(n, _)| &**n == name)
472 .map(|(_, place)| place)
473 }
474
475 fn captures(
485 &self,
486 mentioned: &BTreeSet<String>,
487 span: Span,
488 ) -> Result<Vec<(Rc<str>, Value)>, RuntimeError> {
489 let mut captured: Vec<(Rc<str>, Value)> = Vec::new();
490 for (name, place) in self.captures.iter().chain(self.frame.iter()) {
491 if !mentioned.contains(&**name) {
492 continue;
493 }
494 let value = place.read(span)?;
495 match captured.iter_mut().find(|(n, _)| n == name) {
496 Some(slot) => slot.1 = value,
497 None => captured.push((name.clone(), value)),
498 }
499 }
500 Ok(captured)
501 }
502}
503
504impl Drop for Env {
509 fn drop(&mut self) {
510 self.roots.borrow_mut().truncate(self.base);
511 }
512}
513
514struct EvaluatedArg {
516 label: Option<Rc<str>>,
517 spread: bool,
518 slot: ArgSlot,
519 span: Span,
520}
521
522enum ArgSlot {
524 Value(Value),
525 Alias(Place),
526}
527
528struct Target<'t> {
530 name: &'t str,
531 params: &'t [Param],
532 body: &'t Block,
533 module: Rc<str>,
534 receiver: Option<Receiver>,
535 is_async: bool,
536 captures: &'t [(Rc<str>, Value)],
537 return_type: Option<&'t Type>,
541}
542
543impl Tasking for Interpreter<'_> {
551 fn runtime(&self) -> &Runtime {
552 self.runtime
553 }
554
555 fn hosts(&self) -> &HostRegistry {
556 self.hosts
557 }
558
559 fn charge_wait(&mut self, wait: Duration) {
560 Interpreter::charge_wait(self, wait);
561 }
562
563 fn running_task(&self) -> Option<u64> {
564 self.task_stack.last().copied()
565 }
566}
567
568pub struct Interpreter<'a> {
586 pub program: &'a Program,
587 pub sources: &'a SourceMap,
588 pub hosts: &'a HostRegistry,
589 runtime: &'a Runtime,
592 depth: usize,
593 call_sites: Vec<Span>,
607 budget: Option<Meter>,
619 call_depth_limit: Option<usize>,
626 cancellation: Option<Cancellation>,
634 stops: Vec<Cancellation>,
641 reentry_depth: usize,
648 task_stack: Vec<u64>,
651 timings: Vec<Timing>,
657 roots: Rc<RefCell<SlotRoots>>,
661 heap: Heap,
668 method_key: std::cell::Cell<(String, String)>,
677 assertion_failure: Option<(Span, String)>,
688}
689
690impl<'a> Interpreter<'a> {
691 pub fn new(runtime: &'a Runtime) -> Self {
702 let mut interpreter = Interpreter {
703 program: runtime.program(),
704 sources: runtime.sources(),
705 hosts: runtime.hosts(),
706 runtime,
707 depth: 0,
708 call_sites: Vec::new(),
709 budget: None,
710 call_depth_limit: None,
711 cancellation: None,
712 stops: Vec::new(),
713 reentry_depth: 0,
714 task_stack: Vec::new(),
715 timings: Vec::new(),
716 roots: Rc::new(RefCell::new(SlotRoots::new())),
717 heap: Heap::new(),
718 method_key: std::cell::Cell::new((String::new(), String::new())),
719 assertion_failure: None,
720 };
721 interpreter.bind_budget();
722 interpreter
723 }
724
725 fn bind_budget(&mut self) {
736 self.budget = self.hosts.budget_meter();
737 self.call_depth_limit = self
738 .budget
739 .as_ref()
740 .and_then(|budget| budget.limits().max_call_depth);
741 }
742
743 pub fn heap_stats(&self) -> HeapStats {
752 let mut stats = self.runtime.heap_stats();
753 let mine = self.heap.stats();
754 stats.live_bytes = mine.live_bytes;
755 stats.live_objects = mine.live_objects;
756 stats
757 }
758
759 pub fn allocate_vector(&mut self, elements: Vec<Value>) -> Value {
764 Value(Repr::Vector(self.heap.allocate(elements)))
765 }
766
767 fn task_id(&self) -> u64 {
773 self.task_stack.last().copied().unwrap_or(ENTRY_TASK)
774 }
775
776 pub fn collect(&mut self) -> Collection {
781 let roots = Rc::clone(&self.roots);
782 let collected = {
783 let roots = roots.borrow();
784 self.heap.collect(&*roots)
785 };
786 let task = self.task_id();
787 self.runtime.trace(TraceEvent::HeapCollected {
788 task,
789 allocated: collected.allocated,
790 freed: collected.freed_objects,
791 live_objects: collected.live_objects,
792 live_bytes: collected.live_bytes,
793 pause: collected.pause,
794 });
795 collected
796 }
797
798 fn collect_if_due(&mut self) {
800 if self.heap.should_collect() {
801 self.collect();
802 }
803 }
804
805 fn retire_heap(&mut self) {
816 if !self.heap.is_empty() {
817 self.collect();
818 }
819 let stats = self.heap.take_stats();
820 self.runtime.retire_heap(&stats);
821 }
822
823 pub fn assertion_failure(&self) -> Option<(Span, &str)> {
830 self.assertion_failure
831 .as_ref()
832 .map(|(span, message)| (*span, message.as_str()))
833 }
834
835 fn source_text(&self, span: Span) -> &str {
838 source_text(self.sources, span)
839 }
840
841 fn for_task(runtime: &'a Runtime, id: u64, cancellation: Cancellation) -> Self {
844 let mut interpreter = Interpreter::new(runtime);
845 interpreter.cancellation = Some(cancellation);
846 interpreter.task_stack.push(id);
847 interpreter
848 }
849
850 pub fn run_entry(
907 &mut self,
908 module: &str,
909 name: &str,
910 args: Vec<Rc<str>>,
911 ) -> Result<Value, RuntimeError> {
912 let outcome = self.enter(module, name, args);
913 self.ended(outcome)
914 }
915
916 pub fn invoke(
957 &mut self,
958 module: &str,
959 name: &str,
960 args: Vec<Value>,
961 ) -> Result<Value, RuntimeError> {
962 let outcome = self.invoke_checked(module, name, args);
963 self.ended(outcome)
964 }
965
966 pub fn invoke_within(
1020 &mut self,
1021 budget: Budget,
1022 module: &str,
1023 name: &str,
1024 args: Vec<Value>,
1025 ) -> Result<Value, RuntimeError> {
1026 crate::invoke::check(self.program, module, name, &args)?;
1027 self.hosts().begin_run(budget);
1028 self.bind_budget();
1029 let outcome = self.enter_with(module, name, args);
1030 self.ended(outcome)
1031 }
1032
1033 pub fn run_entry_within(
1039 &mut self,
1040 budget: Budget,
1041 module: &str,
1042 name: &str,
1043 args: Vec<Rc<str>>,
1044 ) -> Result<Value, RuntimeError> {
1045 self.hosts().begin_run(budget);
1046 self.bind_budget();
1047 let outcome = self.enter(module, name, args);
1048 self.ended(outcome)
1049 }
1050
1051 fn invoke_checked(
1053 &mut self,
1054 module: &str,
1055 name: &str,
1056 args: Vec<Value>,
1057 ) -> Result<Value, RuntimeError> {
1058 crate::invoke::check(self.program, module, name, &args)?;
1059 self.enter_with(module, name, args)
1060 }
1061
1062 fn ended(&self, outcome: Result<Value, RuntimeError>) -> Result<Value, RuntimeError> {
1068 let (classification, message) = match &outcome {
1069 Ok(value) if value.is_err() => (RunOutcome::Error, returned_error_message(value)),
1074 Ok(_) => (RunOutcome::Success, None),
1075 Err(error) => (error.outcome, Some(error.message.clone())),
1076 };
1077 self.runtime.trace(TraceEvent::RunEnded {
1078 outcome: classification,
1079 message,
1080 });
1081 outcome
1082 }
1083
1084 fn enter(
1089 &mut self,
1090 module: &str,
1091 name: &str,
1092 args: Vec<Rc<str>>,
1093 ) -> Result<Value, RuntimeError> {
1094 let entry = self.program.lookup_fn(module, name).ok_or_else(|| {
1095 RuntimeError::new(format!("this package does not declare `{module}.{name}`"))
1096 })?;
1097 let decl = entry.decl.clone();
1098 let span = decl.span;
1099
1100 let arguments = match decl.params.len() {
1101 0 => Vec::new(),
1102 1 => vec![Value(Repr::Array(
1103 args.into_iter().map(Value::string).collect(),
1104 ))],
1105 other => {
1106 return Err(RuntimeError::new(format!(
1107 "entry `{module}.{name}` declares {other} parameters"
1108 ))
1109 .at(span)
1110 .with_rule(
1111 "An entry function takes either no parameters or one `Array<String>` of process arguments.",
1112 )
1113 .with_help(format!(
1114 "write `fn {name}()` or `fn {name}(args: Array<String>)`"
1115 )));
1116 }
1117 };
1118 self.enter_with(module, name, arguments)
1119 }
1120
1121 fn enter_with(
1129 &mut self,
1130 module: &str,
1131 name: &str,
1132 args: Vec<Value>,
1133 ) -> Result<Value, RuntimeError> {
1134 let entry = self.program.lookup_fn(module, name).ok_or_else(|| {
1135 RuntimeError::new(format!("this package does not declare `{module}.{name}`"))
1136 })?;
1137 let decl = entry.decl.clone();
1138 let span = decl.span;
1139 let arguments: Vec<EvaluatedArg> = args
1140 .into_iter()
1141 .enumerate()
1142 .map(|(position, value)| EvaluatedArg {
1143 label: None,
1144 spread: false,
1145 slot: ArgSlot::Value(value),
1146 span: decl.params.get(position).map_or(span, |param| param.span),
1149 })
1150 .collect();
1151
1152 self.runtime.trace(TraceEvent::EntryEnter {
1153 module: module.to_string(),
1154 function: name.to_string(),
1155 });
1156 self.timings.push(Timing::start());
1157
1158 let outcome = self
1159 .call_target(
1160 &Target {
1161 name,
1162 params: &decl.params,
1163 body: &decl.body,
1164 module: module.into(),
1165 receiver: decl.receiver,
1166 is_async: decl.is_async,
1167 captures: &[],
1168 return_type: decl.return_type.as_ref(),
1169 },
1170 None,
1171 arguments,
1172 span,
1173 )
1174 .and_then(|value| match value {
1175 Value(Repr::Task(task)) => self.settle(&task, span),
1179 value => Ok(value),
1180 });
1181
1182 let timing = self
1183 .timings
1184 .pop()
1185 .expect("an entry pushes exactly the one timing it pops");
1186 self.runtime.trace(TraceEvent::EntryExit {
1187 module: module.to_string(),
1188 function: name.to_string(),
1189 cpu: timing.cpu(),
1190 wait: timing.wait(),
1191 });
1192 self.retire_heap();
1196 let heap = self.heap_stats();
1197 self.runtime.trace(TraceEvent::HeapSummary {
1202 collections: heap.collections,
1203 object_count: Some(heap.allocated_objects),
1204 allocated_bytes: Some(heap.allocated_bytes),
1205 live_bytes: Some(heap.live_bytes),
1206 peak_bytes: Some(heap.peak_bytes),
1207 pause: Some(heap.pause),
1208 allocated_words: None,
1209 capacity_words: None,
1210 live_words: None,
1211 });
1212
1213 outcome
1214 }
1215
1216 fn resolved(&self, module: &str) -> Option<&'a ResolvedModule> {
1217 self.program.modules.get(module)
1218 }
1219
1220 fn find_declared<T>(
1229 &self,
1230 module: &str,
1231 name: &str,
1232 select: impl Fn(&'a ResolvedModule, &str) -> Option<T>,
1233 ) -> Option<(Rc<str>, T)> {
1234 let resolved = self.resolved(module)?;
1235 if let Some(found) = select(resolved, name) {
1236 return Some((module.into(), found));
1237 }
1238 let owner_name = resolved.imports.get(name)?;
1239 let owner = self.resolved(owner_name)?;
1240 select(owner, name).map(|found| (owner_name.as_str().into(), found))
1241 }
1242
1243 fn find_function(&self, module: &str, name: &str) -> Option<(Rc<str>, Arc<FnDecl>)> {
1244 self.find_declared(module, name, |resolved, name| {
1245 Some(resolved.functions.get(name)?.decl.clone())
1246 })
1247 }
1248
1249 fn find_method(
1260 &self,
1261 type_module: &str,
1262 type_name: &str,
1263 name: &str,
1264 ) -> Option<(Rc<str>, Arc<FnDecl>)> {
1265 let mut key = self.method_key.take();
1272 key.0.clear();
1273 key.0.push_str(type_name);
1274 key.1.clear();
1275 key.1.push_str(name);
1276
1277 let found = self
1278 .resolved(type_module)
1279 .and_then(|m| m.methods.get(&key))
1280 .map(|entry| (Rc::from(type_module), entry.decl.clone()))
1281 .or_else(|| {
1282 self.program.modules.iter().find_map(|(module, resolved)| {
1283 let conforms = resolved.conformances.values().any(|conformance| {
1284 conformance.type_module == type_module
1285 && conformance.type_name == type_name
1286 && conformance.methods.contains(name)
1287 });
1288 if !conforms {
1289 return None;
1290 }
1291 let entry = resolved.methods.get(&key)?;
1292 Some((Rc::from(module.as_str()), entry.decl.clone()))
1293 })
1294 });
1295
1296 self.method_key.set(key);
1297 found
1298 }
1299
1300 fn find_struct(&self, module: &str, name: &str) -> Option<(Rc<str>, Arc<StructDecl>)> {
1301 self.find_declared(module, name, |resolved, name| {
1302 Some(resolved.structs.get(name)?.decl.clone())
1303 })
1304 }
1305
1306 fn find_enum(&self, module: &str, name: &str) -> Option<(Rc<str>, Arc<EnumDecl>)> {
1307 self.find_declared(module, name, |resolved, name| {
1308 Some(resolved.enums.get(name)?.decl.clone())
1309 })
1310 }
1311
1312 fn declaring_module(&self, module: &str, name: &str) -> Option<Rc<str>> {
1316 let resolved = self.resolved(module)?;
1317 if resolved.traits.contains_key(name) {
1318 return Some(module.into());
1319 }
1320 let owner = resolved.imports.get(name)?;
1321 self.resolved(owner)?
1322 .traits
1323 .contains_key(name)
1324 .then(|| owner.as_str().into())
1325 }
1326
1327 fn imported_module(&self, module: &str, head: &str) -> Option<Rc<str>> {
1329 Some(
1330 self.resolved(module)?
1331 .module_imports
1332 .get(head)?
1333 .as_str()
1334 .into(),
1335 )
1336 }
1337
1338 fn find_exported<T>(
1344 &self,
1345 owner: &str,
1346 name: &str,
1347 select: impl Fn(&'a ResolvedModule) -> Option<T>,
1348 ) -> Option<T> {
1349 let resolved = self.resolved(owner)?;
1350 if resolved.exported(name) != Some(true) {
1351 return None;
1352 }
1353 select(resolved)
1354 }
1355
1356 fn exported_function(&self, owner: &str, name: &str) -> Option<Arc<FnDecl>> {
1358 self.find_exported(owner, name, |resolved| {
1359 Some(resolved.functions.get(name)?.decl.clone())
1360 })
1361 }
1362
1363 fn module_member(&self, owner: &str, name: &str, span: Span) -> Eval {
1367 if let Some(decl) = self.exported_function(owner, name) {
1368 return Ok(declared_as_value(owner.into(), decl));
1369 }
1370 if self
1371 .find_exported(owner, name, |resolved| {
1372 resolved
1373 .structs
1374 .contains_key(name)
1375 .then_some(())
1376 .or_else(|| resolved.enums.contains_key(name).then_some(()))
1377 })
1378 .is_some()
1379 {
1380 return Ok(Value(Repr::Type(format!("{owner}.{name}").into())));
1381 }
1382 Err(self.no_export(owner, name, span).into())
1383 }
1384
1385 fn no_export(&self, owner: &str, name: &str, span: Span) -> RuntimeError {
1388 let exported = self.resolved(owner).map(|resolved| resolved.exported(name));
1389 match exported {
1390 Some(Some(false)) => RuntimeError::new(format!(
1391 "`{name}` is declared by module `{owner}`, but is not exported"
1392 ))
1393 .at(span)
1394 .with_rule("An `export` declaration is public; other declarations are module-private.")
1395 .with_help(format!("write `export` on `{name}` in module `{owner}`")),
1396 _ => RuntimeError::new(format!("module `{owner}` declares no `{name}`"))
1397 .at(span)
1398 .with_help(match self.resolved(owner) {
1399 Some(resolved) if !resolved.exports().is_empty() => {
1400 format!("module `{owner}` exports {}", resolved.exports().join(", "))
1401 }
1402 _ => format!("module `{owner}` exports nothing"),
1403 }),
1404 }
1405 }
1406
1407 fn is_host_module(&self, module: &str, name: &str) -> bool {
1409 self.resolved(module)
1410 .map(|m| m.host_uses.contains(name))
1411 .unwrap_or(false)
1412 || self.hosts.contains(name)
1413 }
1414
1415 fn host_item(&self, module: &str, name: &str) -> Option<Rc<str>> {
1417 self.resolved(module)?
1418 .host_items
1419 .get(name)
1420 .map(|m| m.as_str().into())
1421 }
1422
1423 fn charge_safepoint(&mut self, span: Span) -> Result<(), RuntimeError> {
1435 stopped_here(self.cancellation.as_ref(), &self.stops, span)?;
1436 if let Some(budget) = &self.budget {
1437 if let Err(stopped) = budget.safepoint(SAFEPOINT_FUEL) {
1438 return Err(budget.to_runtime_error(stopped).at(span));
1439 }
1440 }
1441 self.collect_if_due();
1442 Ok(())
1443 }
1444
1445 fn charge_wait(&mut self, wait: Duration) {
1449 for timing in &mut self.timings {
1450 timing.add_wait(wait);
1451 }
1452 }
1453
1454 fn call_host(
1480 &mut self,
1481 module: &str,
1482 op: &str,
1483 values: Vec<Value>,
1484 span: Span,
1485 ) -> Result<Value, RuntimeError> {
1486 stopped_here(self.cancellation.as_ref(), &self.stops, span)?;
1487 let hosts = self.hosts;
1488 let started = Instant::now();
1489 let result = hosts.call_with(
1490 module,
1491 op,
1492 values,
1493 &mut Callback {
1494 interpreter: self,
1495 span,
1496 },
1497 );
1498 self.charge_wait(started.elapsed());
1499 result.map_err(|e| e.at(span))
1500 }
1501
1502 fn call_host_resource(
1505 &mut self,
1506 handle: &ResourceHandle,
1507 op: &str,
1508 values: Vec<Value>,
1509 span: Span,
1510 ) -> Result<Value, RuntimeError> {
1511 stopped_here(self.cancellation.as_ref(), &self.stops, span)?;
1512 let hosts = self.hosts;
1513 let started = Instant::now();
1514 let result = hosts.call_resource(
1515 handle,
1516 op,
1517 values,
1518 &mut Callback {
1519 interpreter: self,
1520 span,
1521 },
1522 );
1523 self.charge_wait(started.elapsed());
1524 result.map_err(|e| e.at(span))
1525 }
1526
1527 fn init_host_type(
1534 &mut self,
1535 module: &str,
1536 declared: TypeSchema,
1537 args: Vec<EvaluatedArg>,
1538 span: Span,
1539 ) -> Result<Value, RuntimeError> {
1540 if declared.is_enum() {
1541 return Err(RuntimeError::new(format!(
1542 "`{module}.{}` is an enum, not a function",
1543 declared.name
1544 ))
1545 .at(span)
1546 .with_help(format!(
1547 "name a case, such as `{module}.{}.{}`",
1548 declared.name, declared.cases[0]
1549 )));
1550 }
1551 let names: Vec<&str> = declared.fields.iter().map(|field| field.name).collect();
1552 let (mut slots, _) = assign_labels(&names, args, declared.name, false)?;
1553 let mut fields = Vec::with_capacity(declared.fields.len());
1554 for (index, field) in declared.fields.iter().enumerate() {
1555 let Some(arg) = slots[index].take() else {
1556 return Err(RuntimeError::new(format!(
1557 "`{module}.{}` needs a value for field `{}`",
1558 declared.name, field.name
1559 ))
1560 .at(span)
1561 .with_rule("Struct initialization is a synthesized labeled call.")
1562 .with_help(format!(
1563 "the Host API schema declares `{module}.{}`",
1564 declared.initializer()
1565 )));
1566 };
1567 fields.push((field.name.into(), value_of(&arg, field.name, arg.span)?));
1568 }
1569 Ok(Value(Repr::Struct(Rc::new(StructValue {
1570 type_name: format!("{module}.{}", declared.name).into(),
1571 fields,
1572 opaque: false,
1573 }))))
1574 }
1575
1576 fn host_enum_case(
1578 &self,
1579 module: &str,
1580 declared: &TypeSchema,
1581 case: &str,
1582 span: Span,
1583 ) -> Result<Value, RuntimeError> {
1584 host_enum_case(module, declared, case, span)
1585 }
1586}
1587
1588pub(crate) fn host_enum_case(
1597 module: &str,
1598 declared: &TypeSchema,
1599 case: &str,
1600 span: Span,
1601) -> Result<Value, RuntimeError> {
1602 {
1603 if !declared.cases.contains(&case) {
1604 return Err(RuntimeError::new(format!(
1605 "host type `{module}.{}` has no case `{case}`",
1606 declared.name
1607 ))
1608 .at(span)
1609 .with_help(format!("known cases: {}", declared.cases.join(", "))));
1610 }
1611 Ok(Value(Repr::Enum(Box::new(EnumValue {
1612 type_name: format!("{module}.{}", declared.name).into(),
1613 case: case.into(),
1614 payload: crate::value::Payload::Empty,
1615 }))))
1616 }
1617}
1618
1619impl<'a> Interpreter<'a> {
1620 fn call_target(
1623 &mut self,
1624 target: &Target<'_>,
1625 receiver: Option<ArgSlot>,
1626 args: Vec<EvaluatedArg>,
1627 span: Span,
1628 ) -> Result<Value, RuntimeError> {
1629 if self.depth >= MAX_CALL_DEPTH {
1630 return Err(RuntimeError::new(format!(
1631 "call depth limit of {MAX_CALL_DEPTH} reached while calling `{}`",
1632 target.name
1633 ))
1634 .at(span)
1635 .with_rule("Recursion depth is a runtime control, not a proof obligation."));
1636 }
1637
1638 let depth = self.depth + 1;
1644 if let Some(limit) = self.call_depth_limit {
1645 if depth > limit {
1646 if let Some(budget) = &self.budget {
1650 return Err(budget.to_runtime_error(Stopped::CallDepth).at(span));
1651 }
1652 }
1653 }
1654 self.charge_safepoint(span)?;
1657
1658 self.depth += 1;
1659 self.call_sites.push(span);
1660 let result = self
1661 .invoke_body(target, receiver, args, span)
1662 .map_err(|error| self.attach_call_chain(error));
1663 self.call_sites.pop();
1664 self.depth -= 1;
1665 if target.is_async {
1666 return Ok(Value(Repr::Task(Task::settled(result?))));
1675 }
1676 result
1677 }
1678
1679 fn attach_call_chain(&self, error: RuntimeError) -> RuntimeError {
1697 error.with_chain(self.call_sites[1..].iter().rev().copied())
1698 }
1699
1700 fn invoke_body(
1701 &mut self,
1702 target: &Target<'_>,
1703 receiver: Option<ArgSlot>,
1704 args: Vec<EvaluatedArg>,
1705 span: Span,
1706 ) -> Result<Value, RuntimeError> {
1707 let mut env = Env::new(target.module.clone(), Rc::clone(&self.roots));
1708 for (name, value) in target.captures {
1709 env.declare_capture(name.clone(), Place::binding(value.clone()));
1710 }
1711
1712 match (target.receiver, receiver) {
1713 (Some(_), Some(slot)) => {
1714 let place = match slot {
1715 ArgSlot::Alias(place) => place,
1716 ArgSlot::Value(value) => Place::binding(value),
1717 };
1718 env.declare("self".into(), place);
1719 }
1720 (Some(_), None) => {
1721 return Err(RuntimeError::new(format!(
1722 "`{}` is a method and needs a receiver",
1723 target.name
1724 ))
1725 .at(span));
1726 }
1727 (None, Some(_)) => {
1728 return Err(
1729 RuntimeError::new(format!("`{}` takes no receiver", target.name)).at(span),
1730 );
1731 }
1732 (None, None) => {}
1733 }
1734
1735 self.bind_params(&mut env, target.params, args, target.name, span)?;
1736 let value = finish(self.eval_block(&mut env, target.body))?;
1737 Ok(match target.return_type {
1738 Some(ty) => self.coerce(&target.module, value, ty),
1739 None => value,
1740 })
1741 }
1742
1743 fn coerce(&self, module: &str, value: Value, ty: &Type) -> Value {
1756 match &ty.kind {
1757 TypeKind::Dyn(trait_name) => {
1758 let qualified: Rc<str> = match self.declaring_module(module, &trait_name.node) {
1763 Some(owner) => format!("{owner}.{}", trait_name.node).into(),
1764 None => trait_name.node.as_str().into(),
1765 };
1766 as_dyn(value, &qualified)
1767 }
1768 TypeKind::Named { path, args } if args.len() == 1 => {
1769 let Some(head) = path.last() else {
1770 return value;
1771 };
1772 match head.node.as_str() {
1773 "Array" | "Option" => {
1774 coerce_inside(value, |item| self.coerce(module, item, &args[0]))
1775 }
1776 _ => value,
1777 }
1778 }
1779 _ => value,
1780 }
1781 }
1782
1783 fn bind_params(
1796 &mut self,
1797 env: &mut Env,
1798 params: &[Param],
1799 args: Vec<EvaluatedArg>,
1800 what: &str,
1801 span: Span,
1802 ) -> Result<(), RuntimeError> {
1803 let names: Vec<&str> = params.iter().map(|p| p.name.node.as_str()).collect();
1804 let variadic = params.last().map(|p| p.variadic).unwrap_or(false);
1805 let (mut slots, rest) = assign_labels(&names, args, what, variadic)?;
1806
1807 for (index, param) in params.iter().enumerate() {
1808 let name: Rc<str> = param.name.node.as_str().into();
1809 if param.variadic {
1810 let mut items = Vec::new();
1811 if let Some(arg) = slots[index].as_ref() {
1812 items.push(value_of(arg, ¶m.name.node, span)?);
1813 }
1814 for arg in &rest {
1815 match &arg.slot {
1816 ArgSlot::Value(Value(Repr::Array(values))) if arg.spread => {
1817 items.extend(values.iter().cloned());
1818 }
1819 ArgSlot::Value(Value(Repr::Vector(storage))) if arg.spread => {
1820 items.extend(storage.elements.borrow().iter().cloned());
1821 }
1822 ArgSlot::Value(_) if arg.spread => {
1823 return Err(builtins::spread_needs_a_sequence(arg.span));
1824 }
1825 _ => items.push(value_of(arg, ¶m.name.node, arg.span)?),
1826 }
1827 }
1828 env.declare(name, Place::binding(Value(Repr::Array(items.into()))));
1830 continue;
1831 }
1832
1833 match slots[index].take() {
1834 Some(arg) => match (param.is_var, arg.slot) {
1835 (true, ArgSlot::Alias(place)) => env.declare(name, place),
1836 (true, ArgSlot::Value(_)) => {
1837 return Err(RuntimeError::new(format!(
1838 "parameter `{}` of `{what}` is declared `var`, but the call site passes a value",
1839 param.name.node
1840 ))
1841 .at(arg.span)
1842 .with_rule(
1843 "A `var` parameter is a non-escaping inout alias, marked at both the declaration and the call site.",
1844 )
1845 .with_help(format!("write `{what}(var {})`", param.name.node)));
1846 }
1847 (false, ArgSlot::Alias(_)) => {
1848 return Err(RuntimeError::new(format!(
1849 "parameter `{}` of `{what}` is not declared `var`, so `var` cannot be written at the call site",
1850 param.name.node
1851 ))
1852 .at(arg.span)
1853 .with_rule(
1854 "A `var` parameter is a non-escaping inout alias, marked at both the declaration and the call site.",
1855 ));
1856 }
1857 (false, ArgSlot::Value(value)) => {
1860 let value = match ¶m.ty {
1861 Some(ty) => self.coerce(&env.module, value, ty),
1862 None => value,
1863 };
1864 env.declare(name, Place::binding(value));
1865 }
1866 },
1867 None => match ¶m.default {
1868 Some(default) => {
1870 let value = finish(self.eval(env, default))?;
1871 let value = match ¶m.ty {
1872 Some(ty) => self.coerce(&env.module, value, ty),
1873 None => value,
1874 };
1875 env.declare(name, Place::binding(value));
1876 }
1877 None => {
1878 return Err(RuntimeError::new(format!(
1879 "`{what}` needs an argument for `{}`",
1880 param.name.node
1881 ))
1882 .at(span));
1883 }
1884 },
1885 }
1886 }
1887 Ok(())
1888 }
1889
1890 fn call_value_slots(
1892 &mut self,
1893 callee: Value,
1894 args: Vec<EvaluatedArg>,
1895 span: Span,
1896 ) -> Result<Value, RuntimeError> {
1897 match callee {
1898 Value(Repr::Closure(closure)) => {
1899 let module = closure.module.clone();
1900 let ClosureBody::Tree {
1907 params,
1908 block,
1909 decl,
1910 } = &closure.body
1911 else {
1912 return Err(RuntimeError::new(
1913 "this closure was built by the VM, and the interpreter runs syntax",
1914 )
1915 .at(span)
1916 .with_rule(
1917 "A run has one backend, and a closure belongs to the run that made it.",
1918 ));
1919 };
1920 self.call_target(
1921 &Target {
1922 name: "this closure",
1923 params,
1924 body: block,
1925 module,
1926 receiver: None,
1927 is_async: closure.is_async,
1928 captures: &closure.captures,
1929 return_type: decl.as_ref().and_then(|decl| decl.return_type.as_ref()),
1930 },
1931 None,
1932 args,
1933 span,
1934 )
1935 }
1936 Value(Repr::HostFn(host)) => {
1937 let values = plain_values(args, &format!("{}.{}", host.module, host.op))?;
1938 self.call_host(&host.module, &host.op, values, span)
1939 }
1940 other => {
1941 Err(RuntimeError::new(format!("`{}` is not callable", other.type_name())).at(span))
1942 }
1943 }
1944 }
1945
1946 fn eval_block(&mut self, env: &mut Env, block: &Block) -> Eval {
1949 env.push();
1950 let result = self.eval_block_body(env, block);
1951 env.pop();
1952 result
1953 }
1954
1955 fn eval_block_body(&mut self, env: &mut Env, block: &Block) -> Eval {
1956 for stmt in &block.statements {
1957 match &stmt.kind {
1958 StmtKind::Let {
1959 name, ty, value, ..
1960 } => {
1961 let value = self.eval(env, value)?;
1962 let value = match ty {
1963 Some(ty) => self.coerce(&env.module, value, ty),
1964 None => value,
1965 };
1966 env.declare(name.node.as_str().into(), Place::binding(value));
1967 }
1968 StmtKind::Expr(expr) => {
1969 self.eval(env, expr)?;
1970 }
1971 StmtKind::Item(item) => match &item.kind {
1972 ItemKind::Fn(decl) => {
1973 let closure = self.make_closure(
1974 env,
1975 decl.is_async,
1976 decl.params.clone(),
1977 decl.body.clone(),
1978 stmt.span,
1979 )?;
1980 env.declare(decl.name.node.as_str().into(), Place::binding(closure));
1981 }
1982 _ => {
1983 return Err(unsupported(
1984 "declaring a type inside a function body",
1985 stmt.span,
1986 )
1987 .into())
1988 }
1989 },
1990 }
1991 }
1992 match &block.tail {
1993 Some(tail) => self.eval(env, tail),
1994 None => Ok(Value(Repr::Unit)),
1995 }
1996 }
1997
1998 fn eval(&mut self, env: &mut Env, expr: &Expr) -> Eval {
2001 let span = expr.span;
2002 match &expr.kind {
2003 ExprKind::Int(value) => Ok(Value(Repr::Int(*value))),
2004 ExprKind::Float(value) => Ok(Value(Repr::Float(*value))),
2005 ExprKind::Bool(value) => Ok(Value(Repr::Bool(*value))),
2006 ExprKind::Duration(value) => Ok(Value(Repr::Duration(*value))),
2007 ExprKind::Unit => Ok(Value(Repr::Unit)),
2008 ExprKind::Str(parts) => {
2009 let mut text = String::new();
2010 for part in parts {
2011 match part {
2012 StrPart::Text(literal) => text.push_str(literal),
2013 StrPart::Interpolation(expr) => {
2014 let value = self.eval(env, expr)?;
2015 text.push_str(&value.to_string());
2016 }
2017 }
2018 }
2019 Ok(Value(Repr::Str(text.into())))
2020 }
2021 ExprKind::Ident(name) => self.eval_ident(env, name, span),
2022 ExprKind::ArrayLit(items) => {
2023 let mut values = Vec::with_capacity(items.len());
2024 for item in items {
2025 values.push(self.eval(env, item)?);
2026 }
2027 Ok(Value(Repr::Array(values.into())))
2028 }
2029 ExprKind::Field { base, name } => self.eval_field(env, base, &name.node, span),
2030 ExprKind::Call {
2031 callee,
2032 generics: _,
2033 args,
2034 trailing,
2035 } => self.eval_call(env, callee, args, trailing.as_deref(), span),
2036 ExprKind::Unary { op, operand } => {
2037 let value = self.eval(env, operand)?;
2038 Ok(unary(*op, value, span)?)
2039 }
2040 ExprKind::Binary { op, lhs, rhs } => match op {
2041 BinaryOp::And | BinaryOp::Or => {
2043 let left = expect_bool(self.eval(env, lhs)?, *op, span)?;
2044 if (*op == BinaryOp::And && !left) || (*op == BinaryOp::Or && left) {
2045 return Ok(Value(Repr::Bool(left)));
2046 }
2047 let right = expect_bool(self.eval(env, rhs)?, *op, span)?;
2048 Ok(Value(Repr::Bool(right)))
2049 }
2050 _ => {
2051 let left = self.eval(env, lhs)?;
2052 let right = self.eval(env, rhs)?;
2053 Ok(binary(*op, left, right, span)?)
2054 }
2055 },
2056 ExprKind::Assign { op, target, value } => {
2057 let place = self.resolve_place(env, target)?;
2062 let new_value = match op {
2063 None => self.eval(env, value)?,
2064 Some(op) => {
2065 let current = place.read(span)?;
2066 let rhs = self.eval(env, value)?;
2067 binary(*op, current, rhs, span)?
2068 }
2069 };
2070 place.write(span, new_value)?;
2071 Ok(Value(Repr::Unit))
2072 }
2073 ExprKind::Try(inner) => {
2074 let value = self.eval(env, inner)?;
2075 match &value {
2076 Value(Repr::Enum(result)) if &*result.type_name == RESULT.name => {
2077 match value.ok_payload() {
2078 Some(payload) => {
2079 Ok(payload.first().cloned().unwrap_or(Value(Repr::Unit)))
2080 }
2081 None => Err(Control::Return(value)),
2082 }
2083 }
2084 Value(Repr::Enum(option)) if &*option.type_name == OPTION.name => {
2085 match value.some_payload() {
2086 Some(payload) => {
2087 Ok(payload.first().cloned().unwrap_or(Value(Repr::Unit)))
2088 }
2089 None => Err(Control::Return(Value::none())),
2090 }
2091 }
2092 other => {
2093 let error = RuntimeError::new(format!(
2094 "`?` needs a `Result` or an `Option`, but found `{}`",
2095 other.type_name()
2096 ))
2097 .at(span)
2098 .with_rule("`expr?` returns the error from the current function.");
2099 Err(match other {
2102 Value(Repr::Task(_)) => {
2103 error.with_help("settle the task first, as in `task.await()?`")
2104 }
2105 _ => error,
2106 }
2107 .into())
2108 }
2109 }
2110 }
2111 ExprKind::Await(inner) => {
2112 let value = self.eval(env, inner)?;
2113 self.charge_safepoint(span)?;
2114 Ok(self.settle_value(value, span)?)
2115 }
2116 ExprKind::Scope { name, body } => self.eval_scope(env, name, body),
2117 ExprKind::Block(block) => self.eval_block(env, block),
2118 ExprKind::If {
2119 condition,
2120 then_branch,
2121 else_branch,
2122 } => {
2123 let test = self.eval(env, condition)?;
2124 let Value(Repr::Bool(test)) = test else {
2125 return Err(RuntimeError::new(format!(
2126 "an `if` condition must be a `Bool`, but found `{}`",
2127 test.type_name()
2128 ))
2129 .at(condition.span)
2130 .with_rule("There are no implicit boolean conversions.")
2131 .into());
2132 };
2133 if test {
2134 let value = self.eval_block(env, then_branch)?;
2135 Ok(match else_branch {
2141 Some(_) => value,
2142 None => Value(Repr::Unit),
2143 })
2144 } else {
2145 match else_branch {
2146 Some(branch) => self.eval(env, branch),
2147 None => Ok(Value(Repr::Unit)),
2148 }
2149 }
2150 }
2151 ExprKind::Match { scrutinee, arms } => {
2152 let value = self.eval(env, scrutinee)?;
2153 for arm in arms {
2154 env.push();
2155 let matched = self.match_pattern(env, &arm.pattern, &value);
2156 match matched {
2157 Ok(true) => {
2158 let result = self.eval(env, &arm.body);
2159 env.pop();
2160 return result;
2161 }
2162 Ok(false) => env.pop(),
2163 Err(error) => {
2164 env.pop();
2165 return Err(error);
2166 }
2167 }
2168 }
2169 Err(no_match(&value, span).into())
2170 }
2171 ExprKind::For {
2172 binding,
2173 iterable,
2174 body,
2175 } => {
2176 let items = self.iterable_items(env, iterable)?;
2177 for item in items {
2178 self.charge_safepoint(span)?;
2182 env.push();
2183 env.declare(binding.node.as_str().into(), Place::binding(item));
2184 let result = self.eval_block(env, body);
2185 env.pop();
2186 match result {
2187 Ok(_) => {}
2188 Err(Control::Break) => break,
2196 Err(Control::Continue) => continue,
2197 Err(other) => return Err(other),
2198 }
2199 }
2200 Ok(Value(Repr::Unit))
2201 }
2202 ExprKind::While { condition, body } => loop {
2203 let test = self.eval(env, condition)?;
2204 let Value(Repr::Bool(test)) = test else {
2205 return Err(RuntimeError::new(format!(
2206 "a `while` condition must be a `Bool`, but found `{}`",
2207 test.type_name()
2208 ))
2209 .at(condition.span)
2210 .into());
2211 };
2212 if !test {
2213 return Ok(Value(Repr::Unit));
2214 }
2215 self.charge_safepoint(span)?;
2219 match self.eval_block(env, body) {
2220 Ok(_) => {}
2221 Err(Control::Break) => return Ok(Value(Repr::Unit)),
2227 Err(Control::Continue) => continue,
2228 Err(other) => return Err(other),
2229 }
2230 },
2231 ExprKind::Return(value) => {
2232 let value = match value {
2233 Some(expr) => self.eval(env, expr)?,
2234 None => Value(Repr::Unit),
2235 };
2236 Err(Control::Return(value))
2237 }
2238 ExprKind::Break(value) => {
2239 if let Some(expr) = value {
2243 self.eval(env, expr)?;
2244 }
2245 Err(Control::Break)
2246 }
2247 ExprKind::Continue => Err(Control::Continue),
2248 ExprKind::Lambda {
2249 is_async,
2250 params,
2251 body,
2252 } => self
2253 .make_closure(env, *is_async, params.clone(), body.clone(), span)
2254 .map_err(Control::from),
2255 ExprKind::Range {
2258 start,
2259 end,
2260 inclusive_end,
2261 } => {
2262 let start = expect_int(self.eval(env, start)?, "a range bound", span)?;
2263 let end = expect_int(self.eval(env, end)?, "a range bound", span)?;
2264 Ok(Value(Repr::Range {
2265 start,
2266 end,
2267 inclusive_end: *inclusive_end,
2268 }))
2269 }
2270 }
2271 }
2272
2273 fn make_closure(
2274 &mut self,
2275 env: &mut Env,
2276 is_async: bool,
2277 params: Vec<Param>,
2278 body: Block,
2279 span: Span,
2280 ) -> Result<Value, RuntimeError> {
2281 let mut mentioned = BTreeSet::new();
2283 mention_block(&body, &mut mentioned);
2284 let captures = env.captures(&mentioned, span)?;
2285 Ok(Value(Repr::Closure(Rc::new(Closure {
2286 is_async,
2287 arity: params.len(),
2288 body: ClosureBody::Tree {
2289 params,
2290 block: Arc::new(body),
2291 decl: None,
2292 },
2293 module: env.module.clone(),
2294 captures,
2295 }))))
2296 }
2297
2298 fn eval_scope(&mut self, env: &mut Env, name: &Ident, body: &Block) -> Eval {
2306 let scope = TaskScope::new(name.node.as_str().into());
2307 env.push();
2308 env.declare(
2309 name.node.as_str().into(),
2310 Place::binding(Value(Repr::TaskScope(scope.clone()))),
2311 );
2312 let result = self.eval_block(env, body);
2313 env.pop();
2314 let left = self.leave_scope(&scope, result);
2315 scope.close();
2316 left
2317 }
2318
2319 fn leave_scope(&mut self, scope: &Rc<TaskScope>, result: Eval) -> Eval {
2334 let value = match result {
2335 Ok(value) => value,
2336 early => {
2337 self.cancel_scope(scope);
2338 return early;
2339 }
2340 };
2341 match task::wait_for_children(self, scope) {
2342 None => Ok(value),
2343 Some(failure) => {
2344 self.cancel_scope(scope);
2345 Err(match failure {
2346 ChildFailure::Returned(value) => Control::Return(value),
2347 ChildFailure::Raised(error) => Control::Error(error),
2348 })
2349 }
2350 }
2351 }
2352
2353 fn cancel_scope(&mut self, scope: &Rc<TaskScope>) {
2355 task::cancel_children(self, scope);
2356 }
2357
2358 fn settle(&mut self, task: &Rc<Task>, span: Span) -> Result<Value, RuntimeError> {
2360 task::settle(self, task, span)
2361 }
2362
2363 fn settle_value(&mut self, value: Value, span: Span) -> Result<Value, RuntimeError> {
2365 match value {
2366 Value(Repr::Task(task)) => self.settle(&task, span),
2367 other => Err(RuntimeError::new(format!(
2368 "`await` needs a task, but found `{}`",
2369 other.type_name()
2370 ))
2371 .at(span)
2372 .with_rule(
2373 "`await` settles a task. Only a task spawned into a scope, or one returned by an `async fn`, has a value to settle.",
2374 )
2375 .with_help("call an `async fn`, or spawn the work into a task scope, and await that handle")),
2376 }
2377 }
2378
2379 fn spawn(
2388 &mut self,
2389 scope: &Rc<TaskScope>,
2390 body: Value,
2391 span: Span,
2392 ) -> Result<Value, RuntimeError> {
2393 task::spawn_into(self, scope, body, span, run_task)
2394 }
2395
2396 fn call_task_method(
2398 &mut self,
2399 env: &mut Env,
2400 receiver: Value,
2401 name: &str,
2402 args: &[Arg],
2403 trailing: Option<&Expr>,
2404 span: Span,
2405 ) -> Eval {
2406 let arguments = self.eval_args(env, args, trailing)?;
2407 let mut values = plain_values(arguments, name)?;
2408 match (&receiver, name) {
2409 (Value(Repr::TaskScope(scope)), "spawn") => {
2410 if values.len() != 1 {
2411 return Err(RuntimeError::new(format!(
2412 "`spawn` takes one trailing closure, but {} argument(s) were given",
2413 values.len()
2414 ))
2415 .at(span)
2416 .with_help(format!("write `{}.spawn {{ ... }}`", scope.name))
2417 .into());
2418 }
2419 Ok(self.spawn(scope, values.remove(0), span)?)
2420 }
2421 (Value(Repr::Task(task)), "await") => {
2422 expect_no_arguments("await", &values, span)?;
2423 self.charge_safepoint(span)?;
2424 Ok(self.settle(task, span)?)
2425 }
2426 (Value(Repr::Task(task)), "cancel") => {
2427 expect_no_arguments("cancel", &values, span)?;
2428 task.cancel();
2434 Ok(Value(Repr::Unit))
2435 }
2436 (_, "await") => {
2437 self.charge_safepoint(span)?;
2438 Ok(self.settle_value(receiver.clone(), span)?)
2439 }
2440 (other, _) => Err(RuntimeError::new(format!(
2441 "`{}` has no method `{name}`",
2442 other.type_name()
2443 ))
2444 .at(span)
2445 .into()),
2446 }
2447 }
2448
2449 fn call_shared_method(
2455 &mut self,
2456 env: &mut Env,
2457 receiver: Value,
2458 name: &str,
2459 args: &[Arg],
2460 trailing: Option<&Expr>,
2461 span: Span,
2462 ) -> Eval {
2463 let Value(Repr::Shared(cell)) = receiver else {
2464 unreachable!("only a `Shared` receiver reaches this dispatch");
2465 };
2466 if name != "lock" {
2467 return Err(RuntimeError::new(format!("`Shared` has no method `{name}`"))
2468 .at(span)
2469 .with_rule(
2470 "`lock` is a `Shared`'s only operation: every access to the value it holds is scoped, so there is no `get` and no `set`.",
2471 )
2472 .with_help("write `shared.lock(fn(var value) { ... })`")
2473 .into());
2474 }
2475 let arguments = self.eval_args(env, args, trailing)?;
2476 let mut values = plain_values(arguments, name)?;
2477 if values.len() != 1 {
2478 return Err(RuntimeError::new(format!(
2479 "`lock` takes one closure, but {} argument(s) were given",
2480 values.len()
2481 ))
2482 .at(span)
2483 .with_help("write `shared.lock(fn(var value) { ... })`")
2484 .into());
2485 }
2486 let body = values.remove(0);
2487 let Value(Repr::Closure(closure)) = &body else {
2488 return Err(RuntimeError::new(format!(
2489 "`lock` takes the work to run as a closure, but found `{}`",
2490 body.type_name()
2491 ))
2492 .at(span)
2493 .with_help("write `shared.lock(fn(var value) { ... })`")
2494 .into());
2495 };
2496 if closure.arity == 0 {
2497 return Err(RuntimeError::new(
2498 "`lock` gives the wrapped value to its closure, but this closure takes no parameter",
2499 )
2500 .at(span)
2501 .with_help("write `shared.lock(fn(var value) { ... })`")
2502 .into());
2503 }
2504 let wants_alias = match &closure.body {
2519 ClosureBody::Tree { params, .. } => params.first().is_some_and(|param| param.is_var),
2520 ClosureBody::Linear(_) => false,
2521 };
2522 Ok(cell.lock(span, |value| {
2523 let place = Place::binding(value);
2524 let slot = match wants_alias {
2525 true => ArgSlot::Alias(place.clone()),
2526 false => ArgSlot::Value(place.read(span)?),
2527 };
2528 let result = self.call_value_slots(
2529 body.clone(),
2530 vec![EvaluatedArg {
2531 label: None,
2532 spread: false,
2533 slot,
2534 span,
2535 }],
2536 span,
2537 )?;
2538 let updated = place.read(span)?;
2539 Ok((result, updated))
2540 })?)
2541 }
2542
2543 fn iterable_items(&mut self, env: &mut Env, expr: &Expr) -> Result<Vec<Value>, Control> {
2544 let value = self.eval(env, expr)?;
2545 Ok(items_of(value, expr.span)?)
2546 }
2547
2548 fn eval_ident(&mut self, env: &mut Env, name: &str, span: Span) -> Eval {
2549 if let Some(place) = env.lookup(name) {
2550 return Ok(place.read(span)?);
2551 }
2552 if name == NONE_CASE.name {
2553 return Ok(Value::none());
2554 }
2555 let module = env.module.clone();
2556 if let Some((owner, decl)) = self.find_function(&module, name) {
2557 return Ok(declared_as_value(owner, decl));
2558 }
2559 if let Some((owner, _)) = self.find_struct(&module, name) {
2563 return Ok(Value(Repr::Type(format!("{owner}.{name}").into())));
2564 }
2565 if let Some((owner, _)) = self.find_enum(&module, name) {
2566 return Ok(Value(Repr::Type(format!("{owner}.{name}").into())));
2567 }
2568 if builtins::is_builtin_type(name) {
2569 return Ok(Value(Repr::Type(name.into())));
2570 }
2571 if let Some(owner) = self.imported_module(&module, name) {
2572 return Err(RuntimeError::new(format!("`{name}` is a module, not a value"))
2573 .at(span)
2574 .with_rule(
2575 "A module imported whole is a namespace; its exported declarations are the values.",
2576 )
2577 .with_help(format!(
2578 "name one of its exports, such as `{name}.<declaration>`, or import the declaration with `use {owner}.<declaration>`"
2579 ))
2580 .into());
2581 }
2582 if self.is_host_module(&module, name) {
2583 return Ok(Value(Repr::HostModule(name.into())));
2584 }
2585 if let Some(host) = self.host_item(&module, name) {
2586 return Ok(Value(Repr::HostFn(Rc::new(HostFnValue {
2587 module: host,
2588 op: name.into(),
2589 }))));
2590 }
2591 Err(
2592 RuntimeError::new(format!("cannot find `{name}` in this scope"))
2593 .at(span)
2594 .into(),
2595 )
2596 }
2597
2598 fn eval_field(&mut self, env: &mut Env, base: &Expr, name: &str, span: Span) -> Eval {
2599 if let ExprKind::Ident(head) = &base.kind {
2600 if env.lookup(head).is_none() {
2601 let module = env.module.clone();
2602 if let Some((owner, decl)) = self.find_enum(&module, head) {
2603 return Ok(self.enum_case(&owner, &decl, name, Vec::new(), span)?);
2604 }
2605 if self.is_host_module(&module, head) {
2606 if self.hosts.host_type(head, name).is_some() {
2610 return Ok(Value(Repr::Type(format!("{head}.{name}").into())));
2611 }
2612 return Ok(Value(Repr::HostFn(Rc::new(HostFnValue {
2613 module: head.as_str().into(),
2614 op: name.into(),
2615 }))));
2616 }
2617 if let Some(owner) = self.imported_module(&module, head) {
2620 return self.module_member(&owner, name, span);
2621 }
2622 }
2623 }
2624
2625 let base_value = self.eval(env, base)?;
2626 match &base_value {
2627 Value(Repr::Struct(value)) => match value.get(name) {
2628 Some(field) => Ok(field.clone()),
2629 None => Err(no_field(&value.type_name, name, span).into()),
2630 },
2631 Value(Repr::Type(type_name)) => match type_name.rsplit_once('.') {
2634 Some((owner, short)) => match self.find_enum(owner, short) {
2635 Some((owner, decl)) => {
2636 Ok(self.enum_case(&owner, &decl, name, Vec::new(), span)?)
2637 }
2638 None => match self.hosts.host_type(owner, short) {
2640 Some(declared) => Ok(self.host_enum_case(owner, &declared, name, span)?),
2641 None => Err(no_field(type_name, name, span).into()),
2642 },
2643 },
2644 None => Err(no_field(type_name, name, span).into()),
2645 },
2646 Value(Repr::HostModule(module)) => match self.hosts.host_type(module, name) {
2647 Some(_) => Ok(Value(Repr::Type(format!("{module}.{name}").into()))),
2648 None => Ok(Value(Repr::HostFn(Rc::new(HostFnValue {
2649 module: module.clone(),
2650 op: name.into(),
2651 })))),
2652 },
2653 other => Err(RuntimeError::new(format!(
2654 "`{}` has no field `{name}`",
2655 other.type_name()
2656 ))
2657 .at(span)
2658 .into()),
2659 }
2660 }
2661
2662 fn enum_case(
2664 &mut self,
2665 module: &str,
2666 decl: &Arc<EnumDecl>,
2667 case: &str,
2668 mut payload: Vec<Value>,
2669 span: Span,
2670 ) -> Result<Value, RuntimeError> {
2671 enum_case(self.program, module, decl, case, &mut payload, span)
2672 }
2673
2674 fn eval_call(
2677 &mut self,
2678 env: &mut Env,
2679 callee: &Expr,
2680 args: &[Arg],
2681 trailing: Option<&Expr>,
2682 span: Span,
2683 ) -> Eval {
2684 match &callee.kind {
2685 ExprKind::Ident(name) => {
2686 if let Some(place) = env.lookup(name) {
2687 let value = place.read(span)?;
2688 let args = self.eval_args(env, args, trailing)?;
2689 return Ok(self.call_value_slots(value, args, span)?);
2690 }
2691 let module = env.module.clone();
2692 if let Some((owner, decl)) = self.find_function(&module, name) {
2693 let args = self.eval_args(env, args, trailing)?;
2694 return Ok(self.call_target(
2695 &Target {
2696 name,
2697 params: &decl.params,
2698 body: &decl.body,
2699 module: owner,
2700 receiver: decl.receiver,
2701 is_async: decl.is_async,
2702 captures: &[],
2703 return_type: decl.return_type.as_ref(),
2704 },
2705 None,
2706 args,
2707 span,
2708 )?);
2709 }
2710 if let Some((owner, decl)) = self.find_struct(&module, name) {
2711 let args = self.eval_args(env, args, trailing)?;
2712 return Ok(self.init_struct(&owner, &decl, args, span)?);
2713 }
2714 if self.find_enum(&module, name).is_some() {
2715 return Err(
2716 RuntimeError::new(format!("`{name}` is an enum, not a function"))
2717 .at(span)
2718 .with_help(format!("name a case, such as `{name}.Case(...)`"))
2719 .into(),
2720 );
2721 }
2722 if let Some(host) = self.host_item(&module, name) {
2723 let args = self.eval_args(env, args, trailing)?;
2724 let values = plain_values(args, name)?;
2725 return Ok(self.call_host(&host, name, values, span)?);
2726 }
2727 if name == MAP_ENTRY.name {
2728 let args = self.eval_args(env, args, trailing)?;
2729 return Ok(init_map_entry(args, span)?);
2730 }
2731 if let Some(schema) = builtins::free_builtin(name) {
2736 return match schema.kind {
2737 FreeBuiltinKind::Assertion => {
2738 self.assertion(env, name, args, trailing, span)
2739 }
2740 FreeBuiltinKind::Constructor => {
2741 let args = self.eval_args(env, args, trailing)?;
2742 let mut values = plain_values(args, name)?;
2743 Ok(builtins::call_constructor(name, &mut values, span)?)
2744 }
2745 };
2746 }
2747 if name == NONE_CASE.name {
2748 return Err(RuntimeError::new("`None` is a value, not a call")
2749 .at(span)
2750 .with_help("write `None`")
2751 .into());
2752 }
2753 Err(
2754 RuntimeError::new(format!("cannot find `{name}` in this scope"))
2755 .at(span)
2756 .into(),
2757 )
2758 }
2759 ExprKind::Field { base, name } => {
2760 if let ExprKind::Ident(head) = &base.kind {
2761 if env.lookup(head).is_none() {
2762 let module = env.module.clone();
2763 if self.is_host_module(&module, head) {
2764 if let Some(declared) = self.hosts.host_type(head, &name.node) {
2768 let args = self.eval_args(env, args, trailing)?;
2769 return Ok(self.init_host_type(head, declared, args, span)?);
2770 }
2771 let args = self.eval_args(env, args, trailing)?;
2772 let values = plain_values(args, &format!("{head}.{}", name.node))?;
2773 return Ok(self.call_host(head, &name.node, values, span)?);
2774 }
2775 if let Some((owner, enum_decl)) = self.find_enum(&module, head) {
2776 let is_case = enum_decl
2780 .cases
2781 .iter()
2782 .any(|case| case.name.node == name.node);
2783 if !is_case {
2784 if let Some((declaring, decl)) =
2785 self.find_method(&owner, head, &name.node)
2786 {
2787 let args = self.eval_args(env, args, trailing)?;
2788 return Ok(self.call_target(
2789 &Target {
2790 name: &name.node,
2791 params: &decl.params,
2792 body: &decl.body,
2793 module: declaring,
2794 receiver: decl.receiver,
2795 is_async: decl.is_async,
2796 captures: &[],
2797 return_type: decl.return_type.as_ref(),
2798 },
2799 None,
2800 args,
2801 span,
2802 )?);
2803 }
2804 }
2805 let args = self.eval_args(env, args, trailing)?;
2806 let values = plain_values(args, &format!("{head}.{}", name.node))?;
2807 return Ok(
2808 self.enum_case(&owner, &enum_decl, &name.node, values, span)?
2809 );
2810 }
2811 if let Some((owner, _)) = self.find_struct(&module, head) {
2812 if let Some((declaring, decl)) =
2813 self.find_method(&owner, head, &name.node)
2814 {
2815 let args = self.eval_args(env, args, trailing)?;
2816 return Ok(self.call_target(
2817 &Target {
2818 name: &name.node,
2819 params: &decl.params,
2820 body: &decl.body,
2821 module: declaring,
2822 receiver: decl.receiver,
2823 is_async: decl.is_async,
2824 captures: &[],
2825 return_type: decl.return_type.as_ref(),
2826 },
2827 None,
2828 args,
2829 span,
2830 )?);
2831 }
2832 }
2833 if let Some(owner) = self.imported_module(&module, head) {
2836 if let Some(decl) = self.exported_function(&owner, &name.node) {
2837 let args = self.eval_args(env, args, trailing)?;
2838 return Ok(self.call_target(
2839 &Target {
2840 name: &name.node,
2841 params: &decl.params,
2842 body: &decl.body,
2843 module: owner,
2844 receiver: decl.receiver,
2845 is_async: decl.is_async,
2846 captures: &[],
2847 return_type: decl.return_type.as_ref(),
2848 },
2849 None,
2850 args,
2851 span,
2852 )?);
2853 }
2854 if let Some(decl) = self.find_exported(&owner, &name.node, |resolved| {
2855 Some(resolved.structs.get(&name.node)?.decl.clone())
2856 }) {
2857 let args = self.eval_args(env, args, trailing)?;
2858 return Ok(self.init_struct(&owner, &decl, args, span)?);
2859 }
2860 if self
2861 .find_exported(&owner, &name.node, |resolved| {
2862 resolved.enums.get(&name.node)
2863 })
2864 .is_some()
2865 {
2866 return Err(RuntimeError::new(format!(
2867 "`{head}.{}` is an enum, not a function",
2868 name.node
2869 ))
2870 .at(span)
2871 .with_help(format!(
2872 "name a case, such as `{head}.{}.Case(...)`",
2873 name.node
2874 ))
2875 .into());
2876 }
2877 return Err(self.no_export(&owner, &name.node, span).into());
2878 }
2879 if builtins::is_builtin_type(head) {
2880 if let Some(binding) =
2891 cove_schema::builtins::standard_associated_binding(head, &name.node)
2892 {
2893 return self.call_std_associated_binding(
2894 env, binding, args, trailing, span,
2895 );
2896 }
2897 let args = self.eval_args(env, args, trailing)?;
2898 let mut values = plain_values(args, &format!("{head}.{}", name.node))?;
2899 return Ok(builtins::call_associated(
2900 self,
2901 head,
2902 &name.node,
2903 &mut values,
2904 span,
2905 )?);
2906 }
2907 }
2908 }
2909 self.eval_method_call(env, base, &name.node, args, trailing, span)
2910 }
2911 _ => {
2912 let value = self.eval(env, callee)?;
2913 let args = self.eval_args(env, args, trailing)?;
2914 Ok(self.call_value_slots(value, args, span)?)
2915 }
2916 }
2917 }
2918
2919 fn assertion(
2926 &mut self,
2927 env: &mut Env,
2928 name: &str,
2929 args: &[Arg],
2930 trailing: Option<&Expr>,
2931 span: Span,
2932 ) -> Eval {
2933 let spans: Vec<Span> = args
2934 .iter()
2935 .map(|arg| arg.value.span)
2936 .chain(trailing.map(|expr| expr.span))
2937 .collect();
2938 let evaluated = self.eval_args(env, args, trailing)?;
2939 let mut values = plain_values(evaluated, name)?;
2940 let sources: Vec<&str> = spans.iter().map(|span| self.source_text(*span)).collect();
2941 let outcome = builtins::call_assertion(name, &mut values, &sources, span)?;
2942 if let Some(payload) = outcome.err_payload() {
2943 self.assertion_failure = Some((span, payload[0].to_string()));
2944 }
2945 Ok(outcome)
2946 }
2947
2948 fn eval_method_call(
2949 &mut self,
2950 env: &mut Env,
2951 receiver: &Expr,
2952 name: &str,
2953 args: &[Arg],
2954 trailing: Option<&Expr>,
2955 span: Span,
2956 ) -> Eval {
2957 let place = self.resolve_place_opt(env, receiver)?;
2960 let mut temporary = match &place {
2961 Some(_) => None,
2962 None => Some(self.eval(env, receiver)?),
2963 };
2964
2965 let mut place = place;
2970 let dispatch_from = match (&place, &temporary) {
2971 (Some(place), _) => place.with_ref(span, dyn_receiver)?,
2972 (_, Some(value)) => dyn_receiver(value),
2973 _ => None,
2974 };
2975 if let Some(concrete) = dispatch_from {
2976 place = None;
2977 temporary = Some(concrete);
2978 }
2979
2980 let declared = match (&place, &temporary) {
2986 (Some(place), _) => {
2987 place.with_ref(span, |value| value.declared_type_name().cloned())?
2988 }
2989 (_, Some(value)) => value.declared_type_name().cloned(),
2990 _ => unreachable!("a receiver is either a place or a temporary"),
2991 };
2992
2993 let handle = match (&place, &temporary) {
2997 (Some(place), _) => place.with_ref(span, |value| match value {
2998 Value(Repr::Resource(handle)) => Some(handle.clone()),
2999 _ => None,
3000 })?,
3001 (_, Some(Value(Repr::Resource(handle)))) => Some(handle.clone()),
3002 _ => None,
3003 };
3004 if let Some(handle) = handle {
3005 let what = format!("{}.{name}", handle.qualified_type());
3006 let args = self.eval_args(env, args, trailing)?;
3007 let values = plain_values(args, &what)?;
3008 return Ok(self.call_host_resource(&handle, name, values, span)?);
3009 }
3010
3011 if let Some((type_module, short)) =
3012 declared.as_deref().and_then(|name| name.rsplit_once('.'))
3013 {
3014 if let Some((module, decl)) = self.find_method(type_module, short, name) {
3015 let receiver_slot = match decl.receiver {
3016 Some(Receiver { is_var: true, .. }) => {
3021 let Some(place) = place else {
3022 return Err(var_self_needs_place(name, receiver, span).into());
3023 };
3024 ArgSlot::Alias(place)
3025 }
3026 _ => ArgSlot::Value(match (place, temporary) {
3027 (Some(place), _) => place.read(span)?,
3028 (_, Some(value)) => value,
3029 _ => unreachable!("a receiver is either a place or a temporary"),
3030 }),
3031 };
3032 let args = self.eval_args(env, args, trailing)?;
3033 return Ok(self.call_target(
3034 &Target {
3035 name,
3036 params: &decl.params,
3037 body: &decl.body,
3038 module,
3039 receiver: decl.receiver,
3040 is_async: decl.is_async,
3041 captures: &[],
3042 return_type: decl.return_type.as_ref(),
3043 },
3044 Some(receiver_slot),
3045 args,
3046 span,
3047 )?);
3048 }
3049 }
3050
3051 if declared.as_deref().is_none_or(|name| !name.contains('.')) {
3072 let builtin_receiver = match (&place, &temporary) {
3073 (Some(place), _) => place.with_ref(span, |value| value.type_name())?,
3074 (_, Some(value)) => value.type_name(),
3075 _ => unreachable!("a receiver is either a place or a temporary"),
3076 };
3077 if let Some(binding) = cove_schema::builtins::standard_binding(&builtin_receiver, name)
3078 {
3079 let receiver_value = match (place, temporary) {
3080 (Some(place), _) => place.read(span)?,
3081 (_, Some(value)) => value,
3082 _ => unreachable!("a receiver is either a place or a temporary"),
3083 };
3084 return self.call_std_binding(env, binding, receiver_value, args, trailing, span);
3085 }
3086 }
3087
3088 if name == "snapshot" {
3094 let args = self.eval_args(env, args, trailing)?;
3095 if !args.is_empty() {
3096 return Err(RuntimeError::new(format!(
3097 "`snapshot` takes 0 argument(s), but {} were given",
3098 args.len()
3099 ))
3100 .at(span)
3101 .into());
3102 }
3103 let receiver_value = match (place, temporary) {
3104 (Some(place), _) => place.read(span)?,
3105 (_, Some(value)) => value,
3106 _ => unreachable!("a receiver is either a place or a temporary"),
3107 };
3108 return Ok(self.snapshot(&receiver_value, span)?);
3109 }
3110
3111 let is_shared = match (&place, &temporary) {
3118 (Some(place), _) => {
3119 place.with_ref(span, |value| matches!(value, Value(Repr::Shared(_))))?
3120 }
3121 (_, Some(value)) => matches!(value, Value(Repr::Shared(_))),
3122 _ => unreachable!("a receiver is either a place or a temporary"),
3123 };
3124 if is_shared {
3125 let receiver_value = match (&place, &temporary) {
3126 (Some(place), _) => place.read(span)?,
3127 (_, Some(value)) => value.clone(),
3128 _ => unreachable!("a receiver is either a place or a temporary"),
3129 };
3130 return self.call_shared_method(env, receiver_value, name, args, trailing, span);
3131 }
3132
3133 let is_task = match (&place, &temporary) {
3138 (Some(place), _) => place.with_ref(span, |value| {
3139 matches!(value, Value(Repr::Task(_)) | Value(Repr::TaskScope(_)))
3140 })?,
3141 (_, Some(value)) => matches!(value, Value(Repr::Task(_)) | Value(Repr::TaskScope(_))),
3142 _ => unreachable!("a receiver is either a place or a temporary"),
3143 };
3144 if name == "await" || is_task {
3145 let receiver_value = match (&place, &temporary) {
3146 (Some(place), _) => place.read(span)?,
3147 (_, Some(value)) => value.clone(),
3148 _ => unreachable!("a receiver is either a place or a temporary"),
3149 };
3150 return self.call_task_method(env, receiver_value, name, args, trailing, span);
3151 }
3152
3153 if name != "freeze" && place.is_none() {
3176 let is_var_self = temporary.as_ref().is_some_and(|value| {
3177 cove_schema::builtins::builtin(&value.type_name())
3178 .and_then(|schema| schema.method(name))
3179 .is_some_and(|method| method.mutating)
3180 });
3181 if is_var_self {
3182 return Err(var_self_needs_place(name, receiver, span).into());
3183 }
3184 }
3185
3186 let args = self.eval_args(env, args, trailing)?;
3187 let mut values = plain_values(args, name)?;
3188
3189 if name == "freeze" {
3190 if let Some(place) = &place {
3193 return Ok(place.with_mut(span, |slot| match slot {
3194 Value(Repr::Vector(storage)) => builtins::freeze(storage, span),
3195 other => Err(RuntimeError::new(format!(
3196 "`{}` has no method `freeze`",
3197 other.type_name()
3198 ))
3199 .at(span)),
3200 })??);
3201 }
3202 }
3203
3204 let receiver_value = match (place, temporary) {
3205 (Some(place), _) => place.read(span)?,
3206 (_, Some(value)) => value,
3207 _ => unreachable!("a receiver is either a place or a temporary"),
3208 };
3209 Ok(builtins::call_method(
3210 self,
3211 &receiver_value,
3212 name,
3213 &mut values,
3214 span,
3215 )?)
3216 }
3217
3218 fn call_std_binding(
3231 &mut self,
3232 env: &mut Env,
3233 binding: &cove_schema::builtins::StdBinding,
3234 receiver: Value,
3235 args: &[Arg],
3236 trailing: Option<&Expr>,
3237 span: Span,
3238 ) -> Eval {
3239 let Some((owner, decl)) = self.find_function(binding.module, binding.function) else {
3240 return Err(RuntimeError::new(format!(
3247 "`{}.{}` names no function of `{}` — the package is missing the standard \
3248 library module `cove_sema::stdlib::attach` adds",
3249 binding.receiver, binding.method, binding.module
3250 ))
3251 .at(span)
3252 .into());
3253 };
3254 let mut evaluated = Vec::with_capacity(args.len() + 1);
3255 evaluated.push(EvaluatedArg {
3256 label: None,
3257 spread: false,
3258 slot: ArgSlot::Value(receiver),
3259 span,
3260 });
3261 evaluated.extend(self.eval_args(env, args, trailing)?);
3262 Ok(self.call_target(
3263 &Target {
3264 name: binding.function,
3265 params: &decl.params,
3266 body: &decl.body,
3267 module: owner,
3268 receiver: decl.receiver,
3269 is_async: decl.is_async,
3270 captures: &[],
3271 return_type: decl.return_type.as_ref(),
3272 },
3273 None,
3274 evaluated,
3275 span,
3276 )?)
3277 }
3278
3279 fn call_std_associated_binding(
3287 &mut self,
3288 env: &mut Env,
3289 binding: &cove_schema::builtins::StdBinding,
3290 args: &[Arg],
3291 trailing: Option<&Expr>,
3292 span: Span,
3293 ) -> Eval {
3294 let Some((owner, decl)) = self.find_function(binding.module, binding.function) else {
3295 return Err(RuntimeError::new(format!(
3298 "`{}.{}` names no function of `{}` — the package is missing the standard \
3299 library module `cove_sema::stdlib::attach` adds",
3300 binding.receiver, binding.method, binding.module
3301 ))
3302 .at(span)
3303 .into());
3304 };
3305 let evaluated = self.eval_args(env, args, trailing)?;
3306 Ok(self.call_target(
3307 &Target {
3308 name: binding.function,
3309 params: &decl.params,
3310 body: &decl.body,
3311 module: owner,
3312 receiver: decl.receiver,
3313 is_async: decl.is_async,
3314 captures: &[],
3315 return_type: decl.return_type.as_ref(),
3316 },
3317 None,
3318 evaluated,
3319 span,
3320 )?)
3321 }
3322
3323 fn init_struct(
3325 &mut self,
3326 module: &str,
3327 decl: &Arc<StructDecl>,
3328 args: Vec<EvaluatedArg>,
3329 span: Span,
3330 ) -> Result<Value, RuntimeError> {
3331 let names: Vec<&str> = decl.fields.iter().map(|f| f.name.node.as_str()).collect();
3332 let (mut slots, _) = assign_labels(&names, args, &decl.name.node, false)?;
3333 let mut fields = Vec::with_capacity(decl.fields.len());
3334 for (index, field) in decl.fields.iter().enumerate() {
3335 let Some(arg) = slots[index].take() else {
3336 return Err(RuntimeError::new(format!(
3337 "`{}` needs a value for field `{}`",
3338 decl.name.node, field.name.node
3339 ))
3340 .at(span)
3341 .with_rule("Struct initialization is a synthesized labeled call.")
3342 .with_help(format!(
3343 "add `{}: <value>` to the initializer",
3344 field.name.node
3345 )));
3346 };
3347 let value = value_of(&arg, &field.name.node, arg.span)?;
3348 fields.push((
3349 field.name.node.as_str().into(),
3350 self.coerce(module, value, &field.ty),
3351 ));
3352 }
3353 Ok(Value(Repr::Struct(Rc::new(StructValue {
3354 type_name: format!("{module}.{}", decl.name.node).into(),
3355 fields,
3356 opaque: self.is_opaque(module, &decl.name.node),
3357 }))))
3358 }
3359
3360 fn is_opaque(&self, module: &str, name: &str) -> bool {
3367 self.resolved(module)
3368 .and_then(|resolved| resolved.structs.get(name))
3369 .is_some_and(|entry| entry.opaque)
3370 }
3371
3372 fn eval_args(
3373 &mut self,
3374 env: &mut Env,
3375 args: &[Arg],
3376 trailing: Option<&Expr>,
3377 ) -> Result<Vec<EvaluatedArg>, Control> {
3378 let mut evaluated = Vec::with_capacity(args.len() + usize::from(trailing.is_some()));
3379 for arg in args {
3380 let slot = if arg.is_var {
3381 let place = self.resolve_place(env, &arg.value)?;
3385 ArgSlot::Alias(place)
3386 } else {
3387 ArgSlot::Value(self.eval(env, &arg.value)?)
3388 };
3389 evaluated.push(EvaluatedArg {
3390 label: arg.label.as_ref().map(|l| l.node.as_str().into()),
3391 spread: arg.spread,
3392 slot,
3393 span: arg.span,
3394 });
3395 }
3396 if let Some(trailing) = trailing {
3397 let value = self.eval_trailing(env, trailing)?;
3398 evaluated.push(EvaluatedArg {
3399 label: None,
3400 spread: false,
3401 slot: ArgSlot::Value(value),
3402 span: trailing.span,
3403 });
3404 }
3405 Ok(evaluated)
3406 }
3407
3408 fn eval_trailing(&mut self, env: &mut Env, expr: &Expr) -> Eval {
3410 match &expr.kind {
3411 ExprKind::Block(block) => self
3412 .make_closure(env, false, Vec::new(), block.clone(), expr.span)
3413 .map_err(Control::from),
3414 _ => self.eval(env, expr),
3415 }
3416 }
3417
3418 fn resolve_place(&mut self, env: &mut Env, expr: &Expr) -> Result<Place, Control> {
3428 match &expr.kind {
3429 ExprKind::Ident(name) => match env.lookup(name) {
3430 Some(place) => Ok(place.clone()),
3431 None => Err(
3432 RuntimeError::new(format!("cannot find `{name}` in this scope"))
3433 .at(expr.span)
3434 .into(),
3435 ),
3436 },
3437 ExprKind::Field { base, name } => {
3438 let base_place = self.resolve_place(env, base)?;
3439 base_place.with_ref(expr.span, |value| match value {
3440 Value(Repr::Struct(value)) => match value.get(&name.node) {
3441 Some(_) => Ok(()),
3442 None => Err(no_field(&value.type_name, &name.node, expr.span)),
3443 },
3444 other => Err(not_a_struct(other, &name.node, expr.span)),
3445 })??;
3446 Ok(base_place.field(name.node.as_str().into()))
3447 }
3448 _ => Err(RuntimeError::new(
3449 "this expression is not a place, so it cannot be assigned or aliased",
3450 )
3451 .at(expr.span)
3452 .with_rule("Only variables and their struct fields are places.")
3453 .into()),
3454 }
3455 }
3456
3457 fn resolve_place_opt(&mut self, env: &mut Env, expr: &Expr) -> Result<Option<Place>, Control> {
3459 match &expr.kind {
3460 ExprKind::Ident(name) => Ok(env.lookup(name).cloned()),
3461 ExprKind::Field { base, name } => {
3462 let Some(base_place) = self.resolve_place_opt(env, base)? else {
3463 return Ok(None);
3464 };
3465 let is_field = base_place.with_ref(expr.span, |value| match value {
3466 Value(Repr::Struct(value)) => value.get(&name.node).is_some(),
3467 _ => false,
3468 })?;
3469 Ok(is_field.then(|| base_place.field(name.node.as_str().into())))
3470 }
3471 _ => Ok(None),
3472 }
3473 }
3474
3475 fn match_pattern(
3478 &mut self,
3479 env: &mut Env,
3480 pattern: &Pattern,
3481 value: &Value,
3482 ) -> Result<bool, Control> {
3483 match &pattern.kind {
3484 PatternKind::Wildcard => Ok(true),
3485 PatternKind::Binding(name) => {
3486 if name == NONE_CASE.name {
3488 if let Value(Repr::Enum(option)) = value {
3489 if &*option.type_name == OPTION.name {
3490 return Ok(&*option.case == NONE_CASE.name);
3491 }
3492 }
3493 }
3494 env.declare(name.as_str().into(), Place::binding(value.clone()));
3495 Ok(true)
3496 }
3497 PatternKind::Literal(expr) => {
3498 let literal = self.eval(env, expr)?;
3499 Ok(value.eq_value(&literal))
3500 }
3501 PatternKind::Variant { path, payload } => {
3502 let Value(Repr::Enum(subject)) = value else {
3503 return Ok(false);
3504 };
3505 let Some(case) = path.last() else {
3506 return Ok(false);
3507 };
3508 if &*subject.case != case.node.as_str() {
3509 return Ok(false);
3510 }
3511 if path.len() >= 2 {
3512 let expected = &path[path.len() - 2].node;
3513 let actual = subject
3514 .type_name
3515 .rsplit('.')
3516 .next()
3517 .unwrap_or(&subject.type_name);
3518 if actual != expected {
3519 return Ok(false);
3520 }
3521 }
3522 if payload.len() != subject.payload.len() {
3523 return Err(RuntimeError::new(format!(
3524 "case `{}` carries {} value(s), but the pattern binds {}",
3525 case.node,
3526 subject.payload.len(),
3527 payload.len()
3528 ))
3529 .at(pattern.span)
3530 .into());
3531 }
3532 for (sub, value) in payload.iter().zip(subject.payload.iter()) {
3533 if !self.match_pattern(env, sub, value)? {
3534 return Ok(false);
3535 }
3536 }
3537 Ok(true)
3538 }
3539 }
3540 }
3541
3542 fn snapshot(&mut self, value: &Value, span: Span) -> Result<Value, RuntimeError> {
3561 match value {
3562 Value(Repr::Dyn(wrapped)) => Ok(Value(Repr::Dyn(Rc::new(DynValue {
3563 trait_name: wrapped.trait_name.clone(),
3564 value: self.snapshot(&wrapped.value, span)?,
3565 })))),
3566 Value(Repr::Struct(s)) => self.dispatch_snapshot(&s.type_name, value.clone(), span),
3567 Value(Repr::Enum(e)) => self.dispatch_snapshot(&e.type_name, value.clone(), span),
3568 other => builtins::snapshot(self, other, span),
3572 }
3573 }
3574
3575 fn dispatch_snapshot(
3578 &mut self,
3579 type_name: &str,
3580 receiver: Value,
3581 span: Span,
3582 ) -> Result<Value, RuntimeError> {
3583 let Some((type_module, short)) = type_name.rsplit_once('.') else {
3584 return Err(builtins::no_snapshot_conformance(&receiver, span));
3585 };
3586 let Some((module, decl)) = self.find_method(type_module, short, "snapshot") else {
3587 return Err(builtins::no_snapshot_conformance(&receiver, span));
3588 };
3589 self.call_target(
3590 &Target {
3591 name: "snapshot",
3592 params: &decl.params,
3593 body: &decl.body,
3594 module,
3595 receiver: decl.receiver,
3596 is_async: decl.is_async,
3597 captures: &[],
3598 return_type: decl.return_type.as_ref(),
3599 },
3600 Some(ArgSlot::Value(receiver)),
3601 Vec::new(),
3602 span,
3603 )
3604 }
3605}
3606
3607impl Callable for Interpreter<'_> {
3608 fn allocate_vector(&mut self, elements: Vec<Value>) -> Value {
3609 Interpreter::allocate_vector(self, elements)
3610 }
3611
3612 fn snapshot(&mut self, value: &Value, span: Span) -> Result<Value, RuntimeError> {
3613 Interpreter::snapshot(self, value, span)
3614 }
3615
3616 fn call_value(
3630 &mut self,
3631 callee: &Value,
3632 args: &mut Vec<Value>,
3633 span: Span,
3634 ) -> Result<Value, RuntimeError> {
3635 let args = args
3636 .drain(..)
3637 .map(|value| EvaluatedArg {
3638 label: None,
3639 spread: false,
3640 slot: ArgSlot::Value(value),
3641 span,
3642 })
3643 .collect();
3644 self.call_value_slots(callee.clone(), args, span)
3645 }
3646
3647 fn arity(&self, callee: &Value) -> Option<usize> {
3648 match callee {
3649 Value(Repr::Closure(closure)) => Some(closure.arity),
3650 _ => None,
3651 }
3652 }
3653}
3654
3655fn declared_as_value(module: Rc<str>, decl: Arc<FnDecl>) -> Value {
3668 Value(Repr::Closure(Rc::new(Closure {
3669 is_async: decl.is_async,
3670 arity: decl.params.len(),
3671 body: ClosureBody::Tree {
3672 params: decl.params.clone(),
3673 block: Arc::new(decl.body.clone()),
3674 decl: Some(decl),
3675 },
3676 module,
3677 captures: Vec::new(),
3678 })))
3679}
3680
3681fn conformable(value: &Value) -> bool {
3694 matches!(value, Value(Repr::Struct(_)) | Value(Repr::Enum(_)))
3695}
3696
3697pub(crate) fn as_dyn(value: Value, trait_name: &Rc<str>) -> Value {
3714 if matches!(value, Value(Repr::Dyn(_))) {
3715 return value;
3716 }
3717 Value(Repr::Dyn(Rc::new(DynValue {
3718 trait_name: Rc::clone(trait_name),
3719 value,
3720 })))
3721}
3722
3723pub(crate) fn coerce_inside(value: Value, mut each: impl FnMut(Value) -> Value) -> Value {
3739 match value {
3740 Value(Repr::Array(items)) => Value(Repr::Array(items.iter().cloned().map(each).collect())),
3741 Value(Repr::Enum(mut option)) if &*option.type_name == "Option" => {
3742 for item in &mut option.payload {
3743 *item = each(item.clone());
3744 }
3745 Value(Repr::Enum(option))
3746 }
3747 other => other,
3748 }
3749}
3750
3751pub(crate) fn dyn_receiver(value: &Value) -> Option<Value> {
3769 match value {
3770 Value(Repr::Dyn(object)) => Some(object.value.clone()),
3771 _ => None,
3772 }
3773}
3774
3775pub(crate) fn binary(
3776 op: BinaryOp,
3777 lhs: Value,
3778 rhs: Value,
3779 span: Span,
3780) -> Result<Value, RuntimeError> {
3781 match op {
3782 BinaryOp::Eq | BinaryOp::Ne => {
3783 let objects = (matches!(lhs, Value(Repr::Dyn(_)))
3798 || matches!(rhs, Value(Repr::Dyn(_))))
3799 && conformable(lhs.erased())
3800 && conformable(rhs.erased());
3801 let (lhs, rhs) = (lhs.erased(), rhs.erased());
3802 if !objects && !lhs.same_type_as(rhs) {
3803 return Err(RuntimeError::new(format!(
3804 "cannot compare `{}` with `{}`",
3805 lhs.type_name(),
3806 rhs.type_name()
3807 ))
3808 .at(span)
3809 .with_rule("`==` means value equality between values of the same type."));
3810 }
3811 let equal = lhs.eq_value(rhs);
3812 Ok(Value(Repr::Bool(if op == BinaryOp::Eq {
3813 equal
3814 } else {
3815 !equal
3816 })))
3817 }
3818 BinaryOp::Is => {
3823 let (lhs, rhs) = (lhs.erased(), rhs.erased());
3829 if !lhs.same_type_as(rhs) {
3830 return Err(RuntimeError::new(format!(
3831 "cannot compare the identity of `{}` with `{}`",
3832 lhs.type_name(),
3833 rhs.type_name()
3834 ))
3835 .at(span)
3836 .with_rule("`is` compares identity between values of the same type."));
3837 }
3838 match (lhs, rhs) {
3839 (Value(Repr::Vector(a)), Value(Repr::Vector(b))) => {
3840 Ok(Value(Repr::Bool(Rc::ptr_eq(a, b))))
3841 }
3842 _ => Err(identity_not_available(lhs, span)),
3843 }
3844 }
3845 BinaryOp::And | BinaryOp::Or => unreachable!("short-circuited in `eval`"),
3846 BinaryOp::Add | BinaryOp::Sub | BinaryOp::Mul | BinaryOp::Div | BinaryOp::Rem => {
3847 match (&lhs, &rhs) {
3848 (Value(Repr::Int(a)), Value(Repr::Int(b))) => {
3849 let (a, b) = (*a, *b);
3850 let value = match op {
3851 BinaryOp::Add => a.checked_add(b).ok_or_else(|| overflow("addition", span)),
3852 BinaryOp::Sub => a
3853 .checked_sub(b)
3854 .ok_or_else(|| overflow("subtraction", span)),
3855 BinaryOp::Mul => a
3856 .checked_mul(b)
3857 .ok_or_else(|| overflow("multiplication", span)),
3858 BinaryOp::Div => {
3859 if b == 0 {
3860 Err(divide_by_zero("division", span))
3861 } else {
3862 a.checked_div(b).ok_or_else(|| overflow("division", span))
3863 }
3864 }
3865 BinaryOp::Rem => {
3866 if b == 0 {
3867 Err(divide_by_zero("remainder", span))
3868 } else {
3869 a.checked_rem(b).ok_or_else(|| overflow("remainder", span))
3870 }
3871 }
3872 _ => unreachable!("checked above"),
3873 }?;
3874 Ok(Value(Repr::Int(value)))
3875 }
3876 (Value(Repr::Float(a)), Value(Repr::Float(b))) => {
3877 Ok(Value(Repr::Float(match op {
3878 BinaryOp::Add => a + b,
3879 BinaryOp::Sub => a - b,
3880 BinaryOp::Mul => a * b,
3881 BinaryOp::Div => a / b,
3882 BinaryOp::Rem => a % b,
3883 _ => unreachable!("checked above"),
3884 })))
3885 }
3886 (Value(Repr::Duration(a)), Value(Repr::Duration(b)))
3887 if matches!(op, BinaryOp::Add | BinaryOp::Sub) =>
3888 {
3889 let value = match op {
3890 BinaryOp::Add => a.checked_add(*b),
3891 _ => a.checked_sub(*b),
3892 }
3893 .ok_or_else(|| overflow("duration arithmetic", span))?;
3894 Ok(Value(Repr::Duration(value)))
3895 }
3896 (Value(Repr::Str(_)), Value(Repr::Str(_))) if op == BinaryOp::Add => {
3897 Err(RuntimeError::new("`+` is not defined for `String`")
3898 .at(span)
3899 .with_rule("There are no implicit string conversions.")
3900 .with_help("use string interpolation, such as \"{left}{right}\""))
3901 }
3902 _ => Err(operator_type_error(op, &lhs, &rhs, span)),
3903 }
3904 }
3905 BinaryOp::Lt | BinaryOp::Le | BinaryOp::Gt | BinaryOp::Ge => {
3906 let ordering = match (&lhs, &rhs) {
3907 (Value(Repr::Int(a)), Value(Repr::Int(b))) => a.partial_cmp(b),
3908 (Value(Repr::Float(a)), Value(Repr::Float(b))) => a.partial_cmp(b),
3909 (Value(Repr::Duration(a)), Value(Repr::Duration(b))) => a.partial_cmp(b),
3910 (Value(Repr::Str(a)), Value(Repr::Str(b))) => a.partial_cmp(b),
3911 _ => return Err(operator_type_error(op, &lhs, &rhs, span)),
3912 };
3913 let Some(ordering) = ordering else {
3914 return Ok(Value(Repr::Bool(false)));
3915 };
3916 Ok(Value(Repr::Bool(match op {
3917 BinaryOp::Lt => ordering.is_lt(),
3918 BinaryOp::Le => ordering.is_le(),
3919 BinaryOp::Gt => ordering.is_gt(),
3920 _ => ordering.is_ge(),
3921 })))
3922 }
3923 }
3924}
3925
3926pub(crate) fn unary(op: UnaryOp, value: Value, span: Span) -> Result<Value, RuntimeError> {
3927 match (op, value) {
3928 (UnaryOp::Not, Value(Repr::Bool(value))) => Ok(Value(Repr::Bool(!value))),
3929 (UnaryOp::Neg, Value(Repr::Int(value))) => Ok(Value(Repr::Int(
3930 value
3931 .checked_neg()
3932 .ok_or_else(|| overflow("negation", span))?,
3933 ))),
3934 (UnaryOp::Neg, Value(Repr::Float(value))) => Ok(Value(Repr::Float(-value))),
3935 (UnaryOp::Neg, Value(Repr::Duration(value))) => Ok(Value(Repr::Duration(
3936 value
3937 .checked_neg()
3938 .ok_or_else(|| overflow("negation", span))?,
3939 ))),
3940 (op, value) => Err(RuntimeError::new(format!(
3941 "`{}` is not defined for `{}`",
3942 match op {
3943 UnaryOp::Not => "!",
3944 UnaryOp::Neg => "-",
3945 },
3946 value.type_name()
3947 ))
3948 .at(span)
3949 .with_rule("There are no implicit numeric, string, or boolean conversions.")),
3950 }
3951}
3952
3953pub(crate) fn enum_case(
3971 program: &Program,
3972 module: &str,
3973 decl: &Arc<EnumDecl>,
3974 case: &str,
3975 payload: &mut Vec<Value>,
3976 span: Span,
3977) -> Result<Value, RuntimeError> {
3978 let Some(found) = decl.cases.iter().find(|c| c.name.node == case) else {
3979 return Err(RuntimeError::new(format!(
3980 "enum `{}` has no case or associated function `{case}`",
3981 decl.name.node
3982 ))
3983 .at(span)
3984 .with_rule(
3985 "`Enum.name` is a case when the enum declares one, and otherwise an associated function declared in an `impl` block.",
3986 )
3987 .with_help(known_members(program, module, decl)));
3988 };
3989 if found.payload.len() != payload.len() {
3990 return Err(RuntimeError::new(format!(
3991 "case `{}.{case}` carries {} value(s), but {} were given",
3992 decl.name.node,
3993 found.payload.len(),
3994 payload.len()
3995 ))
3996 .at(span));
3997 }
3998 Ok(Value(Repr::Enum(Box::new(EnumValue {
3999 type_name: format!("{module}.{}", decl.name.node).into(),
4000 case: case.into(),
4001 payload: payload.drain(..).collect(),
4006 }))))
4007}
4008
4009fn known_members(program: &Program, module: &str, decl: &Arc<EnumDecl>) -> String {
4011 let cases: Vec<&str> = decl
4012 .cases
4013 .iter()
4014 .map(|case| case.name.node.as_str())
4015 .collect();
4016 let mut help = format!("known cases: {}", cases.join(", "));
4017 let functions: Vec<&str> = match program.modules.get(module) {
4018 Some(resolved) => resolved
4019 .methods
4020 .keys()
4021 .filter(|(type_name, _)| *type_name == decl.name.node)
4022 .map(|(_, name)| name.as_str())
4023 .collect(),
4024 None => Vec::new(),
4025 };
4026 if !functions.is_empty() {
4027 help.push_str(&format!("; known functions: {}", functions.join(", ")));
4028 }
4029 help
4030}
4031
4032pub(crate) fn no_match(value: &Value, span: Span) -> RuntimeError {
4042 RuntimeError::new(format!("no `match` arm covers `{value}`"))
4043 .at(span)
4044 .with_rule("`match` must cover every enum case.")
4045 .with_help("add an arm for this case, or a `_` arm")
4046}
4047
4048fn init_map_entry(args: Vec<EvaluatedArg>, span: Span) -> Result<Value, RuntimeError> {
4058 let labels: Vec<&str> = MAP_ENTRY.fields.iter().map(|field| field.name).collect();
4059 let (mut slots, _) = assign_labels(&labels, args, MAP_ENTRY.name, false)?;
4060 let mut fields = Vec::with_capacity(labels.len());
4061 for (index, field_name) in labels.iter().enumerate() {
4062 let Some(arg) = slots[index].take() else {
4063 return Err(RuntimeError::new(format!(
4064 "`{}` needs a value for field `{field_name}`",
4065 MAP_ENTRY.name
4066 ))
4067 .at(span)
4068 .with_rule("Struct initialization is a synthesized labeled call.")
4069 .with_help(format!("add `{field_name}: <value>` to the initializer")));
4070 };
4071 fields.push(((*field_name).into(), value_of(&arg, field_name, arg.span)?));
4072 }
4073 Ok(Value(Repr::Struct(Rc::new(StructValue {
4074 type_name: MAP_ENTRY.name.into(),
4075 fields,
4076 opaque: false,
4077 }))))
4078}
4079
4080#[allow(clippy::type_complexity)]
4093fn assign_labels(
4094 names: &[&str],
4095 args: Vec<EvaluatedArg>,
4096 what: &str,
4097 variadic_last: bool,
4098) -> Result<(Vec<Option<EvaluatedArg>>, Vec<EvaluatedArg>), RuntimeError> {
4099 let mut slots: Vec<Option<EvaluatedArg>> = (0..names.len()).map(|_| None).collect();
4100 let mut rest = Vec::new();
4101 let mut next = 0usize;
4102 let mut labeled = false;
4103
4104 for arg in args {
4105 match &arg.label {
4106 Some(label) => {
4107 labeled = true;
4108 let Some(index) = names.iter().position(|n| *n == &**label) else {
4109 return Err(RuntimeError::new(format!(
4110 "`{what}` has no parameter labeled `{label}`"
4111 ))
4112 .at(arg.span)
4113 .with_rule("Argument labels are parameter names and part of the API contract.")
4114 .with_help(format!("known labels: {}", names.join(", "))));
4115 };
4116 if slots[index].is_some() {
4117 return Err(RuntimeError::new(format!(
4118 "`{what}` was given `{label}` more than once"
4119 ))
4120 .at(arg.span));
4121 }
4122 if index < next {
4125 return Err(RuntimeError::new(format!(
4126 "`{what}` was given the label `{label}` out of declaration order"
4127 ))
4128 .at(arg.span)
4129 .with_rule(
4130 "Labeled arguments appear in declaration order, so argument order matches parameter order.",
4131 )
4132 .with_help(format!(
4133 "write the arguments in this order: {}",
4134 names.join(", ")
4135 )));
4136 }
4137 slots[index] = Some(arg);
4138 next = index + 1;
4139 }
4140 None => {
4141 if labeled {
4142 return Err(RuntimeError::new(format!(
4143 "`{what}` was given a positional argument after a labeled one"
4144 ))
4145 .at(arg.span)
4146 .with_rule(
4147 "Positional arguments may precede labels; after the first label every argument must be labeled.",
4148 ));
4149 }
4150 if variadic_last && next + 1 >= names.len() {
4151 rest.push(arg);
4152 } else if next < names.len() {
4153 slots[next] = Some(arg);
4154 next += 1;
4155 } else {
4156 return Err(RuntimeError::new(format!(
4157 "`{what}` takes {} argument(s), but more were given",
4158 names.len()
4159 ))
4160 .at(arg.span));
4161 }
4162 }
4163 }
4164 }
4165 Ok((slots, rest))
4166}
4167
4168fn plain_values(args: Vec<EvaluatedArg>, what: &str) -> Result<Vec<Value>, RuntimeError> {
4170 let mut values = Vec::with_capacity(args.len());
4171 for arg in &args {
4172 values.push(value_of(arg, what, arg.span)?);
4173 }
4174 Ok(values)
4175}
4176
4177fn value_of(arg: &EvaluatedArg, what: &str, span: Span) -> Result<Value, RuntimeError> {
4178 match &arg.slot {
4179 ArgSlot::Value(value) => Ok(value.clone()),
4180 ArgSlot::Alias(_) => Err(RuntimeError::new(format!(
4181 "`{what}` does not take a `var` argument"
4182 ))
4183 .at(span)
4184 .with_rule(
4185 "A `var` parameter is a non-escaping inout alias, marked at both the declaration and the call site.",
4186 )),
4187 }
4188}
4189
4190fn mention_block(block: &Block, out: &mut BTreeSet<String>) {
4199 for stmt in &block.statements {
4200 match &stmt.kind {
4201 StmtKind::Let { value, .. } => mention_expr(value, out),
4202 StmtKind::Expr(expr) => mention_expr(expr, out),
4203 StmtKind::Item(item) => match &item.kind {
4204 ItemKind::Fn(decl) => mention_fn(decl, out),
4205 ItemKind::Impl(block) => {
4206 for item in &block.items {
4207 if let ItemKind::Fn(decl) = &item.kind {
4208 mention_fn(decl, out);
4209 }
4210 }
4211 }
4212 ItemKind::Struct(_)
4216 | ItemKind::Enum(_)
4217 | ItemKind::Trait(_)
4218 | ItemKind::TypeAlias(_) => {}
4219 },
4220 }
4221 }
4222 if let Some(tail) = &block.tail {
4223 mention_expr(tail, out);
4224 }
4225}
4226
4227fn mention_fn(decl: &FnDecl, out: &mut BTreeSet<String>) {
4228 mention_params(&decl.params, out);
4229 mention_block(&decl.body, out);
4230}
4231
4232fn mention_params(params: &[Param], out: &mut BTreeSet<String>) {
4235 for param in params {
4236 if let Some(default) = ¶m.default {
4237 mention_expr(default, out);
4238 }
4239 }
4240}
4241
4242fn mention_expr(expr: &Expr, out: &mut BTreeSet<String>) {
4243 match &expr.kind {
4244 ExprKind::Int(_)
4245 | ExprKind::Float(_)
4246 | ExprKind::Bool(_)
4247 | ExprKind::Duration(_)
4248 | ExprKind::Unit => {}
4249 ExprKind::Str(parts) => {
4250 for part in parts {
4251 if let StrPart::Interpolation(inner) = part {
4252 mention_expr(inner, out);
4253 }
4254 }
4255 }
4256 ExprKind::Ident(name) => {
4257 out.insert(name.clone());
4258 }
4259 ExprKind::ArrayLit(items) => {
4260 for item in items {
4261 mention_expr(item, out);
4262 }
4263 }
4264 ExprKind::Field { base, .. } => mention_expr(base, out),
4266 ExprKind::Call {
4267 callee,
4268 args,
4269 trailing,
4270 ..
4271 } => {
4272 mention_expr(callee, out);
4273 for arg in args {
4274 mention_expr(&arg.value, out);
4275 }
4276 if let Some(trailing) = trailing {
4277 mention_expr(trailing, out);
4278 }
4279 }
4280 ExprKind::Unary { operand, .. } => mention_expr(operand, out),
4281 ExprKind::Binary { lhs, rhs, .. } => {
4282 mention_expr(lhs, out);
4283 mention_expr(rhs, out);
4284 }
4285 ExprKind::Assign { target, value, .. } => {
4286 mention_expr(target, out);
4287 mention_expr(value, out);
4288 }
4289 ExprKind::Try(inner) | ExprKind::Await(inner) => mention_expr(inner, out),
4290 ExprKind::Block(block) => mention_block(block, out),
4291 ExprKind::If {
4292 condition,
4293 then_branch,
4294 else_branch,
4295 } => {
4296 mention_expr(condition, out);
4297 mention_block(then_branch, out);
4298 if let Some(branch) = else_branch {
4299 mention_expr(branch, out);
4300 }
4301 }
4302 ExprKind::Match { scrutinee, arms } => {
4303 mention_expr(scrutinee, out);
4304 for arm in arms {
4305 mention_pattern(&arm.pattern, out);
4306 mention_expr(&arm.body, out);
4307 }
4308 }
4309 ExprKind::For { iterable, body, .. } => {
4310 mention_expr(iterable, out);
4311 mention_block(body, out);
4312 }
4313 ExprKind::While { condition, body } => {
4314 mention_expr(condition, out);
4315 mention_block(body, out);
4316 }
4317 ExprKind::Return(inner) | ExprKind::Break(inner) => {
4318 if let Some(inner) = inner {
4319 mention_expr(inner, out);
4320 }
4321 }
4322 ExprKind::Continue => {}
4323 ExprKind::Lambda { params, body, .. } => {
4324 mention_params(params, out);
4325 mention_block(body, out);
4326 }
4327 ExprKind::Scope { body, .. } => mention_block(body, out),
4330 ExprKind::Range { start, end, .. } => {
4331 mention_expr(start, out);
4332 mention_expr(end, out);
4333 }
4334 }
4335}
4336
4337fn mention_pattern(pattern: &Pattern, out: &mut BTreeSet<String>) {
4339 match &pattern.kind {
4340 PatternKind::Wildcard | PatternKind::Binding(_) => {}
4341 PatternKind::Literal(expr) => mention_expr(expr, out),
4342 PatternKind::Variant { payload, .. } => {
4343 for sub in payload {
4344 mention_pattern(sub, out);
4345 }
4346 }
4347 }
4348}
4349
4350fn run_task(
4362 runtime: Runtime,
4363 id: u64,
4364 cancellation: Cancellation,
4365 body: Transfer,
4366 span: Span,
4367) -> TaskOutcome {
4368 let mut interpreter = Interpreter::for_task(&runtime, id, cancellation.clone());
4369 interpreter.timings.push(Timing::start());
4370 let result = interpreter.call_value_slots(body.into_value(), Vec::new(), span);
4371 let timing = interpreter
4372 .timings
4373 .pop()
4374 .expect("a task pushes exactly the one timing it pops");
4375 interpreter.retire_heap();
4379 task::finished(&runtime, id, &cancellation, span, result, timing.cpu())
4380}
4381
4382pub(crate) fn returned_error_message(value: &Value) -> Option<String> {
4388 let Value(Repr::Enum(result)) = value else {
4389 return None;
4390 };
4391 result.payload.first().map(ToString::to_string)
4392}
4393
4394struct Callback<'i, 'a> {
4409 interpreter: &'i mut Interpreter<'a>,
4410 span: Span,
4413}
4414
4415impl Callback<'_, '_> {
4416 fn run(&mut self, callee: &Value, args: Vec<Value>) -> Result<Value, RuntimeError> {
4417 let span = self.span;
4418 if self.interpreter.reentry_depth >= MAX_REENTRY_DEPTH {
4424 return Err(reentry_too_deep(span));
4425 }
4426 let args: Vec<EvaluatedArg> = args
4427 .into_iter()
4428 .map(|value| EvaluatedArg {
4429 label: None,
4430 spread: false,
4431 slot: ArgSlot::Value(value),
4432 span,
4433 })
4434 .collect();
4435 self.interpreter.reentry_depth += 1;
4436 let result = self
4437 .interpreter
4438 .call_value_slots(callee.clone(), args, span);
4439 self.interpreter.reentry_depth -= 1;
4440 match result? {
4444 Value(Repr::Task(task)) => self.interpreter.settle(&task, span),
4445 other => Ok(other),
4446 }
4447 }
4448}
4449
4450impl Reentry for Callback<'_, '_> {
4451 fn call(&mut self, callee: &Value, args: Vec<Value>) -> Result<Value, RuntimeError> {
4452 self.run(callee, args)
4453 }
4454
4455 fn call_until(
4456 &mut self,
4457 callee: &Value,
4458 args: Vec<Value>,
4459 stop: &Cancellation,
4460 ) -> Result<Value, RuntimeError> {
4461 self.interpreter.stops.push(stop.clone());
4462 let result = self.run(callee, args);
4463 self.interpreter.stops.pop();
4464 result
4465 }
4466
4467 fn is_cancelled(&self) -> bool {
4478 if self
4479 .interpreter
4480 .cancellation
4481 .as_ref()
4482 .is_some_and(Cancellation::is_cancelled)
4483 {
4484 return true;
4485 }
4486 if self
4487 .interpreter
4488 .stops
4489 .iter()
4490 .any(Cancellation::is_cancelled)
4491 {
4492 return true;
4493 }
4494 self.interpreter
4495 .budget
4496 .as_ref()
4497 .is_some_and(Meter::is_cancelled)
4498 }
4499
4500 fn time_left(&self) -> Option<Duration> {
4508 let budget = self.interpreter.budget.as_ref()?;
4509 let deadline = budget.limits().deadline?;
4510 Some(deadline.saturating_sub(budget.elapsed()))
4511 }
4512
4513 fn task(&self) -> u64 {
4521 self.interpreter.task_id()
4522 }
4523}
4524
4525pub(crate) fn stopped_here(
4550 cancellation: Option<&Cancellation>,
4551 stops: &[Cancellation],
4552 span: Span,
4553) -> Result<(), RuntimeError> {
4554 if cancellation.is_some_and(Cancellation::is_cancelled) {
4555 return Err(task_cancelled(span));
4556 }
4557 if stops.iter().any(Cancellation::is_cancelled) {
4562 return Err(work_stopped(span));
4563 }
4564 Ok(())
4565}
4566
4567pub(crate) fn reentry_too_deep(span: Span) -> RuntimeError {
4576 RuntimeError::new(format!(
4577 "reentry depth limit of {MAX_REENTRY_DEPTH} reached while a host ran a Cove callback"
4578 ))
4579 .at(span)
4580 .with_rule("A host runs a Cove callback on the calling task's own stack, and how deep that may nest is a runtime control.")
4581 .with_help("a callback is Cove code and may call a host that is handed work of its own; that nesting is what this bounds")
4582}
4583
4584pub(crate) fn work_stopped(span: Span) -> RuntimeError {
4590 RuntimeError::new("this work was stopped before it finished")
4591 .at(span)
4592 .with_rule(
4593 "A host call that bounds the work it was given stops that work at its next safepoint.",
4594 )
4595}
4596
4597pub(crate) fn task_cancelled(span: Span) -> RuntimeError {
4602 RuntimeError::new("this task was cancelled")
4603 .at(span)
4604 .with_rule("Leaving a task scope waits for or cancels its child tasks.")
4605}
4606
4607fn expect_no_arguments(what: &str, values: &[Value], span: Span) -> Result<(), RuntimeError> {
4608 if values.is_empty() {
4609 return Ok(());
4610 }
4611 Err(RuntimeError::new(format!(
4612 "`{what}` takes no arguments, but {} were given",
4613 values.len()
4614 ))
4615 .at(span))
4616}
4617
4618fn unsupported(what: &str, span: Span) -> RuntimeError {
4621 RuntimeError::new(format!(
4622 "{what} is not implemented yet in the MVP interpreter"
4623 ))
4624 .at(span)
4625 .with_rule("The MVP interpreter runs the subset of Cove that the MVP defines.")
4626}
4627
4628pub(crate) fn items_of(value: Value, span: Span) -> Result<Vec<Value>, RuntimeError> {
4644 match value {
4647 Value(Repr::Array(items)) => Ok(items.iter().cloned().collect()),
4648 Value(Repr::Vector(storage)) => Ok(storage.elements.borrow().clone()),
4649 Value(Repr::Range {
4651 start,
4652 end,
4653 inclusive_end,
4654 }) => Ok(RangeBounds::of(start, end, inclusive_end).items()),
4655 Value(Repr::Set(items)) => Ok(items.iter().map(|key| key.to_value()).collect()),
4658 Value(Repr::Map(entries)) => Ok(entries
4662 .iter()
4663 .map(|(key, value)| {
4664 Value(Repr::Struct(Rc::new(StructValue {
4665 type_name: MAP_ENTRY.name.into(),
4666 fields: vec![
4667 ("key".into(), key.to_value()),
4668 ("value".into(), value.clone()),
4669 ],
4670 opaque: false,
4671 })))
4672 })
4673 .collect()),
4674 other => Err(RuntimeError::new(format!(
4675 "`for` iterates an `Array`, a `Vector`, a `Range`, a `Set`, or a `Map`, but found `{}`",
4676 other.type_name()
4677 ))
4678 .at(span)),
4679 }
4680}
4681
4682pub(crate) fn overflow(operation: &str, span: Span) -> RuntimeError {
4683 RuntimeError::new(format!("`Int` {operation} overflowed"))
4684 .at(span)
4685 .with_rule("Integer overflow is a broken invariant, not a wrapped result.")
4686}
4687
4688pub(crate) fn divide_by_zero(operation: &str, span: Span) -> RuntimeError {
4696 RuntimeError::new(format!("`Int` {operation} by zero"))
4697 .at(span)
4698 .with_rule("Division and remainder by zero are broken invariants.")
4699}
4700
4701fn operator_type_error(op: BinaryOp, lhs: &Value, rhs: &Value, span: Span) -> RuntimeError {
4702 RuntimeError::new(format!(
4703 "`{}` is not defined for `{}` and `{}`",
4704 operator_text(op),
4705 lhs.type_name(),
4706 rhs.type_name()
4707 ))
4708 .at(span)
4709 .with_rule("There are no implicit numeric, string, or boolean conversions.")
4710}
4711
4712fn operator_text(op: BinaryOp) -> &'static str {
4713 match op {
4714 BinaryOp::Add => "+",
4715 BinaryOp::Sub => "-",
4716 BinaryOp::Mul => "*",
4717 BinaryOp::Div => "/",
4718 BinaryOp::Rem => "%",
4719 BinaryOp::Eq => "==",
4720 BinaryOp::Ne => "!=",
4721 BinaryOp::Lt => "<",
4722 BinaryOp::Le => "<=",
4723 BinaryOp::Gt => ">",
4724 BinaryOp::Ge => ">=",
4725 BinaryOp::Is => "is",
4726 BinaryOp::And => "&&",
4727 BinaryOp::Or => "||",
4728 }
4729}
4730
4731fn identity_not_available(value: &Value, span: Span) -> RuntimeError {
4734 RuntimeError::new(format!("identity is not available for `{}`", value.type_name()))
4735 .at(span)
4736 .with_rule("`==` means value equality. Identity, when available, is explicit.")
4737 .with_help(
4738 "`is` is defined for `Vector`; compare other values with `==`, or call `toArray()` for an independent copy",
4739 )
4740}
4741
4742pub(crate) fn source_text(sources: &SourceMap, span: Span) -> &str {
4750 let file = sources.get(span.file);
4751 file.text
4752 .get(span.start as usize..span.end as usize)
4753 .unwrap_or("?")
4754}
4755
4756pub(crate) fn no_field(type_name: &str, field: &str, span: Span) -> RuntimeError {
4757 RuntimeError::new(format!("`{type_name}` has no field `{field}`")).at(span)
4758}
4759
4760pub(crate) fn not_a_struct(value: &Value, field: &str, span: Span) -> RuntimeError {
4761 RuntimeError::new(format!("`{}` has no field `{field}`", value.type_name()))
4762 .at(span)
4763 .with_rule("Only struct fields are places.")
4764}
4765
4766fn var_self_needs_place(method: &str, receiver: &Expr, span: Span) -> RuntimeError {
4775 RuntimeError::new(format!(
4776 "`{method}` takes a `var self` receiver, but `{}` is not a place",
4777 describe_place(receiver)
4778 ))
4779 .at(span)
4780 .with_rule("A mutating receiver declares `var self` and mutates the caller's place.")
4781 .with_help("bind the value with `var` first, then call the method on that binding")
4782}
4783
4784fn expect_bool(value: Value, op: BinaryOp, span: Span) -> Result<bool, RuntimeError> {
4785 match value {
4786 Value(Repr::Bool(value)) => Ok(value),
4787 other => Err(RuntimeError::new(format!(
4788 "`{}` needs `Bool` operands, but found `{}`",
4789 operator_text(op),
4790 other.type_name()
4791 ))
4792 .at(span)
4793 .with_rule("There are no implicit boolean conversions.")),
4794 }
4795}
4796
4797fn expect_int(value: Value, what: &str, span: Span) -> Result<i64, RuntimeError> {
4798 match value {
4799 Value(Repr::Int(value)) => Ok(value),
4800 other => Err(RuntimeError::new(format!(
4801 "{what} must be an `Int`, but found `{}`",
4802 other.type_name()
4803 ))
4804 .at(span)),
4805 }
4806}
4807
4808fn describe_place(expr: &Expr) -> String {
4810 match &expr.kind {
4811 ExprKind::Ident(name) => name.clone(),
4812 ExprKind::Field { base, name } => format!("{}.{}", describe_place(base), name.node),
4813 _ => "this expression".to_string(),
4814 }
4815}
4816
4817#[cfg(test)]
4818mod tests {
4819 use super::*;
4820 use std::collections::BTreeMap;
4821 use std::io::Write;
4822 use std::path::{Path, PathBuf};
4823
4824 use std::sync::Mutex;
4825 use std::time::Duration;
4826
4827 use cove_diag::Diagnostic;
4828 use cove_sema::config::Config;
4829 use cove_sema::package::{Module, Package, Unit};
4830
4831 use crate::budget::{Budget, Limits};
4832 use crate::host::{Console, Documents, Env as EnvHost, Grants, HostRegistry};
4833 use crate::trace::TraceSink;
4834
4835 #[derive(Clone, Default)]
4840 struct Buffer(Arc<Mutex<Vec<u8>>>);
4841
4842 impl Write for Buffer {
4843 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
4844 self.written().extend_from_slice(buf);
4845 Ok(buf.len())
4846 }
4847
4848 fn flush(&mut self) -> std::io::Result<()> {
4849 Ok(())
4850 }
4851 }
4852
4853 impl Buffer {
4854 fn written(&self) -> std::sync::MutexGuard<'_, Vec<u8>> {
4855 self.0.lock().expect("no test panics while printing")
4856 }
4857
4858 fn text(&self) -> String {
4859 String::from_utf8(self.written().clone()).expect("console output is UTF-8")
4860 }
4861 }
4862
4863 fn program_of(source: &str) -> (Arc<SourceMap>, Arc<Program>) {
4865 let mut sources = SourceMap::new();
4866 let path = PathBuf::from("test/main.cove");
4867 let file = sources.add(path.clone(), source);
4868 let ast = cove_syntax::parse_file(&sources, file).expect("test source parses");
4869 let mut modules = BTreeMap::new();
4870 modules.insert(
4871 "test".to_string(),
4872 Module {
4873 name: "test".to_string(),
4874 dir: PathBuf::from("test"),
4875 units: vec![Unit { file, path, ast }],
4876 },
4877 );
4878 for (name, module) in cove_sema::stdlib::attach(&mut sources).expect("stdlib parses") {
4879 modules.insert(name, module);
4880 }
4881 let package = Package {
4882 root: PathBuf::new(),
4883 config: Config::default(),
4884 modules,
4885 };
4886 let program = cove_sema::resolve::resolve(&package).expect("test source resolves");
4887 (Arc::new(sources), Arc::new(program))
4888 }
4889
4890 fn check_errors_of(source: &str) -> Vec<Diagnostic> {
4899 let mut sources = SourceMap::new();
4900 let path = PathBuf::from("test/main.cove");
4901 let file = sources.add(path.clone(), source);
4902 let ast = cove_syntax::parse_file(&sources, file).expect("test source parses");
4903 let mut modules = BTreeMap::new();
4904 modules.insert(
4905 "test".to_string(),
4906 Module {
4907 name: "test".to_string(),
4908 dir: PathBuf::from("test"),
4909 units: vec![Unit { file, path, ast }],
4910 },
4911 );
4912 for (name, module) in cove_sema::stdlib::attach(&mut sources).expect("stdlib parses") {
4913 modules.insert(name, module);
4914 }
4915 let package = Package {
4916 root: PathBuf::new(),
4917 config: Config::default(),
4918 modules,
4919 };
4920 let program = cove_sema::resolve::resolve(&package).expect("test source resolves");
4921 cove_sema::typeck::check(&package, &program)
4922 .into_iter()
4923 .filter(|d| d.severity == cove_diag::Severity::Error)
4924 .collect()
4925 }
4926
4927 fn program_of_modules(modules: &[(&str, &str)]) -> (Arc<SourceMap>, Arc<Program>) {
4929 let mut sources = SourceMap::new();
4930 let mut map = BTreeMap::new();
4931 for (name, source) in modules {
4932 let path = PathBuf::from(format!("{name}/main.cove"));
4933 let file = sources.add(path.clone(), *source);
4934 let ast = cove_syntax::parse_file(&sources, file).expect("test source parses");
4935 map.insert(
4936 (*name).to_string(),
4937 Module {
4938 name: (*name).to_string(),
4939 dir: PathBuf::from(*name),
4940 units: vec![Unit { file, path, ast }],
4941 },
4942 );
4943 }
4944 for (name, module) in cove_sema::stdlib::attach(&mut sources).expect("stdlib parses") {
4945 map.insert(name, module);
4946 }
4947 let package = Package {
4948 root: PathBuf::new(),
4949 config: Config::default(),
4950 modules: map,
4951 };
4952 let program = cove_sema::resolve::resolve(&package).expect("test package resolves");
4953 (Arc::new(sources), Arc::new(program))
4954 }
4955
4956 fn run_modules(modules: &[(&str, &str)]) -> Run {
4958 let (sources, program) = program_of_modules(modules);
4959 run_in(
4960 &program,
4961 &sources,
4962 "app",
4963 "main",
4964 &[],
4965 &["console"],
4966 BTreeMap::new(),
4967 )
4968 }
4969
4970 struct Run {
4971 value: Result<Value, RuntimeError>,
4972 output: String,
4973 }
4974
4975 impl Run {
4976 fn value(self) -> Value {
4977 self.value.expect("the program ran without a runtime error")
4978 }
4979
4980 fn error(self) -> RuntimeError {
4981 match self.value {
4982 Ok(value) => panic!("expected a runtime error, but the program returned {value}"),
4983 Err(error) => error,
4984 }
4985 }
4986 }
4987
4988 fn run_in(
4989 program: &Arc<Program>,
4990 sources: &Arc<SourceMap>,
4991 module: &str,
4992 entry: &str,
4993 args: &[&str],
4994 grants: &[&str],
4995 env: BTreeMap<String, String>,
4996 ) -> Run {
4997 let buffer = Buffer::default();
4998 let mut hosts = HostRegistry::new(Grants::new(grants.to_vec()));
4999 hosts.register(Box::new(Console::new(buffer.clone(), Buffer::default())));
5000 hosts.register(Box::new(EnvHost::new(env)));
5001 let runtime = Runtime::new(program.clone(), sources.clone(), Arc::new(hosts));
5002 let value = Interpreter::new(&runtime).run_entry(
5003 module,
5004 entry,
5005 args.iter().map(|a| (*a).into()).collect(),
5006 );
5007 Run {
5008 value,
5009 output: buffer.text(),
5010 }
5011 }
5012
5013 fn run_entry_of(source: &str, entry: &str, args: &[&str]) -> Run {
5015 let (sources, program) = program_of(source);
5016 run_in(
5017 &program,
5018 &sources,
5019 "test",
5020 entry,
5021 args,
5022 &["console", "env"],
5023 BTreeMap::new(),
5024 )
5025 }
5026
5027 fn run_body(body: &str) -> Run {
5029 let source = format!(
5030 "use console.println\n\nexport fn main() -> Result<Unit, Error> {{\n{body}\n Ok(())\n}}\n"
5031 );
5032 run_entry_of(&source, "main", &[])
5033 }
5034
5035 fn output_of(body: &str) -> String {
5036 run_body(body).output
5037 }
5038
5039 fn error_of(body: &str) -> RuntimeError {
5040 run_body(body).error()
5041 }
5042
5043 fn run_assertion(body: &str) -> Run {
5048 let source = format!("test fn check() -> Result<Unit, Error> {{\n{body}\n}}\n");
5049 let (sources, program) = program_of(&source);
5050 run_in(
5051 &program,
5052 &sources,
5053 "test",
5054 "check",
5055 &[],
5056 &[],
5057 BTreeMap::new(),
5058 )
5059 }
5060
5061 fn assertion_message(body: &str) -> Option<String> {
5063 run_assertion(body)
5064 .value()
5065 .err_payload()
5066 .map(|payload| payload[0].to_string())
5067 }
5068
5069 #[test]
5070 fn a_holding_assertion_produces_ok() {
5071 assert!(run_assertion(" assert(1 + 1 == 2)").value().is_ok());
5072 }
5073
5074 #[test]
5075 fn a_failing_assertion_names_the_conditions_source_text() {
5076 assert_eq!(
5077 assertion_message(" assert(1 + 1 == 3)").as_deref(),
5078 Some("assertion failed: `1 + 1 == 3`")
5079 );
5080 }
5081
5082 #[test]
5083 fn a_failing_assertion_is_an_err_rather_than_a_panic() {
5084 assert_eq!(
5086 assertion_message(" assert(false)?\n Ok(())").as_deref(),
5087 Some("assertion failed: `false`")
5088 );
5089 }
5090
5091 #[test]
5092 fn assert_equal_reports_both_values_and_the_actual_expressions_source() {
5093 assert_eq!(assertion_message(" assertEqual(2 + 2, 4)"), None);
5094 assert_eq!(
5095 assertion_message(" assertEqual(2 + 2, 5)").as_deref(),
5096 Some("assertion failed: `2 + 2` is `4`, expected `5`")
5097 );
5098 }
5099
5100 #[test]
5101 fn a_failed_assertion_records_where_it_was_written() {
5102 let source = "test fn check() -> Result<Unit, Error> {\n assert(1 == 2)\n}\n";
5103 let (sources, program) = program_of(source);
5104 let hosts = HostRegistry::new(Grants::default());
5105 let runtime = Runtime::new(program, sources.clone(), Arc::new(hosts));
5106 let mut interpreter = Interpreter::new(&runtime);
5107 interpreter
5108 .run_entry("test", "check", Vec::new())
5109 .expect("the assertion fails as an `Err`, not a runtime error");
5110 let (span, message) = interpreter
5111 .assertion_failure()
5112 .expect("the failure was recorded");
5113 assert_eq!(message, "assertion failed: `1 == 2`");
5114 assert_eq!(sources.get(span.file).line_col(span.start).0, 2);
5115 }
5116
5117 #[test]
5118 fn a_holding_assertion_records_nothing() {
5119 let source = "test fn check() -> Result<Unit, Error> {\n assert(1 == 1)\n}\n";
5120 let (sources, program) = program_of(source);
5121 let hosts = HostRegistry::new(Grants::default());
5122 let runtime = Runtime::new(program, sources, Arc::new(hosts));
5123 let mut interpreter = Interpreter::new(&runtime);
5124 interpreter.run_entry("test", "check", Vec::new()).unwrap();
5125 assert!(interpreter.assertion_failure().is_none());
5126 }
5127
5128 #[test]
5129 fn assert_equal_refuses_the_comparison_that_equality_refuses() {
5130 let error = run_assertion(" assertEqual(1, \"1\")").error();
5131 assert!(
5132 error.message.contains("cannot compare `Int` with `String`"),
5133 "{}",
5134 error.message
5135 );
5136 }
5137
5138 #[test]
5139 fn a_module_declaration_wins_over_the_assertion_builtin() {
5140 let source = "fn assert(value: Int) -> Int {\n value\n}\n\n export fn main() -> Int {\n assert(7)\n}\n";
5141 let (sources, program) = program_of(source);
5142 let run = run_in(
5143 &program,
5144 &sources,
5145 "test",
5146 "main",
5147 &[],
5148 &[],
5149 BTreeMap::new(),
5150 );
5151 assert!(matches!(run.value(), Value(Repr::Int(7))));
5152 }
5153
5154 const TRAITS: &str = r##"
5160use console.println
5161
5162trait Display {
5163 fn describe(self) -> String
5164
5165 fn label(self) -> String { "<{self.describe()}>" }
5166}
5167
5168struct Booking(id: Int)
5169
5170struct Receipt(total: Int)
5171
5172impl Display for Booking {
5173 fn describe(self) -> String { "booking {self.id}" }
5174 fn label(self) -> String { "#{self.id}" }
5175}
5176
5177impl Display for Receipt {
5178 fn describe(self) -> String { "receipt for {self.total}" }
5179}
5180
5181fn render<T: Display>(value: T) -> String {
5182 "{value.label()} / {value.describe()}"
5183}
5184
5185fn renderAll(values: Array<dyn Display>) -> String {
5186 var out = Vector.of("")
5187 for value in values {
5188 out.push(value.label())
5189 }
5190 "{out.toArray()}"
5191}
5192"##;
5193
5194 fn run_with_traits(body: &str) -> Run {
5195 let source =
5196 format!("{TRAITS}\nexport fn main() -> Result<Unit, Error> {{\n{body}\n Ok(())\n}}\n");
5197 run_entry_of(&source, "main", &[])
5198 }
5199
5200 #[test]
5201 fn a_default_body_runs_unless_the_conformance_overrides_it() {
5202 let output = run_with_traits(
5203 " console.println(render(Booking(id: 7)))?\n console.println(render(Receipt(total: 12)))?",
5204 )
5205 .output;
5206 assert_eq!(
5207 output,
5208 "#7 / booking 7\n<receipt for 12> / receipt for 12\n"
5209 );
5210 }
5211
5212 #[test]
5213 fn dynamic_dispatch_finds_the_implementation_from_the_value() {
5214 let output = run_with_traits(
5217 " let mixed: Array<dyn Display> = [Booking(id: 1), Receipt(total: 2)]\n console.println(renderAll(mixed))?",
5218 )
5219 .output;
5220 assert_eq!(output, "[, #1, <receipt for 2>]\n");
5221 }
5222
5223 #[test]
5224 fn a_dyn_value_carries_its_concrete_value_and_its_trait() {
5225 let (sources, program) = program_of(&format!(
5226 "{TRAITS}\nexport fn main() -> dyn Display {{\n Booking(id: 3)\n}}\n"
5227 ));
5228 let value = run_in(
5229 &program,
5230 &sources,
5231 "test",
5232 "main",
5233 &[],
5234 &["console"],
5235 BTreeMap::new(),
5236 )
5237 .value();
5238 let Value(Repr::Dyn(trait_object)) = &value else {
5239 panic!("expected a trait object, found {value:?}");
5240 };
5241 assert_eq!(&*trait_object.trait_name, "test.Display");
5242 assert_eq!(trait_object.value.type_name(), "test.Booking");
5243 assert_eq!(value.type_name(), "dyn test.Display");
5244 assert_eq!(value.to_string(), "Booking(id: 3)");
5247 }
5248
5249 #[test]
5250 fn a_trait_object_keys_as_the_value_it_holds() {
5251 let output = run_with_traits(
5257 " let written: dyn Display = Booking(id: 1)\n let make: fn(Int) -> dyn Display = fn(id) { Booking(id: id) }\n let inferred = make(1)\n console.println(\"{written == inferred}\")?\n console.println(\"{Set.of(written) == Set.of(inferred)}\")?",
5258 )
5259 .output;
5260 assert_eq!(output, "true\ntrue\n");
5261 }
5262
5263 #[test]
5264 fn a_trait_object_is_still_incomparable_with_an_unrelated_value() {
5265 let (sources, program) = program_of(&format!(
5271 "{TRAITS}\nexport fn main() -> dyn Display {{\n Booking(id: 3)\n}}\n"
5272 ));
5273 let object = run_in(
5274 &program,
5275 &sources,
5276 "test",
5277 "main",
5278 &[],
5279 &["console"],
5280 BTreeMap::new(),
5281 )
5282 .value();
5283 let span = Span::new(cove_diag::FileId(0), 0, 0);
5284 let error = binary(BinaryOp::Eq, object.clone(), Value(Repr::Int(1)), span)
5285 .expect_err("a trait object and an `Int` are not the same type");
5286 assert_eq!(error.message, "cannot compare `test.Booking` with `Int`");
5287 let other = Value(Repr::Dyn(Rc::new(DynValue {
5290 trait_name: "test.Display".into(),
5291 value: Value(Repr::Struct(Rc::new(StructValue {
5292 type_name: "test.Receipt".into(),
5293 fields: vec![("total".into(), Value(Repr::Int(2)))],
5294 opaque: false,
5295 }))),
5296 })));
5297 let answer = binary(BinaryOp::Eq, object, other, span)
5298 .expect("two trait objects at one trait are comparable");
5299 assert!(answer.eq_value(&Value(Repr::Bool(false))));
5300 }
5301
5302 #[test]
5303 fn static_and_dynamic_dispatch_reach_the_same_implementation() {
5304 let output = run_with_traits(
5305 " let one: dyn Display = Booking(id: 5)\n console.println(render(Booking(id: 5)))?\n console.println(\"{one.label()} / {one.describe()}\")?",
5306 )
5307 .output;
5308 let lines: Vec<&str> = output.lines().collect();
5309 assert_eq!(lines[0], lines[1]);
5310 }
5311
5312 #[test]
5313 fn a_trait_object_is_equal_to_one_holding_an_equal_value() {
5314 let output = run_with_traits(
5315 " let a: dyn Display = Booking(id: 1)\n let b: dyn Display = Booking(id: 1)\n let c: dyn Display = Receipt(total: 1)\n console.println(\"{a == b} {a == c}\")?",
5316 )
5317 .output;
5318 assert_eq!(output, "true false\n");
5319 }
5320
5321 #[test]
5326 fn an_imported_function_runs_in_the_module_that_declares_it() {
5327 let run = run_modules(&[
5328 (
5329 "greet",
5330 "use console.println\n\nfn punctuation() -> String {\n \"!\"\n}\n\n\
5331 /// Greets by name.\nexport fn greeting(name: String) -> String {\n \"Hello, {name}{punctuation()}\"\n}\n",
5332 ),
5333 (
5334 "app",
5335 "use console.println\nuse greet.greeting\n\n\
5336 /// Entry point.\nexport fn main() -> Result<Unit, Error> {\n console.println(greeting(\"world\"))?\n Ok(())\n}\n",
5337 ),
5338 ]);
5339 assert_eq!(run.output, "Hello, world!\n");
5340 }
5341
5342 #[test]
5343 fn a_module_imported_whole_is_called_qualified() {
5344 let run = run_modules(&[
5345 (
5346 "greet",
5347 "/// Greets by name.\nexport fn greeting(name: String) -> String {\n \"Hello, {name}!\"\n}\n",
5348 ),
5349 (
5350 "app",
5351 "use console.println\nuse greet\n\n\
5352 /// Entry point.\nexport fn main() -> Result<Unit, Error> {\n console.println(greet.greeting(\"world\"))?\n Ok(())\n}\n",
5353 ),
5354 ]);
5355 assert_eq!(run.output, "Hello, world!\n");
5356 }
5357
5358 #[test]
5359 fn an_imported_struct_is_constructed_and_its_methods_run() {
5360 let run = run_modules(&[
5361 (
5362 "booking",
5363 "/// A booking.\nexport struct Booking {\n id: String\n}\n\n\
5364 impl Booking {\n /// The id, in a sentence.\n export fn describe(self) -> String {\n \"booking {self.id}\"\n }\n}\n",
5365 ),
5366 (
5367 "app",
5368 "use console.println\nuse booking.Booking\n\n\
5369 /// Entry point.\nexport fn main() -> Result<Unit, Error> {\n \
5370 let made = Booking(id: \"7\")\n console.println(made.describe())?\n Ok(())\n}\n",
5371 ),
5372 ]);
5373 assert_eq!(run.output, "booking 7\n");
5374 }
5375
5376 #[test]
5379 fn an_imported_type_s_value_keeps_its_methods_across_a_boundary() {
5380 let run = run_modules(&[
5381 (
5382 "booking",
5383 "/// A booking.\nexport struct Booking {\n id: String\n}\n\n\
5384 impl Booking {\n /// The id, in a sentence.\n export fn describe(self) -> String {\n \"booking {self.id}\"\n }\n}\n\n\
5385 /// Makes one.\nexport fn make() -> Booking {\n Booking(id: \"9\")\n}\n",
5386 ),
5387 (
5388 "app",
5389 "use console.println\nuse booking.make\n\n\
5390 /// Entry point.\nexport fn main() -> Result<Unit, Error> {\n console.println(make().describe())?\n Ok(())\n}\n",
5391 ),
5392 ]);
5393 assert_eq!(run.output, "booking 9\n");
5394 }
5395
5396 #[test]
5397 fn an_imported_enum_s_cases_are_built_and_matched() {
5398 let run = run_modules(&[
5399 (
5400 "levels",
5401 "/// Levels.\nexport enum LogLevel {\n Debug\n Info\n}\n",
5402 ),
5403 (
5404 "app",
5405 "use console.println\nuse levels.LogLevel\n\n\
5406 /// Names a level.\nfn name(level: LogLevel) -> String {\n \
5407 match level {\n LogLevel.Debug => \"debug\"\n LogLevel.Info => \"info\"\n }\n}\n\n\
5408 /// Entry point.\nexport fn main() -> Result<Unit, Error> {\n console.println(name(LogLevel.Info))?\n Ok(())\n}\n",
5409 ),
5410 ]);
5411 assert_eq!(run.output, "info\n");
5412 }
5413
5414 #[test]
5417 fn an_enum_case_is_reached_through_a_module_imported_whole() {
5418 let run = run_modules(&[
5419 (
5420 "levels",
5421 "/// Levels.\nexport enum LogLevel {\n Debug\n Info\n}\n",
5422 ),
5423 (
5424 "app",
5425 "use console.println\nuse levels\n\n\
5426 /// Entry point.\nexport fn main() -> Result<Unit, Error> {\n console.println(\"{levels.LogLevel.Info}\")?\n Ok(())\n}\n",
5427 ),
5428 ]);
5429 assert_eq!(run.output, "Info\n");
5430 }
5431
5432 #[test]
5435 fn an_imported_function_is_an_ordinary_value() {
5436 let run = run_modules(&[
5437 (
5438 "greet",
5439 "/// Greets by name.\nexport fn greeting(name: String) -> String {\n \"Hello, {name}!\"\n}\n",
5440 ),
5441 (
5442 "app",
5443 "use console.println\nuse greet\n\n\
5444 /// Applies `f`.\nfn apply(f: fn(String) -> String) -> String {\n f(\"world\")\n}\n\n\
5445 /// Entry point.\nexport fn main() -> Result<Unit, Error> {\n console.println(apply(greet.greeting))?\n Ok(())\n}\n",
5446 ),
5447 ]);
5448 assert_eq!(run.output, "Hello, world!\n");
5449 }
5450
5451 #[test]
5454 fn a_qualified_name_that_is_not_exported_is_refused() {
5455 let (sources, program) = program_of_modules(&[
5456 (
5457 "greet",
5458 "fn secret() -> String {\n \"s\"\n}\n\n/// Greets.\nexport fn greeting() -> String {\n \"hi\"\n}\n",
5459 ),
5460 (
5461 "app",
5462 "use greet\n\n/// Entry point.\nexport fn main() -> String {\n greet.secret()\n}\n",
5463 ),
5464 ]);
5465 let error = run_in(
5466 &program,
5467 &sources,
5468 "app",
5469 "main",
5470 &[],
5471 &["console"],
5472 BTreeMap::new(),
5473 )
5474 .error();
5475 assert!(error.message.contains("not exported"), "{}", error.message);
5476 }
5477
5478 #[test]
5479 fn a_module_used_as_a_value_is_refused() {
5480 let (sources, program) = program_of_modules(&[
5481 (
5482 "greet",
5483 "/// Greets.\nexport fn greeting() -> String {\n \"hi\"\n}\n",
5484 ),
5485 (
5486 "app",
5487 "use greet\n\n/// Entry point.\nexport fn main() -> String {\n let m = greet\n \"x\"\n}\n",
5488 ),
5489 ]);
5490 let error = run_in(
5491 &program,
5492 &sources,
5493 "app",
5494 "main",
5495 &[],
5496 &["console"],
5497 BTreeMap::new(),
5498 )
5499 .error();
5500 assert!(
5501 error.message.contains("is a module, not a value"),
5502 "{}",
5503 error.message
5504 );
5505 }
5506
5507 #[test]
5510 fn a_host_call_inside_an_imported_function_still_needs_the_grant() {
5511 let (sources, program) = program_of_modules(&[
5512 (
5513 "log",
5514 "use console.println\n\n/// Logs.\nexport fn log(msg: String) -> Result<Unit, Error> {\n console.println(msg)\n}\n",
5515 ),
5516 (
5517 "app",
5518 "use log.log\n\n/// Entry point.\nexport fn main() -> Result<Unit, Error> {\n log(\"hi\")?\n Ok(())\n}\n",
5519 ),
5520 ]);
5521 let granted = run_in(
5522 &program,
5523 &sources,
5524 "app",
5525 "main",
5526 &[],
5527 &["console"],
5528 BTreeMap::new(),
5529 );
5530 assert_eq!(granted.output, "hi\n");
5531
5532 let denied = run_in(&program, &sources, "app", "main", &[], &[], BTreeMap::new());
5533 assert!(denied.value.is_err() || denied.output.is_empty());
5534 }
5535
5536 const DISPLAY: &str = "\
5539/// Renders itself.
5540export trait Display {
5541 /// The full form.
5542 fn describe(self) -> String
5543
5544 /// A short form, defaulting to the full one.
5545 fn label(self) -> String { \"<{self.describe()}>\" }
5546}
5547
5548/// Renders anything that conforms, through static dispatch.
5549export fn render<T: Display>(value: T) -> String {
5550 value.label()
5551}
5552
5553/// Renders through dynamic dispatch.
5554export fn renderDyn(value: dyn Display) -> String {
5555 value.label()
5556}
5557";
5558
5559 const BOOKING: &str = "\
5560/// A booking.
5561export struct Booking {
5562 id: Int
5563}
5564";
5565
5566 #[test]
5569 fn a_conformance_to_an_imported_trait_dispatches_both_ways() {
5570 let booking = format!(
5571 "use display.Display\n\n{BOOKING}\nimpl Display for Booking {{\n \
5572 /// The full form.\n fn describe(self) -> String {{\n \"booking {{self.id}}\"\n }}\n}}\n"
5573 );
5574 let run = run_modules(&[
5575 ("display", DISPLAY),
5576 ("booking", &booking),
5577 (
5578 "app",
5579 "use console.println\nuse booking.Booking\nuse display.render\nuse display.renderDyn\n\n\
5580 /// Entry point.\nexport fn main() -> Result<Unit, Error> {\n \
5581 let one = Booking(id: 7)\n \
5582 console.println(render(one))?\n \
5583 console.println(renderDyn(one))?\n \
5584 Ok(())\n}\n",
5585 ),
5586 ]);
5587 assert_eq!(run.output, "<booking 7>\n<booking 7>\n");
5590 }
5591
5592 #[test]
5595 fn a_conformance_to_an_imported_type_dispatches_both_ways() {
5596 let display = format!(
5597 "use booking.Booking\n\n{DISPLAY}\nimpl Display for Booking {{\n \
5598 /// The full form.\n fn describe(self) -> String {{\n \"booking {{self.id}}\"\n }}\n}}\n"
5599 );
5600 let run = run_modules(&[
5601 ("booking", BOOKING),
5602 ("display", &display),
5603 (
5604 "app",
5605 "use console.println\nuse booking.Booking\nuse display.render\nuse display.Display\n\n\
5606 /// Entry point.\nexport fn main() -> Result<Unit, Error> {\n \
5607 let one = Booking(id: 7)\n \
5608 console.println(render(one))?\n \
5609 console.println(one.describe())?\n \
5610 let shown: dyn Display = one\n \
5611 console.println(shown.label())?\n \
5612 Ok(())\n}\n",
5613 ),
5614 ]);
5615 assert_eq!(run.output, "<booking 7>\nbooking 7\n<booking 7>\n");
5616 }
5617
5618 #[test]
5622 fn a_dyn_value_names_its_trait_by_the_module_that_declares_it() {
5623 let booking = format!(
5624 "use display.Display\n\n{BOOKING}\nimpl Display for Booking {{\n \
5625 /// The full form.\n fn describe(self) -> String {{\n \"b\"\n }}\n}}\n\n\
5626 /// Wraps one here, in the module that declares the type.\n\
5627 export fn shown(value: Booking) -> dyn Display {{\n value\n}}\n"
5628 );
5629 let (sources, program) = program_of_modules(&[
5630 ("display", DISPLAY),
5631 ("booking", &booking),
5632 (
5633 "app",
5634 "use booking.Booking\nuse booking.shown\nuse display.Display\n\n\
5635 /// Entry point: wraps one here too.\n\
5636 export fn main() -> Bool {\n \
5637 let here: dyn Display = Booking(id: 1)\n \
5638 here == shown(Booking(id: 1))\n}\n",
5639 ),
5640 ]);
5641 let run = run_in(
5642 &program,
5643 &sources,
5644 "app",
5645 "main",
5646 &[],
5647 &["console"],
5648 BTreeMap::new(),
5649 );
5650 assert_eq!(run.value().to_string(), "true");
5651 }
5652
5653 #[test]
5656 fn struct_fields_copy_and_vector_handles_alias() {
5657 let source = r#"
5658use console.println
5659
5660struct Draft {
5661 count: Int
5662 guests: Vector<String>
5663}
5664
5665export fn main() -> Result<Unit, Error> {
5666 var original = Draft(count: 1, guests: Vector.of("Alice"))
5667 var alias = original
5668 alias.count = 2
5669 alias.guests.push("Bob")
5670 console.println("{original.count} {alias.count}")?
5671 console.println("{original.guests.length()} {alias.guests.length()}")?
5672 Ok(())
5673}
5674"#;
5675 let run = run_entry_of(source, "main", &[]);
5676 assert_eq!(run.output, "1 2\n2 2\n");
5677 }
5678
5679 #[test]
5680 fn passing_a_struct_argument_copies_it() {
5681 let source = r#"
5682use console.println
5683
5684struct Point {
5685 x: Int
5686}
5687
5688fn shift(point: Point) -> Int {
5689 point.x
5690}
5691
5692export fn main() -> Result<Unit, Error> {
5693 var origin = Point(x: 1)
5694 let seen = shift(origin)
5695 origin.x = 9
5696 console.println("{seen} {origin.x}")?
5697 Ok(())
5698}
5699"#;
5700 assert_eq!(run_entry_of(source, "main", &[]).output, "1 9\n");
5701 }
5702
5703 #[test]
5713 fn assigning_to_a_var_field_updates_the_place() {
5714 let source = r#"
5715use console.println
5716
5717struct Counter {
5718 value: Int
5719}
5720
5721export fn main() -> Result<Unit, Error> {
5722 var counter = Counter(value: 1)
5723 counter.value += 4
5724 console.println("{counter.value}")?
5725 Ok(())
5726}
5727"#;
5728 assert_eq!(run_entry_of(source, "main", &[]).output, "5\n");
5729 }
5730
5731 const COUNTER: &str = r#"
5734use console.println
5735
5736struct Counter {
5737 value: Int
5738}
5739
5740impl Counter {
5741 fn bump(var self) {
5742 self.value = self.value + 1
5743 }
5744
5745 fn read(self) -> Int {
5746 self.value
5747 }
5748}
5749"#;
5750
5751 #[test]
5752 fn var_self_mutation_is_visible_in_the_caller() {
5753 let source = format!(
5754 "{COUNTER}
5755export fn main() -> Result<Unit, Error> {{
5756 var counter = Counter(value: 1)
5757 counter.bump()
5758 counter.bump()
5759 console.println(\"{{counter.value}} {{counter.read()}}\")?
5760 Ok(())
5761}}
5762"
5763 );
5764 assert_eq!(run_entry_of(&source, "main", &[]).output, "3 3\n");
5765 }
5766
5767 #[test]
5771 fn var_self_on_a_temporary_is_rejected() {
5772 let source = format!(
5773 "{COUNTER}
5774export fn main() -> Result<Unit, Error> {{
5775 Counter(value: 1).bump()
5776 Ok(())
5777}}
5778"
5779 );
5780 let error = run_entry_of(&source, "main", &[]).error();
5781 assert!(
5782 error.message.contains("is not a place"),
5783 "{}",
5784 error.message
5785 );
5786 }
5787
5788 #[test]
5789 fn a_var_parameter_aliases_the_caller_place() {
5790 let source = r#"
5791use console.println
5792
5793fn fill(var output: Vector<Int>) {
5794 output.push(1)
5795 output.push(2)
5796}
5797
5798export fn main() -> Result<Unit, Error> {
5799 var items = Vector.of()
5800 fill(var items)
5801 console.println("{items}")?
5802 Ok(())
5803}
5804"#;
5805 assert_eq!(run_entry_of(source, "main", &[]).output, "[1, 2]\n");
5806 }
5807
5808 #[test]
5809 fn a_var_parameter_must_be_marked_at_the_call_site() {
5810 let source = r#"
5811fn fill(var output: Vector<Int>) {
5812 output.push(1)
5813}
5814
5815export fn main() -> Result<Unit, Error> {
5816 var items = Vector.of()
5817 fill(items)
5818 Ok(())
5819}
5820"#;
5821 let error = run_entry_of(source, "main", &[]).error();
5822 assert!(
5823 error.message.contains("declared `var`"),
5824 "{}",
5825 error.message
5826 );
5827 assert_eq!(error.help.as_deref(), Some("write `fill(var output)`"));
5828 }
5829
5830 #[test]
5833 fn array_literals_are_arrays_and_vector_of_builds_a_vector() {
5834 assert_eq!(
5835 output_of(" console.println(\"{[1, 2].length()} {Vector.of(1, 2, 3).length()}\")?"),
5836 "2 3\n"
5837 );
5838 }
5839
5840 #[test]
5841 fn freeze_consumes_uniquely_owned_storage() {
5842 let source = r#"
5843use console.println
5844
5845export fn main() -> Result<Unit, Error> {
5846 var items = Vector.of(1)
5847 items.push(2)
5848 let frozen = items.freeze()
5849 console.println("{frozen.length()} {frozen}")?
5850 Ok(())
5851}
5852"#;
5853 assert_eq!(run_entry_of(source, "main", &[]).output, "2 [1, 2]\n");
5854 }
5855
5856 #[test]
5857 fn a_frozen_vector_is_no_longer_usable() {
5858 let source = r#"
5859export fn main() -> Result<Unit, Error> {
5860 var items = Vector.of(1)
5861 let frozen = items.freeze()
5862 items.push(2)
5863 Ok(())
5864}
5865"#;
5866 let error = run_entry_of(source, "main", &[]).error();
5867 assert!(
5868 error.message.contains("already consumed"),
5869 "{}",
5870 error.message
5871 );
5872 }
5873
5874 #[test]
5875 fn freeze_on_aliased_storage_points_at_to_array() {
5876 let source = r#"
5877export fn main() -> Result<Unit, Error> {
5878 var items = Vector.of(1)
5879 var alias = items
5880 let frozen = items.freeze()
5881 Ok(())
5882}
5883"#;
5884 let error = run_entry_of(source, "main", &[]).error();
5885 assert!(error.message.contains("freeze()"), "{}", error.message);
5886 assert!(
5887 error.help.unwrap().contains("toArray()"),
5888 "the diagnostic names the O(n) fallback"
5889 );
5890 }
5891
5892 #[test]
5893 fn to_array_produces_an_independent_array() {
5894 let source = r#"
5895use console.println
5896
5897export fn main() -> Result<Unit, Error> {
5898 var items = Vector.of(1)
5899 let snapshot = items.toArray()
5900 items.push(2)
5901 console.println("{snapshot.length()} {items.length()}")?
5902 Ok(())
5903}
5904"#;
5905 assert_eq!(run_entry_of(source, "main", &[]).output, "1 2\n");
5906 }
5907
5908 #[test]
5911 fn is_compares_vector_storage_identity() {
5912 assert_eq!(
5913 output_of(
5914 " var a = Vector.of(1, 2)\n var b = a\n var c = Vector.of(1, 2)\n \
5915 println(\"{a is b} {a is c}\")?"
5916 ),
5917 "true false\n"
5918 );
5919 }
5920
5921 #[test]
5922 fn is_rejects_a_type_mismatch_at_runtime() {
5923 let error = error_of(" println(\"{Vector.of(1) is 1}\")?");
5924 assert!(
5925 error.message.contains("cannot compare the identity"),
5926 "{}",
5927 error.message
5928 );
5929 }
5930
5931 #[test]
5932 fn is_rejects_a_value_type_at_runtime() {
5933 let error = error_of(" println(\"{1 is 1}\")?");
5934 assert_eq!(error.message, "identity is not available for `Int`");
5935 }
5936
5937 #[test]
5938 fn snapshot_of_a_vector_allocates_independent_storage() {
5939 let source = r#"
5940use console.println
5941
5942export fn main() -> Result<Unit, Error> {
5943 var original = Vector.of(1, 2)
5944 var copy = original.snapshot()
5945 copy.push(3)
5946 console.println("{original.length()} {copy.length()}")?
5947 Ok(())
5948}
5949"#;
5950 assert_eq!(run_entry_of(source, "main", &[]).output, "2 3\n");
5951 }
5952
5953 #[test]
5954 fn snapshot_recurses_into_a_vector_s_own_vector_elements() {
5955 let source = r#"
5956use console.println
5957
5958export fn main() -> Result<Unit, Error> {
5959 var inner = Vector.of(1)
5960 var outer = Vector.of(inner)
5961 var copy = outer.snapshot()
5962 var innerCopy = copy.get(0).unwrapOr(Vector.of())
5963 innerCopy.push(2)
5964 console.println("{inner.length()} {innerCopy.length()}")?
5965 Ok(())
5966}
5967"#;
5968 assert_eq!(run_entry_of(source, "main", &[]).output, "1 2\n");
5969 }
5970
5971 #[test]
5972 fn snapshot_dispatches_to_a_struct_s_own_conformance() {
5973 let source = r#"
5974use console.println
5975
5976struct Booking(id: Int)
5977
5978impl Snapshot for Booking {
5979 fn snapshot(self) -> Booking { Booking(id: self.id) }
5980}
5981
5982export fn main() -> Result<Unit, Error> {
5983 let booking = Booking(id: 1)
5984 console.println("{booking.snapshot()}")?
5985 Ok(())
5986}
5987"#;
5988 assert_eq!(run_entry_of(source, "main", &[]).output, "Booking(id: 1)\n");
5989 }
5990
5991 #[test]
5992 fn snapshot_is_not_implemented_for_a_closure() {
5993 let error =
5994 error_of(" let handler = fn(x: Int) { x }\n println(\"{handler.snapshot()}\")?");
5995 assert_eq!(error.message, "`fn` does not implement `Snapshot`");
5996 assert!(error.rule.unwrap().contains("Closures"));
5997 }
5998
5999 #[test]
6003 fn push_on_a_temporary_is_rejected() {
6004 let source = r#"
6005export fn main() -> Result<Unit, Error> {
6006 Vector.of(1).push(2)
6007 Ok(())
6008}
6009"#;
6010 let error = run_entry_of(source, "main", &[]).error();
6011 assert!(
6012 error.message.contains("is not a place"),
6013 "{}",
6014 error.message
6015 );
6016 }
6017
6018 #[test]
6021 fn every_var_self_method_on_a_temporary_is_rejected() {
6022 for call in ["push(2)", "set(0, 2)", "pop()", "remove(0)"] {
6023 let source = format!(
6024 "
6025export fn main() -> Result<Unit, Error> {{
6026 Vector.of(1).{call}
6027 Ok(())
6028}}
6029"
6030 );
6031 let error = run_entry_of(&source, "main", &[]).error();
6032 assert!(
6033 error.message.contains("is not a place"),
6034 "`{call}`: {}",
6035 error.message
6036 );
6037 }
6038 }
6039
6040 const TRY: &str = r#"
6043use console.println
6044
6045fn okValue() -> Result<Int, Error> {
6046 Ok(1)
6047}
6048
6049fn errValue() -> Result<Int, Error> {
6050 Err(Error("boom"))
6051}
6052
6053fn someValue() -> Option<Int> {
6054 Some(2)
6055}
6056
6057fn noneValue() -> Option<Int> {
6058 None
6059}
6060"#;
6061
6062 #[test]
6063 fn try_unwraps_ok_and_some() {
6064 let source = format!(
6065 "{TRY}
6066export fn main() -> Result<Unit, Error> {{
6067 let a = okValue()?
6068 let b = someValue()?
6069 console.println(\"{{a}} {{b}}\")?
6070 Ok(())
6071}}
6072"
6073 );
6074 assert_eq!(run_entry_of(&source, "main", &[]).output, "1 2\n");
6075 }
6076
6077 #[test]
6078 fn try_returns_the_error_from_the_current_function() {
6079 let source = format!(
6080 "{TRY}
6081export fn main() -> Result<Int, Error> {{
6082 let a = errValue()?
6083 console.println(\"unreachable\")?
6084 Ok(a)
6085}}
6086"
6087 );
6088 let run = run_entry_of(&source, "main", &[]);
6089 assert_eq!(run.output, "");
6090 assert_eq!(run.value().to_string(), "Err(boom)");
6091 }
6092
6093 #[test]
6094 fn try_returns_none_from_the_current_function() {
6095 let source = format!(
6096 "{TRY}
6097fn firstDigit() -> Option<Int> {{
6098 let value = noneValue()?
6099 Some(value)
6100}}
6101
6102export fn main() -> Option<Int> {{
6103 firstDigit()
6104}}
6105"
6106 );
6107 assert_eq!(
6108 run_entry_of(&source, "main", &[]).value().to_string(),
6109 "None"
6110 );
6111 }
6112
6113 #[test]
6114 fn try_on_a_plain_value_is_rejected() {
6115 let error = error_of(" let x = 1?");
6116 assert!(
6117 error
6118 .message
6119 .contains("`?` needs a `Result` or an `Option`"),
6120 "{}",
6121 error.message
6122 );
6123 }
6124
6125 #[test]
6128 fn arguments_are_evaluated_left_to_right() {
6129 let source = r#"
6130use console.println
6131
6132fn note(var log: Vector<String>, name: String) -> Int {
6133 log.push(name)
6134 0
6135}
6136
6137export fn main() -> Result<Unit, Error> {
6138 var log = Vector.of()
6139 let total = note(var log, "a") + note(var log, "b")
6140 console.println("{log}")?
6141 Ok(())
6142}
6143"#;
6144 assert_eq!(run_entry_of(source, "main", &[]).output, "[a, b]\n");
6145 }
6146
6147 #[test]
6150 fn integer_overflow_names_the_operation() {
6151 let error = error_of(" var big = 9223372036854775807\n big = big + 1");
6152 assert_eq!(error.message, "`Int` addition overflowed");
6153 }
6154
6155 #[test]
6156 fn division_by_zero_is_a_runtime_error() {
6157 assert_eq!(
6158 error_of(" let x = 1 / 0").message,
6159 "`Int` division by zero"
6160 );
6161 assert_eq!(
6162 error_of(" let x = 1 % 0").message,
6163 "`Int` remainder by zero"
6164 );
6165 }
6166
6167 #[test]
6168 fn mixed_numeric_operands_are_rejected() {
6169 let error = error_of(" let x = 1 + 1.0");
6170 assert!(
6171 error.message.contains("not defined for `Int` and `Float`"),
6172 "{}",
6173 error.message
6174 );
6175 }
6176
6177 #[test]
6178 fn adding_a_string_to_an_int_is_rejected() {
6179 let error = error_of(" let x = \"a\" + 1");
6180 assert!(
6181 error.message.contains("not defined for `String` and `Int`"),
6182 "{}",
6183 error.message
6184 );
6185 }
6186
6187 #[test]
6188 fn adding_two_strings_points_at_interpolation() {
6189 let error = error_of(" let x = \"a\" + \"b\"");
6190 assert_eq!(error.message, "`+` is not defined for `String`");
6191 assert!(error.help.unwrap().contains("interpolation"));
6192 }
6193
6194 #[test]
6203 fn a_match_with_no_matching_arm_is_a_runtime_error() {
6204 let source = r#"
6205enum Color {
6206 Red
6207 Green
6208}
6209
6210enum Wine {
6211 Red
6212 White
6213}
6214
6215export fn main() -> Result<Unit, Error> {
6216 let color = Color.Green
6217 let name = match color {
6218 Red => "red"
6219 }
6220 Ok(())
6221}
6222"#;
6223 let error = run_entry_of(source, "main", &[]).error();
6224 assert!(
6225 error.message.contains("no `match` arm covers"),
6226 "{}",
6227 error.message
6228 );
6229 assert_eq!(
6230 error.rule.as_deref(),
6231 Some("`match` must cover every enum case.")
6232 );
6233 }
6234
6235 #[test]
6236 fn match_binds_enum_payloads_and_literals() {
6237 let source = r#"
6238use console.println
6239
6240enum Shape {
6241 Dot
6242 Line(Int)
6243}
6244
6245fn describe(shape: Shape) -> String {
6246 match shape {
6247 Shape.Dot => "dot"
6248 Shape.Line(length) => "line {length}"
6249 }
6250}
6251
6252export fn main() -> Result<Unit, Error> {
6253 console.println(describe(Shape.Dot))?
6254 console.println(describe(Shape.Line(3)))?
6255 let word = match 2 {
6256 1 => "one"
6257 other => "many"
6258 }
6259 console.println(word)?
6260 Ok(())
6261}
6262"#;
6263 assert_eq!(
6264 run_entry_of(source, "main", &[]).output,
6265 "dot\nline 3\nmany\n"
6266 );
6267 }
6268
6269 #[test]
6272 fn equality_is_value_equality() {
6273 let source = r#"
6274use console.println
6275
6276struct Point {
6277 x: Int
6278}
6279
6280export fn main() -> Result<Unit, Error> {
6281 console.println("{Point(x: 1) == Point(x: 1)} {[1, 2] == [1, 3]}")?
6282 Ok(())
6283}
6284"#;
6285 assert_eq!(run_entry_of(source, "main", &[]).output, "true false\n");
6286 }
6287
6288 #[test]
6289 fn comparing_different_types_is_rejected() {
6290 let error = error_of(" let same = 1 == \"1\"");
6291 assert!(
6292 error.message.contains("cannot compare `Int` with `String`"),
6293 "{}",
6294 error.message
6295 );
6296 }
6297
6298 #[test]
6301 fn blocks_ifs_and_matches_are_expressions() {
6302 let source = r#"
6303use console.println
6304
6305fn classify(value: Int) -> String {
6306 if value > 0 {
6307 return "positive"
6308 }
6309 "other"
6310}
6311
6312export fn main() -> Result<Unit, Error> {
6313 let doubled = {
6314 let base = 3
6315 base * 2
6316 }
6317 let label = if doubled > 5 { "big" } else { "small" }
6318 console.println("{doubled} {label} {classify(1)} {classify(0)}")?
6319 Ok(())
6320}
6321"#;
6322 assert_eq!(
6323 run_entry_of(source, "main", &[]).output,
6324 "6 big positive other\n"
6325 );
6326 }
6327
6328 #[test]
6329 fn loops_run_to_completion() {
6330 let source = r#"
6331use console.println
6332
6333export fn main() -> Result<Unit, Error> {
6334 var total = 0
6335 for value in [1, 2, 3] {
6336 total += value
6337 }
6338 var count = 0
6339 while count < 2 {
6340 count += 1
6341 }
6342 console.println("{total} {count}")?
6343 Ok(())
6344}
6345"#;
6346 assert_eq!(run_entry_of(source, "main", &[]).output, "6 2\n");
6347 }
6348
6349 #[test]
6350 fn a_for_loop_is_unit_however_it_leaves() {
6351 let source = r#"
6356use console.println
6357
6358export fn main() -> Result<Unit, Error> {
6359 var seen = 0
6360 let found = for value in [1, 2, 3, 4] {
6361 seen = value
6362 if value == 3 {
6363 break value * 10
6364 }
6365 }
6366 console.println("{seen} {found}")?
6367 Ok(())
6368}
6369"#;
6370 assert_eq!(run_entry_of(source, "main", &[]).output, "3 ()\n");
6371 }
6372
6373 #[test]
6374 fn a_loop_that_never_breaks_evaluates_to_unit() {
6375 let source = r#"
6376use console.println
6377
6378export fn main() -> Result<Unit, Error> {
6379 let result = for value in [1, 2] {
6380 value
6381 }
6382 console.println("{result}")?
6383 Ok(())
6384}
6385"#;
6386 assert_eq!(run_entry_of(source, "main", &[]).output, "()\n");
6387 }
6388
6389 #[test]
6390 fn continue_skips_the_rest_of_an_iteration() {
6391 let source = r#"
6392use console.println
6393
6394export fn main() -> Result<Unit, Error> {
6395 var total = 0
6396 for value in [1, 2, 3, 4] {
6397 if value % 2 == 0 {
6398 continue
6399 }
6400 total += value
6401 }
6402 console.println("{total}")?
6403 Ok(())
6404}
6405"#;
6406 assert_eq!(run_entry_of(source, "main", &[]).output, "4\n");
6407 }
6408
6409 #[test]
6410 fn a_while_true_is_unit_like_every_other_loop() {
6411 let source = r#"
6415use console.println
6416
6417export fn main() -> Result<Unit, Error> {
6418 var count = 0
6419 let found = while true {
6420 count += 1
6421 if count == 3 {
6422 break count
6423 }
6424 }
6425 console.println("{count} {found}")?
6426 Ok(())
6427}
6428"#;
6429 assert_eq!(run_entry_of(source, "main", &[]).output, "3 ()\n");
6430 }
6431
6432 #[test]
6433 fn a_while_that_can_reach_its_end_is_unit_however_it_leaves() {
6434 let source = r#"
6435use console.println
6436
6437export fn main() -> Result<Unit, Error> {
6438 var count = 0
6439 let found = while count < 10 {
6440 count += 1
6441 if count == 3 {
6442 break count
6443 }
6444 }
6445 console.println("{count} {found}")?
6446 Ok(())
6447}
6448"#;
6449 assert_eq!(run_entry_of(source, "main", &[]).output, "3 ()\n");
6450 }
6451
6452 #[test]
6453 fn an_if_with_no_else_is_unit_even_when_its_branch_runs() {
6454 let source = r#"
6455use console.println
6456
6457export fn main() -> Result<Unit, Error> {
6458 var ran = false
6459 let taken = if true {
6460 ran = true
6461 1
6462 }
6463 let skipped = if false {
6464 2
6465 }
6466 console.println("{ran} {taken} {skipped}")?
6467 Ok(())
6468}
6469"#;
6470 assert_eq!(run_entry_of(source, "main", &[]).output, "true () ()\n");
6471 }
6472
6473 #[test]
6476 fn closures_capture_by_value_at_creation_time() {
6477 let source = r#"
6478use console.println
6479
6480export fn main() -> Result<Unit, Error> {
6481 var seen = 1
6482 let read = fn() {
6483 seen
6484 }
6485 seen = 2
6486 console.println("{read()} {seen}")?
6487 Ok(())
6488}
6489"#;
6490 assert_eq!(run_entry_of(source, "main", &[]).output, "1 2\n");
6491 }
6492
6493 #[test]
6496 fn an_unqualified_use_reaches_the_host_module() {
6497 let source = r#"
6498use console.println
6499
6500export fn main() -> Result<Unit, Error> {
6501 println("direct")?
6502 Ok(())
6503}
6504"#;
6505 assert_eq!(run_entry_of(source, "main", &[]).output, "direct\n");
6506 }
6507
6508 #[test]
6509 fn an_ungranted_capability_is_rejected_at_the_host_boundary() {
6510 let source = r#"
6511use console.println
6512
6513export fn main() -> Result<Unit, Error> {
6514 console.println("secret")?
6515 Ok(())
6516}
6517"#;
6518 let (sources, program) = program_of(source);
6519 let run = run_in(
6520 &program,
6521 &sources,
6522 "test",
6523 "main",
6524 &[],
6525 &[],
6526 BTreeMap::new(),
6527 );
6528 assert_eq!(run.output, "");
6529 let error = run.error();
6530 assert!(
6531 error.message.contains("requires the `console` capability"),
6532 "{}",
6533 error.message
6534 );
6535 }
6536
6537 #[test]
6538 fn the_env_host_reads_only_what_the_host_supplied() {
6539 let source = r#"
6540use env.get
6541use console.println
6542
6543export fn main() -> Result<Unit, Error> {
6544 console.println(env.get("PORT").unwrapOr("none"))?
6545 console.println(env.get("MISSING").unwrapOr("none"))?
6546 Ok(())
6547}
6548"#;
6549 let (sources, program) = program_of(source);
6550 let env = BTreeMap::from([("PORT".to_string(), "9000".to_string())]);
6551 let run = run_in(
6552 &program,
6553 &sources,
6554 "test",
6555 "main",
6556 &[],
6557 &["console", "env"],
6558 env,
6559 );
6560 assert_eq!(run.output, "9000\nnone\n");
6561 }
6562
6563 #[test]
6566 fn array_and_string_builtins() {
6567 let body = " let items = [10, 20]\n console.println(\"{items.get(0).unwrapOr(0)} {items.get(5).isNone()} {items.length()} {items.isEmpty()}\")?\n console.println(\"{\"a bc d\".words().length()} {\"abc\".length()} {\"\".isEmpty()}\")?";
6568 assert_eq!(output_of(body), "10 true 2 false\n3 3 true\n");
6569 }
6570
6571 #[test]
6572 fn int_parse_returns_a_result() {
6573 assert_eq!(
6574 output_of(
6575 " console.println(\"{Int.parse(\"12\").isOk()} {Int.parse(\"x\").isError()}\")?"
6576 ),
6577 "true true\n"
6578 );
6579 }
6580
6581 #[test]
6585 fn result_unwrap_or_answers_the_ok_or_the_fallback() {
6586 let body = " console.println(\"{Int.parse(\"12\").unwrapOr(0)} {Int.parse(\"x\").unwrapOr(0)}\")?";
6587 assert_eq!(output_of(body), "12 0\n");
6588 }
6589
6590 #[test]
6594 fn result_unwrap_or_says_nothing_about_the_error_type() {
6595 let source = r#"
6596use console.println
6597
6598enum ConfigError {
6599 InvalidPort(String)
6600}
6601
6602fn port(text: String) -> Result<Int, ConfigError> {
6603 Int.parse(text).mapError(fn(error) { ConfigError.InvalidPort(text) })
6604}
6605
6606export fn main() -> Result<Unit, Error> {
6607 console.println("{port("7").unwrapOr(80)} {port("x").unwrapOr(80)}")?
6608 Ok(())
6609}
6610"#;
6611 assert_eq!(run_entry_of(source, "main", &[]).output, "7 80\n");
6612 }
6613
6614 #[test]
6618 fn int_parse_radix_reads_the_base_it_is_given() {
6619 let body = " console.println(\"{Int.parseRadix(\"ff\", 16).unwrapOr(0)} {Int.parseRadix(\"1010\", 2).unwrapOr(0)} {Int.parseRadix(\"z\", 36).unwrapOr(0)}\")?";
6620 assert_eq!(output_of(body), "255 10 35\n");
6621 let signs = " console.println(\"{Int.parseRadix(\"-ff\", 16).unwrapOr(0)} {Int.parseRadix(\"+10\", 8).unwrapOr(0)}\")?";
6622 assert_eq!(output_of(signs), "-255 8\n");
6623 let wrong = " console.println(\"{Int.parseRadix(\"ff\", 10)}\")?";
6624 assert_eq!(output_of(wrong), "Err(`ff` is not an Int in radix 10)\n");
6625 let decimal = " console.println(\"{Int.parse(\"12\")} {Int.parseRadix(\"12\", 10)}\")?";
6627 assert_eq!(output_of(decimal), "Ok(12) Ok(12)\n");
6628 }
6629
6630 #[test]
6634 fn int_parse_radix_refuses_a_radix_that_names_no_notation() {
6635 for radix in ["1", "0", "37", "-16"] {
6636 let error = error_of(&format!(" let n = Int.parseRadix(\"1\", {radix})"));
6637 assert_eq!(
6638 error.message,
6639 format!("`Int.parseRadix` cannot read a number in radix `{radix}`")
6640 );
6641 assert!(error.rule.as_ref().unwrap().contains("2 through 36"));
6642 }
6643 }
6644
6645 #[test]
6648 fn string_from_code_point_builds_one_character() {
6649 let body = " console.println(\"{String.fromCodePoint(65).unwrapOr(\"?\")}{String.fromCodePoint(12354).unwrapOr(\"?\")}{String.fromCodePoint(128512).unwrapOr(\"?\")}\")?";
6650 assert_eq!(output_of(body), "Aあ😀\n");
6651 let counted =
6654 " console.println(\"{String.fromCodePoint(128512).unwrapOr(\"\").length()}\")?";
6655 assert_eq!(output_of(counted), "1\n");
6656 let zero = " console.println(\"{String.fromCodePoint(0).isOk()}\")?";
6657 assert_eq!(output_of(zero), "true\n");
6658 }
6659
6660 #[test]
6665 fn string_from_code_point_says_which_way_a_number_names_no_character() {
6666 let out_of_range =
6667 " console.println(\"{String.fromCodePoint(1114112)} {String.fromCodePoint(-1)}\")?";
6668 assert_eq!(
6669 output_of(out_of_range),
6670 "Err(`1114112` is not a Unicode code point) \
6671 Err(`-1` is not a Unicode code point)\n"
6672 );
6673 let surrogate = " console.println(\"{String.fromCodePoint(55296)}\")?";
6674 assert_eq!(
6675 output_of(surrogate),
6676 "Err(`55296` is a surrogate half, which is not a character on its own)\n"
6677 );
6678 let edges = " console.println(\"{String.fromCodePoint(1114111).isOk()} {String.fromCodePoint(57343).isOk()} {String.fromCodePoint(57344).isOk()}\")?";
6680 assert_eq!(output_of(edges), "true false true\n");
6681 }
6682
6683 #[test]
6687 fn a_hex_escape_can_be_decoded_in_cove() {
6688 let source = r#"
6689use console.println
6690
6691/// The character a four-hex-digit escape names.
6692fn unescape(digits: String) -> Result<String, Error> {
6693 String.fromCodePoint(Int.parseRadix(digits, 16)?)
6694}
6695
6696/// The character a UTF-16 surrogate pair names.
6697fn unescapePair(high: String, low: String) -> Result<String, Error> {
6698 let lead = Int.parseRadix(high, 16)?
6699 let trail = Int.parseRadix(low, 16)?
6700 String.fromCodePoint(65536 + (lead - 55296) * 1024 + (trail - 56320))
6701}
6702
6703export fn main() -> Result<Unit, Error> {
6704 console.println("{unescape("0041")?}{unescape("3042")?}")?
6705 console.println("{unescapePair("D83D", "DE00")?}")?
6706 console.println("{unescape("D83D")}")?
6707 Ok(())
6708}
6709"#;
6710 assert_eq!(
6711 run_entry_of(source, "main", &[]).output,
6712 "Aあ\n😀\nErr(`55357` is a surrogate half, which is not a character on its own)\n"
6713 );
6714 }
6715
6716 #[test]
6723 fn map_error_rejects_a_trailing_closure() {
6724 let source = r#"
6725use console.println
6726
6727enum ConfigError {
6728 InvalidPort(String)
6729}
6730
6731export fn main() -> Result<Unit, Error> {
6732 let failed = Int.parse("x").mapError { ConfigError.InvalidPort("x") }
6733 let kept = Int.parse("7").mapError { ConfigError.InvalidPort("7") }
6734 console.println("{failed} {kept}")?
6735 Ok(())
6736}
6737"#;
6738 let errors = check_errors_of(source);
6739 assert!(
6740 errors.iter().any(|error| error.code == "cove::type::arity"
6741 && error.message == "this function takes 0 parameter(s), but 1 were expected here"),
6742 "{errors:?}"
6743 );
6744 }
6745
6746 #[test]
6751 fn a_method_that_does_not_exist_names_the_receiver_type() {
6752 let error = error_of(" let x = [1].pop()");
6753 assert_eq!(error.message, "`Array` has no method `pop`");
6754 }
6755
6756 #[test]
6761 fn a_sequence_walks_the_same_whichever_sequence_it_is() {
6762 let output = output_of(
6763 r#" let fixed = [3, 1, 2]
6764 var growable = Vector.of(3, 1, 2)
6765 console.println("{fixed.map(fn(n) { n * 2 })} {growable.map(fn(n) { n * 2 })}")?
6766 console.println("{fixed.filter(fn(n) { n > 1 })} {growable.filter(fn(n) { n > 1 })}")?
6767 console.println("{fixed.fold(0, fn(t, n) { t + n })} {growable.fold(0, fn(t, n) { t + n })}")?
6768 console.println("{fixed.sorted(by: fn(a, b) { a < b })} {growable.sorted(by: fn(a, b) { a < b })}")?
6769 console.println("{fixed} {growable}")?"#,
6770 );
6771 assert_eq!(
6772 output,
6773 "[6, 2, 4] [6, 2, 4]\n[3, 2] [3, 2]\n6 6\n[1, 2, 3] [1, 2, 3]\n[3, 1, 2] [3, 1, 2]\n"
6774 );
6775 }
6776
6777 #[test]
6780 fn an_empty_sequence_answers_without_calling_its_callback() {
6781 let output = output_of(
6782 r#" let empty: Array<Int> = []
6783 console.println("{empty.map(fn(n) { n / 0 })}")?
6784 console.println("{empty.filter(fn(n) { n / 0 > 0 })}")?
6785 console.println("{empty.sorted(by: fn(a, b) { a / 0 < b })}")?
6786 console.println("{empty.fold(7, fn(t, n) { t / 0 })}")?"#,
6787 );
6788 assert_eq!(output, "[]\n[]\n[]\n7\n");
6789 }
6790
6791 #[test]
6797 fn sorted_is_stable() {
6798 let output = output_of(
6799 r#" let items = [5, 4, 3, 2, 1, 0]
6800 console.println("{items.sorted(by: fn(a, b) { false })}")?"#,
6801 );
6802 assert_eq!(output, "[5, 4, 3, 2, 1, 0]\n");
6803 }
6804
6805 #[test]
6809 fn an_inconsistent_ordering_answers_a_permutation() {
6810 let output = output_of(
6811 r#" let items = [1, 2, 3, 4]
6812 let sorted = items.sorted(by: fn(a, b) { true })
6813 console.println("{sorted.length()} {sorted.fold(0, fn(t, n) { t + n })}")?"#,
6814 );
6815 assert_eq!(output, "4 10\n");
6816 }
6817
6818 #[test]
6821 fn a_failing_callback_answers_nothing() {
6822 for body in [
6823 " let x = [1, 2].map(fn(n) { n / 0 })",
6824 " let x = [1, 2].filter(fn(n) { n / 0 > 1 })",
6825 " let x = [1, 2].fold(0, fn(t, n) { n / 0 })",
6826 " let x = [2, 1].sorted(by: fn(a, b) { a / 0 < b })",
6827 ] {
6828 assert_eq!(
6829 error_of(body).message,
6830 "`Int` division by zero",
6831 "for `{body}`"
6832 );
6833 }
6834 }
6835
6836 #[test]
6843 fn a_callback_may_read_the_vector_it_is_walking() {
6844 let output = output_of(
6845 r#" var items = Vector.of(2, 1, 3)
6846 console.println("{items.map(fn(n) { n + items.length() })}")?
6847 console.println("{items.sorted(by: fn(a, b) { a + items.length() < b + items.length() })}")?"#,
6848 );
6849 assert_eq!(output, "[5, 4, 6]\n[1, 2, 3]\n");
6850 }
6851
6852 #[test]
6855 fn a_range_is_an_ordinary_value() {
6856 let output = output_of(
6857 r#" let exclusive = 0..<3
6858 let inclusive = 0..3
6859 console.println("{exclusive} {inclusive}")?"#,
6860 );
6861 assert_eq!(output, "0..<3 0..3\n");
6862 }
6863
6864 #[test]
6865 fn a_range_value_iterates_like_a_range_literal() {
6866 let output = output_of(
6867 r#" let bounds = 0..<3
6868 var total = 0
6869 for value in bounds {
6870 total += value
6871 }
6872 for value in 1..3 {
6873 total += value
6874 }
6875 console.println("{total}")?"#,
6876 );
6877 assert_eq!(output, "9\n");
6878 }
6879
6880 #[test]
6881 fn a_range_has_the_sequence_methods() {
6882 let output = output_of(
6883 r#" let exclusive = 0..<3
6884 let inclusive = 0..3
6885 console.println("{exclusive.length()} {inclusive.length()}")?
6886 console.println("{exclusive.isEmpty()} {exclusive.contains(2)} {exclusive.contains(3)}")?
6887 console.println("{inclusive.contains(3)} {inclusive.contains(-1)}")?"#,
6888 );
6889 assert_eq!(output, "3 4\nfalse true false\ntrue false\n");
6890 }
6891
6892 #[test]
6893 fn a_reversed_range_is_empty_and_iterates_zero_times() {
6894 let output = output_of(
6895 r#" let reversed = 3..<0
6896 var rounds = 0
6897 for _value in reversed {
6898 rounds += 1
6899 }
6900 console.println("{reversed} {reversed.length()} {reversed.isEmpty()} {rounds}")?"#,
6901 );
6902 assert_eq!(output, "3..<0 0 true 0\n");
6903 }
6904
6905 #[test]
6906 fn ranges_compare_by_value() {
6907 let output =
6908 output_of(r#" console.println("{0..<3 == 0..<3} {0..<3 == 0..3} {0..<3 == 1..<3}")?"#);
6909 assert_eq!(output, "true false false\n");
6910 }
6911
6912 #[test]
6913 fn a_range_bound_must_be_an_int() {
6914 let error = error_of(" let bad = 0..<\"3\"");
6915 assert!(
6916 error.message.contains("a range bound must be an `Int`"),
6917 "{}",
6918 error.message
6919 );
6920 }
6921
6922 #[test]
6923 fn a_range_has_no_method_it_does_not_declare() {
6924 let error = error_of(" let bounds = 0..<3\n let bad = bounds.reverse()");
6925 assert_eq!(error.message, "`Range` has no method `reverse`");
6926 }
6927
6928 #[test]
6931 fn count_is_rejected_and_names_the_length_spelling() {
6932 let bodies = [
6933 " let n = [1, 2].count()",
6934 " let n = Vector.of(1, 2).count()",
6935 " let n = \"a b\".count()",
6936 " let n = (0..<3).count()",
6937 " let n = Map.of().count()",
6938 " let n = Set.of().count()",
6939 ];
6940 for body in bodies {
6941 let error = error_of(body);
6942 assert!(
6943 error
6944 .message
6945 .contains("Cove spells the number of elements `length()`"),
6946 "{body}: {}",
6947 error.message
6948 );
6949 assert_eq!(
6950 error.help.as_deref(),
6951 Some("write `length()` instead of `count()`"),
6952 "{body}"
6953 );
6954 }
6955 }
6956
6957 #[test]
6958 fn length_is_the_one_spelling_every_sequence_answers() {
6959 let output = output_of(
6960 r#" console.println("{[1, 2].length()} {Vector.of(1).length()} {"ab".length()} {(0..<4).length()}")?"#,
6961 );
6962 assert_eq!(output, "2 1 2 4\n");
6963 }
6964
6965 #[test]
6968 fn map_of_builds_a_map_and_answers_its_methods() {
6969 let output = output_of(
6970 r#" let ages = Map.of(
6971 MapEntry(key: "Alice", value: 30),
6972 MapEntry(key: "Bob", value: 25)
6973 )
6974 console.println("{ages}")?
6975 console.println("{ages.length()} {ages.isEmpty()}")?
6976 console.println("{ages.get("Alice")} {ages.get("Zoe")}")?
6977 console.println("{ages.contains("Bob")} {ages.contains("Zoe")}")?
6978 console.println("{ages.keys()} {ages.values()}")?"#,
6979 );
6980 assert_eq!(
6981 output,
6982 "{Alice: 30, Bob: 25}\n2 false\nSome(30) None\ntrue false\n[Alice, Bob] [30, 25]\n"
6983 );
6984 }
6985
6986 #[test]
6987 fn an_empty_map_is_empty() {
6988 let output = output_of(r#" console.println("{Map.of()} {Map.of().isEmpty()}")?"#);
6989 assert_eq!(output, "{} true\n");
6990 }
6991
6992 #[test]
6993 fn map_of_rejects_a_duplicate_key() {
6994 let error = error_of(
6995 r#" let bad = Map.of(
6996 MapEntry(key: "x", value: 1),
6997 MapEntry(key: "x", value: 2)
6998 )"#,
6999 );
7000 assert_eq!(
7001 error.message,
7002 "`Map.of` was given the key `x` more than once"
7003 );
7004 }
7005
7006 #[test]
7007 fn map_of_rejects_an_argument_that_is_not_a_map_entry() {
7008 let error = error_of(" let bad = Map.of(1)");
7009 assert!(
7010 error.message.contains("`Map.of` expects `MapEntry` values"),
7011 "{}",
7012 error.message
7013 );
7014 }
7015
7016 #[test]
7017 fn map_entry_labels_are_key_then_value_in_declaration_order() {
7018 let error = error_of(r#" let bad = MapEntry(value: 1, key: "x")"#);
7019 assert!(
7020 error.message.contains("out of declaration order"),
7021 "{}",
7022 error.message
7023 );
7024 }
7025
7026 #[test]
7027 fn map_get_and_contains_reject_an_invalid_key_type() {
7028 let error = error_of(
7029 r#" let m = Map.of()
7030 let bad = m.get(Vector.of(1))"#,
7031 );
7032 assert_eq!(
7033 error.message,
7034 "`Map.get` cannot use a `Vector` as a map key"
7035 );
7036 assert!(
7037 error
7038 .rule
7039 .as_deref()
7040 .unwrap_or_default()
7041 .contains("Mutable handles and structs containing them are not valid map keys"),
7042 "{:?}",
7043 error.rule
7044 );
7045 }
7046
7047 #[test]
7048 fn map_inserted_and_removed_return_a_new_map_and_do_not_mutate_the_original() {
7049 let output = output_of(
7050 r#" let original = Map.of(MapEntry(key: "a", value: 1))
7051 let inserted = original.inserted("b", 2)
7052 let removed = inserted.removed("a")
7053 console.println("{original} {inserted} {removed}")?"#,
7054 );
7055 assert_eq!(output, "{a: 1} {a: 1, b: 2} {b: 2}\n");
7056 }
7057
7058 #[test]
7059 fn maps_compare_by_structural_equality() {
7060 let output = output_of(
7061 r#" let a = Map.of(MapEntry(key: "x", value: 1))
7062 let b = Map.of(MapEntry(key: "x", value: 1))
7063 let c = Map.of(MapEntry(key: "x", value: 2))
7064 console.println("{a == b} {a == c}")?"#,
7065 );
7066 assert_eq!(output, "true false\n");
7067 }
7068
7069 #[test]
7070 fn map_iterates_map_entries_in_ascending_key_order() {
7071 let output = output_of(
7072 r#" let scores = Map.of(
7073 MapEntry(key: "b", value: 2),
7074 MapEntry(key: "a", value: 1)
7075 )
7076 for entry in scores {
7077 console.println("{entry.key} {entry.value}")?
7078 }"#,
7079 );
7080 assert_eq!(output, "a 1\nb 2\n");
7081 }
7082
7083 #[test]
7084 fn set_of_builds_a_set_and_answers_its_methods() {
7085 let output = output_of(
7086 r#" let names = Set.of("b", "a", "c")
7087 console.println("{names}")?
7088 console.println("{names.length()} {names.isEmpty()}")?
7089 console.println("{names.contains("a")} {names.contains("z")}")?
7090 console.println("{names.toArray()}")?"#,
7091 );
7092 assert_eq!(output, "{a, b, c}\n3 false\ntrue false\n[a, b, c]\n");
7093 }
7094
7095 #[test]
7096 fn set_of_rejects_a_duplicate_element() {
7097 let error = error_of(" let bad = Set.of(1, 1)");
7098 assert_eq!(
7099 error.message,
7100 "`Set.of` was given the element `1` more than once"
7101 );
7102 }
7103
7104 #[test]
7105 fn set_of_rejects_an_invalid_element_type() {
7106 let error = error_of(" let bad = Set.of(Vector.of(1))");
7107 assert_eq!(
7108 error.message,
7109 "`Set.of` cannot use a `Vector` as a set element"
7110 );
7111 }
7112
7113 #[test]
7114 fn set_inserted_and_removed_return_a_new_set_and_do_not_mutate_the_original() {
7115 let output = output_of(
7116 r#" let original = Set.of(1, 2)
7117 let inserted = original.inserted(3)
7118 let removed = inserted.removed(1)
7119 console.println("{original} {inserted} {removed}")?"#,
7120 );
7121 assert_eq!(output, "{1, 2} {1, 2, 3} {2, 3}\n");
7122 }
7123
7124 #[test]
7125 fn sets_compare_by_structural_equality() {
7126 let output = output_of(
7127 r#" let a = Set.of(1, 2)
7128 let b = Set.of(2, 1)
7129 let c = Set.of(1)
7130 console.println("{a == b} {a == c}")?"#,
7131 );
7132 assert_eq!(output, "true false\n");
7133 }
7134
7135 #[test]
7136 fn set_iterates_in_ascending_order() {
7137 let output = output_of(
7138 r#" var total = 0
7139 for item in Set.of(3, 1, 2) {
7140 total = total * 10 + item
7141 }
7142 console.println("{total}")?"#,
7143 );
7144 assert_eq!(output, "123\n");
7145 }
7146
7147 #[test]
7148 fn a_payload_free_enum_case_is_a_valid_map_key() {
7149 let run = colour_body(
7150 r#" let byColour = Map.of(MapEntry(key: Colour.Red, value: "stop"))
7151 console.println("{byColour.get(Colour.Red)}")?"#,
7152 );
7153 assert_eq!(run.output, "Some(stop)\n");
7154 }
7155
7156 #[test]
7157 fn an_enum_case_with_a_payload_is_a_valid_set_element() {
7158 let run = colour_body(
7159 r#" let colours = Set.of(Colour.Red, Colour.Named("teal"))
7160 console.println("{colours.contains(Colour.Named("teal"))} {colours.contains(Colour.Named("blue"))}")?"#,
7161 );
7162 assert_eq!(run.output, "true false\n");
7163 }
7164
7165 const OPAQUE: &str = r#"
7168use console.println
7169
7170/// A token.
7171export opaque struct Token {
7172 raw: String
7173 count: Int
7174}
7175
7176/// A token with nothing to hide.
7177export struct Label {
7178 raw: String
7179 count: Int
7180}
7181"#;
7182
7183 #[test]
7189 fn an_opaque_value_renders_as_its_name_alone() {
7190 let source = format!(
7191 "{OPAQUE}{}",
7192 r#"
7193/// Entry point.
7194export fn main() -> Result<Unit, Error> {
7195 let token = Token(raw: "secret", count: 1)
7196 let label = Label(raw: "secret", count: 1)
7197 println("{token}")?
7198 println("{label}")?
7199 Ok(())
7200}
7201"#
7202 );
7203 let run = run_entry_of(&source, "main", &[]);
7204 assert_eq!(run.output, "Token\nLabel(raw: secret, count: 1)\n");
7205 }
7206
7207 #[test]
7211 fn an_opaque_value_taken_out_of_a_set_still_renders_as_its_name() {
7212 let source = format!(
7213 "{OPAQUE}{}",
7214 r#"
7215/// Entry point.
7216export fn main() -> Result<Unit, Error> {
7217 for token in Set.of(Token(raw: "secret", count: 1)) {
7218 println("{token}")?
7219 }
7220 Ok(())
7221}
7222"#
7223 );
7224 let run = run_entry_of(&source, "main", &[]);
7225 assert_eq!(run.output, "Token\n");
7226 }
7227
7228 #[test]
7234 fn a_trait_object_over_an_opaque_value_keys_and_renders_as_that_value() {
7235 let source = format!(
7236 "{OPAQUE}{}",
7237 r#"
7238/// Something that can describe itself.
7239trait Described {
7240 fn describe(self) -> String
7241}
7242
7243impl Described for Token {
7244 fn describe(self) -> String { "token {self.count}" }
7245}
7246
7247impl Described for Label {
7248 fn describe(self) -> String { "label {self.count}" }
7249}
7250
7251/// Entry point.
7252export fn main() -> Result<Unit, Error> {
7253 let token: dyn Described = Token(raw: "secret", count: 1)
7254 let label: dyn Described = Label(raw: "secret", count: 1)
7255 let keys = Set.of(token, label)
7256 println("{keys.contains(Token(raw: "secret", count: 1))}")?
7257 println("{token}")?
7258 for key in keys {
7259 println("{key}")?
7260 }
7261 Ok(())
7262}
7263"#
7264 );
7265 let run = run_entry_of(&source, "main", &[]);
7266 assert_eq!(
7272 run.output,
7273 "true\nToken\nLabel(raw: secret, count: 1)\nToken\n"
7274 );
7275 }
7276
7277 #[test]
7278 fn a_struct_built_only_from_ints_is_a_valid_set_element() {
7279 let run = point_body(
7280 r#" let points = Set.of(Point(x: 1, y: 2), Point(x: 3, y: 4))
7281 console.println("{points.contains(Point(x: 1, y: 2))} {points.contains(Point(x: 9, y: 9))}")?"#,
7282 );
7283 assert_eq!(run.output, "true false\n");
7284 }
7285
7286 #[test]
7287 fn a_struct_nested_inside_a_struct_is_a_valid_set_element() {
7288 let source = r#"
7289use console.println
7290
7291struct Address {
7292 city: String
7293}
7294
7295struct Person {
7296 name: String
7297 address: Address
7298}
7299
7300export fn main() -> Result<Unit, Error> {
7301 let people = Set.of(
7302 Person(name: "Ada", address: Address(city: "London")),
7303 Person(name: "Grace", address: Address(city: "New York"))
7304 )
7305 console.println("{people.contains(Person(name: "Ada", address: Address(city: "London")))}")?
7306 console.println("{people.contains(Person(name: "Ada", address: Address(city: "Paris")))}")?
7307 Ok(())
7308}
7309"#;
7310 assert_eq!(run_entry_of(source, "main", &[]).output, "true\nfalse\n");
7311 }
7312
7313 #[test]
7314 fn an_array_built_only_from_ints_is_a_valid_set_element() {
7315 let output = output_of(
7316 r#" let pairs = Set.of([1, 2], [3, 4])
7317 console.println("{pairs.contains([1, 2])} {pairs.contains([9])}")?"#,
7318 );
7319 assert_eq!(output, "true false\n");
7320 }
7321
7322 #[test]
7323 fn a_struct_containing_a_vector_is_rejected_naming_the_nested_field() {
7324 let source = r#"
7325use console.println
7326
7327struct Point {
7328 tags: Vector<Int>
7329}
7330
7331export fn main() -> Result<Unit, Error> {
7332 let bad = Set.of(Point(tags: Vector.of(1)))
7333 Ok(())
7334}
7335"#;
7336 let error = run_entry_of(source, "main", &[]).error();
7337 assert_eq!(
7338 error.message,
7339 "`Set.of` cannot use a `Vector` inside `Point.tags` as a set element"
7340 );
7341 }
7342
7343 #[test]
7344 fn a_float_is_rejected_as_a_key_for_a_reason_distinct_from_mutability() {
7345 let error = error_of(" let bad = Set.of(1.5)");
7346 assert_eq!(
7347 error.message,
7348 "`Set.of` cannot use a `Float` as a set element"
7349 );
7350 assert!(
7351 error.rule.as_deref().unwrap_or_default().contains("NaN"),
7352 "{:?}",
7353 error.rule
7354 );
7355 }
7356
7357 const COLOUR: &str = r#"
7360use console.println
7361
7362enum Colour {
7363 Red
7364 Named(String)
7365}
7366
7367impl Colour {
7368 /// Returns the colour used when nothing was chosen.
7369 fn fallback() -> Colour {
7370 Colour.Red
7371 }
7372
7373 /// Names this colour.
7374 fn describe(self) -> String {
7375 match self {
7376 Colour.Red => "red"
7377 Colour.Named(name) => name
7378 }
7379 }
7380}
7381"#;
7382
7383 fn colour_body(body: &str) -> Run {
7384 run_entry_of(
7385 &format!(
7386 "{COLOUR}\nexport fn main() -> Result<Unit, Error> {{\n{body}\n Ok(())\n}}\n"
7387 ),
7388 "main",
7389 &[],
7390 )
7391 }
7392
7393 #[test]
7394 fn an_enum_can_declare_an_associated_function() {
7395 let run = colour_body(" console.println(\"{Colour.fallback()}\")?");
7396 assert_eq!(run.output, "Red\n");
7397 }
7398
7399 #[test]
7400 fn an_enum_value_answers_its_methods() {
7401 let run = colour_body(
7402 " console.println(\"{Colour.Red.describe()} {Colour.Named(\"teal\").describe()}\")?",
7403 );
7404 assert_eq!(run.output, "red teal\n");
7405 }
7406
7407 #[test]
7408 fn a_case_wins_over_an_associated_function_of_the_same_name() {
7409 let source = r#"
7410use console.println
7411
7412enum Signal {
7413 Ready
7414}
7415
7416impl Signal {
7417 /// Shadowed by the case of the same name, which keeps naming the case.
7418 fn Ready() -> String {
7419 "the function"
7420 }
7421}
7422
7423export fn main() -> Result<Unit, Error> {
7424 console.println("{Signal.Ready()}")?
7425 Ok(())
7426}
7427"#;
7428 assert_eq!(run_entry_of(source, "main", &[]).output, "Ready\n");
7429 }
7430
7431 #[test]
7432 fn an_unknown_enum_member_names_both_possibilities() {
7433 let error = colour_body(" let missing = Colour.missing()").error();
7434 assert_eq!(
7435 error.message,
7436 "enum `Colour` has no case or associated function `missing`"
7437 );
7438 let help = error.help.unwrap();
7439 assert!(help.contains("known cases: Red, Named"), "{help}");
7440 assert!(
7441 help.contains("known functions: describe, fallback"),
7442 "{help}"
7443 );
7444 }
7445
7446 const POINT: &str = r#"
7449use console.println
7450
7451struct Point {
7452 x: Int
7453 y: Int
7454}
7455"#;
7456
7457 fn point_body(body: &str) -> Run {
7458 run_entry_of(
7459 &format!("{POINT}\nexport fn main() -> Result<Unit, Error> {{\n{body}\n Ok(())\n}}\n"),
7460 "main",
7461 &[],
7462 )
7463 }
7464
7465 #[test]
7466 fn positional_arguments_may_precede_labels() {
7467 let run = point_body(" console.println(\"{Point(1, y: 2)}\")?");
7468 assert_eq!(run.output, "Point(x: 1, y: 2)\n");
7469 }
7470
7471 #[test]
7472 fn struct_initialization_reports_missing_unknown_and_duplicate_labels() {
7473 let missing = point_body(" let p = Point(x: 1)").error();
7474 assert!(missing.message.contains("field `y`"), "{}", missing.message);
7475
7476 let unknown = point_body(" let p = Point(x: 1, z: 2)").error();
7477 assert!(
7478 unknown.message.contains("no parameter labeled `z`"),
7479 "{}",
7480 unknown.message
7481 );
7482
7483 let duplicate = point_body(" let p = Point(x: 1, x: 2)").error();
7484 assert!(
7485 duplicate.message.contains("`x` more than once"),
7486 "{}",
7487 duplicate.message
7488 );
7489 }
7490
7491 #[test]
7492 fn struct_initializer_labels_must_be_in_declaration_order() {
7493 let error = point_body(" let p = Point(y: 2, x: 1)").error();
7494 assert_eq!(
7495 error.message,
7496 "`Point` was given the label `x` out of declaration order"
7497 );
7498 assert_eq!(
7499 error.help.as_deref(),
7500 Some("write the arguments in this order: x, y")
7501 );
7502 }
7503
7504 #[test]
7505 fn call_labels_must_be_in_declaration_order() {
7506 let source = r#"
7507use console.println
7508
7509fn between(low: Int, high: Int) -> String {
7510 "[{low}, {high}]"
7511}
7512
7513export fn main() -> Result<Unit, Error> {
7514 console.println(between(high: 6, low: 5))?
7515 Ok(())
7516}
7517"#;
7518 let error = run_entry_of(source, "main", &[]).error();
7519 assert_eq!(
7520 error.message,
7521 "`between` was given the label `low` out of declaration order"
7522 );
7523 assert_eq!(
7524 error.rule.as_deref(),
7525 Some(
7526 "Labeled arguments appear in declaration order, so argument order matches parameter order."
7527 )
7528 );
7529 assert_eq!(
7530 error.help.as_deref(),
7531 Some("write the arguments in this order: low, high")
7532 );
7533 }
7534
7535 #[test]
7536 fn labels_in_declaration_order_are_accepted_after_positional_arguments() {
7537 let source = r#"
7538use console.println
7539
7540fn measure(value: Int, unit: String = "m", prefix: String = "length") -> String {
7541 "{prefix} {value}{unit}"
7542}
7543
7544export fn main() -> Result<Unit, Error> {
7545 console.println(measure(3, unit: "cm", prefix: "width"))?
7546 console.println(measure(3, prefix: "width"))?
7547 console.println(measure(value: 4, unit: "cm"))?
7548 Ok(())
7549}
7550"#;
7551 assert_eq!(
7552 run_entry_of(source, "main", &[]).output,
7553 "width 3cm
7554width 3m
7555length 4cm
7556"
7557 );
7558 }
7559
7560 #[test]
7563 fn an_entry_takes_no_parameters_or_one_array_of_strings() {
7564 let source = r#"
7565export fn main(first: String, second: String) -> Result<Unit, Error> {
7566 Ok(())
7567}
7568"#;
7569 let error = run_entry_of(source, "main", &[]).error();
7570 assert!(
7571 error
7572 .rule
7573 .unwrap()
7574 .contains("either no parameters or one `Array<String>`"),
7575 "{}",
7576 error.message
7577 );
7578 }
7579
7580 const TASKS: &str = r#"
7588use console.println
7589
7590async fn answer() -> Int {
7591 7
7592}
7593
7594async fn load(ok: Bool) -> Result<Int, Error> {
7595 if ok {
7596 Ok(1)
7597 } else {
7598 Err(Error("boom"))
7599 }
7600}
7601"#;
7602
7603 const SPINNING_TASK: &str =
7613 " var i = 0\n while i < 1000000000 {\n i += 1\n }\n println(\"this must not run\")?";
7614
7615 fn run_task_body(body: &str) -> Run {
7618 run_entry_of(
7619 &format!("{TASKS}\nexport fn main() -> Result<Unit, Error> {{\n{body}\n Ok(())\n}}\n"),
7620 "main",
7621 &[],
7622 )
7623 }
7624
7625 #[test]
7626 fn an_async_fn_is_called_like_any_other_function_and_awaited() {
7627 let run = run_task_body(" let value = await answer()\n println(\"{value}\")?");
7628 assert_eq!(run.output, "7\n");
7629 }
7630
7631 #[test]
7636 fn an_async_fn_that_is_never_awaited_still_runs() {
7637 let source = r#"
7638use console.println
7639
7640async fn announce() -> Result<Unit, Error> {
7641 println("announced")?
7642 Ok(())
7643}
7644
7645export fn main() -> Result<Unit, Error> {
7646 let ignored = announce()
7647 Ok(())
7648}
7649"#;
7650 let run = run_entry_of(source, "main", &[]);
7651 assert!(run.output.contains("announced"), "{:?}", run.output);
7652 }
7653
7654 #[test]
7655 fn awaiting_a_result_propagates_with_a_question_mark() {
7656 let source = format!(
7657 "{TASKS}
7658export fn main() -> Result<Int, Error> {{
7659 let good = load(true).await()?
7660 println(\"{{good}}\")?
7661 let bad = load(false).await()?
7662 println(\"unreachable\")?
7663 Ok(bad)
7664}}
7665"
7666 );
7667 let run = run_entry_of(&source, "main", &[]);
7668 assert_eq!(run.output, "1\n");
7669 assert_eq!(run.value().to_string(), "Err(boom)");
7670 }
7671
7672 #[test]
7676 fn a_question_mark_on_a_task_points_at_await() {
7677 let error = run_task_body(" let task = load(true)\n let value = task?").error();
7678 assert_eq!(
7679 error.message,
7680 "`?` needs a `Result` or an `Option`, but found `Task`"
7681 );
7682 assert!(
7683 error.help.unwrap().contains("task.await()?"),
7684 "the diagnostic shows the correction"
7685 );
7686 }
7687
7688 #[test]
7692 fn a_question_mark_after_await_propagates_the_awaited_error() {
7693 let run = run_task_body(" let value = await load(true)?\n println(\"{value}\")?");
7694 assert_eq!(run.output, "1\n");
7695
7696 let error = run_task_body(" let value = await load(false)?").value;
7697 match error {
7698 Ok(Value(Repr::Enum(result))) => {
7699 assert_eq!(&*result.case, "Err");
7700 assert_eq!(result.payload[0].to_string(), "boom");
7701 }
7702 other => panic!("expected the awaited `Err` to propagate, found {other:?}"),
7703 }
7704 }
7705
7706 #[test]
7707 fn both_await_spellings_settle_the_same_task() {
7708 let run = run_task_body(
7709 " let prefix = await answer()\n let postfix = answer().await()\n println(\"{prefix} {postfix}\")?",
7710 );
7711 assert_eq!(run.output, "7 7\n");
7712 }
7713
7714 #[test]
7715 fn a_scope_awaits_the_tasks_it_spawned() {
7716 let run = run_task_body(
7717 " scope tasks {\n let first = tasks.spawn { 1 }\n let second = tasks.spawn { 2 }\n let a = await first\n let b = second.await()\n println(\"{a} {b}\")?\n }",
7718 );
7719 assert_eq!(run.output, "1 2\n");
7720 }
7721
7722 #[test]
7723 fn leaving_a_scope_settles_a_task_the_body_never_awaited() {
7724 let run = run_task_body(
7725 " scope tasks {\n let ignored = tasks.spawn { println(\"the task ran\")? }\n }\n println(\"after the scope\")?",
7726 );
7727 assert_eq!(run.output, "the task ran\nafter the scope\n");
7728 }
7729
7730 #[test]
7731 fn returning_from_a_scope_cancels_a_task_that_is_still_running() {
7732 let source = format!(
7733 "{TASKS}
7734export fn main() -> Result<Unit, Error> {{
7735 scope tasks {{
7736 let ignored = tasks.spawn {{
7737{SPINNING_TASK}
7738 }}
7739 return Ok(())
7740 }}
7741}}
7742"
7743 );
7744 let run = run_entry_of(&source, "main", &[]);
7745 assert_eq!(run.output, "");
7746 assert_eq!(run.value().to_string(), "Ok(())");
7747 }
7748
7749 #[test]
7750 fn an_error_inside_a_scope_cancels_a_task_that_is_still_running() {
7751 let source = format!(
7752 "{TASKS}
7753export fn main() -> Result<Int, Error> {{
7754 scope tasks {{
7755 let ignored = tasks.spawn {{
7756{SPINNING_TASK}
7757 }}
7758 let value = load(false).await()?
7759 Ok(value)
7760 }}
7761}}
7762"
7763 );
7764 let run = run_entry_of(&source, "main", &[]);
7765 assert_eq!(run.output, "");
7766 assert_eq!(run.value().to_string(), "Err(boom)");
7767 }
7768
7769 #[test]
7770 fn a_task_that_fails_propagates_its_error_out_of_the_scope() {
7771 let source = format!(
7772 "{TASKS}
7773export fn main() -> Result<Unit, Error> {{
7774 scope tasks {{
7775 let failing = tasks.spawn {{ Err(Error(\"the task failed\")) }}
7776 println(\"the body finished\")?
7777 }}
7778 println(\"unreachable\")?
7779 Ok(())
7780}}
7781"
7782 );
7783 let run = run_entry_of(&source, "main", &[]);
7784 assert_eq!(run.output, "the body finished\n");
7785 assert_eq!(run.value().to_string(), "Err(the task failed)");
7786 }
7787
7788 #[test]
7789 fn awaiting_a_cancelled_task_is_rejected() {
7790 let run = run_task_body(&format!(
7791 " scope tasks {{\n let timer = tasks.spawn {{\n{SPINNING_TASK}\n }}\n timer.cancel()\n let value = await timer\n }}"
7792 ));
7793 assert_eq!(run.output, "");
7794 let error = run.error();
7795 assert!(error.message.contains("was cancelled"), "{}", error.message);
7796 assert!(error.rule.unwrap().contains("waits for or cancels"));
7797 }
7798
7799 #[test]
7800 fn awaiting_the_same_handle_twice_runs_the_body_once() {
7801 let run = run_task_body(
7802 " scope tasks {\n let once = tasks.spawn {\n println(\"the body ran\")?\n 7\n }\n let first = await once\n let second = await once\n println(\"{first} {second}\")?\n }",
7803 );
7804 assert_eq!(run.output, "the body ran\n7 7\n");
7805 }
7806
7807 #[test]
7808 fn awaiting_a_value_that_is_not_a_task_is_rejected() {
7809 let error = run_task_body(" let value = await 1").error();
7810 assert_eq!(error.message, "`await` needs a task, but found `Int`");
7811 assert!(error.rule.unwrap().contains("`await` settles a task"));
7812 }
7813
7814 #[test]
7817 fn spawning_a_closure_that_captures_a_vector_is_rejected() {
7818 let source = r#"
7819export fn main() -> Result<Unit, Error> {
7820 var items = Vector.of(1, 2)
7821 scope tasks {
7822 let counting = tasks.spawn { items.length() }
7823 }
7824 Ok(())
7825}
7826"#;
7827 let error = run_entry_of(source, "main", &[]).error();
7828 assert_eq!(
7829 error.message,
7830 "`spawn` cannot capture `items`, which is a `Vector`"
7831 );
7832 assert!(error
7833 .rule
7834 .unwrap()
7835 .contains("A vector cannot cross, even through `let`"));
7836 let help = error.help.unwrap();
7837 assert!(
7838 help.contains("freeze()") && help.contains("toArray()"),
7839 "{help}"
7840 );
7841 }
7842
7843 #[test]
7844 fn spawning_a_closure_that_captures_the_frozen_array_is_accepted() {
7845 let source = r#"
7846use console.println
7847
7848export fn main() -> Result<Unit, Error> {
7849 var items = Vector.of(1, 2)
7850 let frozen = items.freeze()
7851 scope tasks {
7852 let counting = tasks.spawn { frozen.length() }
7853 let total = await counting
7854 println("{total}")?
7855 }
7856 Ok(())
7857}
7858"#;
7859 assert_eq!(run_entry_of(source, "main", &[]).output, "2\n");
7860 }
7861
7862 #[test]
7863 fn task_safety_names_the_field_that_cannot_cross() {
7864 let source = r#"
7865struct Draft {
7866 guests: Vector<String>
7867}
7868
7869export fn main() -> Result<Unit, Error> {
7870 let draft = Draft(guests: Vector.of("Alice"))
7871 scope tasks {
7872 let counting = tasks.spawn { draft.guests.length() }
7873 }
7874 Ok(())
7875}
7876"#;
7877 let error = run_entry_of(source, "main", &[]).error();
7878 assert_eq!(
7879 error.message,
7880 "`spawn` cannot capture `draft.guests`, which is a `Vector`"
7881 );
7882 }
7883
7884 #[test]
7885 fn a_closure_is_task_safe_only_when_every_capture_is() {
7886 let source = r#"
7887export fn main() -> Result<Unit, Error> {
7888 var seen = Vector.of(1)
7889 let count = fn() {
7890 seen.length()
7891 }
7892 scope tasks {
7893 let counting = tasks.spawn { count() }
7894 }
7895 Ok(())
7896}
7897"#;
7898 let error = run_entry_of(source, "main", &[]).error();
7899 assert_eq!(
7900 error.message,
7901 "`spawn` cannot capture `count -> seen`, which is a `Vector`"
7902 );
7903 }
7904
7905 #[test]
7910 fn task_safety_names_the_array_element_that_cannot_cross() {
7911 let source = r#"
7912struct Draft {
7913 guests: Vector<String>
7914}
7915
7916export fn main() -> Result<Unit, Error> {
7917 let drafts = [Draft(guests: Vector.of("Alice"))]
7918 scope tasks {
7919 let counting = tasks.spawn { drafts.length() }
7920 }
7921 Ok(())
7922}
7923"#;
7924 let error = run_entry_of(source, "main", &[]).error();
7925 assert_eq!(
7926 error.message,
7927 "`spawn` cannot capture `drafts[0].guests`, which is a `Vector`"
7928 );
7929 }
7930
7931 #[test]
7934 fn task_safety_names_the_enum_payload_that_cannot_cross() {
7935 let source = r#"
7936enum Draft {
7937 Empty
7938 Guests(Vector<String>)
7939}
7940
7941export fn main() -> Result<Unit, Error> {
7942 let draft = Draft.Guests(Vector.of("Alice"))
7943 scope tasks {
7944 let counting = tasks.spawn { draft }
7945 }
7946 Ok(())
7947}
7948"#;
7949 let error = run_entry_of(source, "main", &[]).error();
7950 assert_eq!(
7951 error.message,
7952 "`spawn` cannot capture `draft.Guests(0)`, which is a `Vector`"
7953 );
7954 }
7955
7956 #[test]
7959 fn an_enum_case_that_carries_nothing_crosses_a_task_boundary() {
7960 let source = r#"
7961use console.println
7962
7963enum Draft {
7964 Empty
7965 Guests(Vector<String>)
7966}
7967
7968export fn main() -> Result<Unit, Error> {
7969 let draft = Draft.Empty
7970 scope tasks {
7971 let crossing = tasks.spawn { draft }
7972 println("{await crossing}")?
7973 }
7974 Ok(())
7975}
7976"#;
7977 assert_eq!(run_entry_of(source, "main", &[]).output, "Empty\n");
7978 }
7979
7980 #[test]
7984 fn task_safety_looks_through_a_trait_object_to_the_value_it_holds() {
7985 let source = r#"
7986trait Summary {
7987 fn summarize(self) -> String
7988}
7989
7990struct Draft {
7991 guests: Vector<String>
7992}
7993
7994impl Summary for Draft {
7995 fn summarize(self) -> String {
7996 "a draft"
7997 }
7998}
7999
8000export fn main() -> Result<Unit, Error> {
8001 let entry: dyn Summary = Draft(guests: Vector.of("Alice"))
8002 scope tasks {
8003 let describing = tasks.spawn { entry.summarize() }
8004 }
8005 Ok(())
8006}
8007"#;
8008 let error = run_entry_of(source, "main", &[]).error();
8009 assert_eq!(
8010 error.message,
8011 "`spawn` cannot capture `entry.guests`, which is a `Vector`"
8012 );
8013 }
8014
8015 #[test]
8019 fn a_trait_object_over_a_task_safe_value_crosses_and_still_dispatches() {
8020 let source = r#"
8021use console.println
8022
8023trait Summary {
8024 fn summarize(self) -> String
8025}
8026
8027struct Draft {
8028 guests: Array<String>
8029}
8030
8031impl Summary for Draft {
8032 fn summarize(self) -> String {
8033 "a draft of {self.guests.length()}"
8034 }
8035}
8036
8037export fn main() -> Result<Unit, Error> {
8038 let entry: dyn Summary = Draft(guests: ["Alice"])
8039 scope tasks {
8040 let describing = tasks.spawn { entry.summarize() }
8041 println("{await describing}")?
8042 }
8043 Ok(())
8044}
8045"#;
8046 assert_eq!(run_entry_of(source, "main", &[]).output, "a draft of 1\n");
8047 }
8048
8049 fn run_timed(source: &str) -> (Run, Duration) {
8054 let (sources, program) = program_of(source);
8055 let buffer = Buffer::default();
8056 let mut hosts = HostRegistry::new(Grants::new(["console", "clock"]));
8057 hosts.register(Box::new(Console::new(buffer.clone(), Buffer::default())));
8058 hosts.register(Box::new(crate::clock::Clock::real()));
8059 let runtime = Runtime::new(program, sources, Arc::new(hosts));
8060 let started = Instant::now();
8061 let value = Interpreter::new(&runtime).run_entry("test", "main", Vec::new());
8062 let elapsed = started.elapsed();
8063 (
8064 Run {
8065 value,
8066 output: buffer.text(),
8067 },
8068 elapsed,
8069 )
8070 }
8071
8072 #[derive(Clone, Default)]
8074 struct RecordingSink(Arc<Mutex<Vec<TraceEvent>>>);
8075
8076 impl RecordingSink {
8077 fn events(&self) -> Vec<TraceEvent> {
8078 self.0.lock().expect("no test panics while tracing").clone()
8079 }
8080 }
8081
8082 impl crate::trace::TraceSink for RecordingSink {
8083 fn record(&self, event: TraceEvent) {
8084 self.0
8085 .lock()
8086 .expect("no test panics while tracing")
8087 .push(event);
8088 }
8089 }
8090
8091 fn run_traced(source: &str) -> (Run, Vec<TraceEvent>, Duration) {
8094 run_traced_under(source, Limits::default())
8095 }
8096
8097 fn run_traced_under(source: &str, limits: Limits) -> (Run, Vec<TraceEvent>, Duration) {
8100 let (sources, program) = program_of(source);
8101 let buffer = Buffer::default();
8102 let sink = RecordingSink::default();
8103 let mut hosts = HostRegistry::new(Grants::new(["console", "clock"]));
8104 hosts.register(Box::new(Console::new(buffer.clone(), Buffer::default())));
8105 hosts.register(Box::new(crate::clock::Clock::real()));
8106 hosts.set_budget(Budget::new(limits));
8107 hosts.set_trace(Arc::new(sink.clone()));
8108 let runtime =
8109 Runtime::new(program, sources, Arc::new(hosts)).with_trace(Arc::new(sink.clone()));
8110 let started = Instant::now();
8111 let value = Interpreter::new(&runtime).run_entry("test", "main", Vec::new());
8112 let elapsed = started.elapsed();
8113 (
8114 Run {
8115 value,
8116 output: buffer.text(),
8117 },
8118 sink.events(),
8119 elapsed,
8120 )
8121 }
8122
8123 fn run_ended(
8131 source: &str,
8132 limits: Limits,
8133 grants: &[&str],
8134 cancellation: Cancellation,
8135 ) -> (RunOutcome, Option<String>) {
8136 let (sources, program) = program_of(source);
8137 let sink = RecordingSink::default();
8138 let mut hosts = HostRegistry::new(Grants::new(grants.iter().copied()));
8139 hosts.register(Box::new(Console::new(Buffer::default(), Buffer::default())));
8140 hosts.set_budget(Budget::with_cancellation(limits, cancellation));
8141 hosts.set_trace(Arc::new(sink.clone()));
8142 let runtime =
8143 Runtime::new(program, sources, Arc::new(hosts)).with_trace(Arc::new(sink.clone()));
8144 let _ = Interpreter::new(&runtime).run_entry("test", "main", Vec::new());
8145 let events = sink.events();
8146 match events.last() {
8149 Some(TraceEvent::RunEnded { outcome, message }) => (*outcome, message.clone()),
8150 other => panic!("a run's last event must be `run_ended`, found {other:?}"),
8151 }
8152 }
8153
8154 fn ended(source: &str) -> (RunOutcome, Option<String>) {
8156 run_ended(source, Limits::default(), &["console"], Cancellation::new())
8157 }
8158
8159 fn main_of(body: &str) -> String {
8161 format!("use console.println\n\nexport fn main() -> Result<Unit, Error> {{\n{body}\n}}\n")
8162 }
8163
8164 #[test]
8165 fn a_run_that_finished_ends_with_success_and_says_nothing_more() {
8166 assert_eq!(
8167 ended(&main_of(" println(\"hi\")?\n Ok(())")),
8168 (RunOutcome::Success, None)
8169 );
8170 }
8171
8172 #[test]
8176 fn a_run_whose_entry_returned_an_error_ends_with_that_error_and_its_message() {
8177 assert_eq!(
8178 ended(&main_of(" Err(Error(message: \"no report\"))")),
8179 (RunOutcome::Error, Some("no report".to_string()))
8180 );
8181 }
8182
8183 #[test]
8184 fn a_run_that_broke_an_invariant_ends_with_that() {
8185 let (outcome, message) = ended(&main_of(" let n = 1 / 0\n Ok(())"));
8186 assert_eq!(outcome, RunOutcome::Invariant);
8187 assert_eq!(message.as_deref(), Some("`Int` division by zero"));
8188 }
8189
8190 #[test]
8193 fn a_run_the_host_boundary_refused_ends_with_that() {
8194 let (outcome, message) = run_ended(
8195 &main_of(" println(\"hi\")?\n Ok(())"),
8196 Limits::default(),
8197 &[],
8198 Cancellation::new(),
8199 );
8200 assert_eq!(outcome, RunOutcome::HostBoundary);
8201 assert!(
8202 message.is_some_and(|message| message.contains("requires the `console` capability")),
8203 "the message names what was refused"
8204 );
8205 }
8206
8207 #[test]
8210 fn each_limit_that_stops_a_run_ends_it_with_that_limit_s_own_name() {
8211 let looping = main_of(" var i = 0\n while true {\n i = i + 1\n }\n Ok(())");
8212 let stopped = |limits: Limits, source: &str| {
8213 run_ended(source, limits, &["console"], Cancellation::new()).0
8214 };
8215 assert_eq!(
8216 stopped(
8217 Limits {
8218 fuel: Some(100),
8219 ..Limits::default()
8220 },
8221 &looping
8222 ),
8223 RunOutcome::Fuel
8224 );
8225 assert_eq!(
8226 stopped(
8227 Limits {
8228 deadline: Some(Duration::from_millis(1)),
8229 ..Limits::default()
8230 },
8231 &looping
8232 ),
8233 RunOutcome::Deadline
8234 );
8235 assert_eq!(
8236 stopped(
8237 Limits {
8238 max_host_calls: Some(0),
8239 ..Limits::default()
8240 },
8241 &main_of(" println(\"hi\")?\n Ok(())")
8242 ),
8243 RunOutcome::HostCalls
8244 );
8245 assert_eq!(
8246 stopped(
8247 Limits {
8248 max_call_depth: Some(2),
8249 ..Limits::default()
8250 },
8251 &format!(
8252 "fn down(n: Int) -> Int {{\n if n == 0 {{ 0 }} else {{ down(n - 1) }}\n}}\n\n{}",
8253 main_of(" let n = down(8)\n Ok(())")
8254 )
8255 ),
8256 RunOutcome::CallDepth
8257 );
8258 assert_eq!(
8259 stopped(
8260 Limits {
8261 max_tasks: Some(1),
8262 ..Limits::default()
8263 },
8264 &main_of(
8265 " scope many {\n let a = many.spawn { 1 }\n let b = many.spawn { 2 }\n let total = await a + await b\n }\n Ok(())"
8266 )
8267 ),
8268 RunOutcome::Concurrency
8269 );
8270 }
8271
8272 #[test]
8276 fn a_run_cancelled_from_outside_ends_with_that() {
8277 let cancellation = Cancellation::new();
8278 cancellation.cancel();
8279 assert_eq!(
8280 run_ended(
8281 &main_of(" println(\"hi\")?\n Ok(())"),
8282 Limits::default(),
8283 &["console"],
8284 cancellation,
8285 )
8286 .0,
8287 RunOutcome::Cancelled
8288 );
8289 }
8290
8291 #[test]
8294 fn a_run_that_could_not_find_its_entry_still_ends_with_an_event() {
8295 let (sources, program) = program_of(&main_of(" Ok(())"));
8296 let sink = RecordingSink::default();
8297 let runtime = Runtime::new(
8298 program,
8299 sources,
8300 Arc::new(HostRegistry::new(Grants::new(["console"]))),
8301 )
8302 .with_trace(Arc::new(sink.clone()));
8303 let outcome = Interpreter::new(&runtime).run_entry("test", "absent", Vec::new());
8304 assert!(outcome.is_err());
8305 let events = sink.events();
8306 assert!(
8307 matches!(
8308 events.as_slice(),
8309 [TraceEvent::RunEnded {
8310 outcome: RunOutcome::Invariant,
8311 ..
8312 }]
8313 ),
8314 "{events:?}"
8315 );
8316 }
8317
8318 #[test]
8328 fn every_host_call_names_the_task_that_made_it() {
8329 let source = r#"
8330use clock.sleep
8331use console.println
8332
8333fn work(label: String) -> Result<Unit, Error> {
8334 println("{label} started")?
8335 sleep(1ms)
8336 println("{label} finished")
8337}
8338
8339export fn main() -> Result<Unit, Error> {
8340 println("entry")?
8341 scope workers {
8342 let a = workers.spawn { work("a") }
8343 let b = workers.spawn { work("b") }
8344 let c = workers.spawn { work("c") }
8345 await a?
8346 await b?
8347 await c?
8348 }
8349 Ok(())
8350}
8351"#;
8352 let (run, events, _) = run_traced(source);
8353 run.value();
8354
8355 let mut said: std::collections::BTreeMap<u64, Vec<String>> =
8356 std::collections::BTreeMap::new();
8357 for event in &events {
8358 let TraceEvent::HostCall { task, op, args, .. } = event else {
8359 continue;
8360 };
8361 if op != "println" {
8362 continue;
8363 }
8364 let crate::trace::RecordedValue::Carried(transfer) = &args[0] else {
8365 panic!("a printed line is a string, which crosses a boundary whole");
8366 };
8367 said.entry(*task)
8368 .or_default()
8369 .push(transfer.clone().into_value().to_string());
8370 }
8371
8372 assert_eq!(said.remove(&ENTRY_TASK), Some(vec!["entry".to_string()]));
8375
8376 assert_eq!(said.len(), 3, "{said:?}");
8380 let mut labels: Vec<String> = Vec::new();
8381 for (task, lines) in &said {
8382 assert_ne!(*task, ENTRY_TASK);
8383 let label = lines[0]
8384 .split_once(' ')
8385 .expect("a line is `<label> <what>`")
8386 .0
8387 .to_string();
8388 assert_eq!(
8389 *lines,
8390 vec![format!("{label} started"), format!("{label} finished")],
8391 "task {task} said something another task said"
8392 );
8393 labels.push(label);
8394 }
8395 labels.sort();
8396 assert_eq!(labels, ["a", "b", "c"]);
8397 }
8398
8399 #[test]
8400 fn a_task_can_spawn_tasks_of_its_own() {
8401 let run = run_task_body(
8402 " scope outer {\n let parent = outer.spawn {\n scope inner {\n let a = inner.spawn { 1 }\n let b = inner.spawn { 2 }\n await a + await b\n }\n }\n println(\"{await parent}\")?\n }",
8403 );
8404 assert_eq!(run.output, "3\n");
8405 }
8406
8407 #[test]
8411 fn a_task_cannot_produce_a_value_that_may_not_cross() {
8412 let source = r#"
8413export fn main() -> Result<Unit, Error> {
8414 scope tasks {
8415 let building = tasks.spawn { Vector.of(1, 2) }
8416 let items = await building
8417 }
8418 Ok(())
8419}
8420"#;
8421 let error = run_entry_of(source, "main", &[]).error();
8422 assert_eq!(
8423 error.message,
8424 "this task produced a `Vector`, which cannot leave a task"
8425 );
8426 }
8427
8428 #[test]
8432 fn a_trace_attributes_each_task_s_wait_to_that_task() {
8433 let source = r#"
8434use clock.sleep
8435
8436export fn main() -> Result<Unit, Error> {
8437 scope waits {
8438 let first = waits.spawn { sleep(300ms) }
8439 let second = waits.spawn { sleep(300ms) }
8440 await first
8441 await second
8442 }
8443 Ok(())
8444}
8445"#;
8446 let (run, events, elapsed) = run_traced(source);
8447 run.value();
8448
8449 let sleeps: Vec<&TraceEvent> = events
8454 .iter()
8455 .filter(|event| matches!(event, TraceEvent::HostCall { op, .. } if op == "sleep"))
8456 .collect();
8457 assert_eq!(sleeps.len(), 2);
8458 for event in &sleeps {
8459 let TraceEvent::HostCall { args, .. } = event else {
8460 unreachable!("filtered to host calls")
8461 };
8462 assert_eq!(args.len(), 1);
8463 assert_eq!(
8464 crate::trace::value_to_json(
8465 &match &args[0] {
8466 crate::trace::RecordedValue::Carried(transfer) =>
8467 transfer.clone().into_value(),
8468 other => panic!("expected a carried duration, found {other:?}"),
8469 },
8470 crate::trace::ValueCapture::Full
8471 ),
8472 r#"{"type":"duration","ns":300000000}"#
8473 );
8474 }
8475 let waited: Duration = sleeps
8476 .iter()
8477 .filter_map(|event| match event {
8478 TraceEvent::HostCall { wait, .. } => Some(*wait),
8479 _ => None,
8480 })
8481 .sum();
8482 assert!(
8483 waited > elapsed,
8484 "the two waits total {waited:?}, which is not more than the {elapsed:?} the run took"
8485 );
8486 assert_eq!(
8487 events
8488 .iter()
8489 .filter(|event| matches!(event, TraceEvent::TaskCompleted { .. }))
8490 .count(),
8491 2
8492 );
8493 }
8494
8495 #[test]
8499 fn cancelling_a_running_task_stops_it_and_traces_it() {
8500 let source = format!(
8501 "{TASKS}
8502export fn main() -> Result<Unit, Error> {{
8503 scope tasks {{
8504 let ignored = tasks.spawn {{
8505{SPINNING_TASK}
8506 }}
8507 return Ok(())
8508 }}
8509}}
8510"
8511 );
8512 let (run, events, _) = run_traced(&source);
8513 assert_eq!(run.output, "");
8514 assert!(events
8515 .iter()
8516 .any(|event| matches!(event, TraceEvent::TaskCancelled { id: 1 })));
8517 assert!(!events
8518 .iter()
8519 .any(|event| matches!(event, TraceEvent::TaskCompleted { .. })));
8520 }
8521
8522 #[test]
8528 fn two_tasks_wait_at_the_same_time() {
8529 let source = r#"
8530use clock.sleep
8531
8532export fn main() -> Result<Unit, Error> {
8533 scope waits {
8534 let first = waits.spawn { sleep(300ms) }
8535 let second = waits.spawn { sleep(300ms) }
8536 await first
8537 await second
8538 }
8539 Ok(())
8540}
8541"#;
8542 let (run, elapsed) = run_timed(source);
8543 run.value();
8544 assert!(
8545 elapsed >= Duration::from_millis(250),
8546 "both tasks really waited, but the run took {elapsed:?}"
8547 );
8548 assert!(
8549 elapsed < Duration::from_millis(550),
8550 "the waits overlapped, but the run took {elapsed:?}, which is closer to their sum"
8551 );
8552 }
8553
8554 #[test]
8555 fn a_scope_with_two_tasks_produces_both_values() {
8556 let run = run_task_body(
8557 " scope tasks {\n let first = tasks.spawn { 1 }\n let second = tasks.spawn { 2 }\n println(\"{await first} {await second}\")?\n }",
8558 );
8559 assert_eq!(run.output, "1 2\n");
8560 }
8561
8562 #[test]
8565 fn a_budget_exhausted_inside_a_task_stops_the_run() {
8566 let source = r#"
8567export fn main() -> Result<Unit, Error> {
8568 scope tasks {
8569 let spinning = tasks.spawn {
8570 var i = 0
8571 while i < 1000000000 {
8572 i += 1
8573 }
8574 i
8575 }
8576 await spinning
8577 }
8578 Ok(())
8579}
8580"#;
8581 let (sources, program) = program_of(source);
8582 let mut hosts = HostRegistry::new(Grants::new(["console"]));
8583 hosts.register(Box::new(Console::new(Buffer::default(), Buffer::default())));
8584 hosts.set_budget(Budget::new(Limits {
8585 fuel: Some(10_000),
8586 ..Limits::default()
8587 }));
8588 let runtime = Runtime::new(program, sources, Arc::new(hosts));
8589 let error = Interpreter::new(&runtime)
8590 .run_entry("test", "main", Vec::new())
8591 .expect_err("the fuel budget stops the run");
8592 assert!(error.message.contains("fuel budget"), "{}", error.message);
8593 assert!(
8594 runtime
8595 .hosts()
8596 .with_budget(|budget| budget.fuel_spent())
8597 .unwrap_or_default()
8598 >= 10_000
8599 );
8600 }
8601
8602 fn children_of(events: &[TraceEvent]) -> Vec<(u64, bool, bool)> {
8620 let mut children: Vec<(u64, bool, bool)> = Vec::new();
8621 for event in events {
8622 match event {
8623 TraceEvent::TaskSpawned { id, .. } => children.push((*id, false, false)),
8626 TraceEvent::TaskCompleted { id, .. } => {
8627 if let Some(child) = children.iter_mut().find(|child| child.0 == *id) {
8628 child.1 = true;
8629 }
8630 }
8631 TraceEvent::TaskCancelled { id } => {
8632 if let Some(child) = children.iter_mut().find(|child| child.0 == *id) {
8633 child.2 = true;
8634 }
8635 }
8636 _ => {}
8637 }
8638 }
8639 children
8640 }
8641
8642 fn assert_every_child_settled(events: &[TraceEvent]) {
8644 let children = children_of(events);
8645 assert!(
8646 !children.is_empty(),
8647 "the run spawned no task, so it cannot show what a scope does with one"
8648 );
8649 for (id, joined, cancelled) in &children {
8650 assert!(
8651 *joined || *cancelled,
8652 "task {id} was neither joined nor cancelled: {children:?}"
8653 );
8654 }
8655 }
8656
8657 #[test]
8661 fn a_scope_that_completes_normally_joins_the_child_it_never_awaited() {
8662 let source = r#"
8663use console.println
8664
8665export fn main() -> Result<Unit, Error> {
8666 scope tasks {
8667 let awaited = tasks.spawn { 1 }
8668 let ignored = tasks.spawn { println("the child the body never awaited ran")? }
8669 await awaited
8670 }
8671 println("the scope was left")?
8672 Ok(())
8673}
8674"#;
8675 let (run, events, _) = run_traced(source);
8676 assert_eq!(
8677 run.output,
8678 "the child the body never awaited ran\nthe scope was left\n"
8679 );
8680 run.value();
8681 assert_every_child_settled(&events);
8682 assert_eq!(
8683 children_of(&events),
8684 vec![(1, true, false), (2, true, false)]
8685 );
8686 }
8687
8688 #[test]
8693 fn a_scope_left_by_return_cancels_only_the_child_still_running() {
8694 let source = format!(
8695 "use console.println
8696
8697export fn main() -> Result<Unit, Error> {{
8698 scope tasks {{
8699 let quick = tasks.spawn {{ println(\"the quick child ran\")? }}
8700 let spinning = tasks.spawn {{
8701{SPINNING_TASK}
8702 }}
8703 await quick
8704 return Ok(())
8705 }}
8706}}
8707"
8708 );
8709 let (run, events, _) = run_traced(&source);
8710 assert_eq!(run.output, "the quick child ran\n");
8711 run.value();
8712 assert_every_child_settled(&events);
8713 assert_eq!(
8714 children_of(&events),
8715 vec![(1, true, false), (2, false, true)]
8716 );
8717 }
8718
8719 #[test]
8723 fn a_scope_left_by_a_propagated_err_cancels_the_child_still_running() {
8724 let source = format!(
8725 "{TASKS}
8726export fn main() -> Result<Unit, Error> {{
8727 scope tasks {{
8728 let quick = tasks.spawn {{ println(\"the quick child ran\")? }}
8729 let spinning = tasks.spawn {{
8730{SPINNING_TASK}
8731 }}
8732 await quick
8733 await load(false)?
8734 println(\"never printed\")?
8735 }}
8736 Ok(())
8737}}
8738"
8739 );
8740 let (run, events, _) = run_traced(&source);
8741 assert_eq!(run.output, "the quick child ran\n");
8742 assert_eq!(run.value().to_string(), "Err(boom)");
8743 assert_every_child_settled(&events);
8744 assert_eq!(
8745 children_of(&events),
8746 vec![(1, true, false), (2, false, true)]
8747 );
8748 }
8749
8750 #[test]
8754 fn a_child_the_program_cancelled_is_still_waited_for_at_scope_exit() {
8755 let source = format!(
8756 "use console.println
8757
8758export fn main() -> Result<Unit, Error> {{
8759 scope tasks {{
8760 let quick = tasks.spawn {{ println(\"the quick child ran\")? }}
8761 let spinning = tasks.spawn {{
8762{SPINNING_TASK}
8763 }}
8764 await quick
8765 spinning.cancel()
8766 }}
8767 println(\"the scope was left\")?
8768 Ok(())
8769}}
8770"
8771 );
8772 let (run, events, _) = run_traced(&source);
8773 assert_eq!(run.output, "the quick child ran\nthe scope was left\n");
8774 run.value();
8775 assert_every_child_settled(&events);
8776 assert_eq!(
8777 children_of(&events),
8778 vec![(1, true, false), (2, false, true)]
8779 );
8780 }
8781
8782 #[test]
8787 fn a_scope_left_by_a_broken_invariant_cancels_the_child_still_running() {
8788 let source = format!(
8789 "use console.println
8790
8791export fn main() -> Result<Unit, Error> {{
8792 scope tasks {{
8793 let spinning = tasks.spawn {{
8794{SPINNING_TASK}
8795 }}
8796 let largest = 9223372036854775807
8797 println(\"never printed {{largest + 1}}\")?
8798 }}
8799 Ok(())
8800}}
8801"
8802 );
8803 let (run, events, _) = run_traced(&source);
8804 assert_eq!(run.output, "");
8805 let error = run.error();
8806 assert_eq!(error.message, "`Int` addition overflowed");
8807 assert_eq!(
8808 error.rule.as_deref(),
8809 Some("Integer overflow is a broken invariant, not a wrapped result.")
8810 );
8811 assert_every_child_settled(&events);
8812 assert_eq!(children_of(&events), vec![(1, false, true)]);
8813 }
8814
8815 #[test]
8825 fn a_broken_invariant_in_a_child_leaves_the_scope_and_stops_its_sibling() {
8826 let source = format!(
8827 "use console.println
8828
8829export fn main() -> Result<Unit, Error> {{
8830 scope tasks {{
8831 let spinning = tasks.spawn {{
8832{SPINNING_TASK}
8833 }}
8834 let broken = tasks.spawn {{
8835 let largest = 9223372036854775807
8836 largest + 1
8837 }}
8838 await broken
8839 }}
8840 Ok(())
8841}}
8842"
8843 );
8844 let (run, events, _) = run_traced(&source);
8845 assert_eq!(run.output, "");
8846 assert_eq!(run.error().message, "`Int` addition overflowed");
8847 assert_every_child_settled(&events);
8848 assert_eq!(
8849 children_of(&events),
8850 vec![(1, false, true), (2, true, false)]
8851 );
8852 }
8853
8854 #[test]
8864 fn spawning_past_a_concurrency_limit_is_refused_before_a_thread_exists() {
8865 let source = r#"
8866export fn main() -> Result<Unit, Error> {
8867 scope tasks {
8868 var i = 0
8869 while i < 64 {
8870 let ignored = tasks.spawn { 1 }
8871 i += 1
8872 }
8873 }
8874 Ok(())
8875}
8876"#;
8877 let (run, events, _) = run_traced_under(
8878 source,
8879 Limits {
8880 max_tasks: Some(8),
8881 ..Limits::default()
8882 },
8883 );
8884 let error = run.error();
8885 assert!(
8886 error
8887 .message
8888 .contains("concurrency limit of 8 task(s) exceeded"),
8889 "{}",
8890 error.message
8891 );
8892 assert!(error.span.is_some(), "the stop points at the `spawn`");
8893 assert!(error.rule.is_some());
8894 assert_eq!(
8895 events
8896 .iter()
8897 .filter(|event| matches!(event, TraceEvent::TaskSpawned { .. }))
8898 .count(),
8899 8,
8900 "the refused `spawn` was never given a thread, so it was never traced"
8901 );
8902 }
8903
8904 #[test]
8911 fn a_task_whose_end_was_observed_gives_its_place_back() {
8912 let source = r#"
8913use console.println
8914
8915export fn main() -> Result<Unit, Error> {
8916 scope finishing {
8917 let one = finishing.spawn { 1 }
8918 let value = await one
8919 }
8920 scope failing {
8921 let two = failing.spawn { Err(Error("this task produced an error")) }
8922 let outcome = await two
8923 }
8924 scope cancelling {
8925 let three = cancelling.spawn { 3 }
8926 three.cancel()
8927 }
8928 scope last {
8929 let four = last.spawn { 4 }
8930 println("{await four}")?
8931 }
8932 Ok(())
8933}
8934"#;
8935 let (run, _, _) = run_traced_under(
8936 source,
8937 Limits {
8938 max_tasks: Some(1),
8939 ..Limits::default()
8940 },
8941 );
8942 assert_eq!(run.output, "4\n");
8943 run.value();
8944 }
8945
8946 #[test]
8951 fn the_concurrency_limit_is_the_run_s_and_not_one_scope_s() {
8952 let source = r#"
8953export fn main() -> Result<Unit, Error> {
8954 scope outer {
8955 let one = outer.spawn { 1 }
8956 scope inner {
8957 let two = inner.spawn { 2 }
8958 let three = inner.spawn { 3 }
8959 let ignored = await two + await three
8960 }
8961 let value = await one
8962 }
8963 Ok(())
8964}
8965"#;
8966 let (run, _, _) = run_traced_under(
8967 source,
8968 Limits {
8969 max_tasks: Some(2),
8970 ..Limits::default()
8971 },
8972 );
8973 let error = run.error();
8974 assert!(
8975 error
8976 .message
8977 .contains("concurrency limit of 2 task(s) exceeded"),
8978 "{}",
8979 error.message
8980 );
8981 }
8982
8983 #[test]
8986 fn a_run_within_the_concurrency_limit_is_not_stopped() {
8987 let source = r#"
8988use console.println
8989
8990export fn main() -> Result<Unit, Error> {
8991 var total = 0
8992 var i = 0
8993 while i < 8 {
8994 scope tasks {
8995 let one = tasks.spawn { 1 }
8996 let two = tasks.spawn { 2 }
8997 total += await one + await two
8998 }
8999 i += 1
9000 }
9001 println("{total}")?
9002 Ok(())
9003}
9004"#;
9005 let (run, _, _) = run_traced_under(
9006 source,
9007 Limits {
9008 max_tasks: Some(2),
9009 ..Limits::default()
9010 },
9011 );
9012 assert_eq!(run.output, "24\n");
9013 run.value();
9014 }
9015
9016 #[test]
9022 fn a_run_that_finishes_inside_its_deadline_is_not_stopped() {
9023 let source = r#"
9024use console.println
9025
9026export fn main() -> Result<Unit, Error> {
9027 println("inside the deadline")?
9028 Ok(())
9029}
9030"#;
9031 let (run, _, _) = run_traced_under(
9032 source,
9033 Limits {
9034 deadline: Some(Duration::from_secs(30)),
9035 ..Limits::default()
9036 },
9037 );
9038 assert_eq!(run.output, "inside the deadline\n");
9039 run.value();
9040 }
9041
9042 #[test]
9047 fn a_deadline_that_expires_while_cove_code_runs_stops_it_at_a_safepoint() {
9048 let source = r#"
9049use console.println
9050
9051export fn main() -> Result<Unit, Error> {
9052 var i = 0
9053 while i < 1000000000 {
9054 i += 1
9055 }
9056 println("never printed")?
9057 Ok(())
9058}
9059"#;
9060 let (run, _, _) = run_traced_under(
9061 source,
9062 Limits {
9063 deadline: Some(Duration::from_millis(50)),
9064 ..Limits::default()
9065 },
9066 );
9067 assert_eq!(run.output, "");
9068 let error = run.error();
9069 assert_eq!(
9070 error.message,
9071 "execution stopped: wall-clock deadline of 50ms exceeded"
9072 );
9073 assert!(error.rule.is_some(), "the stop cites the rule it enforces");
9074 assert!(error.span.is_some(), "the stop points at the loop");
9075 }
9076
9077 #[test]
9087 fn a_deadline_that_expires_while_a_host_call_blocks_stops_the_run_when_it_returns() {
9088 let source = r#"
9089use clock.sleep
9090use console.println
9091
9092export fn main() -> Result<Unit, Error> {
9093 sleep(750ms)?
9094 println("never printed")?
9095 Ok(())
9096}
9097"#;
9098 let (run, events, elapsed) = run_traced_under(
9099 source,
9100 Limits {
9101 deadline: Some(Duration::from_millis(250)),
9102 ..Limits::default()
9103 },
9104 );
9105 assert_eq!(run.output, "");
9106 assert_eq!(
9107 run.error().message,
9108 "execution stopped: wall-clock deadline of 250ms exceeded"
9109 );
9110 let waits: Vec<Duration> = events
9111 .iter()
9112 .filter_map(|event| match event {
9113 TraceEvent::HostCall { op, wait, .. } if op == "sleep" => Some(*wait),
9114 _ => None,
9115 })
9116 .collect();
9117 assert_eq!(waits.len(), 1, "the sleep was recorded once: {waits:?}");
9118 assert!(
9119 waits[0] >= Duration::from_millis(500),
9120 "the sleep ran to its end rather than being cut short, but waited {:?}",
9121 waits[0]
9122 );
9123 assert!(
9124 elapsed >= Duration::from_millis(500),
9125 "the run outlived its deadline for as long as the call held it, but took {elapsed:?}"
9126 );
9127 }
9128
9129 #[test]
9133 fn a_timeout_answers_ok_when_the_bounded_work_finishes_inside_it() {
9134 let source = r#"
9135use clock.timeout
9136use console.println
9137
9138export fn main() -> Result<Unit, Error> {
9139 let answer = clock.timeout(30s) {
9140 7
9141 }?
9142 println("the bounded work answered {answer}")?
9143 Ok(())
9144}
9145"#;
9146 let (run, events, _) = run_traced(source);
9147 assert_eq!(run.output, "the bounded work answered 7\n");
9148 run.value();
9149 assert!(
9150 events.iter().any(|event| {
9151 matches!(event, TraceEvent::HostCall { op, granted, .. } if op == "timeout" && *granted)
9152 }),
9153 "the bound is a granted host call, and the trace says so: {events:?}"
9154 );
9155 }
9156
9157 #[test]
9162 fn a_timeout_stops_cove_code_that_runs_past_its_bound() {
9163 let source = r#"
9164use clock.timeout
9165use console.println
9166
9167export fn main() -> Result<Unit, Error> {
9168 let outcome = clock.timeout(50ms) {
9169 var i = 0
9170 while i < 1000000000 {
9171 i += 1
9172 }
9173 i
9174 }
9175 println("{outcome}")?
9176 Ok(())
9177}
9178"#;
9179 let (run, events, _) = run_traced(source);
9180 assert_eq!(run.output, "Err(clock: timed out after 50ms)\n");
9181 run.value();
9182 assert!(
9183 events
9184 .iter()
9185 .any(|event| matches!(event, TraceEvent::HostCall { op, .. } if op == "timeout")),
9186 "the bound is a granted host call, and the trace says so: {events:?}"
9187 );
9188 }
9189
9190 fn nested_reentry(levels: usize) -> Run {
9196 let source = format!(
9197 r#"
9198use clock.timeout
9199use console.println
9200
9201fn nest(n: Int) -> Int {{
9202 if n <= 0 {{
9203 0
9204 }} else {{
9205 let inner = clock.timeout(60s) {{ nest(n - 1) }}
9206 match inner {{
9207 Ok(deeper) => deeper + 1,
9208 Err(stopped) => 0 - 1,
9209 }}
9210 }}
9211}}
9212
9213export fn main() -> Result<Unit, Error> {{
9214 println("{{nest({levels})}}")?
9215 Ok(())
9216}}
9217"#
9218 );
9219 run_traced(&source).0
9220 }
9221
9222 #[test]
9226 fn a_callback_may_call_a_host_that_runs_a_callback_of_its_own() {
9227 let run = nested_reentry(MAX_REENTRY_DEPTH);
9228 assert_eq!(run.output, format!("{MAX_REENTRY_DEPTH}\n"));
9229 run.value();
9230 }
9231
9232 #[test]
9238 fn nested_reentry_past_the_bound_stops_the_run_rather_than_the_process() {
9239 let error = nested_reentry(MAX_REENTRY_DEPTH + 1).error();
9240 assert_eq!(
9241 error.message,
9242 format!(
9243 "reentry depth limit of {MAX_REENTRY_DEPTH} reached while a host ran a Cove callback"
9244 )
9245 );
9246 assert!(error.span.is_some(), "the stop points at the host call");
9247 assert!(error.rule.is_some());
9248 }
9249
9250 #[test]
9262 fn the_depth_limit_stops_a_spawned_task_the_way_it_stops_the_entry() {
9263 let recursing = r#"
9264fn nest(n: Int) -> Int {
9265 if n <= 0 {
9266 0
9267 } else {
9268 nest(n - 1) + 1
9269 }
9270}
9271"#;
9272 let depth = MAX_CALL_DEPTH + 16;
9273 let stop = |source: String| {
9274 crate::on_cove_stack(move || run_entry_of(&source, "main", &[]).error().message)
9275 .expect("a thread to run Cove on")
9276 };
9277
9278 let on_the_entry = format!(
9279 r#"{recursing}
9280export fn main() -> Result<Unit, Error> {{
9281 let answer = nest({depth})
9282 Ok(())
9283}}
9284"#
9285 );
9286 let in_a_task = format!(
9287 r#"{recursing}
9288export fn main() -> Result<Unit, Error> {{
9289 scope tasks {{
9290 let task = tasks.spawn {{ nest({depth}) }}
9291 let answer = task.await()
9292 Ok(())
9293 }}
9294}}
9295"#
9296 );
9297
9298 let expected = format!("call depth limit of {MAX_CALL_DEPTH} reached while calling `nest`");
9299 assert_eq!(stop(on_the_entry), expected);
9300 assert_eq!(stop(in_a_task), expected);
9301 }
9302
9303 #[test]
9308 fn work_a_callback_does_is_charged_to_the_budget_that_made_the_host_call() {
9309 let source = r#"
9310use clock.timeout
9311
9312export fn main() -> Result<Unit, Error> {
9313 let outcome = clock.timeout(60s) {
9314 var i = 0
9315 while i < 1000000000 {
9316 i += 1
9317 }
9318 i
9319 }
9320 Ok(())
9321}
9322"#;
9323 let (run, _, _) = run_traced_under(
9324 source,
9325 Limits {
9326 fuel: Some(10_000),
9327 ..Limits::default()
9328 },
9329 );
9330 assert_eq!(
9331 run.error().message,
9332 "execution stopped: fuel budget of 10000 exhausted"
9333 );
9334 }
9335
9336 #[test]
9342 fn every_round_of_a_repeated_callback_is_charged_to_the_run() {
9343 let source = r#"
9344use clock.every
9345use console.println
9346
9347export fn main() -> Result<Unit, Error> {
9348 let outcome = clock.every(1ms, async fn() {
9349 println("round")?
9350 Ok(())
9351 })
9352 Ok(())
9353}
9354"#;
9355 let (run, _, _) = run_traced_under(
9356 source,
9357 Limits {
9358 fuel: Some(500),
9359 ..Limits::default()
9360 },
9361 );
9362 let rounds = run.output.lines().count();
9363 assert_eq!(
9364 run.error().message,
9365 "execution stopped: fuel budget of 500 exhausted"
9366 );
9367 assert!(
9368 rounds > 1,
9369 "the timer ran more than one round before the budget ran out, but ran {rounds}"
9370 );
9371 }
9372
9373 #[test]
9378 fn a_callback_s_own_frame_counts_against_the_run_s_call_depth() {
9379 let recursing = r#"
9380fn nest(n: Int) -> Int {
9381 if n <= 0 {
9382 0
9383 } else {
9384 nest(n - 1) + 1
9385 }
9386}
9387"#;
9388 let limits = || Limits {
9389 max_call_depth: Some(6),
9390 ..Limits::default()
9391 };
9392 let direct = format!(
9393 r#"{recursing}
9394export fn main() -> Result<Unit, Error> {{
9395 let answer = nest(4)
9396 Ok(())
9397}}
9398"#
9399 );
9400 run_traced_under(&direct, limits()).0.value();
9401
9402 let through_a_callback = format!(
9403 r#"
9404use clock.timeout
9405{recursing}
9406export fn main() -> Result<Unit, Error> {{
9407 let answer = clock.timeout(60s) {{ nest(4) }}
9408 Ok(())
9409}}
9410"#
9411 );
9412 assert_eq!(
9413 run_traced_under(&through_a_callback, limits())
9414 .0
9415 .error()
9416 .message,
9417 "execution stopped: call-depth limit of 6 exceeded"
9418 );
9419 }
9420
9421 #[test]
9426 fn a_host_call_a_callback_makes_is_charged_against_the_run_again() {
9427 let source = r#"
9428use clock.timeout
9429
9430export fn main() -> Result<Unit, Error> {
9431 let outcome = clock.timeout(60s) { clock.now() }
9432 Ok(())
9433}
9434"#;
9435 let limited = |max_host_calls| Limits {
9436 max_host_calls: Some(max_host_calls),
9437 ..Limits::default()
9438 };
9439 assert_eq!(
9440 run_traced_under(source, limited(1)).0.error().message,
9441 "execution stopped: host-call limit of 1 exceeded"
9442 );
9443 run_traced_under(source, limited(2)).0.value();
9444 }
9445
9446 #[test]
9452 fn a_deadline_that_passes_while_a_callback_runs_stops_the_callback() {
9453 let source = r#"
9454use clock.timeout
9455
9456export fn main() -> Result<Unit, Error> {
9457 let outcome = clock.timeout(60s) {
9458 var i = 0
9459 while i < 1000000000 {
9460 i += 1
9461 }
9462 i
9463 }
9464 Ok(())
9465}
9466"#;
9467 let (run, _, elapsed) = run_traced_under(
9468 source,
9469 Limits {
9470 deadline: Some(Duration::from_millis(150)),
9471 ..Limits::default()
9472 },
9473 );
9474 assert_eq!(
9475 run.error().message,
9476 "execution stopped: wall-clock deadline of 150ms exceeded"
9477 );
9478 assert!(
9479 elapsed < Duration::from_secs(60),
9480 "the callback stopped at its own safepoint rather than running to the host's bound, but took {elapsed:?}"
9481 );
9482 }
9483
9484 #[test]
9488 fn cancelling_a_task_stops_the_callback_it_is_running() {
9489 let source = r#"
9490use clock.timeout
9491use console.println
9492
9493export fn main() -> Result<Unit, Error> {
9494 scope tasks {
9495 let bounded = tasks.spawn {
9496 clock.timeout(60s) {
9497 var i = 0
9498 while i < 1000000000 {
9499 i += 1
9500 }
9501 i
9502 }
9503 }
9504 println("the parent is not waiting")?
9505 bounded.cancel()
9506 }
9507 println("the scope was left")?
9508 Ok(())
9509}
9510"#;
9511 let (run, _, elapsed) = run_traced(source);
9512 assert_eq!(
9513 run.output,
9514 "the parent is not waiting\nthe scope was left\n"
9515 );
9516 run.value();
9517 assert!(
9518 elapsed < Duration::from_secs(60),
9519 "the cancelled callback stopped rather than running to the host's bound, but took {elapsed:?}"
9520 );
9521 }
9522
9523 #[test]
9530 fn a_host_call_made_inside_a_callback_is_traced_beside_the_one_that_ran_it() {
9531 let source = r#"
9532use clock.timeout
9533
9534export fn main() -> Result<Unit, Error> {
9535 let outcome = clock.timeout(60s) {
9536 clock.sleep(20ms)
9537 clock.now()
9538 }
9539 Ok(())
9540}
9541"#;
9542 let (run, events, _) = run_traced(source);
9543 run.value();
9544 let calls: Vec<(&str, Duration)> = events
9545 .iter()
9546 .filter_map(|event| match event {
9547 TraceEvent::HostCall { op, wait, .. } => Some((op.as_str(), *wait)),
9548 _ => None,
9549 })
9550 .collect();
9551 assert_eq!(
9552 calls.iter().map(|(op, _)| *op).collect::<Vec<_>>(),
9553 vec!["sleep", "now", "timeout"],
9554 "the calls a callback made are recorded before the call that ran it: {events:?}"
9555 );
9556 let sleep = calls[0].1;
9557 let timeout = calls[2].1;
9558 assert!(
9559 timeout >= sleep,
9560 "the outer call's wait contains the inner call's, but {timeout:?} < {sleep:?}"
9561 );
9562 }
9563
9564 const METRICS: &str = r#"
9569use console.println
9570
9571struct Metrics {
9572 requests: Int
9573 failures: Int
9574}
9575
9576impl Metrics {
9577 /// Records one completed request.
9578 fn record(var self, failed: Bool) {
9579 self.requests += 1
9580 if failed {
9581 self.failures += 1
9582 }
9583 }
9584}
9585"#;
9586
9587 fn run_shared_body(body: &str) -> Run {
9589 run_entry_of(
9590 &format!(
9591 "{METRICS}\nexport fn main() -> Result<Unit, Error> {{\n{body}\n Ok(())\n}}\n"
9592 ),
9593 "main",
9594 &[],
9595 )
9596 }
9597
9598 #[test]
9599 fn a_lock_gives_a_var_alias_to_the_wrapped_value() {
9600 let run = run_shared_body(
9601 " let metrics = Shared(Metrics(requests: 0, failures: 0))\n metrics.lock(fn(var value) {\n value.record(true)\n value.record(false)\n })\n metrics.lock(fn(value) {\n println(\"{value.requests} {value.failures}\")\n })?",
9602 );
9603 assert_eq!(run.output, "2 1\n");
9604 }
9605
9606 #[test]
9607 fn a_lock_produces_the_value_its_closure_produces() {
9608 let run = run_shared_body(
9609 " let metrics = Shared(Metrics(requests: 4, failures: 1))\n let doubled = metrics.lock(fn(var value) {\n value.requests = value.requests * 2\n value.requests\n })\n println(\"{doubled}\")?",
9610 );
9611 assert_eq!(run.output, "8\n");
9612 }
9613
9614 #[test]
9622 fn a_lock_closure_without_var_receives_a_read_only_copy() {
9623 let run = run_shared_body(
9624 " let metrics = Shared(Metrics(requests: 1, failures: 0))\n metrics.lock(fn(value) {\n println(\"{value.requests}\")\n })?",
9625 );
9626 assert_eq!(run.output, "1\n");
9627 }
9628
9629 #[test]
9633 fn tasks_share_one_value_through_a_shared() {
9634 let source = format!(
9635 "{METRICS}
9636export fn main() -> Result<Unit, Error> {{
9637 let metrics = Shared(Metrics(requests: 0, failures: 0))
9638 scope requests {{
9639 let first = requests.spawn {{
9640 for i in 0..<100 {{
9641 metrics.lock(fn(var value) {{ value.record(false) }})
9642 }}
9643 }}
9644 let second = requests.spawn {{
9645 for i in 0..<100 {{
9646 metrics.lock(fn(var value) {{ value.record(true) }})
9647 }}
9648 }}
9649 await first
9650 await second
9651 }}
9652 metrics.lock(fn(value) {{
9653 println(\"{{value.requests}} {{value.failures}}\")
9654 }})?
9655 Ok(())
9656}}
9657"
9658 );
9659 let run = run_entry_of(&source, "main", &[]);
9660 assert_eq!(run.output, "200 100\n");
9661 }
9662
9663 #[test]
9664 fn a_shared_refuses_a_payload_that_cannot_cross_a_task_boundary() {
9665 let error = run_shared_body(" let counts = Shared(Vector.of(1, 2))").error();
9666 assert_eq!(
9667 error.message,
9668 "`Shared` cannot wrap a `Vector`, which cannot cross a task boundary"
9669 );
9670 assert!(error
9671 .rule
9672 .unwrap()
9673 .contains("A vector cannot cross, even through `let`"));
9674 }
9675
9676 #[test]
9677 fn a_shared_refuses_a_struct_holding_a_vector() {
9678 let source = r#"
9679struct Draft {
9680 guests: Vector<String>
9681}
9682
9683export fn main() -> Result<Unit, Error> {
9684 let draft = Shared(Draft(guests: Vector.of("Alice")))
9685 Ok(())
9686}
9687"#;
9688 let error = run_entry_of(source, "main", &[]).error();
9689 assert_eq!(
9690 error.message,
9691 "`Shared` cannot wrap a `Vector` in `guests`, which cannot cross a task boundary"
9692 );
9693 }
9694
9695 #[test]
9698 fn a_reentrant_lock_is_reported_rather_than_deadlocking() {
9699 let error = run_shared_body(
9700 " let metrics = Shared(Metrics(requests: 0, failures: 0))\n metrics.lock(fn(var value) {\n metrics.lock(fn(var inner) {\n inner.record(false)\n })\n })",
9701 )
9702 .error();
9703 assert_eq!(
9704 error.message,
9705 "this task already holds this `Shared`, so `lock` would wait for itself"
9706 );
9707 assert!(error.help.unwrap().contains("one `lock`"));
9708 }
9709
9710 #[test]
9713 fn a_lock_inside_a_lock_on_another_shared_is_allowed() {
9714 let run = run_shared_body(
9715 " let left = Shared(Metrics(requests: 1, failures: 0))\n let right = Shared(Metrics(requests: 2, failures: 0))\n let total = left.lock(fn(value) {\n right.lock(fn(other) {\n value.requests + other.requests\n })\n })\n println(\"{total}\")?",
9716 );
9717 assert_eq!(run.output, "3\n");
9718 }
9719
9720 #[test]
9725 fn a_lock_refuses_a_closure_that_stores_a_handle_to_its_own_cell() {
9726 let source = r#"
9727struct Node {
9728 cell: Option<Shared<Node>>
9729}
9730
9731export fn main() -> Result<Unit, Error> {
9732 let n = Shared(Node(cell: None))
9733 n.lock(fn(var value) {
9734 value = Node(cell: Some(n))
9735 })
9736 Ok(())
9737}
9738"#;
9739 let error = run_entry_of(source, "main", &[]).error();
9740 assert_eq!(
9741 error.message,
9742 "this `lock` would leave the cell holding a handle to itself, and no collector reclaims that cycle"
9743 );
9744 assert!(error
9745 .rule
9746 .unwrap()
9747 .contains("`Shared` ownership must stay acyclic"));
9748 }
9749
9750 #[test]
9754 fn a_lock_allows_a_closure_that_stores_a_handle_to_a_different_cell() {
9755 let source = r#"
9756struct Node {
9757 cell: Option<Shared<Node>>
9758}
9759
9760export fn main() -> Result<Unit, Error> {
9761 let a = Shared(Node(cell: None))
9762 let b = Shared(Node(cell: None))
9763 b.lock(fn(var value) {
9764 value = Node(cell: Some(a))
9765 })
9766 Ok(())
9767}
9768"#;
9769 let run = run_entry_of(source, "main", &[]);
9770 assert!(run.value.is_ok());
9771 }
9772
9773 #[test]
9774 fn a_shared_has_no_operation_but_lock() {
9775 let error =
9776 run_shared_body(" let metrics = Shared(Metrics(requests: 0, failures: 0))\n let value = metrics.get()")
9777 .error();
9778 assert_eq!(error.message, "`Shared` has no method `get`");
9779 assert!(error
9780 .rule
9781 .unwrap()
9782 .contains("there is no `get` and no `set`"));
9783 }
9784
9785 fn examples_root() -> PathBuf {
9788 Path::new(env!("CARGO_MANIFEST_DIR")).join("../../examples")
9789 }
9790
9791 fn examples_program() -> (Arc<SourceMap>, Arc<Program>) {
9793 let root = examples_root();
9794 let mut sources = SourceMap::new();
9795 let package = cove_sema::package::load(&root, &mut sources).expect("examples load");
9796 let program = cove_sema::resolve::resolve(&package).expect("examples resolve");
9797 (Arc::new(sources), Arc::new(program))
9798 }
9799
9800 #[test]
9801 fn runs_the_hello_example() {
9802 let (sources, program) = examples_program();
9803 let default = run_in(
9804 &program,
9805 &sources,
9806 "hello",
9807 "main",
9808 &[],
9809 &["console"],
9810 BTreeMap::new(),
9811 );
9812 assert_eq!(default.output, "Hello, world!\n");
9813 assert_eq!(default.value().to_string(), "Ok(())");
9814
9815 let named = run_in(
9816 &program,
9817 &sources,
9818 "hello",
9819 "main",
9820 &["Cove"],
9821 &["console"],
9822 BTreeMap::new(),
9823 );
9824 assert_eq!(named.output, "Hello, Cove!\n");
9825 }
9826
9827 #[test]
9828 fn runs_the_values_example() {
9829 let (sources, program) = examples_program();
9830 let run = run_in(
9831 &program,
9832 &sources,
9833 "values",
9834 "main",
9835 &[],
9836 &["console"],
9837 BTreeMap::new(),
9838 );
9839 assert_eq!(run.output, "Pending\nConfirmed\n2\n2\n2\n1\n");
9840 assert_eq!(run.value().to_string(), "Ok(())");
9841 }
9842
9843 #[test]
9844 fn runs_the_config_example() {
9845 let (sources, program) = examples_program();
9846
9847 let loaded = run_in(
9848 &program,
9849 &sources,
9850 "config",
9851 "loadConfig",
9852 &[],
9853 &["env"],
9854 BTreeMap::from([
9855 ("PORT".to_string(), "9000".to_string()),
9856 ("LOG_LEVEL".to_string(), "debug".to_string()),
9857 ]),
9858 );
9859 assert_eq!(
9860 loaded.value().to_string(),
9861 "Ok(Config(port: 9000, logLevel: Debug))"
9862 );
9863
9864 let defaulted = run_in(
9865 &program,
9866 &sources,
9867 "config",
9868 "loadConfig",
9869 &[],
9870 &["env"],
9871 BTreeMap::new(),
9872 );
9873 assert_eq!(
9874 defaulted.value().to_string(),
9875 "Ok(Config(port: 8080, logLevel: Info))"
9876 );
9877
9878 let rejected = run_in(
9879 &program,
9880 &sources,
9881 "config",
9882 "loadConfig",
9883 &[],
9884 &["env"],
9885 BTreeMap::from([("LOG_LEVEL".to_string(), "verbose".to_string())]),
9886 );
9887 assert_eq!(
9888 rejected.value().to_string(),
9889 "Err(InvalidLogLevel(verbose))"
9890 );
9891
9892 let invalid_port = run_in(
9893 &program,
9894 &sources,
9895 "config",
9896 "loadConfig",
9897 &[],
9898 &["env"],
9899 BTreeMap::from([("PORT".to_string(), "eighty".to_string())]),
9900 );
9901 assert_eq!(invalid_port.value().to_string(), "Err(InvalidPort(eighty))");
9902 }
9903
9904 #[test]
9905 fn runs_the_restricted_example() {
9906 let (sources, program) = examples_program();
9907
9908 let buffer = Buffer::default();
9909 let mut hosts = HostRegistry::new(Grants::new(["documents", "console"]));
9910 hosts.register(Box::new(Console::new(buffer.clone(), Buffer::default())));
9911 hosts.register(Box::new(Documents::rooted(
9912 examples_root().join("documents"),
9913 )));
9914 let runtime = Runtime::new(program, sources, Arc::new(hosts));
9915 let value = Interpreter::new(&runtime)
9916 .run_entry("restricted", "main", Vec::new())
9917 .expect("the program ran without a runtime error");
9918
9919 assert_eq!(buffer.text(), "5 words\n");
9920 assert_eq!(value.to_string(), "Ok(())");
9921 }
9922
9923 #[derive(Clone, Default)]
9931 struct Recorder(Arc<Mutex<Vec<TraceEvent>>>);
9932
9933 impl TraceSink for Recorder {
9934 fn record(&self, event: TraceEvent) {
9935 self.0
9936 .lock()
9937 .expect("no test panics while tracing")
9938 .push(event);
9939 }
9940 }
9941
9942 impl Recorder {
9943 fn events(&self) -> Vec<TraceEvent> {
9944 self.0.lock().expect("no test panics while tracing").clone()
9945 }
9946 }
9947
9948 struct HeapRun {
9950 value: Result<Value, RuntimeError>,
9951 output: String,
9952 events: Vec<TraceEvent>,
9953 stats: HeapStats,
9954 }
9955
9956 impl HeapRun {
9957 fn collections(&self) -> Vec<(u64, u64, u64)> {
9959 self.events
9960 .iter()
9961 .filter_map(|event| match event {
9962 TraceEvent::HeapCollected {
9963 task,
9964 allocated,
9965 freed,
9966 ..
9967 } => Some((*task, *allocated, *freed)),
9968 _ => None,
9969 })
9970 .collect()
9971 }
9972
9973 fn summary(&self) -> HeapStats {
9975 self.events
9976 .iter()
9977 .rev()
9978 .find_map(|event| match event {
9979 TraceEvent::HeapSummary {
9984 collections,
9985 object_count,
9986 allocated_bytes,
9987 live_bytes,
9988 peak_bytes,
9989 pause,
9990 ..
9991 } => Some(HeapStats {
9992 allocated_objects: object_count.expect("the interpreter counts objects"),
9993 allocated_bytes: allocated_bytes.expect("the interpreter counts bytes"),
9994 collections: *collections,
9995 freed_objects: 0,
9996 live_bytes: live_bytes.expect("the interpreter counts bytes"),
9997 live_objects: 0,
9998 peak_bytes: peak_bytes.expect("the interpreter counts bytes"),
9999 pause: pause.expect("the interpreter times its collections"),
10000 }),
10001 _ => None,
10002 })
10003 .expect("a run ends with a heap summary")
10004 }
10005 }
10006
10007 fn run_watching_the_heap(source: &str, limits: crate::budget::Limits) -> HeapRun {
10009 let (sources, program) = program_of(source);
10010 let buffer = Buffer::default();
10011 let mut hosts = HostRegistry::new(Grants::new(["console"]));
10012 hosts.register(Box::new(Console::new(buffer.clone(), Buffer::default())));
10013 hosts.set_budget(crate::budget::Budget::new(limits));
10014 let recorder = Recorder::default();
10015 let runtime =
10016 Runtime::new(program, sources, Arc::new(hosts)).with_trace(Arc::new(recorder.clone()));
10017 let mut interpreter = Interpreter::new(&runtime);
10018 let value = interpreter.run_entry("test", "main", Vec::new());
10019 let stats = interpreter.heap_stats();
10020 HeapRun {
10021 value,
10022 output: buffer.text(),
10023 events: recorder.events(),
10024 stats,
10025 }
10026 }
10027
10028 fn run_collecting(body: &str) -> HeapRun {
10030 let source = format!(
10031 "use console.println\n\nexport fn main() -> Result<Unit, Error> {{\n{body}\n Ok(())\n}}\n"
10032 );
10033 run_watching_the_heap(&source, crate::budget::Limits::default())
10034 }
10035
10036 const CHURN: usize = 200;
10038
10039 fn churn(count: usize) -> String {
10041 format!(
10042 " var i = 0\n while i < {count} {{\n var v = Vector.of()\n v.push(v)\n i += 1\n }}\n"
10043 )
10044 }
10045
10046 #[test]
10050 fn a_cycle_through_a_vector_element_is_reclaimed() {
10051 let run = run_collecting(&churn(CHURN));
10052 run.value.as_ref().expect("the program ran");
10053 assert!(
10054 run.summary().allocated_objects >= CHURN as u64,
10055 "{:?}",
10056 run.summary()
10057 );
10058 assert!(
10059 run.collections().iter().any(|(_, _, freed)| *freed > 0),
10060 "nothing was reclaimed: {:?}",
10061 run.collections()
10062 );
10063 assert_eq!(run.stats.live_objects, 0, "{:?}", run.stats);
10064 }
10065
10066 #[test]
10067 fn a_cycle_through_a_struct_field_is_reclaimed() {
10068 let run = run_watching_the_heap(
10069 &format!(
10070 "struct Node(next: Vector<Node>)\n\nexport fn main() -> Result<Unit, Error> {{\n var i = 0\n while i < {CHURN} {{\n var v: Vector<Node> = Vector.of()\n v.push(Node(next: v))\n i += 1\n }}\n Ok(())\n}}\n"
10071 ),
10072 crate::budget::Limits::default(),
10073 );
10074 run.value.as_ref().expect("the program ran");
10075 assert!(
10076 run.collections().iter().any(|(_, _, freed)| *freed > 0),
10077 "{:?}",
10078 run.collections()
10079 );
10080 assert_eq!(run.stats.live_objects, 0, "{:?}", run.stats);
10081 }
10082
10083 #[test]
10086 fn a_cycle_through_a_closure_capture_is_reclaimed() {
10087 let run = run_collecting(&format!(
10088 " var i = 0\n while i < {CHURN} {{\n var v: Vector<fn() -> Int> = Vector.of()\n let f = fn() {{\n v.length()\n }}\n v.push(f)\n i += 1\n }}\n"
10089 ));
10090 run.value.as_ref().expect("the program ran");
10091 assert!(
10092 run.collections().iter().any(|(_, _, freed)| *freed > 0),
10093 "{:?}",
10094 run.collections()
10095 );
10096 assert_eq!(run.stats.live_objects, 0, "{:?}", run.stats);
10097 }
10098
10099 #[test]
10103 fn a_value_the_environment_chain_holds_is_not_collected() {
10104 let run = run_collecting(&format!(
10105 " var kept = Vector.of(1, 2, 3)\n{} println(\"kept {{kept.length()}} {{kept}}\")?\n",
10106 churn(CHURN)
10107 ));
10108 run.value.as_ref().expect("the program ran");
10109 assert_eq!(run.output, "kept 3 [1, 2, 3]\n");
10110 assert!(run.collections().iter().any(|(_, _, freed)| *freed > 0));
10111 }
10112
10113 #[test]
10116 fn a_value_whose_binding_has_gone_out_of_scope_is_collected() {
10117 let run = run_collecting(&format!(
10118 " {{\n var doomed = Vector.of()\n doomed.push(doomed)\n }}\n{}",
10119 churn(CHURN)
10120 ));
10121 run.value.as_ref().expect("the program ran");
10122 assert_eq!(
10123 run.stats.live_objects, 0,
10124 "the block's vector outlived its block: {:?}",
10125 run.stats
10126 );
10127 }
10128
10129 #[test]
10132 fn a_task_collects_its_own_heap() {
10133 let run = run_watching_the_heap(
10134 &format!(
10135 "fn work() -> Int {{\n{} i\n}}\n\nexport fn main() -> Result<Unit, Error> {{\n scope tasks {{\n let one = tasks.spawn {{ work() }}\n let done = one.await()\n done\n }}\n Ok(())\n}}\n",
10136 churn(CHURN)
10137 ),
10138 crate::budget::Limits::default(),
10139 );
10140 run.value.as_ref().expect("the program ran");
10141 assert!(
10142 run.collections()
10143 .iter()
10144 .any(|(task, _, freed)| *task != ENTRY_TASK && *freed > 0),
10145 "no collection ran inside the task: {:?}",
10146 run.collections()
10147 );
10148 }
10149
10150 #[test]
10163 fn two_tasks_collect_at_the_same_time_without_disturbing_each_other() {
10164 let run = run_watching_the_heap(
10165 &format!(
10166 "use console.println\n\nfn work(gate: Shared<Int>, mark: Int) -> Int {{\n var kept = Vector.of(mark, mark, mark)\n gate.lock(fn(var arrived) {{\n arrived += 1\n }})\n var both = 0\n var spins = 0\n while both < 2 && spins < 100000000 {{\n both = gate.lock(fn(arrived) {{\n arrived\n }})\n spins += 1\n }}\n{} kept.length() * 1000 + both * 100 + mark\n}}\n\nexport fn main() -> Result<Unit, Error> {{\n let gate = Shared(0)\n scope tasks {{\n let one = tasks.spawn {{ work(gate, 1) }}\n let two = tasks.spawn {{ work(gate, 2) }}\n println(\"{{one.await()}} {{two.await()}}\")?\n }}\n Ok(())\n}}\n",
10167 churn(CHURN)
10168 ),
10169 crate::budget::Limits::default(),
10170 );
10171 run.value.as_ref().expect("the program ran");
10172 assert_eq!(run.output, "3201 3202\n");
10177
10178 let collected: BTreeSet<u64> = run
10179 .collections()
10180 .into_iter()
10181 .filter(|(_, _, freed)| *freed > 0)
10182 .map(|(task, _, _)| task)
10183 .collect();
10184 assert!(
10185 collected.contains(&1) && collected.contains(&2),
10186 "both tasks should have collected: {collected:?}"
10187 );
10188 }
10189
10190 #[test]
10196 fn a_task_that_ends_still_naming_a_cycle_leaves_nothing_behind() {
10197 let run = run_watching_the_heap(
10198 &format!(
10199 "struct Node(next: Vector<Node>)\n\nfn holds() -> Int {{\n var kept: Vector<Node> = Vector.of()\n kept.push(Node(next: kept))\n{} kept.length()\n}}\n\nexport fn main() -> Result<Unit, Error> {{\n scope tasks {{\n let one = tasks.spawn {{ holds() }}\n let done = one.await()\n done\n }}\n Ok(())\n}}\n",
10200 churn(CHURN)
10201 ),
10202 crate::budget::Limits::default(),
10203 );
10204 run.value.as_ref().expect("the program ran");
10205 let summary = run.summary();
10206 let freed: u64 = run.collections().iter().map(|(_, _, freed)| freed).sum();
10209 assert_eq!(
10210 freed, summary.allocated_objects,
10211 "a cycle outlived the task that built it: {summary:?}"
10212 );
10213 }
10214
10215 #[test]
10219 fn a_value_transferred_into_a_task_leaves_the_original_to_the_sender() {
10220 let run = run_watching_the_heap(
10221 &format!(
10222 "use console.println\n\nfn sum(items: Array<Int>) -> Int {{\n var total = 0\n for item in items {{\n total += item\n }}\n total\n}}\n\nexport fn main() -> Result<Unit, Error> {{\n let crossed = {{\n var building = Vector.of(1, 2, 3)\n building.toArray()\n }}\n scope tasks {{\n let one = tasks.spawn {{ sum(crossed) }}\n println(\"{{one.await()}}\")?\n }}\n{} Ok(())\n}}\n",
10223 churn(CHURN)
10224 ),
10225 crate::budget::Limits::default(),
10226 );
10227 run.value.as_ref().expect("the program ran");
10228 assert_eq!(run.output, "6\n");
10229 assert_eq!(run.stats.live_objects, 0, "{:?}", run.stats);
10233 }
10234
10235 #[test]
10239 fn a_collection_inside_a_lock_neither_waits_nor_loses_the_cell_s_contents() {
10240 let run = run_watching_the_heap(
10241 &format!(
10242 "use console.println\n\nexport fn main() -> Result<Unit, Error> {{\n let total = Shared(0)\n scope tasks {{\n let one = tasks.spawn {{ bump(total) }}\n let two = tasks.spawn {{ bump(total) }}\n let first = one.await()\n let second = two.await()\n first + second\n }}\n total.lock(fn(value) {{\n println(\"total {{value}}\")\n }})?\n Ok(())\n}}\n\nfn bump(total: Shared<Int>) -> Int {{\n total.lock(fn(var value) {{\n{} value += 1\n value\n }})\n}}\n",
10243 churn(CHURN)
10244 ),
10245 crate::budget::Limits::default(),
10246 );
10247 run.value.as_ref().expect("the program ran");
10248 assert_eq!(run.output, "total 2\n");
10249 assert!(
10252 run.collections()
10253 .iter()
10254 .any(|(task, _, freed)| *task != ENTRY_TASK && *freed > 0),
10255 "{:?}",
10256 run.collections()
10257 );
10258 }
10259
10260 #[test]
10263 fn the_trace_carries_allocation_the_live_heap_collections_and_pause() {
10264 let run = run_collecting(&format!(" var kept = Vector.of(1)\n{}", churn(CHURN)));
10265 run.value.as_ref().expect("the program ran");
10266
10267 let collections = run.collections();
10268 assert!(!collections.is_empty(), "no collection was recorded");
10269 for (_, allocated, _) in &collections {
10270 assert!(*allocated > 0, "a collection recorded no allocation");
10271 }
10272
10273 let summary = run.summary();
10274 assert_eq!(summary.allocated_objects, CHURN as u64 + 1);
10275 assert!(summary.allocated_bytes > 0);
10276 assert_eq!(summary.collections, collections.len() as u64);
10277 assert_eq!(summary.live_bytes, 0);
10282 assert!(summary.peak_bytes > 0, "the kept vector was live");
10283 let live_while_running: Vec<u64> = run
10284 .events
10285 .iter()
10286 .filter_map(|event| match event {
10287 TraceEvent::HeapCollected { live_bytes, .. } => Some(*live_bytes),
10288 _ => None,
10289 })
10290 .collect();
10291 assert!(
10292 live_while_running.iter().any(|bytes| *bytes > 0),
10293 "no collection saw the kept vector: {live_while_running:?}"
10294 );
10295 assert!(
10296 summary.pause > Duration::ZERO,
10297 "a collection took no time at all"
10298 );
10299 }
10300
10301 #[test]
10304 fn a_program_that_allocates_nothing_is_never_collected() {
10305 let run = run_collecting(" println(\"{1 + 1}\")?\n");
10306 run.value.as_ref().expect("the program ran");
10307 assert_eq!(run.output, "2\n");
10308 assert_eq!(run.summary().collections, 0);
10309 assert_eq!(run.summary().allocated_objects, 0);
10310 assert!(run.collections().is_empty());
10311 }
10312}