diff --git a/components/dada-brew/src/brew.rs b/components/dada-brew/src/brew.rs index b3aeae7d..88548519 100644 --- a/components/dada-brew/src/brew.rs +++ b/components/dada-brew/src/brew.rs @@ -4,7 +4,7 @@ use dada_ir::{ bir::{self, BirData}, validated::{self, ExprOrigin}, }, - storage_mode::Atomic, + storage::Atomic, }; use salsa::DebugWithDb; @@ -85,7 +85,7 @@ impl Cursor { validated::ExprData::Continue(from_expr) => { self.push_breakpoint_start(brewery, origin); let loop_context = brewery.loop_context(*from_expr); - self.push_breakpoint_end(brewery, None, origin); + self.push_breakpoint_end(brewery, None::, origin); self.terminate_and_goto(brewery, loop_context.continue_block, origin); } @@ -103,22 +103,49 @@ impl Cursor { validated::ExprData::Error => { self.push_breakpoint_start(brewery, origin); - self.push_breakpoint_end(brewery, None, origin); + self.push_breakpoint_end(brewery, None::, origin); self.terminate_and_diverge(brewery, bir::TerminatorData::Error, origin) } - validated::ExprData::Assign(place, value_expr) => { + validated::ExprData::AssignTemporary(place, value_expr) => { + // temporaries are always created with "any" specifier, which ensures + // that we will never have to apply specifier to `value_expr` + assert_eq!(brewery.validated_tables()[*place].specifier, None); + + // we only ever use this for temporaries, user-created values use `AssignFromPlace` + assert!(matches!( + brewery.origin(*place), + validated::LocalVariableOrigin::Temporary(_) + )); + self.push_breakpoint_start(brewery, origin); - let (place, origins) = self.brew_place(brewery, *place); + let place = self.brew_target_variable(brewery, *place, origin); self.brew_expr_and_assign_to(brewery, place, *value_expr); - self.push_breakpoint_ends(brewery, None, origins, origin) + self.push_breakpoint_end(brewery, None::, origin) + } + + validated::ExprData::AssignFromPlace(target_place, source_place) => { + let (target_place, target_origins) = self.brew_target_place(brewery, *target_place); + let (source_place, source_origins) = self.brew_place(brewery, *source_place); + self.push_breakpoint_starts( + brewery, + target_origins.iter().chain(source_origins.iter()).copied(), + origin, + ); + self.push_assignment_from_place(brewery, target_place, source_place, origin); + self.push_breakpoint_ends( + brewery, + None::, + target_origins.into_iter().chain(source_origins), + origin, + ) } validated::ExprData::Declare(vars, subexpr) => { self.push_breakpoint_start(brewery, origin); self.brew_expr_for_side_effects(brewery, *subexpr); self.pop_declared_variables(brewery, vars, origin); - self.push_breakpoint_end(brewery, None, origin); + self.push_breakpoint_end(brewery, None::, origin); } validated::ExprData::Await(_) @@ -134,6 +161,7 @@ impl Cursor { | validated::ExprData::FloatLiteral(_) | validated::ExprData::StringLiteral(_) | validated::ExprData::Call(_, _) + | validated::ExprData::Reserve(_) | validated::ExprData::Share(_) | validated::ExprData::Lease(_) | validated::ExprData::Give(_) @@ -145,6 +173,8 @@ impl Cursor { self.pop_temporary_scope(brewery, temporary_scope); } + /// Compiles expr into a temporary `t` and returns `Some(t)`. + /// Returns `None` if this is dead code. pub(crate) fn brew_expr_to_temporary( &mut self, brewery: &mut Brewery<'_>, @@ -154,7 +184,7 @@ impl Cursor { // Spill into a temporary let temp_place = add_temporary_place(brewery, origin); self.brew_expr_and_assign_to(brewery, temp_place, expr); - Some(temp_place) + Some(brewery.place_from_target_place(temp_place)) } /// Compiles an expression down to the value it produces. @@ -164,7 +194,7 @@ impl Cursor { pub(crate) fn brew_expr_and_assign_to( &mut self, brewery: &mut Brewery<'_>, - target: bir::Place, + target: bir::TargetPlace, expr: validated::Expr, ) { let origin = brewery.origin(expr); @@ -238,10 +268,18 @@ impl Cursor { ); } - validated::ExprData::Share(place) => { + validated::ExprData::Share(operand) => { + if let Some(temp) = self.brew_expr_to_temporary(brewery, *operand) { + self.push_breakpoint_start(brewery, origin); + self.push_assignment(brewery, target, bir::ExprData::Share(temp), origin); + self.push_breakpoint_end(brewery, Some(target), origin); + } + } + + validated::ExprData::Reserve(place) => { let (place, origins) = self.brew_place(brewery, *place); self.push_breakpoint_starts(brewery, origins.iter().copied(), origin); - self.push_assignment(brewery, target, bir::ExprData::GiveShare(place), origin); + self.push_assignment(brewery, target, bir::ExprData::Reserve(place), origin); self.push_breakpoint_ends(brewery, Some(target), origins, origin); } @@ -375,8 +413,7 @@ impl Cursor { } } - validated::ExprData::Assign(_, _) => { - self.push_breakpoint_start(brewery, origin); + validated::ExprData::AssignTemporary(..) | validated::ExprData::AssignFromPlace(..) => { self.brew_expr_for_side_effects(brewery, expr); self.push_assignment(brewery, target, bir::ExprData::Unit, origin); } @@ -429,7 +466,7 @@ impl Cursor { self.push_breakpoint_start(brewery, origin); self.brew_expr_and_assign_to(brewery, target, *subexpr); self.pop_declared_variables(brewery, vars, origin); - self.push_breakpoint_end(brewery, None, origin); + self.push_breakpoint_end(brewery, None::, origin); } validated::ExprData::Error @@ -485,21 +522,52 @@ impl Cursor { } } } + + pub(crate) fn brew_target_place( + &mut self, + brewery: &mut Brewery<'_>, + place: validated::TargetPlace, + ) -> (bir::TargetPlace, Vec) { + let origin = brewery.origin(place); + match brewery.validated_tables()[place] { + validated::TargetPlaceData::LocalVariable(validated_var) => { + let place = self.brew_target_variable(brewery, validated_var, origin); + (place, vec![origin]) + } + validated::TargetPlaceData::Dot(base, field) => { + let (base, mut origins) = self.brew_place(brewery, base); + let place = brewery.add(bir::TargetPlaceData::Dot(base, field), origin); + origins.push(origin); + (place, origins) + } + } + } + pub(crate) fn brew_target_variable( + &mut self, + brewery: &mut Brewery<'_>, + validated_var: validated::LocalVariable, + origin: ExprOrigin, + ) -> bir::TargetPlace { + let bir_var = brewery.variable(validated_var); + brewery.add(bir::TargetPlaceData::LocalVariable(bir_var), origin) + } } fn add_temporary(brewery: &mut Brewery, origin: ExprOrigin) -> bir::LocalVariable { let temporary = brewery.add( bir::LocalVariableData { name: None, + specifier: None, atomic: Atomic::No, }, validated::LocalVariableOrigin::Temporary(origin.into()), ); + tracing::debug!("created temporary: temp{{{:?}}}", u32::from(temporary)); brewery.push_temporary(temporary); temporary } -fn add_temporary_place(brewery: &mut Brewery, origin: ExprOrigin) -> bir::Place { +fn add_temporary_place(brewery: &mut Brewery, origin: ExprOrigin) -> bir::TargetPlace { let temporary_var = add_temporary(brewery, origin); - brewery.add(bir::PlaceData::LocalVariable(temporary_var), origin) + brewery.add(bir::TargetPlaceData::LocalVariable(temporary_var), origin) } diff --git a/components/dada-brew/src/brewery.rs b/components/dada-brew/src/brewery.rs index c288c224..dda14742 100644 --- a/components/dada-brew/src/brewery.rs +++ b/components/dada-brew/src/brewery.rs @@ -14,7 +14,7 @@ use dada_ir::{ pub struct Brewery<'me> { db: &'me dyn crate::Db, code: Code, - breakpoints: &'me [syntax::Expr], + pub(crate) breakpoints: &'me [syntax::Expr], validated_tree_data: &'me validated::TreeData, validated_origins: &'me validated::Origins, tables: &'me mut bir::Tables, @@ -43,7 +43,7 @@ pub struct Brewery<'me> { pub struct LoopContext { pub continue_block: bir::BasicBlock, pub break_block: bir::BasicBlock, - pub loop_value: bir::Place, + pub loop_value: bir::TargetPlace, } impl<'me> Brewery<'me> { @@ -154,6 +154,18 @@ impl<'me> Brewery<'me> { add(self.tables, self.origins, data, origin) } + /// Converts a target-place into a place. + pub fn place_from_target_place(&mut self, place: bir::TargetPlace) -> bir::Place { + match self.tables[place] { + bir::TargetPlaceData::LocalVariable(lv) => { + self.add(bir::PlaceData::LocalVariable(lv), self.origins[place]) + } + bir::TargetPlaceData::Dot(owner_place, name) => { + self.add(bir::PlaceData::Dot(owner_place, name), self.origins[place]) + } + } + } + /// Find the loop context for a given loop expression. /// /// Panics if that loop context has not been pushed. @@ -185,6 +197,7 @@ impl<'me> Brewery<'me> { /// /// See the comments on the `temporaries` field for more information. pub fn push_temporary(&mut self, lv: bir::LocalVariable) { + tracing::debug!("pushing temporary: {:?}", lv); self.temporaries.push(lv); } @@ -217,6 +230,7 @@ fn map_variables( origins, bir::LocalVariableData { name: validated_var_data.name, + specifier: validated_var_data.specifier, atomic: validated_var_data.atomic, }, validated_var_origin, diff --git a/components/dada-brew/src/cursor.rs b/components/dada-brew/src/cursor.rs index 6280c154..0df9a490 100644 --- a/components/dada-brew/src/cursor.rs +++ b/components/dada-brew/src/cursor.rs @@ -140,13 +140,26 @@ impl Cursor { pub(crate) fn push_assignment( &mut self, brewery: &mut Brewery<'_>, - target: bir::Place, + target: bir::TargetPlace, value: bir::ExprData, origin: ExprOrigin, ) { if self.end_block.is_some() { let value = brewery.add(value, origin); - let statement = brewery.add(bir::StatementData::Assign(target, value), origin); + let statement = brewery.add(bir::StatementData::AssignExpr(target, value), origin); + self.push_statement(brewery, statement); + } + } + + pub(crate) fn push_assignment_from_place( + &mut self, + brewery: &mut Brewery<'_>, + target: bir::TargetPlace, + source: bir::Place, + origin: ExprOrigin, + ) { + if self.end_block.is_some() { + let statement = brewery.add(bir::StatementData::AssignPlace(target, source), origin); self.push_statement(brewery, statement); } } @@ -167,6 +180,11 @@ impl Cursor { /// If `origin` is a breakpoint expression, push a "breakpoint-start" /// statement onto the current basic block. pub(crate) fn push_breakpoint_start(&mut self, brewery: &mut Brewery<'_>, origin: ExprOrigin) { + tracing::debug!( + "push_breakpoint_start: origin={:?} breakpoints={:?}", + origin, + brewery.breakpoints + ); if !origin.synthesized && self.end_block.is_some() { if let Some(breakpoint_index) = brewery.expr_is_breakpoint(origin.syntax_expr) { let filename = brewery.code().filename(brewery.db()); @@ -186,10 +204,11 @@ impl Cursor { pub(crate) fn push_breakpoint_ends( &mut self, brewery: &mut Brewery<'_>, - place: Option, + place: Option, origins: impl IntoIterator, origin: ExprOrigin, ) { + let place = place.map(|p| p.into_place(brewery)); for o in origins.into_iter().chain(Some(origin)) { self.push_breakpoint_end_with_distinct_origin(brewery, origin.syntax_expr, place, o); } @@ -200,9 +219,10 @@ impl Cursor { pub(crate) fn push_breakpoint_end( &mut self, brewery: &mut Brewery<'_>, - place: Option, + place: Option, origin: ExprOrigin, ) { + let place = place.map(|p| p.into_place(brewery)); self.push_breakpoint_end_with_distinct_origin(brewery, origin.syntax_expr, place, origin); } @@ -245,3 +265,19 @@ impl Cursor { Some((place, *name)) } } + +pub(crate) trait AnyPlace { + fn into_place(self, brewery: &mut Brewery<'_>) -> bir::Place; +} + +impl AnyPlace for bir::Place { + fn into_place(self, _brewery: &mut Brewery<'_>) -> bir::Place { + self + } +} + +impl AnyPlace for bir::TargetPlace { + fn into_place(self, brewery: &mut Brewery<'_>) -> bir::Place { + brewery.place_from_target_place(self) + } +} diff --git a/components/dada-db/src/lib.rs b/components/dada-db/src/lib.rs index 6cb5874a..ce5c7833 100644 --- a/components/dada-db/src/lib.rs +++ b/components/dada-db/src/lib.rs @@ -66,7 +66,7 @@ impl Db { for item in filename.items(self) { if let Item::Function(function) = item { let function_name = function.name(self); - if name == function_name { + if name == function_name.word(self) { return Some(*function); } } diff --git a/components/dada-execute/src/heap_graph.rs b/components/dada-execute/src/heap_graph.rs index 55dd7c35..d6cf2095 100644 --- a/components/dada-execute/src/heap_graph.rs +++ b/components/dada-execute/src/heap_graph.rs @@ -10,7 +10,7 @@ use dada_ir::{ class::Class, code::bir::LocalVariable, function::Function, span::FileSpan, word::Word, }; -use crate::machine::{op::MachineOp, Machine, Object, Permission, Value}; +use crate::machine::{op::MachineOp, Machine, Object, Permission, Reservation, Value}; mod capture; mod graphviz; @@ -88,6 +88,7 @@ pub(crate) enum ObjectType { Class(Class), Thunk(Function), RustThunk(&'static str), + Reservation, } id!(pub(crate) struct ValueEdge); @@ -119,7 +120,7 @@ id!(pub(crate) struct PermissionNode); #[derive(Debug)] pub(crate) struct PermissionNodeData { - permission: Permission, + source: PermissionNodeSource, label: PermissionNodeLabel, @@ -131,12 +132,19 @@ pub(crate) struct PermissionNodeData { lessor: Option, } +#[derive(Debug)] +pub(crate) enum PermissionNodeSource { + Permission(Permission), + Reservation(Reservation), +} + #[derive(Copy, Clone, Debug)] pub(crate) enum PermissionNodeLabel { My, Our, Leased, OurLeased, + Reserved, Expired, } @@ -147,6 +155,7 @@ impl PermissionNodeLabel { PermissionNodeLabel::Our => "our", PermissionNodeLabel::Leased => "leased", PermissionNodeLabel::OurLeased => "our leased", + PermissionNodeLabel::Reserved => "reserved", PermissionNodeLabel::Expired => "expired", } } diff --git a/components/dada-execute/src/heap_graph/capture.rs b/components/dada-execute/src/heap_graph/capture.rs index 923a53f9..770ef024 100644 --- a/components/dada-execute/src/heap_graph/capture.rs +++ b/components/dada-execute/src/heap_graph/capture.rs @@ -2,17 +2,17 @@ use dada_collections::Map; use dada_id::InternKey; -use dada_ir::storage_mode::{Joint, Leased}; +use dada_ir::storage::{Joint, Leased}; use crate::machine::{ - op::MachineOp, stringify::DefaultStringify, Frame, Object, ObjectData, Permission, - PermissionData, Value, + op::MachineOp, op::MachineOpExt, stringify::DefaultStringify, Frame, Object, ObjectData, + Permission, PermissionData, Reservation, Value, }; use super::{ DataNodeData, HeapGraph, LocalVariableEdge, ObjectNode, ObjectNodeData, ObjectType, - PermissionNode, PermissionNodeData, PermissionNodeLabel, StackFrameNodeData, ValueEdge, - ValueEdgeData, ValueEdgeTarget, + PermissionNode, PermissionNodeData, PermissionNodeLabel, PermissionNodeSource, + StackFrameNodeData, ValueEdge, ValueEdgeData, ValueEdgeTarget, }; pub(super) struct HeapGraphCapture<'me> { @@ -21,6 +21,7 @@ pub(super) struct HeapGraphCapture<'me> { machine: &'me dyn MachineOp, instances: Map, permissions: Map, + reservations: Map, } impl<'me> HeapGraphCapture<'me> { @@ -35,6 +36,7 @@ impl<'me> HeapGraphCapture<'me> { machine, instances: Default::default(), permissions: Default::default(), + reservations: Default::default(), } } @@ -81,48 +83,55 @@ impl<'me> HeapGraphCapture<'me> { } fn value_edge(&mut self, value: Value) -> ValueEdge { - let db = self.db; - let permission = self.permission_node(value.permission); let target = match &self.machine[value.permission] { PermissionData::Expired(_) => ValueEdgeTarget::Expired, - PermissionData::Valid(_) => match &self.machine[value.object] { - ObjectData::Instance(instance) => ValueEdgeTarget::Object(self.instance_node( - value.object, - ObjectType::Class(instance.class), - &instance.fields, - )), - ObjectData::ThunkFn(thunk) => ValueEdgeTarget::Object(self.instance_node( - value.object, - ObjectType::Thunk(thunk.function), - &thunk.arguments, - )), - ObjectData::ThunkRust(thunk) => ValueEdgeTarget::Object(self.instance_node( - value.object, - ObjectType::RustThunk(thunk.description), - &thunk.arguments, - )), - ObjectData::Tuple(_tuple) => self.data_target(db, value.object, &""), // FIXME - ObjectData::Class(c) => ValueEdgeTarget::Class(*c), - ObjectData::Function(f) => ValueEdgeTarget::Function(*f), - ObjectData::Intrinsic(_) - | ObjectData::Bool(_) - | ObjectData::UnsignedInt(_) - | ObjectData::Int(_) - | ObjectData::SignedInt(_) - | ObjectData::Float(_) - | ObjectData::String(_) - | ObjectData::Unit(_) => { - let string = DefaultStringify::stringify(&*self.machine, self.db, value); - self.data_target(db, value.object, &string) - } - }, + PermissionData::Valid(_) => self.value_edge_target(value.object), }; self.graph.tables.add(ValueEdgeData { permission, target }) } + fn value_edge_target(&mut self, object: Object) -> ValueEdgeTarget { + let db = self.db; + match &self.machine[object] { + ObjectData::Instance(instance) => ValueEdgeTarget::Object(self.instance_node( + object, + ObjectType::Class(instance.class), + &instance.fields, + )), + ObjectData::ThunkFn(thunk) => ValueEdgeTarget::Object(self.instance_node( + object, + ObjectType::Thunk(thunk.function), + &thunk.arguments, + )), + ObjectData::ThunkRust(thunk) => ValueEdgeTarget::Object(self.instance_node( + object, + ObjectType::RustThunk(thunk.description), + &thunk.arguments, + )), + ObjectData::Tuple(_tuple) => self.data_target(db, object, &""), // FIXME + ObjectData::Reservation(reservation) => { + ValueEdgeTarget::Object(self.reservation_node(object, *reservation)) + } + ObjectData::Class(c) => ValueEdgeTarget::Class(*c), + ObjectData::Function(f) => ValueEdgeTarget::Function(*f), + ObjectData::Intrinsic(_) + | ObjectData::Bool(_) + | ObjectData::UnsignedInt(_) + | ObjectData::Int(_) + | ObjectData::SignedInt(_) + | ObjectData::Float(_) + | ObjectData::String(_) + | ObjectData::Unit(_) => { + let string = + DefaultStringify::stringify_object(&*self.machine, self.db, "", object); + self.data_target(db, object, &string) + } + } + } + fn data_target( &mut self, _db: &dyn crate::Db, @@ -155,7 +164,7 @@ impl<'me> HeapGraphCapture<'me> { }; let node = self.graph.tables.add(PermissionNodeData { - permission, + source: PermissionNodeSource::Permission(permission), label, lessor: None, tenants: vec![], @@ -202,4 +211,38 @@ impl<'me> HeapGraphCapture<'me> { node } + + fn reservation_node(&mut self, object: Object, reservation: Reservation) -> ObjectNode { + // Detect cycles and prevent redundant work. + if let Some(n) = self.reservations.get(&reservation) { + return *n; + } + + let node = self.graph.tables.add(ObjectNodeData { + object, + ty: ObjectType::Reservation, + fields: Default::default(), + }); + + self.reservations.insert(reservation, node); + + let mut fields = vec![]; + match self.machine.peek_reservation(self.db, reservation) { + Ok(object) => { + let permission = self.graph.tables.add(PermissionNodeData { + source: PermissionNodeSource::Reservation(reservation), + label: PermissionNodeLabel::Reserved, + tenants: vec![], + lessor: None, + }); + let target = self.value_edge_target(object); + fields.push(self.graph.tables.add(ValueEdgeData { permission, target })); + } + + Err(_err) => { /* should not happen, just ignore I guess */ } + } + self.graph.tables[node].fields = fields; + + node + } } diff --git a/components/dada-execute/src/heap_graph/graphviz.rs b/components/dada-execute/src/heap_graph/graphviz.rs index ca278fbf..590684e6 100644 --- a/components/dada-execute/src/heap_graph/graphviz.rs +++ b/components/dada-execute/src/heap_graph/graphviz.rs @@ -3,7 +3,8 @@ use dada_id::InternKey; use dada_parse::prelude::*; use super::{ - DataNode, HeapGraph, ObjectType, PermissionNode, ValueEdge, ValueEdgeData, ValueEdgeTarget, + DataNode, HeapGraph, ObjectType, PermissionNode, PermissionNodeLabel, PermissionNodeSource, + ValueEdge, ValueEdgeData, ValueEdgeTarget, }; const UNCHANGED: &str = "slategray"; @@ -145,8 +146,23 @@ impl HeapGraph { "solid" }; + let (penwidth, arrowtype) = match permission_data.label { + PermissionNodeLabel::My | PermissionNodeLabel::Our => ("3.0", "normal"), + PermissionNodeLabel::Reserved => ("1.0", "odot"), + PermissionNodeLabel::Expired + | PermissionNodeLabel::Leased + | PermissionNodeLabel::OurLeased => ("1.0", "empty"), + }; + + let color = match permission_data.label { + PermissionNodeLabel::My | PermissionNodeLabel::Leased => "red", + PermissionNodeLabel::OurLeased | PermissionNodeLabel::Our => "blue", + PermissionNodeLabel::Reserved => "grey", + PermissionNodeLabel::Expired => "grey", + }; + w.println(format!( - r#"{source:?}:{source_port} -> {target:?} [label={label:?}, style={style:?}];"#, + r#"{source:?}:{source_port} -> {target:?} [label="{label}", style="{style}", penwidth={penwidth}, arrowtype="{arrowtype}", color="{color}"];"#, source = value_edge.source.node, source_port = value_edge.source.port, target = value_edge.target, @@ -247,6 +263,7 @@ impl HeapGraph { ObjectType::Class(class) => class.name(w.db).as_str(w.db), ObjectType::Thunk(function) => function.name(w.db).as_str(w.db), ObjectType::RustThunk(d) => d, + ObjectType::Reservation => "(reservation)", }; w.println(format!(r#"{class_name}"#))?; self.print_fields(w, &name, field_names, &data.fields, 0)?; @@ -293,6 +310,9 @@ impl HeapGraph { ObjectType::RustThunk(_) => { return (0..num_fields).map(|i| Some(format!("{}", i))).collect() } + ObjectType::Reservation => { + return vec![Some("reserved".to_string())]; + } }; fields .iter() @@ -436,12 +456,22 @@ impl HeapGraph { return true; }; - let machine_permission = permission.data(&self.tables).permission; - Some(&self.machine[machine_permission]) - != diff_against - .machine - .heap - .permission_data(machine_permission) + match permission.data(&self.tables).source { + PermissionNodeSource::Permission(machine_permission) => { + Some(&self.machine[machine_permission]) + != diff_against + .machine + .heap + .permission_data(machine_permission) + } + PermissionNodeSource::Reservation(machine_reservation) => { + Some(&self.machine[machine_reservation]) + != diff_against + .machine + .heap + .reservation_data(machine_reservation) + } + } } } diff --git a/components/dada-execute/src/kernel.rs b/components/dada-execute/src/kernel.rs index d2564507..e1fbf785 100644 --- a/components/dada-execute/src/kernel.rs +++ b/components/dada-execute/src/kernel.rs @@ -317,8 +317,15 @@ impl Kernel for BufferKernel { index ); - let (breakpoint_filename, breakpoint_index, heap_at_start) = - self.started_breakpoints.pop().unwrap(); + let Some((breakpoint_filename, breakpoint_index, heap_at_start)) = + self.started_breakpoints.pop() + else { + panic!( + "breakpoint {index} for `{}` at `{:?}` not found", + filename.as_str(db), + span.debug(db), + ) + }; assert_eq!(filename, breakpoint_filename); assert_eq!(index, breakpoint_index); let breakpoint_record = BreakpointRecord { diff --git a/components/dada-execute/src/machine.rs b/components/dada-execute/src/machine.rs index 434cf83f..fb72a3f4 100644 --- a/components/dada-execute/src/machine.rs +++ b/components/dada-execute/src/machine.rs @@ -1,19 +1,21 @@ //! Defines the "abstract machine" that executes a Dada program. use dada_collections::IndexVec; +use dada_id::id; use dada_ir::{ class::Class, code::bir, function::Function, intrinsic::Intrinsic, span::FileSpan, - storage_mode::{Joint, Leased}, + storage::{Joint, Leased}, }; use dada_parse::prelude::*; use generational_arena::Arena; use crate::thunk::RustThunk; +pub mod assert_invariants; pub mod op; pub mod stringify; @@ -57,6 +59,7 @@ pub struct Value { pub struct Heap { pub objects: Arena, pub permissions: Arena, + pub reservations: Arena, } impl Heap { @@ -115,6 +118,34 @@ impl Heap { pub(crate) fn permission_data(&self, permission: Permission) -> Option<&PermissionData> { self.permissions.get(permission.index) } + + fn new_reservation(&mut self, data: ReservationData) -> Reservation { + let p = Reservation { + index: self.reservations.insert(data), + }; + + tracing::debug!( + "new reservation: {:?} = {:?}", + p, + &self.reservations[p.index] + ); + + p + } + + pub(crate) fn reservation_data(&self, reservation: Reservation) -> Option<&ReservationData> { + self.reservations.get(reservation.index) + } + + fn all_reservations(&self) -> Vec { + let mut vec: Vec<_> = self + .reservations + .iter() + .map(|(index, _)| Reservation { index }) + .collect(); + vec.sort(); + vec + } } /// An "object" is a piece of data in the heap. @@ -144,6 +175,11 @@ pub enum ObjectData { /// An instance of a class. Instance(Instance), + /// A temporary hold that is placed on a place during evaluation, + /// preventing the user from overwriting it. USed for "two-phase borrow"-like + /// patterns, in Rust terms. + Reservation(Reservation), + /// A reference to a class itself. Class(Class), @@ -191,6 +227,7 @@ impl ObjectData { pub fn kind_str(&self, db: &dyn crate::Db) -> String { match self { ObjectData::Instance(i) => format!("an instance of `{}`", i.class.name(db).as_str(db)), + ObjectData::Reservation(_) => "a reservation".to_string(), ObjectData::Class(_) => "a class".to_string(), ObjectData::Function(_) => "a function".to_string(), ObjectData::Intrinsic(_) => "a function".to_string(), @@ -224,6 +261,7 @@ macro_rules! object_data_from_impls { object_data_from_impls! { Instance(Instance), + Reservation(Reservation), Class(Class), Function(Function), Intrinsic(Intrinsic), @@ -259,6 +297,50 @@ pub struct Tuple { pub fields: Vec, } +/// A *reservation* is issued for a place when +/// we evaluate the place before we actually consume it and +/// we wish to ensure that the place is not invalidated in the +/// meantime; it is also used when we do not yet know how the +/// place will be used. +/// +/// Example: +/// +/// ```notrust +/// foo(a.b.c, ...) +/// ``` +/// +/// Here, we have not yet figured out what fn is being +/// called, and so we don't know if `a.b.c` is being shared +/// or leased or what. +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Reservation { + index: generational_arena::Index, +} + +impl std::fmt::Debug for Reservation { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + let (a, b) = self.index.into_raw_parts(); + fmt.debug_tuple("Reservation").field(&a).field(&b).finish() + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ReservationData { + /// PC when the reservation was placed. + pub pc: ProgramCounter, + + /// The active frame when reservation was created. + /// Because reservations are used only in particular + /// ways, we only expect the reservation to be activated + /// when this frame is the top-most frame, but we need + /// to store the frame for use when creating heap graphs + /// etc. + pub frame_index: FrameIndex, + + /// The place which was reserved + pub place: bir::Place, +} + #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Permission { index: generational_arena::Index, @@ -289,6 +371,13 @@ impl PermissionData { } } + pub fn expired(&self) -> Option> { + match self { + PermissionData::Expired(e) => Some(*e), + PermissionData::Valid(_) => None, + } + } + pub fn joint_if_valid(&self) -> Option { self.valid().map(|v| v.joint) } @@ -336,7 +425,43 @@ pub struct ValidPermissionData { /// located in a leased location. pub leased: Leased, - /// A "tenant" is another permission that we have given + /// A *reservation* is placed on a permission when the + /// permission (and its associated value) will be traversed + /// or accessed by some pending operation (typically a function + /// that has yet to be called). The reservation makes + /// it an error to revoke the permission. Reservations are removed + /// when the pending operation occurs. + /// + /// # Example: function calls + /// + /// ```notrust + /// foo(a.b.c, ...) + /// ``` + /// + /// When calling `foo`, we would place a *reservation* on the + /// place `a.b.c` before we go off and evaluate `...`. This allows + /// `...` to access `a.b.c` but ensures that `...` cannot revoke + /// `a.b.c`. + /// + /// # Example: tuples + /// + /// ```notrust + /// (a.b.c, ...) + /// ``` + /// + /// As with function calls, we reserve `a.b.c` while evaluating `...`. + /// + /// # Example: assignments + /// + /// ```notrust + /// a.b.c = ... + /// ``` + /// + /// When performing this assignment, we would reserve `a.b` while + /// evaluating `...`, thus ensuring that `...` cannot revoke `a.b`. + pub reservations: Vec, + + /// A *tenant* is another permission that we have given /// a lease (or sublease, if we ourselves are leased) to /// access `o`. This could be a shared /// or exclusive lease. Accesses to the fields of `o` @@ -350,6 +475,7 @@ impl ValidPermissionData { ValidPermissionData { joint: Joint::No, leased: Leased::No, + reservations: vec![], tenants: vec![], } } @@ -359,16 +485,28 @@ impl ValidPermissionData { ValidPermissionData { joint: Joint::Yes, leased: Leased::No, + reservations: vec![], tenants: vec![], } } + + pub fn as_str(&self) -> &'static str { + match (self.joint, self.leased) { + (Joint::No, Leased::No) => "my", + (Joint::No, Leased::Yes) => "leased", + (Joint::Yes, Leased::No) => "our", + (Joint::Yes, Leased::Yes) => "our leased", + } + } } #[derive(Clone, Debug, Default)] pub struct Stack { - pub frames: Vec, + pub frames: IndexVec, } +id!(pub struct FrameIndex); + #[derive(Clone, Debug)] pub struct Frame { pub pc: ProgramCounter, diff --git a/components/dada-execute/src/machine/assert_invariants.rs b/components/dada-execute/src/machine/assert_invariants.rs new file mode 100644 index 00000000..280dbded --- /dev/null +++ b/components/dada-execute/src/machine/assert_invariants.rs @@ -0,0 +1,226 @@ +use dada_collections::Map; +use dada_ir::{ + code::bir, + in_ir_db::InIrDbExt, + storage::{Joint, Leased}, +}; +use salsa::DebugWithDb; + +use crate::{ + ext::DadaExecuteClassExt, + machine::{ + op::MachineOp, Frame, Object, ObjectData, Permission, PermissionData, Reservation, Value, + }, +}; + +use super::ReservationData; + +pub(super) struct AssertInvariants<'me> { + db: &'me dyn crate::Db, + + machine: &'me dyn MachineOp, + + /// Every permission ought to be associated with "at most one" object. + permission_map: Map, +} + +impl<'me> AssertInvariants<'me> { + pub(super) fn new(db: &'me dyn crate::Db, machine: &'me dyn MachineOp) -> Self { + Self { + db, + machine, + permission_map: Default::default(), + } + } + + pub(super) fn assert_all_ok(&mut self) -> eyre::Result<()> { + for frame in self.machine.frames() { + self.assert_frame_ok(frame)?; + } + + for object in self.machine.all_objects() { + self.assert_object_ok(object)?; + } + + for permission in self.machine.all_permissions() { + self.assert_permission_ok(permission)?; + } + + Ok(()) + } + + fn assert_frame_ok(&mut self, frame: &Frame) -> eyre::Result<()> { + for v in &frame.locals { + self.assert_value_ok(v)?; + } + + Ok(()) + } + + fn assert_object_ok(&mut self, object: Object) -> eyre::Result<()> { + let object_data: &ObjectData = &self.machine[object]; + match object_data { + ObjectData::Instance(i) => self.assert_values_ok(&i.fields)?, + ObjectData::ThunkFn(f) => self.assert_values_ok(&f.arguments)?, + ObjectData::ThunkRust(f) => self.assert_values_ok(&f.arguments)?, + ObjectData::Tuple(t) => self.assert_values_ok(&t.fields)?, + + ObjectData::Reservation(r) => { + let _object = self.assert_reservation_ok(*r)?; + } + + ObjectData::Class(_) + | ObjectData::Function(_) + | ObjectData::Intrinsic(_) + | ObjectData::Bool(_) + | ObjectData::UnsignedInt(_) + | ObjectData::Int(_) + | ObjectData::SignedInt(_) + | ObjectData::Float(_) + | ObjectData::String(_) + | ObjectData::Unit(_) => { + // no reachable data + } + } + Ok(()) + } + + fn assert_permission_ok(&mut self, _permission: Permission) -> eyre::Result<()> { + Ok(()) + } + + /// Asserts that the reservation is ok and returns the reserved object. + pub(super) fn assert_reservation_ok( + &mut self, + reservation: Reservation, + ) -> eyre::Result { + let ReservationData { + pc: _, + frame_index, + place, + } = self.machine[reservation]; + self.assert_reserved_place(reservation, &self.machine[frame_index], place) + } + + /// Assert that the place `place` found in a reservation `reservation` + /// is in fact reserved. We expect to find `reservation` in each permission + /// and we expect this to be a unique place. + fn assert_reserved_place( + &mut self, + reservation: Reservation, + frame: &Frame, + place: bir::Place, + ) -> eyre::Result { + let bir = frame.pc.bir; + let table = &bir.data(self.db).tables; + match &table[place] { + bir::PlaceData::Class(_) + | bir::PlaceData::Function(_) + | bir::PlaceData::Intrinsic(_) => { + eyre::bail!( + "reserved place `{:?}` bottoms out in a constant `{:?}`", + reservation, + table[place], + ); + } + + bir::PlaceData::LocalVariable(lv) => { + let value = frame.locals[*lv]; + self.assert_reserved_value(reservation, value) + } + + bir::PlaceData::Dot(owner, field) => { + let object = self.assert_reserved_place(reservation, frame, *owner)?; + match &self.machine[object] { + ObjectData::Instance(instance) => { + let Some(index) = instance.class.field_index(self.db, *field) else { + eyre::bail!( + "reservation `{:?}` references place `{:?}` with invalid field `{:?}` for object `{:?}`", + reservation, + place.debug(&bir.in_ir_db(self.db)), + field.debug(self.db), + instance, + ); + }; + let value = instance.fields[index]; + self.assert_reserved_value(reservation, value) + } + + data => { + eyre::bail!( + "reservation `{:?}` reserved object with unexpected data `{:?}` at place `{:?}`", + reservation, + data, + place.debug(&bir.in_ir_db(self.db)), + ); + } + } + } + } + } + + fn assert_reserved_value( + &mut self, + reservation: Reservation, + value: Value, + ) -> eyre::Result { + let Value { object, permission } = value; + + let Some(valid) = self.machine[permission].valid() else { + eyre::bail!( + "reservation `{:?}` references expired permission `{:?}`", + reservation, + permission, + ); + }; + + if let Joint::Yes = valid.joint { + eyre::bail!( + "reservation `{:?}` references joint permission `{:?}`", + reservation, + permission, + ); + } + + if !valid.reservations.contains(&reservation) { + eyre::bail!( + "reservation `{:?}` not found in reservation list `{:?}` for permission `{:?}`", + reservation, + valid.reservations, + permission, + ); + } + + Ok(object) + } + + fn assert_values_ok(&mut self, values: &[Value]) -> eyre::Result<()> { + for v in values { + self.assert_value_ok(v)?; + } + + Ok(()) + } + + fn assert_value_ok(&mut self, value: &Value) -> eyre::Result<()> { + let PermissionData::Valid(valid) = &self.machine[value.permission] else { + return Ok(()); + }; + + if let (_, Leased::No) | (Joint::No, _) = (valid.joint, valid.leased) { + // Invariant I0: Every owned or exclusive permission should be associated with exactly one object across the entire machine. + if let Some(other_object) = self.permission_map.insert(value.permission, value.object) { + if value.object != other_object { + eyre::bail!( + "owned permission {:?} associated with at least two objects: {:?} and {:?}", + value.permission, + value.object, + other_object + ); + } + } + } + + Ok(()) + } +} diff --git a/components/dada-execute/src/machine/op.rs b/components/dada-execute/src/machine/op.rs index d6f06d9d..8dfd3ef2 100644 --- a/components/dada-execute/src/machine/op.rs +++ b/components/dada-execute/src/machine/op.rs @@ -4,24 +4,28 @@ use dada_collections::IndexVec; use dada_ir::code::bir; use super::{ - Frame, Machine, Object, ObjectData, Permission, PermissionData, ProgramCounter, - ValidPermissionData, Value, + assert_invariants::AssertInvariants, Frame, FrameIndex, Machine, Object, ObjectData, + Permission, PermissionData, ProgramCounter, Reservation, ReservationData, ValidPermissionData, + Value, }; pub(crate) trait MachineOp: std::ops::IndexMut + std::ops::IndexMut + + std::ops::IndexMut + std::ops::IndexMut + + std::ops::Index + Debug { /// Gives a frozen view onto the state of the machine. fn view(&self) -> &Machine; - fn frames(&self) -> &[Frame]; + fn frames(&self) -> &IndexVec; fn push_frame(&mut self, db: &dyn crate::Db, bir: bir::Bir, arguments: Vec); fn clear_frame(&mut self); fn pop_frame(&mut self) -> Frame; fn top_frame(&self) -> Option<&Frame>; + fn top_frame_index(&self) -> Option; fn object(&self, object: Object) -> &ObjectData; fn object_mut(&mut self, object: Object) -> &mut ObjectData; @@ -37,6 +41,12 @@ pub(crate) trait MachineOp: fn expired_permission(&mut self, origin: Option) -> Permission; fn all_permissions(&self) -> Vec; + fn reservation(&self, reservation: Reservation) -> &ReservationData; + fn reservation_mut(&mut self, reservation: Reservation) -> &mut ReservationData; + fn new_reservation(&mut self, data: ReservationData) -> Reservation; + fn all_reservations(&self) -> Vec; + fn take_reservation(&mut self, reservation: Reservation) -> ReservationData; + // Access locals from the top-most stack frame (panics if stack is empty). fn local(&self, local_variable: bir::LocalVariable) -> &Value; fn local_mut(&mut self, local_variable: bir::LocalVariable) -> &mut Value; @@ -58,7 +68,7 @@ impl MachineOp for Machine { self } - fn frames(&self) -> &[Frame] { + fn frames(&self) -> &IndexVec { &self.stack.frames } @@ -118,6 +128,15 @@ impl MachineOp for Machine { self.stack.frames.last() } + fn top_frame_index(&self) -> Option { + let l = self.stack.frames.len(); + if l == 0 { + None + } else { + Some(FrameIndex::from(l - 1)) + } + } + #[track_caller] fn object(&self, object: Object) -> &ObjectData { self.heap @@ -190,6 +209,32 @@ impl MachineOp for Machine { self.heap.new_permission(PermissionData::Expired(place)) } + #[track_caller] + fn reservation(&self, reservation: Reservation) -> &ReservationData { + self.heap.reservations.get(reservation.index).unwrap() + } + + #[track_caller] + fn reservation_mut(&mut self, reservation: Reservation) -> &mut ReservationData { + self.heap.reservations.get_mut(reservation.index).unwrap() + } + + fn new_reservation(&mut self, data: ReservationData) -> Reservation { + self.heap.new_reservation(data) + } + + #[track_caller] + fn take_reservation(&mut self, reservation: Reservation) -> ReservationData { + self.heap + .reservations + .remove(reservation.index) + .unwrap_or_else(|| panic!("reservation not found: {reservation:?}")) + } + + fn all_reservations(&self) -> Vec { + self.heap.all_reservations() + } + fn local(&self, local_variable: bir::LocalVariable) -> &Value { &self.stack.frames.last().unwrap().locals[local_variable] } @@ -215,6 +260,14 @@ impl MachineOp for Machine { } } +impl std::ops::Index for Machine { + type Output = Frame; + + fn index(&self, index: FrameIndex) -> &Self::Output { + &self.stack.frames[index] + } +} + impl std::ops::Index for Machine { type Output = ObjectData; @@ -243,6 +296,20 @@ impl std::ops::IndexMut for Machine { } } +impl std::ops::Index for Machine { + type Output = ReservationData; + + fn index(&self, index: Reservation) -> &Self::Output { + self.reservation(index) + } +} + +impl std::ops::IndexMut for Machine { + fn index_mut(&mut self, index: Reservation) -> &mut Self::Output { + self.reservation_mut(index) + } +} + impl std::ops::Index for Machine { type Output = Value; @@ -258,7 +325,7 @@ impl std::ops::IndexMut for Machine { } #[extension_trait::extension_trait] -pub(crate) impl MachineOpExt for &mut dyn MachineOp { +pub(crate) impl MachineOpExtMut for &mut dyn MachineOp { fn my_value(&mut self, data: impl Into) -> Value { let permission = self.new_permission(ValidPermissionData::my()); let object = self.new_object(data.into()); @@ -271,3 +338,21 @@ pub(crate) impl MachineOpExt for &mut dyn MachineOp { Value { object, permission } } } + +#[extension_trait::extension_trait] +pub(crate) impl MachineOpExt for &dyn MachineOp { + fn assert_invariants(self, db: &dyn crate::Db) -> eyre::Result<()> { + AssertInvariants::new(db, self).assert_all_ok() + } + + /// Given a reservation, peeks to find the reserved object; + /// returns Err if the machine invariants on the reservation are not meant + /// (indicates a bug in Dada somewhere). + fn peek_reservation( + self, + db: &dyn crate::Db, + reservation: Reservation, + ) -> eyre::Result { + AssertInvariants::new(db, self).assert_reservation_ok(reservation) + } +} diff --git a/components/dada-execute/src/machine/stringify.rs b/components/dada-execute/src/machine/stringify.rs index 154d81b0..820e4adb 100644 --- a/components/dada-execute/src/machine/stringify.rs +++ b/components/dada-execute/src/machine/stringify.rs @@ -1,22 +1,34 @@ use dada_ir::{ - storage_mode::{Joint, Leased}, + storage::{Joint, Leased}, word::Word, }; use crate::machine::{ObjectData, Permission, PermissionData, Value}; -use super::op::MachineOp; +use super::{op::MachineOp, Object}; #[extension_trait::extension_trait] pub(crate) impl DefaultStringify for T { /// Converts a given value into a string. This should /// eventually be customizable. - fn stringify(&self, db: &dyn crate::Db, value: Value) -> String { + fn stringify_value(&self, db: &dyn crate::Db, value: Value) -> String { let Some(p) = self.permission_str(value.permission) else { return "(expired)".to_string(); }; - match &self[value.object] { + self.stringify_object(db, p, value.object) + } + + // FIXME: There is no way for *users* to write a fn that "inspects" the permission + // like this. We should maybe just not print them, but it's kind of useful...? + fn stringify_object(&self, db: &dyn crate::Db, permission: &str, object: Object) -> String { + tracing::debug!( + "stringify(permission = {:?}, object = {:?}, object-data = {:?})", + permission, + object, + self[object] + ); + match &self[object] { ObjectData::String(s) => s.to_string(), ObjectData::Bool(v) => format!("{}", v), ObjectData::SignedInt(v) => format!("{}_i", v), @@ -26,13 +38,19 @@ pub(crate) impl DefaultStringify for T { ObjectData::Unit(_) => "()".to_string(), ObjectData::Intrinsic(i) => i.as_str(db).to_string(), ObjectData::Function(f) => f.name(db).as_str(db).to_string(), - ObjectData::ThunkFn(f) => { - self.object_string(db, p, Some(f.function.name(db)), &f.arguments) + ObjectData::ThunkFn(f) => self.object_string( + db, + permission, + Some(f.function.name(db).word(db)), + &f.arguments, + ), + ObjectData::Instance(i) => { + self.object_string(db, permission, Some(i.class.name(db).word(db)), &i.fields) } - ObjectData::Instance(i) => self.object_string(db, p, Some(i.class.name(db)), &i.fields), ObjectData::Class(c) => c.name(db).as_str(db).to_string(), - ObjectData::ThunkRust(r) => format!("{p} {r:?}"), - ObjectData::Tuple(t) => self.object_string(db, p, None, &t.fields), + ObjectData::ThunkRust(r) => format!("{permission} {r:?}"), + ObjectData::Tuple(t) => self.object_string(db, permission, None, &t.fields), + ObjectData::Reservation(r) => format!("{r:?}"), // can prob do better than this :) } } @@ -56,7 +74,7 @@ pub(crate) impl DefaultStringify for T { if index > 0 { output.push_str(", "); } - output.push_str(&self.stringify(db, *field)); + output.push_str(&self.stringify_value(db, *field)); } output.push(')'); output diff --git a/components/dada-execute/src/step.rs b/components/dada-execute/src/step.rs index 07fcaa16..90b685b4 100644 --- a/components/dada-execute/src/step.rs +++ b/components/dada-execute/src/step.rs @@ -9,6 +9,7 @@ use dada_ir::{ in_ir_db::InIrDbExt, origin_table::HasOriginIn, span::FileSpan, + storage::{Atomic, Joint, Leased, SpannedSpecifier, Specifier}, word::Word, }; use dada_parse::prelude::*; @@ -24,6 +25,8 @@ use crate::{ thunk::RustThunk, }; +use self::traversal::PlaceTraversal; + mod access; mod address; mod apply_op; @@ -35,6 +38,7 @@ mod gc; mod give; mod intrinsic; mod lease; +mod reserve; mod revoke; mod share; mod tenant; @@ -173,7 +177,7 @@ impl<'me> Stepper<'me> { ); match statement.data(table) { - bir::StatementData::Assign(place, expr) => { + bir::StatementData::AssignExpr(place, expr) => { // Subtle: The way this is setup, permissions for the target are not // canceled until the write occurs. Consider something like this: // @@ -185,7 +189,10 @@ impl<'me> Stepper<'me> { // // This works, but the act of assigning to `p.x` cancels the lease from `q`. let value = self.eval_expr(table, *expr)?; - self.assign_place(table, *place, value)?; + self.assign_value_to_place(table, *place, value)?; + } + bir::StatementData::AssignPlace(target_place, source_place) => { + self.assign_place_to_place(table, *target_place, *source_place)?; } bir::StatementData::Clear(lv) => { let permission = self.machine.expired_permission(None); @@ -223,15 +230,121 @@ impl<'me> Stepper<'me> { }) } - fn assign_place( + fn assign_place_to_place( &mut self, table: &bir::Tables, - place: bir::Place, + target_place: bir::TargetPlace, + source_place: bir::Place, + ) -> eyre::Result<()> { + let target_traversal = self.evaluate_target_place(table, target_place)?; + + assert_ne!( + target_traversal.accumulated_permissions.atomic, + Atomic::Yes, + "atomics not yet implemented" + ); + + let specifier = self.specifier(target_traversal.address); + + let value = self.prepare_value_for_specifier(table, specifier, source_place)?; + + self.assign_value_to_traversal(target_traversal, value) + } + + fn evaluate_target_place( + &mut self, + table: &bir::Tables, + target_place: bir::TargetPlace, + ) -> eyre::Result { + match &table[target_place] { + bir::TargetPlaceData::LocalVariable(lv) => { + Ok(self.traverse_to_local_variable(table, *lv)) + } + bir::TargetPlaceData::Dot(owner, name) => { + let owner_traversal = self.traverse_to_object(table, *owner)?; + let owner_traversal = self.confirm_reservation_if_any(table, owner_traversal)?; + self.traverse_to_object_field(target_place, owner_traversal, *name) + } + } + } + + #[tracing::instrument(level = "Debug", skip(self, table))] + fn prepare_value_for_specifier( + &mut self, + table: &bir::Tables, + specifier: Option, + source_place: bir::Place, + ) -> eyre::Result { + let (specifier, specifier_span) = match specifier { + Some(i) => i.into_specifier_and_span(self.db), + None => return self.give_place(table, source_place), + }; + + tracing::debug!(?specifier); + + let value = match specifier { + Specifier::My => self.give_place(table, source_place)?, + Specifier::Our => self.share_place(table, source_place)?, + Specifier::Leased => self.lease_place(table, source_place)?, + Specifier::OurLeased => self.share_leased_place(table, source_place)?, + Specifier::Any => self.give_place(table, source_place)?, + }; + + let permission = &self.machine[value.permission]; + let valid = permission + .valid() + .expect("value to be stored has expired permision"); + + if let (true, Leased::Yes) = (specifier.must_be_owned(), valid.leased) { + let source_place_span = self.span_from_bir(source_place); + return Err(error!(source_place_span, "more permissions needed") + .primary_label(format!( + "this value is `{}`, which is leased, not owned", + valid.as_str() + )) + .secondary_label( + specifier_span, + format!("`{specifier}` requires owned values"), + ) + .eyre(self.db)); + } + + if let (true, Joint::Yes) = (specifier.must_be_unique(), valid.joint) { + let source_place_span = self.span_from_bir(source_place); + return Err(error!(source_place_span, "more permissions needed") + .primary_label(format!( + "this value is `{}`, which is shared, not unique", + valid.as_str() + )) + .secondary_label( + specifier_span, + format!("`{specifier}` requires unique access"), + ) + .eyre(self.db)); + } + + Ok(value) + } + + fn assign_value_to_place( + &mut self, + table: &bir::Tables, + target_place: bir::TargetPlace, + value: Value, + ) -> eyre::Result<()> { + assert!(self.machine[value.permission].valid().is_some()); + + let target_traversal = self.evaluate_target_place(table, target_place)?; + self.assign_value_to_traversal(target_traversal, value) + } + + fn assign_value_to_traversal( + &mut self, + target_traversal: PlaceTraversal, value: Value, - ) -> Result<(), eyre::Error> { - let traversal = self.traverse_to_place(table, place)?; - self.write_place(&traversal)?; - self.poke(traversal.address, value)?; + ) -> eyre::Result<()> { + self.write_place(&target_traversal)?; + self.poke(target_traversal.address, value)?; Ok(()) } @@ -275,7 +388,7 @@ impl<'me> Stepper<'me> { next_block, ) => match self.call(table, terminator, *function, arguments, labels)? { call::CallResult::Returned(return_value) => { - self.assign_place(table, *destination, return_value)?; + self.assign_value_to_place(table, *destination, return_value)?; self.machine.set_pc(pc.move_to_block(*next_block)); Ok(ControlFlow::Next) } @@ -355,8 +468,14 @@ impl<'me> Stepper<'me> { unreachable!("calling frame should be at an assign terminator") }; + // check that the value which was returned didn't get invalidated + // by the return itself + if let Some(expired_at) = self.machine[value.permission].expired() { + return Err(self.report_traversing_expired_permission(top.pc.span(self.db), expired_at)); + } + let new_pc = top.pc.move_to_block(*top_basic_block); - self.assign_place(top_table, *top_place, value)?; + self.assign_value_to_place(top_table, *top_place, value)?; self.machine.set_pc(new_pc); Ok(()) } @@ -366,7 +485,7 @@ impl<'me> Stepper<'me> { /// permissions to read. fn read_place(&mut self, table: &bir::Tables, place: bir::Place) -> eyre::Result { let traversal = self.traverse_to_object(table, place)?; - Ok(self.read(&traversal)) + self.read(&traversal) } fn eval_place_to_bool(&mut self, table: &bir::Tables, place: bir::Place) -> eyre::Result { @@ -412,7 +531,8 @@ impl<'me> Stepper<'me> { object: self.machine.new_object(ObjectData::Unit(())), permission: self.machine.new_permission(ValidPermissionData::our()), }), - bir::ExprData::GiveShare(place) => self.share_place(table, *place), + bir::ExprData::Reserve(place) => self.reserve_place(table, *place), + bir::ExprData::Share(place) => self.share_place(table, *place), bir::ExprData::Lease(place) => self.lease_place(table, *place), bir::ExprData::Give(place) => self.give_place(table, *place), bir::ExprData::Tuple(places) => { @@ -452,7 +572,7 @@ impl<'me> Stepper<'me> { fn no_such_field(db: &dyn crate::Db, span: FileSpan, class: Class, name: Word) -> eyre::Report { let class_name = class.name(db).as_str(db); - let class_span = class.name_span(db); + let class_span = class.name(db).span(db); error!( span, "the class `{}` has no field named `{}`", @@ -484,3 +604,19 @@ impl<'me> Stepper<'me> { syntax_tree.spans(self.db)[syntax_expr].in_file(filename) } } + +trait IntoSpecifierAndSpan: std::fmt::Debug { + fn into_specifier_and_span(self, db: &dyn crate::Db) -> (Specifier, FileSpan); +} + +impl IntoSpecifierAndSpan for (Specifier, FileSpan) { + fn into_specifier_and_span(self, _db: &dyn crate::Db) -> (Specifier, FileSpan) { + self + } +} + +impl IntoSpecifierAndSpan for SpannedSpecifier { + fn into_specifier_and_span(self, db: &dyn crate::Db) -> (Specifier, FileSpan) { + (self.specifier(db), self.span(db)) + } +} diff --git a/components/dada-execute/src/step/access.rs b/components/dada-execute/src/step/access.rs index c01c7b09..bcfadd56 100644 --- a/components/dada-execute/src/step/access.rs +++ b/components/dada-execute/src/step/access.rs @@ -1,6 +1,6 @@ use dada_ir::{ error, - storage_mode::{Atomic, Joint}, + storage::{Atomic, Joint, Leased}, }; use crate::{ @@ -23,10 +23,11 @@ impl Stepper<'_> { /// permissions is encountered, so this could only happen /// if the traversal is "out of date" with respect to the machine /// state). - pub(super) fn read(&mut self, traversal: &ObjectTraversal) -> Object { - self.access(traversal, Self::revoke_exclusive_tenants); + #[tracing::instrument(level = "Debug", skip(self))] + pub(super) fn read(&mut self, traversal: &ObjectTraversal) -> eyre::Result { + self.access(traversal, Self::revoke_exclusive_tenants)?; - traversal.object + Ok(traversal.object) } /// Write to the object that was arrived at via the given traversal. @@ -37,8 +38,22 @@ impl Stepper<'_> { /// permissions is encountered, so this could only happen /// if the traversal is "out of date" with respect to the machine /// state). - pub(super) fn write_object(&mut self, traversal: &ObjectTraversal) { - self.access(traversal, Self::revoke_tenants); + #[tracing::instrument(level = "Debug", skip(self))] + pub(super) fn write_object(&mut self, traversal: &ObjectTraversal) -> eyre::Result<()> { + self.access(traversal, Self::revoke_tenants) + } + + /// Given a traversal that has unique ownership, revokes the last permission + /// in the path and returns the object. This also cancels tenants of traversed + /// paths, as their (transitive) content has changed. + #[tracing::instrument(level = "Debug", skip(self))] + pub(super) fn take_object(&mut self, traversal: ObjectTraversal) -> eyre::Result { + assert_eq!(traversal.accumulated_permissions.joint, Joint::No); + assert_eq!(traversal.accumulated_permissions.leased, Leased::No); + self.write_object(&traversal)?; + let last_permission = *traversal.accumulated_permissions.traversed.last().unwrap(); + self.revoke(last_permission)?; + Ok(traversal.object) } /// Write to the *place* identified by the given traversal (but not the @@ -50,6 +65,7 @@ impl Stepper<'_> { /// permissions is encountered, so this could only happen /// if the traversal is "out of date" with respect to the machine /// state). + #[tracing::instrument(level = "Debug", skip(self))] pub(super) fn write_place(&mut self, traversal: &PlaceTraversal) -> eyre::Result<()> { let ap = &traversal.accumulated_permissions; match (ap.joint, ap.atomic) { @@ -91,7 +107,7 @@ impl Stepper<'_> { // This write to `a.c` does NOT cancel `p`, because it has leased `a.b`. for &permission in &traversal.accumulated_permissions.traversed { assert!(matches!(self.machine[permission], PermissionData::Valid(_))); - self.revoke_tenants(permission); + self.revoke_tenants(permission)?; } // # Discussion @@ -147,17 +163,20 @@ impl Stepper<'_> { /// Apply `revoke_op` to each path that was traversed to reach the /// destination object `o`, along with any data exclusively /// reachable from `o`. + #[tracing::instrument(level = "Debug", skip(self, revoke_op))] fn access( &mut self, traversal: &ObjectTraversal, - mut revoke_op: impl FnMut(&mut Self, Permission), - ) { + mut revoke_op: impl FnMut(&mut Self, Permission) -> eyre::Result<()>, + ) -> eyre::Result<()> { for &permission in &traversal.accumulated_permissions.traversed { assert!(matches!(self.machine[permission], PermissionData::Valid(_))); - revoke_op(self, permission); + revoke_op(self, permission)?; } - self.for_each_reachable_exclusive_permission(traversal.object, revoke_op); + self.for_each_reachable_exclusive_permission(traversal.object, revoke_op)?; + + Ok(()) } /// Whenever an object is accessed (whether via a read or a write), @@ -207,8 +226,8 @@ impl Stepper<'_> { fn for_each_reachable_exclusive_permission( &mut self, object: Object, - mut revoke_op: impl FnMut(&mut Self, Permission), - ) { + mut revoke_op: impl FnMut(&mut Self, Permission) -> eyre::Result<()>, + ) -> eyre::Result<()> { let mut reachable = vec![]; let mut queue = vec![object]; @@ -237,7 +256,8 @@ impl Stepper<'_> { self.push_reachable_via_fields(&v.fields, &mut reachable, &mut queue); } - ObjectData::Bool(_) + ObjectData::Reservation(_) + | ObjectData::Bool(_) | ObjectData::Class(_) | ObjectData::Float(_) | ObjectData::Function(_) @@ -252,8 +272,10 @@ impl Stepper<'_> { } for p in reachable { - revoke_op(self, p); + revoke_op(self, p)?; } + + Ok(()) } fn push_reachable_via_fields( diff --git a/components/dada-execute/src/step/address.rs b/components/dada-execute/src/step/address.rs index fbb2d2b0..39623b35 100644 --- a/components/dada-execute/src/step/address.rs +++ b/components/dada-execute/src/step/address.rs @@ -1,4 +1,4 @@ -use dada_ir::{code::bir, error}; +use dada_ir::{code::bir, error, parameter::Parameter, storage::SpannedSpecifier}; use crate::{ error::DiagnosticBuilderExt, @@ -8,7 +8,7 @@ use crate::{ use super::Stepper; /// Identifies a place in memory -#[derive(Debug)] +#[derive(Copy, Clone, Debug)] pub(super) enum Address { /// A local variable in the top-most stack frame Local(bir::LocalVariable), @@ -16,17 +16,33 @@ pub(super) enum Address { /// A constant, like a Class or a Function Constant(Value), - /// A field of an object - Field(Object, usize), + /// A field with the given index of the given object. + /// If this is a field of a user-declared class (as opposed, + /// say, to a tuple), then includes the [`Parameter`] + /// representing that field. + Field(Object, usize, Option), } impl Stepper<'_> { + pub(super) fn specifier(&self, address: Address) -> Option { + match address { + Address::Local(local) => { + let bir = self.machine.pc().bir; + let local_decl = &bir.data(self.db).tables[local]; + local_decl.specifier + } + Address::Constant(_) => None, + Address::Field(_, _, Some(field)) => Some(field.decl(self.db).specifier), + Address::Field(_, _, None) => None, + } + } + /// Read the value at `address`; does not account for permissions at all. pub(super) fn peek(&self, address: Address) -> Value { match address { Address::Local(lv) => self.machine[lv], Address::Constant(v) => v, - Address::Field(o, f) => match &self.machine[o] { + Address::Field(o, f, _) => match &self.machine[o] { ObjectData::Instance(i) => i.fields[f], ObjectData::Tuple(v) => v.fields[f], d => panic!("unexpected thing with fields: {d:?}"), @@ -45,7 +61,7 @@ impl Stepper<'_> { ) .eyre(self.db)) } - Address::Field(o, f) => match &mut self.machine[o] { + Address::Field(o, f, _) => match &mut self.machine[o] { ObjectData::Instance(i) => i.fields[f] = value, ObjectData::Tuple(v) => v.fields[f] = value, d => panic!("unexpected thing with fields: {d:?}"), diff --git a/components/dada-execute/src/step/apply_op.rs b/components/dada-execute/src/step/apply_op.rs index 2cec6375..d597cfbf 100644 --- a/components/dada-execute/src/step/apply_op.rs +++ b/components/dada-execute/src/step/apply_op.rs @@ -5,7 +5,7 @@ use dada_ir::{ use crate::{ error::DiagnosticBuilderExt, - machine::op::MachineOpExt, + machine::op::MachineOpExtMut, machine::{Object, ObjectData, Value}, }; diff --git a/components/dada-execute/src/step/apply_unary.rs b/components/dada-execute/src/step/apply_unary.rs index 367bb40c..dfd4da54 100644 --- a/components/dada-execute/src/step/apply_unary.rs +++ b/components/dada-execute/src/step/apply_unary.rs @@ -5,7 +5,7 @@ use dada_ir::{ use crate::{ error::DiagnosticBuilderExt, - machine::op::MachineOpExt, + machine::op::MachineOpExtMut, machine::{Object, ObjectData, Value}, }; diff --git a/components/dada-execute/src/step/assert_invariants.rs b/components/dada-execute/src/step/assert_invariants.rs index e44c4760..efe570dc 100644 --- a/components/dada-execute/src/step/assert_invariants.rs +++ b/components/dada-execute/src/step/assert_invariants.rs @@ -6,10 +6,7 @@ //! //! * I0: Every owned or exclusive permission should be associated with exactly one object across the entire machine. -use dada_collections::Map; -use dada_ir::storage_mode::{Joint, Leased}; - -use crate::machine::{op::MachineOp, Frame, Object, ObjectData, Permission, PermissionData, Value}; +use crate::machine::op::MachineOpExt; use super::Stepper; @@ -17,107 +14,7 @@ impl Stepper<'_> { pub(crate) fn assert_invariants(&self) -> eyre::Result<()> { // Convert an assertion failure into a panic intentionally; // it's not the same as other sorts of failures. - AssertInvariants::new(self.db, self.machine) - .assert_all_ok() - .unwrap(); - Ok(()) - } -} - -struct AssertInvariants<'me> { - machine: &'me dyn MachineOp, - - /// Every permission ought to be associated with "at most one" object. - permission_map: Map, -} - -impl<'me> AssertInvariants<'me> { - fn new(_db: &'me dyn crate::Db, machine: &'me dyn MachineOp) -> Self { - Self { - machine, - permission_map: Default::default(), - } - } - - fn assert_all_ok(&mut self) -> eyre::Result<()> { - for frame in self.machine.frames() { - self.assert_frame_ok(frame)?; - } - - for object in self.machine.all_objects() { - self.assert_object_ok(object)?; - } - - for permission in self.machine.all_permissions() { - self.assert_permission_ok(permission)?; - } - - Ok(()) - } - - fn assert_frame_ok(&mut self, frame: &Frame) -> eyre::Result<()> { - for v in &frame.locals { - self.assert_value_ok(v)?; - } - - Ok(()) - } - - fn assert_object_ok(&mut self, object: Object) -> eyre::Result<()> { - let object_data: &ObjectData = &self.machine[object]; - match object_data { - ObjectData::Instance(i) => self.assert_values_ok(&i.fields)?, - ObjectData::ThunkFn(f) => self.assert_values_ok(&f.arguments)?, - ObjectData::ThunkRust(f) => self.assert_values_ok(&f.arguments)?, - ObjectData::Tuple(t) => self.assert_values_ok(&t.fields)?, - - ObjectData::Class(_) - | ObjectData::Function(_) - | ObjectData::Intrinsic(_) - | ObjectData::Bool(_) - | ObjectData::UnsignedInt(_) - | ObjectData::Int(_) - | ObjectData::SignedInt(_) - | ObjectData::Float(_) - | ObjectData::String(_) - | ObjectData::Unit(_) => { - // no reachable data - } - } - Ok(()) - } - - fn assert_permission_ok(&mut self, _permission: Permission) -> eyre::Result<()> { - Ok(()) - } - - fn assert_values_ok(&mut self, values: &[Value]) -> eyre::Result<()> { - for v in values { - self.assert_value_ok(v)?; - } - - Ok(()) - } - - fn assert_value_ok(&mut self, value: &Value) -> eyre::Result<()> { - let PermissionData::Valid(valid) = &self.machine[value.permission] else { - return Ok(()); - }; - - if let (_, Leased::No) | (Joint::No, _) = (valid.joint, valid.leased) { - // Invariant I0: Every owned or exclusive permission should be associated with exactly one object across the entire machine. - if let Some(other_object) = self.permission_map.insert(value.permission, value.object) { - if value.object != other_object { - eyre::bail!( - "owned permission {:?} associated with at least two objects: {:?} and {:?}", - value.permission, - value.object, - other_object - ); - } - } - } - + self.machine.assert_invariants(self.db).unwrap(); Ok(()) } } diff --git a/components/dada-execute/src/step/await_thunk.rs b/components/dada-execute/src/step/await_thunk.rs index 65ab8d60..d161eb20 100644 --- a/components/dada-execute/src/step/await_thunk.rs +++ b/components/dada-execute/src/step/await_thunk.rs @@ -2,7 +2,7 @@ use dada_brew::prelude::*; use dada_ir::{ code::bir, error, - storage_mode::{Joint, Leased}, + storage::{Joint, Leased}, }; use crate::{ diff --git a/components/dada-execute/src/step/call.rs b/components/dada-execute/src/step/call.rs index d6ce32c0..be5f1920 100644 --- a/components/dada-execute/src/step/call.rs +++ b/components/dada-execute/src/step/call.rs @@ -10,12 +10,11 @@ use dada_parse::prelude::*; use crate::{ error::DiagnosticBuilderExt, - ext::DadaExecuteClassExt, - machine::{op::MachineOpExt, Instance, ObjectData, ThunkFn, Value}, + machine::{op::MachineOpExtMut, Instance, ObjectData, ThunkFn, Value}, step::intrinsic::IntrinsicDefinition, }; -use super::Stepper; +use super::{IntoSpecifierAndSpan, Stepper}; pub(super) enum CallResult { Returned(Value), @@ -32,10 +31,6 @@ impl Stepper<'_> { labels: &[SpannedOptionalWord], ) -> eyre::Result { let function_value = self.give_place(table, callee)?; - let arguments: Vec = argument_places - .iter() - .map(|a| self.give_place(table, *a)) - .collect::>()?; assert!( self.machine[function_value.permission].valid().is_some(), @@ -43,20 +38,24 @@ impl Stepper<'_> { ); match &self.machine[function_value.object] { - ObjectData::Class(c) => { - let field_names = c.field_names(self.db); - self.match_labels(terminator, labels, field_names)?; + &ObjectData::Class(c) => { + let fields = c.fields(self.db); + self.match_labels(terminator, labels, fields)?; + let arguments = + self.prepare_arguments_for_parameters(table, fields, argument_places)?; let instance = Instance { - class: *c, + class: c, fields: arguments, }; Ok(CallResult::Returned(self.machine.my_value(instance))) } - ObjectData::Function(function) => { - let function = *function; + &ObjectData::Function(function) => { let parameters = function.parameters(self.db); self.match_labels(terminator, labels, parameters)?; + let arguments = + self.prepare_arguments_for_parameters(table, parameters, argument_places)?; + if function.code(self.db).effect.permits_await() { // If the function can await, then it must be an async function. // Now that we have validated the arguments, return a thunk. @@ -73,9 +72,18 @@ impl Stepper<'_> { Ok(CallResult::PushedNewFrame) } } - ObjectData::Intrinsic(intrinsic) => { - let definition = IntrinsicDefinition::for_intrinsic(self.db, *intrinsic); + &ObjectData::Intrinsic(intrinsic) => { + let definition = IntrinsicDefinition::for_intrinsic(self.db, intrinsic); self.match_labels(callee, labels, &definition.argument_names)?; + let callee_span = self.span_from_bir(callee); + let arguments = self.prepare_arguments( + table, + definition + .argument_specifiers + .iter() + .map(|specifier| (*specifier, callee_span)), + argument_places, + )?; let value = (definition.function)(self, arguments)?; Ok(CallResult::Returned(value)) } @@ -91,6 +99,38 @@ impl Stepper<'_> { } } + /// Prepare the arguments according to the given specifiers. + fn prepare_arguments_for_parameters( + &mut self, + table: &bir::Tables, + parameters: &[Parameter], + argument_places: &[bir::Place], + ) -> eyre::Result> { + self.prepare_arguments( + table, + parameters + .iter() + .map(|parameter| parameter.decl(self.db).specifier), + argument_places, + ) + } + + /// Prepare the arguments according to the given specifiers. + fn prepare_arguments( + &mut self, + table: &bir::Tables, + specifiers: impl Iterator, + argument_places: &[bir::Place], + ) -> eyre::Result> { + argument_places + .iter() + .zip(specifiers) + .map(|(argument_place, specifier)| { + self.prepare_value_for_specifier(table, Some(specifier), *argument_place) + }) + .collect() + } + fn match_labels( &self, call_terminator: impl HasOriginIn, diff --git a/components/dada-execute/src/step/gc.rs b/components/dada-execute/src/step/gc.rs index e829ecd5..95ad9df2 100644 --- a/components/dada-execute/src/step/gc.rs +++ b/components/dada-execute/src/step/gc.rs @@ -5,9 +5,11 @@ //! The gc currently runs after every step, keeping things tidy. use dada_collections::Set; -use dada_ir::storage_mode::Leased; +use dada_ir::storage::Leased; -use crate::machine::{op::MachineOp, Object, ObjectData, Permission, PermissionData, Value}; +use crate::machine::{ + op::MachineOp, Object, ObjectData, Permission, PermissionData, Reservation, Value, +}; use super::Stepper; @@ -23,7 +25,7 @@ impl Stepper<'_> { pub(super) fn gc(&mut self, in_flight_values: &[Value]) { let mut marks = Marks::default(); Marker::new(self.machine, &mut marks).mark(in_flight_values); - self.sweep(&marks); + self.sweep(&marks).unwrap(); } } @@ -54,6 +56,9 @@ struct Marks { /// This function creates an Object and returns a leased copy, /// In the callee, the leased value will be live, but not the owner. live_permissions: Set, + + /// Reservations reachable from live things + live_reservations: Set, } struct Marker<'me> { @@ -126,6 +131,8 @@ impl<'me> Marker<'me> { ObjectData::ThunkRust(f) => self.mark_values(&f.arguments), ObjectData::Tuple(t) => self.mark_values(&t.fields), + ObjectData::Reservation(r) => self.mark_reservation(*r), + ObjectData::Class(_) | ObjectData::Function(_) | ObjectData::Intrinsic(_) @@ -141,6 +148,11 @@ impl<'me> Marker<'me> { } } + #[tracing::instrument(level = "Debug", skip(self))] + fn mark_reservation(&mut self, reservation: Reservation) { + self.marks.live_reservations.insert(reservation); + } + #[tracing::instrument(level = "Debug", skip(self))] fn mark_permission(&mut self, permission: Permission) { if !self.marks.live_permissions.insert(permission) { @@ -153,6 +165,10 @@ impl<'me> Marker<'me> { return; }; + for reservation in &valid.reservations { + self.mark_reservation(*reservation); + } + for tenant in &valid.tenants { self.mark_permission(*tenant); } @@ -161,7 +177,7 @@ impl<'me> Marker<'me> { impl Stepper<'_> { #[tracing::instrument(level = "Debug", skip(self))] - fn sweep(&mut self, marks: &Marks) { + fn sweep(&mut self, marks: &Marks) -> eyre::Result<()> { let mut live_permissions = self.machine.all_permissions(); let mut dead_permissions = live_permissions.clone(); live_permissions.retain(|p| marks.live_permissions.contains(p)); @@ -170,7 +186,7 @@ impl Stepper<'_> { // First: revoke all the dead permissions. for &p in &dead_permissions { tracing::debug!("revoking dead permission {:?}", p); - self.revoke(p); + self.revoke(p)?; } // Next: remove them from the heap. @@ -194,5 +210,15 @@ impl Stepper<'_> { let data = self.machine.take_object(o); tracing::debug!("freeing {:?}: {:?}", o, data); } + + let mut dead_reservations = self.machine.all_reservations(); + dead_reservations.retain(|r| !marks.live_reservations.contains(r)); + + for &r in &dead_reservations { + let data = self.machine.take_reservation(r); + tracing::debug!("freeing {:?}: {:?}", r, data); + } + + Ok(()) } } diff --git a/components/dada-execute/src/step/give.rs b/components/dada-execute/src/step/give.rs index 65d84d66..dcda28a0 100644 --- a/components/dada-execute/src/step/give.rs +++ b/components/dada-execute/src/step/give.rs @@ -1,6 +1,6 @@ use dada_ir::{ code::bir, - storage_mode::{Joint, Leased}, + storage::{Joint, Leased}, }; use crate::machine::{ValidPermissionData, Value}; @@ -20,18 +20,21 @@ impl Stepper<'_> { /// Note that -- unlike sharing and leasing -- giving does NOT ensure that `place` remains /// valid afterwards! In particular, if you give something that you own, the only way to /// preserve both its ownership/sharing properties is to remove the original. + #[tracing::instrument(level = "Debug", skip(self, table))] pub(super) fn give_place( &mut self, table: &bir::Tables, place: bir::Place, ) -> eyre::Result { let object_traversal = self.traverse_to_object(table, place)?; - self.give_traversal(object_traversal) + let object_traversal = self.confirm_reservation_if_any(table, object_traversal)?; + self.give_traversal(table, object_traversal) } #[tracing::instrument(level = "debug", skip(self))] pub(super) fn give_traversal( &mut self, + table: &bir::Tables, object_traversal: ObjectTraversal, ) -> eyre::Result { // Giving something that is jointly accessible is handled via sharing. @@ -70,17 +73,8 @@ impl Stepper<'_> { // The value at `place` is exclusively owned: cancel the old permission (and any tenants) // create a new one to return. + let object = self.take_object(object_traversal)?; - // This counts as a write to the object. - self.write_object(&object_traversal); - - let ObjectTraversal { - object, - accumulated_permissions, - } = object_traversal; - - let last_permission = *accumulated_permissions.traversed.last().unwrap(); - self.revoke(last_permission); let permission = self.machine.new_permission(ValidPermissionData::my()); Ok(Value { object, permission }) } diff --git a/components/dada-execute/src/step/intrinsic.rs b/components/dada-execute/src/step/intrinsic.rs index 3e31274b..1b3fa800 100644 --- a/components/dada-execute/src/step/intrinsic.rs +++ b/components/dada-execute/src/step/intrinsic.rs @@ -1,10 +1,10 @@ -use dada_ir::{error, intrinsic::Intrinsic, word::Word}; +use dada_ir::{error, intrinsic::Intrinsic, storage::Specifier, word::Word}; use eyre::Context; use crate::{ error::DiagnosticBuilderExt, machine::stringify::DefaultStringify, - machine::{op::MachineOpExt, ProgramCounter, Value}, + machine::{op::MachineOpExtMut, ProgramCounter, Value}, thunk::RustThunk, }; @@ -14,6 +14,7 @@ pub(crate) type IntrinsicFn = fn(&mut Stepper<'_>, Vec) -> eyre::Result, + pub(crate) argument_specifiers: Vec, pub(crate) function: IntrinsicFn, } @@ -22,6 +23,7 @@ impl IntrinsicDefinition { match intrinsic { Intrinsic::Print => IntrinsicDefinition { argument_names: vec![Word::from(db, "message")], + argument_specifiers: vec![Specifier::Any], function: |s, v| s.intrinsic_print(v), // FIXME: Stepper::intrinsic_write doesn't type check, why? }, @@ -53,12 +55,13 @@ impl Stepper<'_> { .my_value(RustThunk::new("print", values, Intrinsic::Print))) } + #[tracing::instrument(level = "Debug", skip(self, await_pc))] pub(super) async fn intrinsic_print_async( &mut self, await_pc: ProgramCounter, value: Value, ) -> eyre::Result { - let message_str = DefaultStringify::stringify(&*self.machine, self.db, value); + let message_str = DefaultStringify::stringify_value(&*self.machine, self.db, value); async { self.kernel diff --git a/components/dada-execute/src/step/lease.rs b/components/dada-execute/src/step/lease.rs index 5554b8ad..261771d6 100644 --- a/components/dada-execute/src/step/lease.rs +++ b/components/dada-execute/src/step/lease.rs @@ -1,6 +1,6 @@ use dada_ir::{ code::bir, - storage_mode::{Joint, Leased}, + storage::{Joint, Leased}, }; use crate::machine::{ValidPermissionData, Value}; @@ -23,17 +23,20 @@ impl Stepper<'_> { /// /// The following invariants are maintained: /// - /// * Results in a leased value that refers to the same object as `place` + /// * Results in a value `v` that refers to the same object as `place` /// * Preserves the sharing properties of `place`: /// * If `place` is jointly accessible, result will be jointly accessible /// * If `place` is exclusive, result will be exclusive - /// * `place` remains valid and unchanged; asserting `place` or invalidating it may invalidate the result + /// * `place` remains valid and unchanged; asserting `place` or invalidating + /// it may invalidate the result + #[tracing::instrument(level = "Debug", skip(self, table))] pub(super) fn lease_place( &mut self, table: &bir::Tables, place: bir::Place, ) -> eyre::Result { let object_traversal = self.traverse_to_object(table, place)?; + let object_traversal = self.confirm_reservation_if_any(table, object_traversal)?; self.lease_traversal(object_traversal) } @@ -42,19 +45,26 @@ impl Stepper<'_> { &mut self, object_traversal: ObjectTraversal, ) -> eyre::Result { + // Leasing something that is shared is akin to read it; + // leasing something that is exclusive is akin to writing it. + match object_traversal.accumulated_permissions.joint { + Joint::No => self.write_object(&object_traversal)?, + Joint::Yes => { + self.read(&object_traversal)?; + } + } + let ObjectTraversal { object, accumulated_permissions, } = object_traversal; - // XXX assert the traversed edges are valid for lease (possibly joint lease) - // The last traversed permission is the one that led to the object // (and there must be one, because you can't reach an object without // traversing at least one permission). let last_permission = *accumulated_permissions.traversed.last().unwrap(); - // Special case: If last permission is already a shared lease, we can just duplicate it + // Special case: If last permission is already shared, we can just duplicate it // // # Example // @@ -74,9 +84,7 @@ impl Stepper<'_> { // * Preserves sharing properties. // * `place` is not altered. if let ValidPermissionData { - joint: Joint::Yes, - leased: Leased::Yes, - .. + joint: Joint::Yes, .. } = self.machine[last_permission].assert_valid() { return Ok(Value { @@ -85,6 +93,16 @@ impl Stepper<'_> { }); } + // If the input is `our`, clone it. Note that this preserves all the invariants, + // even though it results in an owned value. + if let (Joint::Yes, Leased::No) = ( + accumulated_permissions.joint, + accumulated_permissions.leased, + ) { + let permission = self.machine.new_permission(ValidPermissionData::our()); + return Ok(Value { object, permission }); + } + // Otherwise, allocate a new lease permission; if we have exclusive // access along this path, make it exclusive, but joint otherwise. // diff --git a/components/dada-execute/src/step/reserve.rs b/components/dada-execute/src/step/reserve.rs new file mode 100644 index 00000000..54c74241 --- /dev/null +++ b/components/dada-execute/src/step/reserve.rs @@ -0,0 +1,72 @@ +use dada_ir::{code::bir, storage::Joint}; + +use crate::machine::{ + op::MachineOpExtMut, ObjectData, Permission, PermissionData, Reservation, ReservationData, + Value, +}; + +use super::Stepper; + +impl Stepper<'_> { + /// The `reserve` operation + #[tracing::instrument(level = "Debug", skip(self, table))] + pub(super) fn reserve_place( + &mut self, + table: &bir::Tables, + place: bir::Place, + ) -> eyre::Result { + let object_traversal = self.traverse_to_object(table, place)?; + + tracing::debug!(?object_traversal, "object_traversal"); + + // If the object is jointly accessible, then we can just share it and hold + // on to that. + if let Joint::Yes = object_traversal.accumulated_permissions.joint { + return self.share_traversal(object_traversal); + } + + // Otherwise, we have to reserve the place. + + let frame_index = self.machine.top_frame_index().unwrap(); + + let reservation = self.machine.new_reservation(ReservationData { + pc: self.machine.pc(), + frame_index, + place, + }); + + for &permission in &object_traversal.accumulated_permissions.traversed { + tracing::debug!("adding reservation {:?} to {:?}", reservation, permission); + + let PermissionData::Valid(valid) = &mut self.machine[permission] else { + panic!("traversed expired permision `{permission:?}`"); + }; + + valid.reservations.push(reservation); + } + + Ok(self.machine.my_value(ObjectData::Reservation(reservation))) + } + + /// Removes the given reservation from the given permisions. + /// Panics if those permissions are not reserved with this reservation. + #[tracing::instrument(level = "Debug", skip(self))] + pub(super) fn remove_reservations( + &mut self, + reservation: Reservation, + traversed: &[Permission], + ) -> eyre::Result<()> { + for &permission in traversed { + let PermissionData::Valid(valid) = &mut self.machine[permission] else { + panic!("traversed expired permision `{permission:?}`"); + }; + + let Some(index) = valid.reservations.iter().position(|r| *r == reservation) else { + panic!("reservation not found") + }; + + valid.reservations.remove(index); + } + Ok(()) + } +} diff --git a/components/dada-execute/src/step/revoke.rs b/components/dada-execute/src/step/revoke.rs index b90dde86..139cb872 100644 --- a/components/dada-execute/src/step/revoke.rs +++ b/components/dada-execute/src/step/revoke.rs @@ -1,21 +1,44 @@ -use dada_ir::storage_mode::Joint; +use dada_ir::{error, storage::Joint}; -use crate::machine::{Permission, PermissionData, ValidPermissionData}; +use crate::{ + error::DiagnosticBuilderExt, + machine::{Permission, PermissionData, ValidPermissionData}, +}; use super::Stepper; impl Stepper<'_> { /// Revokes the given permission, recording the current PC as the "reason". #[tracing::instrument(level = "Debug", skip(self))] - pub(super) fn revoke(&mut self, permission: Permission) { + pub(super) fn revoke(&mut self, permission: Permission) -> eyre::Result<()> { let pc = self.machine.opt_pc(); let p = std::mem::replace(&mut self.machine[permission], PermissionData::Expired(pc)); - if let PermissionData::Valid(ValidPermissionData { tenants, .. }) = p { + if let PermissionData::Valid(ValidPermissionData { + tenants, + reservations, + .. + }) = p + { + if let Some(reservation) = reservations.into_iter().next() { + let data = &self.machine[reservation]; + let error_pc = pc.unwrap_or(data.pc); + let error_span = error_pc.span(self.db); + return Err(error!( + error_span, + "you can't overwrite this value, it is reserved right now" + ) + .primary_label("attempting to invalidate reservation here") + .secondary_label(data.pc.span(self.db), "reservation was placed here") + .eyre(self.db)); + } + for tenant in tenants { - self.revoke(tenant); + self.revoke(tenant)?; } } + + Ok(()) } /// True if the permission `p` is currently sharing access to the object's @@ -34,19 +57,21 @@ impl Stepper<'_> { } #[tracing::instrument(level = "Debug", skip(self))] - pub(super) fn revoke_tenants(&mut self, permission: Permission) { + pub(super) fn revoke_tenants(&mut self, permission: Permission) -> eyre::Result<()> { // Temporarily swap out the data for `permission`... let mut p = std::mem::replace(&mut self.machine[permission], PermissionData::Expired(None)); // Cancel all the tenants and clear the list if let PermissionData::Valid(ValidPermissionData { tenants, .. }) = &mut p { for tenant in std::mem::take(tenants) { - self.revoke(tenant); + self.revoke(tenant)?; } } // Put the (modified) data for `p` back self.machine[permission] = p; + + Ok(()) } /// Revoke any tenant of `permission` that is not currently @@ -57,23 +82,29 @@ impl Stepper<'_> { /// /// (There should be at most one such tenant.) #[tracing::instrument(level = "Debug", skip(self))] - pub(super) fn revoke_exclusive_tenants(&mut self, permission: Permission) { + pub(super) fn revoke_exclusive_tenants(&mut self, permission: Permission) -> eyre::Result<()> { // Temporarily swap out the data for `permission`... let mut p = std::mem::replace(&mut self.machine[permission], PermissionData::Expired(None)); // Cancel all the exclusive tenants and remove them from the list if let PermissionData::Valid(ValidPermissionData { tenants, .. }) = &mut p { + let mut result = Ok(()); tenants.retain(|&tenant| { - if !self.is_sharing_access(tenant) { - self.revoke(tenant); + if result.is_err() { + true + } else if !self.is_sharing_access(tenant) { + result = self.revoke(tenant); false } else { true } }); + result?; } // Put the (modified) data for `p` back self.machine[permission] = p; + + Ok(()) } } diff --git a/components/dada-execute/src/step/share.rs b/components/dada-execute/src/step/share.rs index 6e719b87..24930ad6 100644 --- a/components/dada-execute/src/step/share.rs +++ b/components/dada-execute/src/step/share.rs @@ -1,9 +1,9 @@ use dada_ir::{ code::bir, - storage_mode::{Joint, Leased}, + storage::{Joint, Leased}, }; -use crate::machine::Value; +use crate::machine::{ValidPermissionData, Value}; use super::{traversal::ObjectTraversal, Stepper}; @@ -31,29 +31,56 @@ impl Stepper<'_> { /// Implication: /// /// * Sharing a shared thing is effectively "cloning" it, in the Rust sense + #[tracing::instrument(level = "Debug", skip(self, table))] pub(super) fn share_place( &mut self, table: &bir::Tables, place: bir::Place, ) -> eyre::Result { let object_traversal = self.traverse_to_object(table, place)?; + let object_traversal = self.confirm_reservation_if_any(table, object_traversal)?; self.share_traversal(object_traversal) } + /// Equivalent to `foo.lease.share`, used when assigning to an `our shared` location. + #[tracing::instrument(level = "Debug", skip(self, table))] + pub(super) fn share_leased_place( + &mut self, + table: &bir::Tables, + place: bir::Place, + ) -> eyre::Result { + let object_traversal = self.traverse_to_object(table, place)?; + let object_traversal = self.confirm_reservation_if_any(table, object_traversal)?; + tracing::debug!(?object_traversal); + if let Joint::Yes = object_traversal.accumulated_permissions.joint { + self.lease_traversal(object_traversal) + } else if let Leased::Yes = object_traversal.accumulated_permissions.leased { + self.share_traversal(object_traversal) + } else { + // Fully owned: first lease it, which will create a new tenant. + let Value { + object, + permission: leased_permission, + } = self.lease_traversal(object_traversal)?; + + // Then create a shared sublease of `permission`. + let shared_permission = self.new_tenant_permission(Joint::Yes, leased_permission); + Ok(Value { + object, + permission: shared_permission, + }) + } + } + #[tracing::instrument(level = "debug", skip(self))] pub(super) fn share_traversal(&mut self, traversal: ObjectTraversal) -> eyre::Result { // Sharing counts as a read of the data being shared. - self.read(&traversal); - - let ObjectTraversal { - object, - accumulated_permissions, - } = traversal; + self.read(&traversal)?; // The last traversed permission is the one that led to the object // (and there must be one, because you can't reach an object without // traversing at least one permission). - let last_permission = *accumulated_permissions.traversed.last().unwrap(); + let last_permission = *traversal.accumulated_permissions.traversed.last().unwrap(); // Special case, for simplicity and efficiency: If the final permission to the object // is joint, we can just duplicate it. The resulting permission @@ -73,138 +100,182 @@ impl Stepper<'_> { // ``` if let Joint::Yes = self.machine[last_permission].assert_valid().joint { return Ok(Value { - object, + object: traversal.object, permission: last_permission, }); } - // Otherwise, if this path owns the object, we can convert that last permission - // to be joint. - // - // # Examples - // - // Sharing `a.f` in this scenario... - // - // ```notrust - // a -my-> [ Obj ] - // [ f ] --my------> b - // ``` - // - // ...modifies the existing permission to `b` - // and then duplicates it, yielding (respectively)... - // - // ```notrust - // a -my-> [ Obj ] - // [ f ] --our-----> b - // === ^ - // converted... | - // | - // [] --our-------+ - // ============= ...then duplicated - // to create this - // ``` - // - // Another example: - // - // ```notrust - // a -our-> [ Obj ] - // [ f ] --my------> b - // ``` - // - // becomes - // - // ```notrust - // a -our-> [ Obj ] - // [ f ] --our-----> b - // === ^ - // converted... | - // | - // [] --our-------+ - // ============= ...then duplicated - // to create this - // ``` - // - // # Justification and discussion - // - // This preserves the invariants: - // - // * Result is shared - // * Result is owned, just like input - // * Original path can be read without disturbing input - // - // It mildly surprised me at first to convert the original path from `my` to `our`, but it's the only way to preserve the invariants: - // - // * If we were to sublease, result would not be owned, so when original was dropped, result would become invalid. But we promised - // result is owned iff input is owned. - // * If we were to revoke the `my` permission, you would no longer be able to read the original path, but we promised original path remains valid. - // * If we *just* created a new permission, the `my` permission would be invalid, but we promised original path remains valid. - // - // It's also what you want for something like `p = Point(22, 44).share`. - if let Leased::No = accumulated_permissions.leased { - self.machine[last_permission].assert_valid_mut().joint = Joint::Yes; - - return Ok(Value { - object, - permission: last_permission, - }); - } + match ( + traversal.accumulated_permissions.leased, + traversal.accumulated_permissions.joint, + ) { + // If this path has (exclusive) ownership, we revoke + // that permission to create shared ownership. + // + // We have to revoke the existing permission because the path + // may be typed with a `my` type, and that cannot co-exist with + // the object having a shared permission.. + // + // # Examples + // + // Sharing `a.f` in this scenario... + // + // ```notrust + // a -my-> [ Obj ] + // [ f ] --my------> b + // ``` + // + // ...revokes the old permission and creates a new one: + // + // ```notrust + // a -my-> [ Obj ] + // [ f ] --X b + // === ^ + // revoked | + // | + // [] --our-------+ + // ============= + // ...and this permission + // is created + // ``` + (Leased::No, Joint::No) => { + let object = self.take_object(traversal)?; + let permission = self.machine.new_permission(ValidPermissionData::our()); + Ok(Value { object, permission }) + } - // Otherwise, we don't own the object, so create a joint leased permission. - // This will remain valid so long as the lease is valid (and not re-asserted). - // - // # Examples - // - // Sharing `a.f` in this scenarios... - // - // ```notrust - // a -leased-> [ Obj ] - // [ f ] --my------> b - // ``` - // - // ...creates a shared sublease of the `my` permission: - // - // ```notrust - // a -leased-> [ Obj ] - // [ f ] --my------> b - // : ^ - // : tenant | - // v | - // [] --shared----+ - // ``` - // - // Another example: - // - // ```notrust - // a -my-> [ Obj ] - // [ f ] --leased--> b - // ``` - // - // becomes - // - // ```notrust - // a -my-> [ Obj ] - // [ f ] --leased--> b - // : ^ - // : tenant | - // v | - // [] --shared----+ - // ``` - // - // ## Note - // - // Because of the special case for a "last permission", this case - // is handled differently: - // - // ```notrust - // a -leased-> [ Obj ] - // [ f ] --shared----> b - // ``` - // - // Instead of creating a tenant of the final shared permission, - // we simply clone it. But creating a tenant would be "ok" too, - // just less efficient and maybe a bit confusing. - let permission = self.new_tenant_permission(Joint::Yes, last_permission); + // If object is owned + shared, then we can just create a fresh + // our permission. Note that, because we detected the special case + // where the last step was a "shared" permission above, and because + // we know the object is not leased, the `last_permission` here must + // be a `my` permission, as in this example: + // + // ```notrust + // a -our-> [ Obj ] + // [ f ] --my------> b + // ``` + // + // becomes + // + // ```notrust + // a -our-> [ Obj ] + // [ f ] --my------> b + // ^ + // | + // | + // [] --our-------+ + // ``` + // + // Justification: + // + // * Object is already in a shared ownership state, so we can duplicate those + // permissions at will. + // + // Implications: + // + // * The only way to limit sharing is with leasing: even if you dynamically test + // the ref count of `a`, you cannot "unwrap" it and return to "sole ownership" + // state, because there may be extant references to the data that it owned. + // * `our [String]` is therefore very different from `Rc>`. + (Leased::No, Joint::Yes) => { + let permission = self.machine.new_permission(ValidPermissionData::our()); + Ok(Value { + object: traversal.object, + permission, + }) + } - Ok(Value { object, permission }) + // We don't own the object, so create a joint leased permission. + // This will remain valid so long as the lease is valid (and not re-asserted). + // + // # Examples + // + // Sharing `a.f` in this scenarios... + // + // ```notrust + // a -leased-> [ Obj ] + // [ f ] --my------> b + // ``` + // + // ...creates a shared sublease of the `my` permission: + // + // ```notrust + // a -leased-> [ Obj ] + // [ f ] --my------> b + // : ^ + // : tenant | + // v | + // [] --shared----+ + // ``` + // + // Another example: + // + // ```notrust + // a -my-> [ Obj ] + // [ f ] --leased--> b + // ``` + // + // becomes + // + // ```notrust + // a -my-> [ Obj ] + // [ f ] --leased--> b + // : ^ + // : tenant | + // v | + // [] --shared----+ + // ``` + // + // ## Why not change the final lease to shared? + // + // When sharing something that is leased, we create a sublease rather + // rather converting the lease itself to be a shared lease. The answer + // is that this final lease may not be under our control. + // + // Consider this example: + // + // ```notrust + // a -leased-> [ Obj ] + // [ f ] --leased--> b + // ``` + // + // Here, there are two leases at play. `a` is itself leased, and it + // contains a leased reference to `b`. This implies that *somewhere else*, + // there is an *owner* for `a`. Let's call them `o`. So `o` owns an + // object which has a leased value to `b`, and they've leased it out to `a`. + // They expect to be able to re-assert control over that object at any + // time and find it in the same state in which they left it. + // If we permitted `a` to convert the lease to `b` into a shared lease, + // that would violate `o`'s expectations. + // + // In other words, we want the owner of `o` to be able to do this: + // + // a = o.lease + // arbitraryCode(a) + // o.f.field += 1 # requires leased access + // + // and have it work, without concern for what `arbitraryCode` may do. + // + // ## Note + // + // Because of the special case for a "last permission", this case + // is handled differently: + // + // ```notrust + // a -leased-> [ Obj ] + // [ f ] --shared----> b + // ``` + // + // Instead of creating a tenant of the final shared permission, + // we simply clone it. But creating a tenant would be "ok" too, + // just less efficient and maybe a bit confusing. + (Leased::Yes, _) => { + let permission = self.new_tenant_permission(Joint::Yes, last_permission); + Ok(Value { + object: traversal.object, + permission, + }) + } + } } } diff --git a/components/dada-execute/src/step/tenant.rs b/components/dada-execute/src/step/tenant.rs index 57dc8cd9..77268c62 100644 --- a/components/dada-execute/src/step/tenant.rs +++ b/components/dada-execute/src/step/tenant.rs @@ -1,4 +1,4 @@ -use dada_ir::storage_mode::{Joint, Leased}; +use dada_ir::storage::{Joint, Leased}; use crate::machine::{Permission, ValidPermissionData}; @@ -10,6 +10,7 @@ impl Stepper<'_> { let permission = self.machine.new_permission(ValidPermissionData { joint, leased: Leased::Yes, + reservations: vec![], tenants: vec![], }); diff --git a/components/dada-execute/src/step/traversal.rs b/components/dada-execute/src/step/traversal.rs index a43d5486..55b1d925 100644 --- a/components/dada-execute/src/step/traversal.rs +++ b/components/dada-execute/src/step/traversal.rs @@ -1,9 +1,14 @@ use dada_id::prelude::*; use dada_ir::{ class::Class, - code::bir, + code::{ + bir::{self, LocalVariable}, + syntax, + }, error, - storage_mode::{Atomic, Joint, Leased}, + origin_table::HasOriginIn, + span::FileSpan, + storage::{Atomic, Joint, Leased}, word::Word, }; use dada_parse::prelude::DadaParseClassExt; @@ -11,7 +16,10 @@ use dada_parse::prelude::DadaParseClassExt; use crate::{ error::DiagnosticBuilderExt, ext::DadaExecuteClassExt, - machine::{op::MachineOpExt, Object, ObjectData, Permission, PermissionData, Value}, + machine::{ + op::MachineOpExtMut, Object, ObjectData, Permission, PermissionData, ProgramCounter, + ReservationData, Value, + }, }; use super::{address::Address, Stepper}; @@ -39,6 +47,19 @@ pub(super) struct AccumulatedPermissions { pub(super) atomic: Atomic, } +impl AccumulatedPermissions { + /// Returns the permisions one would have when accessing + /// a uniquely owned location with the given "atomic"-ness. + pub fn unique(atomic: Atomic) -> Self { + AccumulatedPermissions { + traversed: vec![], + leased: Leased::No, + joint: Joint::No, + atomic, + } + } +} + /// A traversal to a place (i.e., a traversal that terminates /// in the location of a value). This is in contrast /// to `ObjectTraversal`, which represents a traversal @@ -106,19 +127,7 @@ impl Stepper<'_> { place: bir::Place, ) -> eyre::Result { match place.data(table) { - bir::PlaceData::LocalVariable(lv) => { - let lv_data = lv.data(table); - let permissions = AccumulatedPermissions { - traversed: vec![], - leased: Leased::No, - joint: Joint::No, - atomic: lv_data.atomic, - }; - Ok(PlaceTraversal { - accumulated_permissions: permissions, - address: Address::Local(*lv), - }) - } + bir::PlaceData::LocalVariable(lv) => Ok(self.traverse_to_local_variable(table, *lv)), bir::PlaceData::Function(f) => Ok(self.traverse_to_constant(ObjectData::Function(*f))), bir::PlaceData::Class(c) => Ok(self.traverse_to_constant(ObjectData::Class(*c))), @@ -134,18 +143,31 @@ impl Stepper<'_> { let (owner_class, field_index) = self.object_field(place, owner_object, *field_name)?; - // Take the field mod einto account - let field = &owner_class.fields(db)[field_index]; + // Take the field mode into account + let field = owner_class.fields(db)[field_index]; accumulated_permissions.atomic |= field.decl(db).atomic; Ok(PlaceTraversal { accumulated_permissions, - address: Address::Field(owner_object, field_index), + address: Address::Field(owner_object, field_index, Some(field)), }) } } } + pub(super) fn traverse_to_local_variable( + &mut self, + table: &bir::Tables, + lv: LocalVariable, + ) -> PlaceTraversal { + let lv_data = lv.data(table); + let permissions = AccumulatedPermissions::unique(lv_data.atomic); + PlaceTraversal { + accumulated_permissions: permissions, + address: Address::Local(lv), + } + } + /// Returns a traversal that reaches the object located at `place`. /// This includes and accounts for the permissions from the reference /// to the object. @@ -160,20 +182,85 @@ impl Stepper<'_> { ) -> eyre::Result { let PlaceTraversal { accumulated_permissions, - address: place, + address, } = self.traverse_to_place(table, bir_place)?; - let Value { permission, object } = self.peek(place); + let Value { permission, object } = self.peek(address); let permissions = self.accumulate_permission(bir_place, accumulated_permissions, permission)?; + Ok(ObjectTraversal { accumulated_permissions: permissions, object, }) } + pub(super) fn traverse_to_object_field( + &mut self, + place: impl HasOriginIn, + object_traversal: ObjectTraversal, + field_name: Word, + ) -> eyre::Result { + let ObjectTraversal { + mut accumulated_permissions, + object: owner_object, + } = object_traversal; + let (owner_class, field_index) = self.object_field(place, owner_object, field_name)?; + + // Take the field mode into account + let field = owner_class.fields(self.db)[field_index]; + accumulated_permissions.atomic |= field.decl(self.db).atomic; + + Ok(PlaceTraversal { + accumulated_permissions, + address: Address::Field(owner_object, field_index, Some(field)), + }) + } + + /// If `traversal` reaches a reservation, then "confirm" the reservation by + /// traversing to the place that was reversal, removing the reservation from + /// each permission along the way, and returning that traversal. + /// + /// Otherwise return `traversal` unchanged. + #[tracing::instrument(level = "Debug", skip(self, table))] + pub(super) fn confirm_reservation_if_any( + &mut self, + table: &bir::Tables, + traversal: ObjectTraversal, + ) -> eyre::Result { + let ObjectData::Reservation(_) = self.machine[traversal.object] else { + return Ok(traversal); + }; + + let (Joint::No, Leased::No) = (traversal.accumulated_permissions.joint, traversal.accumulated_permissions.leased) else { + // our codegen doesn't ever share or lease a temporary containing a reservation + panic!("expected to find reservation in a `my` location"); + }; + + let object = self.take_object(traversal)?; + let ObjectData::Reservation(reservation) = self.machine[object] else { + panic!("object data changed from a reservation"); + }; + let ReservationData { + pc: _, + frame_index, + place: reserved_place, + } = self.machine.take_reservation(reservation); + + assert_eq!( + frame_index, + self.machine.top_frame_index().unwrap(), + "reservation `{reservation:?}` escaped its frame" + ); + + // the reservation should ensure that this place is still valid + let retraversal = self.traverse_to_object(table, reserved_place).unwrap(); + self.remove_reservations(reservation, &retraversal.accumulated_permissions.traversed)?; + Ok(retraversal) + } + fn object_field( &mut self, - place: bir::Place, + place: impl HasOriginIn, owner_object: Object, field_name: Word, ) -> eyre::Result<(Class, usize)> { @@ -232,26 +319,10 @@ impl Stepper<'_> { let atomic = accumulated_permissions.atomic; match &self.machine[permission] { - PermissionData::Expired(None) => { + PermissionData::Expired(expired_at) => { + tracing::debug!("encountered expired permission: {:?}", permission); let place_span = self.span_from_bir(place); - Err(error!(place_span, "accessing uninitialized memory").eyre(self.db)) - } - PermissionData::Expired(Some(expired_at)) => { - let place_span = self.span_from_bir(place); - let expired_at_span = expired_at.span(self.db); - - let secondary_label = if expired_at.is_return(self.db) { - "lease was cancelled when this function returned" - } else { - "lease was cancelled here" - }; - - Err( - error!(place_span, "your lease to this object was cancelled") - .primary_label("cancelled lease used here") - .secondary_label(expired_at_span, secondary_label) - .eyre(self.db), - ) + Err(self.report_traversing_expired_permission(place_span, *expired_at)) } PermissionData::Valid(v) => { match v.joint { @@ -291,6 +362,30 @@ impl Stepper<'_> { } } } + + pub(super) fn report_traversing_expired_permission( + &self, + place_span: FileSpan, + expired_at: Option, + ) -> eyre::Report { + match expired_at { + None => error!(place_span, "accessing uninitialized memory").eyre(self.db), + Some(expired_at) => { + let expired_at_span = expired_at.span(self.db); + + let secondary_label = if expired_at.is_return(self.db) { + "lease was cancelled when this function returned" + } else { + "lease was cancelled here" + }; + + error!(place_span, "your lease to this object was cancelled") + .primary_label("cancelled lease used here") + .secondary_label(expired_at_span, secondary_label) + .eyre(self.db) + } + } + } } #[extension_trait::extension_trait] diff --git a/components/dada-ir/src/class.rs b/components/dada-ir/src/class.rs index e2309ae2..a18d3382 100644 --- a/components/dada-ir/src/class.rs +++ b/components/dada-ir/src/class.rs @@ -1,15 +1,12 @@ -use crate::{span::FileSpan, token_tree::TokenTree, word::Word}; +use crate::{span::FileSpan, token_tree::TokenTree, word::SpannedWord}; salsa::entity2! { entity Class in crate::Jar { - #[id] name: Word, + #[id] name: SpannedWord, field_tokens: TokenTree, /// Overall span of the class (including any body) span: FileSpan, - - /// Span of the class name specifically - name_span: FileSpan, } } diff --git a/components/dada-ir/src/code/bir.rs b/components/dada-ir/src/code/bir.rs index 8751d651..ae58b467 100644 --- a/components/dada-ir/src/code/bir.rs +++ b/components/dada-ir/src/code/bir.rs @@ -10,7 +10,7 @@ use crate::{ intrinsic::Intrinsic, origin_table::HasOriginIn, prelude::InIrDbExt, - storage_mode::Atomic, + storage::{Atomic, SpannedSpecifier}, word::{SpannedOptionalWord, Word}, }; use dada_id::{id, prelude::*, tables}; @@ -113,6 +113,7 @@ tables! { terminators: alloc Terminator => TerminatorData, exprs: alloc Expr => ExprData, places: alloc Place => PlaceData, + target_places: alloc TargetPlace => TargetPlaceData, } } @@ -130,6 +131,7 @@ origin_table! { terminator: Terminator => syntax::Expr, expr: Expr => syntax::Expr, place: Place => syntax::Expr, + target_place: TargetPlace => syntax::Expr, } } @@ -150,6 +152,13 @@ pub struct LocalVariableData { /// If it is None, then this is a temporary /// introduced by the compiler. pub name: Option, + + /// Specifier given this variable by the user + /// (possibly defaulted). If this is `None`, + /// then this is a temporary introduced by the compiler, + /// and it gets the specifier `Any`. + pub specifier: Option, + pub atomic: Atomic, } @@ -217,7 +226,24 @@ impl DebugWithDb> for Statement { #[derive(PartialEq, Eq, Clone, Hash, Debug)] pub enum StatementData { - Assign(Place, Expr), + /// Assign the result of evaluating an expression to a place. + /// This is the preferred form of assignment, and covers + /// cases like `a := b` as well as `a := 22`. In these case, either + /// (a) we know statically the declared mode for `a` and so we + /// can prepare an expression like `b.give` or `b.lease` in advance + /// or (b) the rhs is an rvalue, like `22`, and so is always given. + AssignExpr(TargetPlace, Expr), + + /// Captures an assignment like + /// + /// ```notrust + /// foo.bar := baz + /// ``` + /// + /// This case is challenging because, until we know + /// the declared type of `bar` at runtime, we don't + /// know whether to give `baz`, lease it, or what. + AssignPlace(TargetPlace, Place), /// Clears the value from the given local variable. Clear(LocalVariable), @@ -250,12 +276,18 @@ pub enum StatementData { impl DebugWithDb> for StatementData { fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &InIrDb<'_, Bir>) -> std::fmt::Result { match self { - StatementData::Assign(place, expr) => f - .debug_tuple("Assign") + StatementData::AssignExpr(place, expr) => f + .debug_tuple("AssignExpr") .field(&place.debug(db)) .field(&expr.debug(db)) .finish(), + StatementData::AssignPlace(target, source) => f + .debug_tuple("AssignPlace") + .field(&target.debug(db)) + .field(&source.debug(db)) + .finish(), + StatementData::Clear(lv) => f.debug_tuple("Clear").field(&lv.debug(db)).finish(), StatementData::BreakpointStart(filename, index) => f @@ -290,7 +322,7 @@ pub enum TerminatorData { StartAtomic(BasicBlock), EndAtomic(BasicBlock), Return(Place), - Assign(Place, TerminatorExpr, BasicBlock), + Assign(TargetPlace, TerminatorExpr, BasicBlock), Error, Panic, } @@ -384,8 +416,13 @@ pub enum ExprData { /// `"foo"` with no format strings StringLiteral(Word), + /// `expr.reserve` + /// + /// not (presently) actual syntax, emitted as part of lowering + Reserve(Place), + /// `expr.share` - GiveShare(Place), + Share(Place), /// `expr.lease` Lease(Place), @@ -418,7 +455,8 @@ impl DebugWithDb> for ExprData { ExprData::SignedIntegerLiteral(w) => write!(f, "{}", w), ExprData::StringLiteral(w) => write!(f, "{:?}", w.as_str(db.db())), ExprData::FloatLiteral(w) => write!(f, "{}", w), - ExprData::GiveShare(p) => write!(f, "{:?}.share", p.debug(db)), + ExprData::Reserve(p) => write!(f, "{:?}.reserve", p.debug(db)), + ExprData::Share(p) => write!(f, "{:?}.share", p.debug(db)), ExprData::Lease(p) => write!(f, "{:?}.lease", p.debug(db)), ExprData::Give(p) => write!(f, "{:?}.give", p.debug(db)), ExprData::Unit => write!(f, "()"), @@ -478,3 +516,26 @@ impl DebugWithDb> for PlaceData { } } } + +id!(pub struct TargetPlace); + +impl DebugWithDb> for TargetPlace { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &InIrDb<'_, Bir>) -> std::fmt::Result { + write!(f, "{:?}", self.data(db.tables()).debug(db)) + } +} + +#[derive(PartialEq, Eq, Clone, Hash, Debug)] +pub enum TargetPlaceData { + LocalVariable(LocalVariable), + Dot(Place, Word), +} + +impl DebugWithDb> for TargetPlaceData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &InIrDb<'_, Bir>) -> std::fmt::Result { + match self { + TargetPlaceData::LocalVariable(v) => write!(f, "{:?}", v.debug(db)), + TargetPlaceData::Dot(p, id) => write!(f, "{:?}.{}", p.debug(db), id.as_str(db.db())), + } + } +} diff --git a/components/dada-ir/src/code/syntax.rs b/components/dada-ir/src/code/syntax.rs index 6cd42e05..a7e0d676 100644 --- a/components/dada-ir/src/code/syntax.rs +++ b/components/dada-ir/src/code/syntax.rs @@ -3,7 +3,7 @@ use crate::{ in_ir_db::InIrDb, in_ir_db::InIrDbExt, span::Span, - storage_mode::Atomic, + storage::{Atomic, SpannedSpecifier}, word::{SpannedOptionalWord, Word}, }; use dada_id::{id, prelude::*, tables}; @@ -84,8 +84,7 @@ id!(pub struct Expr); impl DebugWithDb> for Expr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &InIrDb<'_, Tree>) -> std::fmt::Result { - f.debug_tuple("") - .field(self) + f.debug_tuple(&format!("{self:?}")) .field(&self.data(db.tables()).debug(db)) .finish() } @@ -257,6 +256,7 @@ impl DebugWithDb> for LocalVariableDecl { #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Debug)] pub struct LocalVariableDeclData { + pub specifier: SpannedSpecifier, pub atomic: Atomic, pub name: Word, pub ty: Option, @@ -264,9 +264,11 @@ pub struct LocalVariableDeclData { impl DebugWithDb> for LocalVariableDeclData { fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &InIrDb<'_, Tree>) -> std::fmt::Result { - f.debug_tuple("") - .field(&self.name.debug(db.db())) - .field(&self.ty.debug(db.db())) + f.debug_struct("LocalVariableDeclData") + .field("specifier", &self.specifier.specifier(db.db())) + .field("atomic", &self.atomic) + .field("name", &self.name.debug(db.db())) + .field("ty", &self.ty.debug(db.db())) .finish() } } @@ -293,8 +295,7 @@ pub struct NamedExprData { impl DebugWithDb> for NamedExprData { fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &InIrDb<'_, Tree>) -> std::fmt::Result { - f.debug_tuple("") - .field(&self.name.word(db.db()).debug(db.db())) + f.debug_tuple(&format!("{:?}", self.name.word(db.db()).debug(db.db()))) .field(&self.expr.debug(db)) .finish() } diff --git a/components/dada-ir/src/code/validated.rs b/components/dada-ir/src/code/validated.rs index 3d1c70d3..af34b126 100644 --- a/components/dada-ir/src/code/validated.rs +++ b/components/dada-ir/src/code/validated.rs @@ -9,7 +9,7 @@ use crate::{ in_ir_db::InIrDb, intrinsic::Intrinsic, prelude::InIrDbExt, - storage_mode::Atomic, + storage::{Atomic, SpannedSpecifier}, word::{SpannedOptionalWord, Word}, }; use dada_id::{id, prelude::*, tables}; @@ -89,6 +89,7 @@ tables! { exprs: alloc Expr => ExprData, named_exprs: alloc NamedExpr => NamedExprData, places: alloc Place => PlaceData, + target_places: alloc TargetPlace => TargetPlaceData, } } @@ -102,6 +103,7 @@ origin_table! { pub struct Origins { expr_spans: Expr => ExprOrigin, place_spans: Place => ExprOrigin, + target_place_spans: TargetPlace => ExprOrigin, named_exprs: NamedExpr => syntax::NamedExpr, local_variables: LocalVariable => LocalVariableOrigin, } @@ -111,7 +113,7 @@ origin_table! { /// lowering the syntax expressions. We track the expression they came /// from, but also the fact that they are synthetic. This is needed to /// help place cursors and so forth. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct ExprOrigin { pub syntax_expr: syntax::Expr, pub synthesized: bool, @@ -132,6 +134,21 @@ impl ExprOrigin { } } +impl std::fmt::Debug for ExprOrigin { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let ExprOrigin { + synthesized, + syntax_expr, + } = *self; + + if synthesized { + write!(fmt, "synthesized from {syntax_expr:?}") + } else { + write!(fmt, "from {syntax_expr:?}") + } + } +} + impl From for ExprOrigin { fn from(e: syntax::Expr) -> Self { Self::real(e) @@ -173,6 +190,11 @@ pub struct LocalVariableData { /// semantics. pub name: Option, + /// If `Some`, then contains specifier given by the + /// user. If `None`, this is a temporary, and its + /// specifier is effectively [`Specifier::Any`]. + pub specifier: Option, + pub atomic: Atomic, } @@ -187,8 +209,8 @@ id!(pub struct Expr); impl DebugWithDb> for Expr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &InIrDb<'_, Tree>) -> std::fmt::Result { - f.debug_tuple("") - .field(&self) + let name = format!("{:?}", self); + f.debug_tuple(&name) .field(&self.data(db.tables()).debug(db)) .field(&db.origins()[*self]) .finish() @@ -221,8 +243,11 @@ pub enum ExprData { /// `expr(id: expr, ...)` Call(Expr, Vec), + /// `expr.reserve` -- not legal syntax + Reserve(Place), + /// `expr.share` - Share(Place), + Share(Expr), /// `expr.lease` Lease(Place), @@ -246,10 +271,7 @@ pub enum ExprData { /// /// * `from_expr`: Identifies the loop from which we are breaking /// * `with_value`: The value produced by the loop - Break { - from_expr: Expr, - with_value: Expr, - }, + Break { from_expr: Expr, with_value: Expr }, /// `continue` /// @@ -265,10 +287,20 @@ pub enum ExprData { /// `a + b` Op(Expr, Op, Expr), + /// ` x` Unary(Op, Expr), - /// `a := b` - Assign(Place, Expr), + /// `a := b.give` -- it is important that this + /// is only used to create temporaries! This is because + /// we cannot apply all the potential specifiers to an expression + /// (e.g., we cannot lease it). To assign to user-declared variables + /// we must use `AssignFromPlace`. + AssignTemporary(LocalVariable, Expr), + + /// `a := b` -- used when the specifier (`my`, `our`, etc) is not known + /// statically, and we we can't determine whether the place should be + /// given, leased, or what + AssignFromPlace(TargetPlace, Place), /// Bring the variables in scope during the expression Declare(Vec, Expr), @@ -304,11 +336,12 @@ impl ExprData { .field(&expr.debug(db)) .field(&args.debug(db)) .finish(), - ExprData::Share(p) => f.debug_tuple("Share").field(p).finish(), - ExprData::Lease(p) => f.debug_tuple("Lease").field(p).finish(), - ExprData::Give(p) => f.debug_tuple("Give").field(p).finish(), + ExprData::Reserve(p) => f.debug_tuple("Reserve").field(&p.debug(db)).finish(), + ExprData::Share(p) => f.debug_tuple("Share").field(&p.debug(db)).finish(), + ExprData::Lease(p) => f.debug_tuple("Lease").field(&p.debug(db)).finish(), + ExprData::Give(p) => f.debug_tuple("Give").field(&p.debug(db)).finish(), ExprData::Tuple(exprs) => { - let mut f = f.debug_tuple(""); + let mut f = f.debug_tuple("Tuple"); for expr in exprs { f.field(&expr.debug(db)); } @@ -346,11 +379,16 @@ impl ExprData { .field(op) .field(&rhs.debug(db)) .finish(), - ExprData::Assign(place, expr) => f + ExprData::AssignTemporary(place, expr) => f .debug_tuple("Assign") .field(&place.debug(db)) .field(&expr.debug(db)) .finish(), + ExprData::AssignFromPlace(target, source) => f + .debug_tuple("AssignFromPlace") + .field(&target.debug(db)) + .field(&source.debug(db)) + .finish(), ExprData::Declare(vars, expr) => f .debug_tuple("Declare") .field(&vars.debug(db)) @@ -370,8 +408,8 @@ id!(pub struct Place); impl DebugWithDb> for Place { fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &InIrDb<'_, Tree>) -> std::fmt::Result { - f.debug_tuple("") - .field(&self) + let name = format!("{:?}", self); + f.debug_tuple(&name) .field(&self.data(db.tables()).debug(db)) .field(&db.origins()[*self]) .finish() @@ -403,6 +441,37 @@ impl DebugWithDb> for PlaceData { } } +id!(pub struct TargetPlace); + +impl DebugWithDb> for TargetPlace { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &InIrDb<'_, Tree>) -> std::fmt::Result { + let name = format!("{:?}", self); + f.debug_tuple(&name) + .field(&self.data(db.tables()).debug(db)) + .field(&db.origins()[*self]) + .finish() + } +} + +#[derive(PartialEq, Eq, Clone, Hash, Debug)] +pub enum TargetPlaceData { + LocalVariable(LocalVariable), + Dot(Place, Word), +} + +impl DebugWithDb> for TargetPlaceData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &InIrDb<'_, Tree>) -> std::fmt::Result { + match self { + TargetPlaceData::LocalVariable(lv) => DebugWithDb::fmt(lv, f, db), + TargetPlaceData::Dot(place, field) => f + .debug_tuple("Dot") + .field(&place.debug(db)) + .field(&field.debug(db.db())) + .finish(), + } + } +} + id!(pub struct NamedExpr); impl DebugWithDb> for NamedExpr { @@ -419,7 +488,7 @@ pub struct NamedExprData { impl DebugWithDb> for NamedExprData { fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &InIrDb<'_, Tree>) -> std::fmt::Result { - f.debug_tuple("") + f.debug_tuple("NamedExpr") .field(&self.name.debug(db.db())) .field(&self.expr.debug(db)) .finish() diff --git a/components/dada-ir/src/function.rs b/components/dada-ir/src/function.rs index cc826c7b..65e15b2a 100644 --- a/components/dada-ir/src/function.rs +++ b/components/dada-ir/src/function.rs @@ -1,16 +1,18 @@ -use crate::{code::Code, filename::Filename, span::FileSpan, word::Word}; +use crate::{ + code::Code, + filename::Filename, + span::FileSpan, + word::{SpannedWord, Word}, +}; salsa::entity2! { entity Function in crate::Jar { - #[id] name: Word, + #[id] name: SpannedWord, code: Code, /// Overall span of the function (including the code) span: FileSpan, - /// Span of the function name - name_span: FileSpan, - /// If this func has a declared effect, this is the span of that keyword (e.g., `async`) /// Otherwise, it is the span of the `fn` keyword. effect_span: FileSpan, diff --git a/components/dada-ir/src/item.rs b/components/dada-ir/src/item.rs index fde906a8..e1cd7d1b 100644 --- a/components/dada-ir/src/item.rs +++ b/components/dada-ir/src/item.rs @@ -16,15 +16,15 @@ impl Item { pub fn name(self, db: &dyn crate::Db) -> Word { match self { - Item::Function(f) => f.name(db), - Item::Class(c) => c.name(db), + Item::Function(f) => f.name(db).word(db), + Item::Class(c) => c.name(db).word(db), } } pub fn name_span(self, db: &dyn crate::Db) -> FileSpan { match self { - Item::Function(f) => f.name_span(db), - Item::Class(c) => c.name_span(db), + Item::Function(f) => f.name(db).span(db), + Item::Class(c) => c.name(db).span(db), } } diff --git a/components/dada-ir/src/kw.rs b/components/dada-ir/src/kw.rs index 404c9ed7..55990eb8 100644 --- a/components/dada-ir/src/kw.rs +++ b/components/dada-ir/src/kw.rs @@ -43,24 +43,26 @@ impl std::fmt::Display for Keyword { } define_keywords! { + Any => "any", + Async => "async", + Atomic => "atomic", + Await => "await", Class => "class", - Var => "var", + Else => "else", + False => "false", + Fn => "fn", Give => "give", - Share => "share", - Shared => "shared", + If => "if", Lease => "lease", Leased => "leased", - Atomic => "atomic", - Fn => "fn", - Async => "async", - Await => "await", - If => "if", - Else => "else", Loop => "loop", + My => "my", + Return => "return", + Share => "share", + Shared => "shared", True => "true", - False => "false", + Our => "our", While => "while", - Return => "return", } #[salsa::memoized(in crate::Jar ref)] diff --git a/components/dada-ir/src/lib.rs b/components/dada-ir/src/lib.rs index e9993d65..2f841761 100644 --- a/components/dada-ir/src/lib.rs +++ b/components/dada-ir/src/lib.rs @@ -17,7 +17,7 @@ pub mod manifest; pub mod parameter; pub mod prelude; pub mod span; -pub mod storage_mode; +pub mod storage; pub mod token; pub mod token_tree; pub mod ty; @@ -39,6 +39,7 @@ pub struct Jar( lines::line_table, manifest::source_text, parameter::Parameter, + storage::SpannedSpecifier, token_tree::TokenTree, ty::Ty, word::Word, diff --git a/components/dada-ir/src/storage.rs b/components/dada-ir/src/storage.rs new file mode 100644 index 00000000..095c9868 --- /dev/null +++ b/components/dada-ir/src/storage.rs @@ -0,0 +1,140 @@ +use crate::span::FileSpan; + +salsa::entity2! { + /// A "spanned spanned" is a `Specifier` that carries a span for diagnostics. + entity SpannedSpecifier in crate::Jar { + #[id] specifier: Specifier, + + /// If true, the specifier was not explicitly given by the user + /// but was defaulted. + defaulted: bool, + + /// Span of the specifier keywords, or storage name if specified was + /// defaulted. + span: FileSpan, + } +} + +impl SpannedSpecifier { + /// Creates a new `SpannedSpecifier` for a variable/field that didn't + /// have an explicit specifier. + pub fn new_defaulted(db: &dyn crate::Db, name_span: FileSpan) -> Self { + Self::new(db, Specifier::OurLeased, true, name_span) + } +} + +#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Hash, Debug)] +pub enum Specifier { + My, + Our, + Leased, + OurLeased, + Any, +} + +impl Specifier { + /// True if values stored under this specifier must be uniquely + /// accessible (my, leased) and cannot be jointly accessible (our, our leased). + /// + /// [`Specifier::Any`] returns false. + pub fn must_be_unique(self) -> bool { + match self { + Specifier::My | Specifier::Leased => true, + Specifier::Our | Specifier::OurLeased => false, + Specifier::Any => false, + } + } + + /// True if values stored under this specifier must be owned (my, our) + /// and cannot be leased (leased, our leased). + /// + /// [`Specifier::Any`] returns false. + pub fn must_be_owned(self) -> bool { + match self { + Specifier::Our | Specifier::My => true, + Specifier::OurLeased | Specifier::Leased => false, + Specifier::Any => false, + } + } +} + +impl std::fmt::Display for Specifier { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Specifier::Our => write!(f, "our"), + Specifier::My => write!(f, "my"), + Specifier::OurLeased => write!(f, "our leased"), + Specifier::Leased => write!(f, "leased"), + Specifier::Any => write!(f, "any"), + } + } +} + +/// NB: Ordering is significant. As we traverse a path, we take the +/// max of the atomic properties for the various storage modes, +/// and we want that to be atomic if any step was atomic. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub enum Atomic { + No, + Yes, +} + +impl std::ops::BitOr for Atomic { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self::Output { + self.max(rhs) + } +} + +impl std::ops::BitOrAssign for Atomic { + fn bitor_assign(&mut self, rhs: Self) { + *self = rhs.max(*self); + } +} + +/// NB: Ordering is significant. As we traverse a path, we take the +/// max of the joint properties for the various storage modes, +/// and we want that to be atomic if any step was joint. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub enum Joint { + No, + Yes, +} + +impl std::ops::BitOr for Joint { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self::Output { + self.max(rhs) + } +} + +impl std::ops::BitOrAssign for Joint { + fn bitor_assign(&mut self, rhs: Self) { + *self = rhs.max(*self); + } +} + +/// NB: Ordering is significant. As we traverse a path, we take the +/// max of the owned properties for the various storage modes, +/// and we want that to be atomic if any step was joint. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub enum Leased { + No, + Yes, +} + +impl std::ops::BitOr for Leased { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self::Output { + self.max(rhs) + } +} + +impl std::ops::BitOrAssign for Leased { + fn bitor_assign(&mut self, rhs: Self) { + *self = rhs.max(*self); + } +} diff --git a/components/dada-ir/src/storage_mode.rs b/components/dada-ir/src/storage_mode.rs deleted file mode 100644 index 8563567f..00000000 --- a/components/dada-ir/src/storage_mode.rs +++ /dev/null @@ -1,68 +0,0 @@ -/// NB: Ordering is significant. As we traverse a path, we take the -/// max of the atomic properties for the various storage modes, -/// and we want that to be atomic if any step was atomic. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] -pub enum Atomic { - No, - Yes, -} - -impl std::ops::BitOr for Atomic { - type Output = Self; - - fn bitor(self, rhs: Self) -> Self::Output { - self.max(rhs) - } -} - -impl std::ops::BitOrAssign for Atomic { - fn bitor_assign(&mut self, rhs: Self) { - *self = rhs.max(*self); - } -} - -/// NB: Ordering is significant. As we traverse a path, we take the -/// max of the joint properties for the various storage modes, -/// and we want that to be atomic if any step was joint. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] -pub enum Joint { - No, - Yes, -} - -impl std::ops::BitOr for Joint { - type Output = Self; - - fn bitor(self, rhs: Self) -> Self::Output { - self.max(rhs) - } -} - -impl std::ops::BitOrAssign for Joint { - fn bitor_assign(&mut self, rhs: Self) { - *self = rhs.max(*self); - } -} - -/// NB: Ordering is significant. As we traverse a path, we take the -/// max of the owned properties for the various storage modes, -/// and we want that to be atomic if any step was joint. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] -pub enum Leased { - No, - Yes, -} - -impl std::ops::BitOr for Leased { - type Output = Self; - - fn bitor(self, rhs: Self) -> Self::Output { - self.max(rhs) - } -} - -impl std::ops::BitOrAssign for Leased { - fn bitor_assign(&mut self, rhs: Self) { - *self = rhs.max(*self); - } -} diff --git a/components/dada-lang/src/test_harness/heap_graph_query.rs b/components/dada-lang/src/test_harness/heap_graph_query.rs index 163d2889..d62f9fb7 100644 --- a/components/dada-lang/src/test_harness/heap_graph_query.rs +++ b/components/dada-lang/src/test_harness/heap_graph_query.rs @@ -45,6 +45,13 @@ impl super::Options { )); } + self.check_compiled( + db, + &[filename], + |item| db.debug_bir(item), + &path.join(format!("HeapGraph-{query_index}.bir.ref")), + )?; + match db.function_named(filename, "main") { Some(function) => { kernel.interpret_and_buffer(db, function, vec![]).await; diff --git a/components/dada-parse/src/parser.rs b/components/dada-parse/src/parser.rs index f0954f88..273355ee 100644 --- a/components/dada-parse/src/parser.rs +++ b/components/dada-parse/src/parser.rs @@ -32,18 +32,18 @@ impl<'me> Parser<'me> { /// Returns `Some` if the next pending token matches `is`, along /// with the narrowed view of the next token. fn peek(&mut self, test: TT) -> Option { - test.test(self.db, self.tokens.peek()?) + let span = self.tokens.peek_span().in_file(self.filename); + test.test(self.db, self.tokens.peek()?, span) } /// If the next pending token matches `test`, consumes it and /// returns the span + narrowed view. Otherwise returns None /// and has no effect. Returns None if there is no pending token. fn eat(&mut self, test: TT) -> Option<(Span, TT::Narrow)> { - let start_span = self.tokens.peek_span(); + let span = self.tokens.peek_span(); let narrow = self.peek(test)?; self.tokens.consume(); - let end_span = self.tokens.last_span(); - Some((start_span.to(end_span), narrow)) + Some((span, narrow)) } /// Run `op` -- if it returns `None`, then no tokens are consumed. diff --git a/components/dada-parse/src/parser/code.rs b/components/dada-parse/src/parser/code.rs index aa42aba3..5ed6e5aa 100644 --- a/components/dada-parse/src/parser/code.rs +++ b/components/dada-parse/src/parser/code.rs @@ -18,14 +18,14 @@ use dada_ir::{ kw::Keyword, origin_table::PushOriginIn, span::Span, - storage_mode::Atomic, + storage::Atomic, token::Token, token_tree::TokenTree, word::SpannedOptionalWord, }; use salsa::AsId; -use super::{OrReportError, ParseList}; +use super::{parameter::SpannedSpecifierExt, OrReportError, ParseList}; impl Parser<'_> { pub(crate) fn parse_code_body(&mut self, origin: Code) -> Tree { @@ -420,12 +420,14 @@ impl CodeParser<'_, '_> { } } - /// Parses `[shared|var|atomic] x = expr` + /// Parses `[permission-mode] [atomic] x = expr` #[tracing::instrument(level = "debug", skip_all)] fn parse_local_variable_decl(&mut self) -> Option { // Look for `[mode] x = `. If we see that, we are committed to this // being a local variable declaration. Otherwise, we roll fully back. - let (atomic_span, atomic, name_span, name) = self.lookahead(|this| { + let (specifier, atomic_span, atomic, name_span, name) = self.lookahead(|this| { + let specifier = this.parse_permission_specifier(); + // A storage mode like `shared` or `var` *could* be a variable declaration, // but if we see `atomic` it might not be, so check for the `x = ` next. let (atomic_span, atomic) = if let Some(span) = this.parse_atomic() { @@ -438,12 +440,15 @@ impl CodeParser<'_, '_> { this.eat_op(Op::Equal)?; - Some((atomic_span, atomic, name_span, name)) + Some((specifier, atomic_span, atomic, name_span, name)) })?; + let specifier = specifier.or_defaulted(self, name_span); + let local_variable_decl = self.add( LocalVariableDeclData { atomic, + specifier, name, ty: None, // FIXME-- should permit `ty: Ty = ...` }, diff --git a/components/dada-parse/src/parser/items.rs b/components/dada-parse/src/parser/items.rs index 198b9496..57d55826 100644 --- a/components/dada-parse/src/parser/items.rs +++ b/components/dada-parse/src/parser/items.rs @@ -1,4 +1,4 @@ -use crate::{parser::Parser, token_test::Identifier}; +use crate::{parser::Parser, token_test::SpannedIdentifier}; use dada_ir::{ class::Class, code::Code, effect::Effect, function::Function, item::Item, kw::Keyword, @@ -31,8 +31,8 @@ impl<'db> Parser<'db> { fn parse_class(&mut self) -> Option { let (class_span, _) = self.eat(Keyword::Class)?; - let (class_name_span, class_name) = self - .eat(Identifier) + let (_, class_name) = self + .eat(SpannedIdentifier) .or_report_error(self, || "expected a class name")?; let (_, field_tokens) = self .delimited('(') @@ -42,7 +42,6 @@ impl<'db> Parser<'db> { class_name, field_tokens, self.span_consumed_since(class_span).in_file(self.filename), - class_name_span.in_file(self.filename), )) } @@ -55,8 +54,8 @@ impl<'db> Parser<'db> { let (fn_span, _) = self .eat(Keyword::Fn) .or_report_error(self, || "expected `fn`".to_string())?; - let (func_name_span, func_name) = self - .eat(Identifier) + let (_, func_name) = self + .eat(SpannedIdentifier) .or_report_error(self, || "expected function name".to_string())?; let (_, parameter_tokens) = self .delimited('(') @@ -71,7 +70,6 @@ impl<'db> Parser<'db> { func_name, code, self.span_consumed_since(start_span).in_file(self.filename), - func_name_span.in_file(self.filename), effect_span.unwrap_or(fn_span).in_file(self.filename), )) } diff --git a/components/dada-parse/src/parser/parameter.rs b/components/dada-parse/src/parser/parameter.rs index 19694eb8..997d0e2c 100644 --- a/components/dada-parse/src/parser/parameter.rs +++ b/components/dada-parse/src/parser/parameter.rs @@ -6,7 +6,7 @@ use dada_ir::{ kw::Keyword, parameter::Parameter, span::Span, - storage_mode::Atomic, + storage::{Atomic, SpannedSpecifier, Specifier}, }; use super::ParseList; @@ -19,6 +19,7 @@ impl<'db> Parser<'db> { } fn parse_parameter(&mut self) -> Option { + let opt_specifier = self.parse_permission_specifier(); let opt_storage_mode = self.parse_atomic(); if let Some((name_span, name)) = self.eat(Identifier) { let opt_ty = if let Some(colon_span) = self.eat_op(Op::Colon) { @@ -40,8 +41,11 @@ impl<'db> Parser<'db> { None => (name_span, Atomic::No), }; + let specifier = opt_specifier.or_defaulted(self, name_span); + let decl = LocalVariableDeclData { atomic, + specifier, name, ty: opt_ty, }; @@ -72,4 +76,58 @@ impl<'db> Parser<'db> { None } } + + pub(crate) fn parse_permission_specifier(&mut self) -> Option { + let filename = self.filename; + let some_specifier = |specifier, span: Span| { + Some(SpannedSpecifier::new( + self.db, + specifier, + false, + span.in_file(filename), + )) + }; + if let Some((my_span, _)) = self.eat(Keyword::My) { + if let Some((leased_span, _)) = self.eat(Keyword::Leased) { + some_specifier(Specifier::Leased, my_span.to(leased_span)) + } else { + some_specifier(Specifier::My, my_span) + } + } else if let Some((our_span, _)) = self.eat(Keyword::Our) { + if let Some((leased_span, _)) = self.eat(Keyword::Leased) { + some_specifier(Specifier::OurLeased, our_span.to(leased_span)) + } else { + some_specifier(Specifier::Our, our_span) + } + } else if let Some((leased_span, _)) = self.eat(Keyword::Leased) { + if let Some((my_span, _)) = self.eat(Keyword::My) { + self.error(my_span, "this should be written `leased`, not `leased my`") + .emit(self.db); + some_specifier(Specifier::Leased, leased_span.to(my_span)) + } else if let Some((our_span, _)) = self.eat(Keyword::Our) { + self.error( + our_span, + "this should be written `our leased`, not `leased our`", + ) + .emit(self.db); + some_specifier(Specifier::OurLeased, leased_span.to(our_span)) + } else { + some_specifier(Specifier::Leased, leased_span) + } + } else if let Some((any_span, _)) = self.eat(Keyword::Any) { + some_specifier(Specifier::Any, any_span) + } else { + None + } + } +} + +#[extension_trait::extension_trait] +pub(crate) impl SpannedSpecifierExt for Option { + fn or_defaulted(self, parser: &Parser<'_>, name_span: Span) -> SpannedSpecifier { + match self { + Some(s) => s, + None => SpannedSpecifier::new_defaulted(parser.db, name_span.in_file(parser.filename)), + } + } } diff --git a/components/dada-parse/src/token_test.rs b/components/dada-parse/src/token_test.rs index 9a59a881..00648246 100644 --- a/components/dada-parse/src/token_test.rs +++ b/components/dada-parse/src/token_test.rs @@ -1,5 +1,10 @@ use dada_ir::{ - format_string::FormatString, kw::Keyword, token::Token, token_tree::TokenTree, word::Word, + format_string::FormatString, + kw::Keyword, + span::FileSpan, + token::Token, + token_tree::TokenTree, + word::{SpannedWord, Word}, }; /// Represents some kind of "condition test" that can be applied to a single token @@ -11,13 +16,13 @@ pub(crate) trait TokenTest: std::fmt::Debug { /// If `token` matches the condition, return `Some` with a potentially transformed /// version of the token. Else returns None. - fn test(self, db: &dyn crate::Db, token: Token) -> Option; + fn test(self, db: &dyn crate::Db, token: Token, span: FileSpan) -> Option; } impl TokenTest for Keyword { type Narrow = Self; - fn test(self, db: &dyn crate::Db, token: Token) -> Option { + fn test(self, db: &dyn crate::Db, token: Token, _span: FileSpan) -> Option { let Some(str) = token.alphabetic_str(db) else { return None; }; @@ -36,7 +41,7 @@ pub(crate) struct AnyKeyword; impl TokenTest for AnyKeyword { type Narrow = Keyword; - fn test(self, db: &dyn crate::Db, token: Token) -> Option { + fn test(self, db: &dyn crate::Db, token: Token, _span: FileSpan) -> Option { let word = token.alphabetic()?; dada_ir::kw::keywords(db).get(&word).copied() } @@ -48,7 +53,7 @@ pub(crate) struct Identifier; impl TokenTest for Identifier { type Narrow = Word; - fn test(self, db: &dyn crate::Db, token: Token) -> Option { + fn test(self, db: &dyn crate::Db, token: Token, _span: FileSpan) -> Option { let word = token.alphabetic()?; if dada_ir::kw::keywords(db).contains_key(&word) { None @@ -63,11 +68,23 @@ pub(crate) struct Alphabetic; impl TokenTest for Alphabetic { type Narrow = Word; - fn test(self, _db: &dyn crate::Db, token: Token) -> Option { + fn test(self, _db: &dyn crate::Db, token: Token, _span: FileSpan) -> Option { token.alphabetic() } } +/// An `Alphabetic` that is not a keyword +#[derive(Debug)] +pub(crate) struct SpannedIdentifier; +impl TokenTest for SpannedIdentifier { + type Narrow = SpannedWord; + + fn test(self, db: &dyn crate::Db, token: Token, span: FileSpan) -> Option { + let word = Identifier.test(db, token, span)?; + Some(SpannedWord::new(db, word, span)) + } +} + /// A number like `22` or `22_000`. /// /// Note that `.` is not accepted. @@ -77,7 +94,7 @@ pub(crate) struct Number; impl TokenTest for Number { type Narrow = Word; - fn test(self, _db: &dyn crate::Db, token: Token) -> Option { + fn test(self, _db: &dyn crate::Db, token: Token, _span: FileSpan) -> Option { match token { Token::Number(w) => Some(w), _ => None, @@ -90,7 +107,7 @@ pub(crate) struct FormatStringLiteral; impl TokenTest for FormatStringLiteral { type Narrow = FormatString; - fn test(self, _db: &dyn crate::Db, token: Token) -> Option { + fn test(self, _db: &dyn crate::Db, token: Token, _span: FileSpan) -> Option { match token { Token::FormatString(fs) => Some(fs), _ => None, @@ -101,7 +118,7 @@ impl TokenTest for FormatStringLiteral { impl TokenTest for Token { type Narrow = Token; - fn test(self, _: &dyn crate::Db, token: Token) -> Option { + fn test(self, _: &dyn crate::Db, token: Token, _span: FileSpan) -> Option { if self == token { Some(token) } else { @@ -116,7 +133,7 @@ pub(crate) struct Any; impl TokenTest for Any { type Narrow = Token; - fn test(self, _: &dyn crate::Db, token: Token) -> Option { + fn test(self, _: &dyn crate::Db, token: Token, _span: FileSpan) -> Option { Some(token) } } @@ -127,7 +144,7 @@ pub(crate) struct AnyTree; impl TokenTest for AnyTree { type Narrow = TokenTree; - fn test(self, _: &dyn crate::Db, token: Token) -> Option { + fn test(self, _: &dyn crate::Db, token: Token, _span: FileSpan) -> Option { token.tree() } } diff --git a/components/dada-validate/src/lib.rs b/components/dada-validate/src/lib.rs index bcd52ea0..194ae0ab 100644 --- a/components/dada-validate/src/lib.rs +++ b/components/dada-validate/src/lib.rs @@ -2,6 +2,7 @@ #![feature(trait_upcasting)] #![feature(try_blocks)] +#![feature(let_else)] #![allow(incomplete_features)] mod validate; diff --git a/components/dada-validate/src/validate.rs b/components/dada-validate/src/validate.rs index 294feb87..f56ad3c9 100644 --- a/components/dada-validate/src/validate.rs +++ b/components/dada-validate/src/validate.rs @@ -35,7 +35,7 @@ pub fn validate_function(db: &dyn crate::Db, function: Function) -> validated::T } let num_parameters = validator.num_local_variables(); - let root_expr = validator.validate_expr(syntax_tree.data(db).root_expr); + let root_expr = validator.give_validated_expr(syntax_tree.data(db).root_expr); std::mem::drop(validator); let data = validated::TreeData::new(tables, num_parameters, root_expr); validated::Tree::new(db, function, data, origins) diff --git a/components/dada-validate/src/validate/name_lookup.rs b/components/dada-validate/src/validate/name_lookup.rs index 29ae7817..fc521a78 100644 --- a/components/dada-validate/src/validate/name_lookup.rs +++ b/components/dada-validate/src/validate/name_lookup.rs @@ -24,6 +24,17 @@ pub(crate) enum Definition { Intrinsic(Intrinsic), } +impl Definition { + pub(crate) fn plural_description(&self) -> &str { + match self { + Definition::LocalVariable(_) => "variables", + Definition::Function(_) => "functions", + Definition::Class(_) => "classes", + Definition::Intrinsic(_) => "functions", + } + } +} + impl From for Definition { fn from(value: Item) -> Self { match value { diff --git a/components/dada-validate/src/validate/validator.rs b/components/dada-validate/src/validate/validator.rs index 6c81c4a0..1df2afe6 100644 --- a/components/dada-validate/src/validate/validator.rs +++ b/components/dada-validate/src/validate/validator.rs @@ -12,7 +12,8 @@ use dada_ir::origin_table::HasOriginIn; use dada_ir::origin_table::PushOriginIn; use dada_ir::span::FileSpan; use dada_ir::span::Span; -use dada_ir::storage_mode::Atomic; +use dada_ir::storage::Atomic; +use dada_ir::storage::Specifier; use dada_ir::word::Word; use dada_lex::prelude::*; use dada_parse::prelude::*; @@ -35,6 +36,22 @@ pub(crate) struct Validator<'me> { synthesized: bool, } +#[derive(Copy, Clone, Debug)] +pub enum ExprMode { + Specifier(Specifier), + Reserve, +} + +impl ExprMode { + fn give() -> Self { + Self::Specifier(Specifier::My) + } + + fn leased() -> Self { + Self::Specifier(Specifier::Leased) + } +} + impl<'me> Validator<'me> { pub(crate) fn new( db: &'me dyn crate::Db, @@ -75,13 +92,6 @@ impl<'me> Validator<'me> { } } - fn with_synthesized(self) -> Self { - Self { - synthesized: true, - ..self - } - } - fn effect_span(&self) -> FileSpan { (self.effect_span)(self) } @@ -142,6 +152,7 @@ impl<'me> Validator<'me> { let local_variable = self.add( validated::LocalVariableData { name: Some(decl_data.name), + specifier: Some(decl_data.specifier), atomic: decl_data.atomic, }, validated::LocalVariableOrigin::Parameter(decl), @@ -149,9 +160,9 @@ impl<'me> Validator<'me> { self.scope.insert(decl_data.name, local_variable); } - #[tracing::instrument(level = "debug", skip_all)] - pub(crate) fn validate_expr(&mut self, expr: syntax::Expr) -> validated::Expr { - let result = self.validate_expr1(expr); + #[tracing::instrument(level = "debug", skip(self, expr))] + pub(crate) fn give_validated_expr(&mut self, expr: syntax::Expr) -> validated::Expr { + let result = self.validate_expr_in_mode(expr, ExprMode::give()); // Check that the validated expression always has the same // origin as the expression we started with. @@ -160,12 +171,23 @@ impl<'me> Validator<'me> { result } - fn validate_expr1(&mut self, expr: syntax::Expr) -> validated::Expr { + #[tracing::instrument(level = "debug", skip(self, expr))] + pub(crate) fn reserve_validated_expr(&mut self, expr: syntax::Expr) -> validated::Expr { + let result = self.validate_expr_in_mode(expr, ExprMode::Reserve); + + // Check that the validated expression always has the same + // origin as the expression we started with. + assert_eq!(result.origin_in(self.origins).syntax_expr, expr); + + result + } + + fn validate_expr_in_mode(&mut self, expr: syntax::Expr, mode: ExprMode) -> validated::Expr { tracing::trace!("expr.data = {:?}", expr.data(self.syntax_tables())); match expr.data(self.syntax_tables()) { syntax::ExprData::Dot(..) | syntax::ExprData::Id(_) => { let place = self.validate_expr_as_place(expr); - self.place_to_expr(place, expr) + self.place_to_expr(place, expr.synthesized(), mode) } syntax::ExprData::BooleanLiteral(b) => { @@ -282,12 +304,12 @@ impl<'me> Validator<'me> { } } - let validated_future_expr = self.validate_expr(*future_expr); + let validated_future_expr = self.give_validated_expr(*future_expr); self.add(validated::ExprData::Await(validated_future_expr), expr) } syntax::ExprData::Call(func_expr, named_exprs) => { - let validated_func_expr = self.validate_expr_to_value(*func_expr); + let validated_func_expr = self.reserve_validated_expr(*func_expr); let validated_named_exprs = self.validate_named_exprs(named_exprs); let mut name_required = false; for named_expr in &validated_named_exprs { @@ -308,7 +330,8 @@ impl<'me> Validator<'me> { } syntax::ExprData::Share(target_expr) => { - self.validate_permission_expr(expr, *target_expr, validated::ExprData::Share) + let validated_target_expr = self.give_validated_expr(*target_expr); + self.add(validated::ExprData::Share(validated_target_expr), expr) } syntax::ExprData::Lease(target_expr) => { @@ -316,7 +339,11 @@ impl<'me> Validator<'me> { } syntax::ExprData::Give(target_expr) => { - self.validate_permission_expr(expr, *target_expr, validated::ExprData::Give) + if self.is_place_expression(*target_expr) { + self.validate_permission_expr(expr, *target_expr, validated::ExprData::Give) + } else { + self.give_validated_expr(*target_expr) + } } syntax::ExprData::Var(decl, initializer_expr) => { @@ -324,41 +351,39 @@ impl<'me> Validator<'me> { let local_variable = self.add( validated::LocalVariableData { name: Some(decl_data.name), + specifier: Some(decl_data.specifier), atomic: decl_data.atomic, }, validated::LocalVariableOrigin::LocalVariable(*decl), ); - let place = self.add( - validated::PlaceData::LocalVariable(local_variable), + self.scope.insert(decl_data.name, local_variable); + + let target_place = self.add( + validated::TargetPlaceData::LocalVariable(local_variable), expr.synthesized(), ); - let validated_initializer_expr = self.validate_expr_to_value(*initializer_expr); - self.scope.insert(decl_data.name, local_variable); - self.add( - validated::ExprData::Assign(place, validated_initializer_expr), - expr, - ) + self.validated_assignment(target_place, *initializer_expr, expr) } syntax::ExprData::Parenthesized(parenthesized_expr) => { - self.validate_expr(*parenthesized_expr) + self.validate_expr_in_mode(*parenthesized_expr, mode) } syntax::ExprData::Tuple(element_exprs) => { let validated_exprs = element_exprs .iter() - .map(|expr| self.validate_expr(*expr)) + .map(|expr| self.reserve_validated_expr(*expr)) .collect(); self.add(validated::ExprData::Tuple(validated_exprs), expr) } syntax::ExprData::If(condition_expr, then_expr, else_expr) => { - let validated_condition_expr = self.validate_expr(*condition_expr); - let validated_then_expr = self.subscope().validate_expr_and_exit(*then_expr); + let validated_condition_expr = self.give_validated_expr(*condition_expr); + let validated_then_expr = self.subscope().validate_expr_and_exit(*then_expr, mode); let validated_else_expr = match else_expr { None => self.empty_tuple(expr), - Some(else_expr) => self.subscope().validate_expr_and_exit(*else_expr), + Some(else_expr) => self.subscope().validate_expr_and_exit(*else_expr, mode), }; self.add( validated::ExprData::If( @@ -376,7 +401,7 @@ impl<'me> Validator<'me> { .with_effect(Effect::Atomic, |this| { this.span(expr).leading_keyword(this.db, Keyword::Atomic) }) - .validate_expr_and_exit(*atomic_expr); + .validate_expr_and_exit(*atomic_expr, mode); self.add(validated::ExprData::Atomic(validated_atomic_expr), expr) } @@ -388,7 +413,7 @@ impl<'me> Validator<'me> { let validated_body_expr = self .subscope() .with_loop_expr(loop_expr) - .validate_expr_and_exit(*body_expr); + .validate_expr_and_exit(*body_expr, ExprMode::Specifier(Specifier::My)); self.tables[loop_expr] = validated::ExprData::Loop(validated_body_expr); @@ -405,13 +430,13 @@ impl<'me> Validator<'me> { let loop_expr = self.add(validated::ExprData::Error, expr); // lower the condition C - let validated_condition_expr = self.validate_expr(*condition_expr); + let validated_condition_expr = self.give_validated_expr(*condition_expr); // lower the body E, in a subscope so that `break` breaks out from `loop_expr` let validated_body_expr = self .subscope() .with_loop_expr(loop_expr) - .validate_expr_and_exit(*body_expr); + .validate_expr_and_exit(*body_expr, mode); let if_break_expr = { // break @@ -442,8 +467,8 @@ impl<'me> Validator<'me> { } syntax::ExprData::Op(lhs_expr, op, rhs_expr) => { - let validated_lhs_expr = self.validate_expr(*lhs_expr); - let validated_rhs_expr = self.validate_expr(*rhs_expr); + let validated_lhs_expr = self.give_validated_expr(*lhs_expr); + let validated_rhs_expr = self.give_validated_expr(*rhs_expr); let validated_op = self.validated_op(*op); self.add( validated::ExprData::Op(validated_lhs_expr, validated_op, validated_rhs_expr), @@ -452,7 +477,7 @@ impl<'me> Validator<'me> { } syntax::ExprData::Unary(op, rhs_expr) => { - let validated_rhs_expr = self.validate_expr(*rhs_expr); + let validated_rhs_expr = self.give_validated_expr(*rhs_expr); let validated_op = self.validated_op(*op); self.add( validated::ExprData::Unary(validated_op, validated_rhs_expr), @@ -460,68 +485,35 @@ impl<'me> Validator<'me> { ) } - syntax::ExprData::OpEq(lhs_expr, op, rhs_expr) => { - // FIXME: This desugaring is overly simplistic. It will break on cases - // like `foo(a, b).field += 1` because it will execute `foo(a, b)` twice. - // What we should do for dotted paths is to lease the LHS - // "up to" the last field. + syntax::ExprData::OpEq(..) => { + let result = self.validate_op_eq(expr); + self.or_error(result, expr) + } + syntax::ExprData::Assign(lhs_expr, rhs_expr) => { let result = try { - let validated_lhs_expr = { - let (validated_opt_temp_expr, validated_lhs_place) = - self.validate_expr_as_place(*lhs_expr)?; - let validated_lhs_expr = - self.add(validated::ExprData::Give(validated_lhs_place), expr); - self.maybe_seq(validated_opt_temp_expr, validated_lhs_expr, expr) - }; - let validated_rhs_expr = self.validate_expr(*rhs_expr); - let validated_op = self.validated_op(*op); - let validated_op_expr = self.add( - validated::ExprData::Op( - validated_lhs_expr, - validated_op, - validated_rhs_expr, - ), - expr, - ); + let (validated_lhs_opt_temp_expr, validated_lhs_place) = + self.validate_expr_as_target_place(*lhs_expr, ExprMode::Reserve)?; - let (validated_opt_temp_expr, validated_target_place) = self - .subscope() - .with_synthesized() - .validate_expr_as_place(*lhs_expr)?; + let assign_expr = + self.validated_assignment(validated_lhs_place, *rhs_expr, expr); - let assign_expr = self.add( - validated::ExprData::Assign(validated_target_place, validated_op_expr), - expr, - ); - self.maybe_seq(validated_opt_temp_expr, assign_expr, expr) + self.seq(validated_lhs_opt_temp_expr, assign_expr) }; self.or_error(result, expr) } - syntax::ExprData::Assign(lhs_expr, rhs_expr) => { - let place = try { - let (validated_opt_temp_expr, validated_lhs_place) = - self.validate_expr_as_place(*lhs_expr)?; - let validated_rhs_expr = self.validate_expr(*rhs_expr); - let assign_expr = self.add( - validated::ExprData::Assign(validated_lhs_place, validated_rhs_expr), - expr, - ); - self.maybe_seq(validated_opt_temp_expr, assign_expr, expr) - }; - self.or_error(place, expr) - } - syntax::ExprData::Error => self.add(validated::ExprData::Error, expr), syntax::ExprData::Seq(exprs) => { - let validated_exprs: Vec<_> = - exprs.iter().map(|expr| self.validate_expr(*expr)).collect(); + let validated_exprs: Vec<_> = exprs + .iter() + .map(|expr| self.give_validated_expr(*expr)) + .collect(); self.add(validated::ExprData::Seq(validated_exprs), expr) } syntax::ExprData::Return(with_value) => { if let Some(return_expr) = with_value { - let validated_return_expr = self.validate_expr(*return_expr); + let validated_return_expr = self.give_validated_expr(*return_expr); return self.add(validated::ExprData::Return(validated_return_expr), expr); } let empty_tuple = self.empty_tuple(expr); @@ -530,47 +522,266 @@ impl<'me> Validator<'me> { } } + fn validate_op_eq( + &mut self, + op_eq_expr: syntax::Expr, + ) -> Result { + // if user wrote `x += `, we generate + // + // { + // temp_value = x + + // x = temp2 + // } + // + // if user wrote `owner.field += `, we generate + // + // { + // temp_leased_owner = owner.lease + // temp_value = temp_leased_owner.x + + // temp_leased_owner.x = temp2 + // } + // + // below, we will leave comments for the more complex version. + + let syntax::ExprData::OpEq(lhs_expr, op, rhs_expr) = self.syntax_tables()[op_eq_expr] else { + panic!("validated_op_eq invoked on something that was not an op-eq expr") + }; + + // `temp_leased_owner = owner.lease` (if this is a field) + let (lease_owner_expr, validated_target_place) = + self.validate_expr_as_target_place(lhs_expr, ExprMode::leased())?; + + // `temp_value = x + ` or `temp_value = temp_leased_owner.x + ` + let (temporary_assign_expr, temporary_place) = { + let validated_op = self.validated_op(op); + + // `x` or `temp_leased_owner.x` + let validated_lhs_expr = { + let lhs_place = match self.tables[validated_target_place] { + validated::TargetPlaceData::LocalVariable(lv) => self.add( + validated::PlaceData::LocalVariable(lv), + lhs_expr.synthesized(), + ), + + validated::TargetPlaceData::Dot(owner, field) => self.add( + validated::PlaceData::Dot(owner, field), + lhs_expr.synthesized(), + ), + }; + self.add(validated::ExprData::Give(lhs_place), lhs_expr.synthesized()) + }; + + // + let validated_rhs_expr = self.give_validated_expr(rhs_expr); + + // `x + ` or `temp_leased_owner.x + ` + let validated_op_expr = self.add( + validated::ExprData::Op(validated_lhs_expr, validated_op, validated_rhs_expr), + op_eq_expr.synthesized(), + ); + + self.store_validated_expr_in_temporary(validated_op_expr) + }; + + // `x = temp_value` or `temp_leased_owner.x = temp_value` + let assign_field_expr = { + self.add( + validated::ExprData::AssignFromPlace(validated_target_place, temporary_place), + op_eq_expr, + ) + }; + + Ok(self.seq( + lease_owner_expr + .into_iter() + .chain(Some(temporary_assign_expr)), + assign_field_expr, + )) + } + + fn validated_assignment( + &mut self, + target_place: validated::TargetPlace, + initializer_expr: syntax::Expr, + origin: syntax::Expr, + ) -> validated::Expr { + if self.is_place_expression(initializer_expr) { + // Compile + // + // Specifier x = + // + // to + // + // x = + // + // directly. + let result = try { + let (validated_opt_temp_expr, validated_initializer_place) = + self.validate_expr_as_place(initializer_expr)?; + let assignment_expr = self.add( + validated::ExprData::AssignFromPlace(target_place, validated_initializer_place), + origin, + ); + self.seq(validated_opt_temp_expr, assignment_expr) + }; + self.or_error(result, origin) + } else { + // Compile + // + // Specifier x = + // + // to + // + // temp = + // x = temp + // + // This temporary lives as long as `x` does. + // + // The reason for introducing this temporary is because + // some specifiers (notably `lease`) need a place + // to "borrow" from. For other specifiers (e.g., `my`, `our`, `any`) + // we simply move/copy out from `temp` immediately and it has no + // ill-effect. + let (temp_initializer_expr, temp_place) = + self.validate_expr_in_temporary(initializer_expr, ExprMode::give()); + let assignment_expr = self.add( + validated::ExprData::AssignFromPlace(target_place, temp_place), + origin, + ); + self.seq(Some(temp_initializer_expr), assignment_expr) + } + } + + fn validate_expr_as_target_place( + &mut self, + expr: syntax::Expr, + owner_mode: ExprMode, + ) -> Result<(Option, validated::TargetPlace), ErrorReported> { + match expr.data(self.syntax_tables()) { + syntax::ExprData::Dot(owner, field_name) => { + let (assign_expr, owner_place) = + self.validate_expr_in_temporary(*owner, owner_mode); + let place = self.add( + validated::TargetPlaceData::Dot(owner_place, *field_name), + expr, + ); + Ok((Some(assign_expr), place)) + } + + syntax::ExprData::Id(name) => match self.scope.lookup(*name) { + Some(Definition::LocalVariable(lv)) => { + let place = self.add(validated::TargetPlaceData::LocalVariable(lv), expr); + Ok((None, place)) + } + + Some(definition @ Definition::Function(_)) + | Some(definition @ Definition::Class(_)) + | Some(definition @ Definition::Intrinsic(_)) => Err(dada_ir::error!( + self.span(expr), + "you can only assign to local variables or fields, not {} like `{}`", + definition.plural_description(), + name.as_str(self.db), + ) + .emit(self.db)), + + None => Err(dada_ir::error!( + self.span(expr), + "can't find anything named `{}`", + name.as_str(self.db) + ) + .emit(self.db)), + }, + + syntax::ExprData::Parenthesized(target_expr) => { + self.validate_expr_as_target_place(*target_expr, owner_mode) + } + + _ => { + let _ = self.give_validated_expr(expr); + Err(dada_ir::error!( + self.span(expr), + "you can only assign to local variables and fields, not arbitrary expressions", + ) + .emit(self.db)) + } + } + } + /// Validate the expression and then exit the subscope (consumes self). + /// See [`Self::exit`]. + fn validate_expr_and_exit(mut self, expr: syntax::Expr, mode: ExprMode) -> validated::Expr { + let validated_expr = self.validate_expr_in_mode(expr, mode); + self.exit(validated_expr) + } + + /// Exit the subscope (consumes self) and produce `validated_expr` + /// (possibly wrapped in a `Declare` with any variables that were + /// declared within this scope). /// /// Exiting the subscope will pop-off any variables that were declared /// within. /// /// Returns the validated result, wrapped in `Declare` if necessary. - fn validate_expr_and_exit(mut self, expr: syntax::Expr) -> validated::Expr { - let expr = self.validate_expr(expr); - + fn exit(mut self, validated_expr: validated::Expr) -> validated::Expr { let vars = self.scope.take_inserted(); if vars.is_empty() { - return expr; + return validated_expr; } - let origin = self.origins[expr].synthesized(); - self.add(validated::ExprData::Declare(vars, expr), origin) + let origin = self.origins[validated_expr].synthesized(); + self.add(validated::ExprData::Declare(vars, validated_expr), origin) } - fn maybe_seq( + /// Creates a sequence that first executes `exprs` (if any) and then `final_expr`, + /// taking its final result from `final_expr`. Commonly used to combine + /// an initializer for an (optional) temporary followed by code that uses the + /// temporary (e.g., `t = 22; t + u`). + fn seq( &mut self, - expr1: Option, - expr2: validated::Expr, - origin: syntax::Expr, + exprs: impl IntoIterator, + final_expr: validated::Expr, ) -> validated::Expr { - if let Some(expr1) = expr1 { - self.add(validated::ExprData::Seq(vec![expr1, expr2]), origin) + let mut exprs: Vec = exprs.into_iter().collect(); + if exprs.is_empty() { + final_expr } else { - expr2 + let origin = self.origins[final_expr].synthesized(); + exprs.push(final_expr); + self.add(validated::ExprData::Seq(exprs), origin) } } fn place_to_expr( &mut self, data: Result<(Option, validated::Place), ErrorReported>, - origin: syntax::Expr, + origin: ExprOrigin, + mode: ExprMode, ) -> validated::Expr { match data { - Ok((opt_assign_expr, place)) => { - let place_expr = self.add(validated::ExprData::Give(place), origin); - self.maybe_seq(opt_assign_expr, place_expr, origin) - } + Ok((opt_assign_expr, place)) => match mode { + ExprMode::Specifier(Specifier::My) | ExprMode::Specifier(Specifier::Any) => { + let place_expr = self.add(validated::ExprData::Give(place), origin); + self.seq(opt_assign_expr, place_expr) + } + ExprMode::Specifier(Specifier::Leased) => { + let place_expr = self.add(validated::ExprData::Lease(place), origin); + self.seq(opt_assign_expr, place_expr) + } + ExprMode::Reserve => { + let place_expr = self.add(validated::ExprData::Reserve(place), origin); + self.seq(opt_assign_expr, place_expr) + } + ExprMode::Specifier(Specifier::Our) => { + let given_expr = self.add(validated::ExprData::Give(place), origin); + let shared_expr = self.add(validated::ExprData::Share(given_expr), origin); + self.seq(opt_assign_expr, shared_expr) + } + ExprMode::Specifier(Specifier::OurLeased) => { + let given_expr = self.add(validated::ExprData::Lease(place), origin); + let shared_expr = self.add(validated::ExprData::Share(given_expr), origin); + self.seq(opt_assign_expr, shared_expr) + } + }, Err(ErrorReported) => self.add(validated::ExprData::Error, origin), } } @@ -584,11 +795,21 @@ impl<'me> Validator<'me> { let validated_data = try { let (opt_temporary_expr, place) = self.validate_expr_as_place(target_expr)?; let permission_expr = self.add(perm_variant(place), perm_expr); - self.maybe_seq(opt_temporary_expr, permission_expr, perm_expr) + self.seq(opt_temporary_expr, permission_expr) }; self.or_error(validated_data, perm_expr) } + fn is_place_expression(&self, expr: syntax::Expr) -> bool { + match expr.data(self.syntax_tables()) { + syntax::ExprData::Id(_) | syntax::ExprData::Dot(..) => true, + syntax::ExprData::Parenthesized(parenthesized_expr) => { + self.is_place_expression(*parenthesized_expr) + } + _ => false, + } + } + fn validate_expr_as_place( &mut self, expr: syntax::Expr, @@ -633,7 +854,8 @@ impl<'me> Validator<'me> { } syntax::ExprData::Error => Err(ErrorReported), _ => { - let (assign_expr, temporary_place) = self.validate_expr_in_temporary(expr); + let (assign_expr, temporary_place) = + self.validate_expr_in_temporary(expr, ExprMode::give()); Ok((Some(assign_expr), temporary_place)) } } @@ -643,39 +865,38 @@ impl<'me> Validator<'me> { fn validate_expr_in_temporary( &mut self, expr: syntax::Expr, + mode: ExprMode, + ) -> (validated::Expr, validated::Place) { + let validated_expr = self.validate_expr_in_mode(expr, mode); + self.store_validated_expr_in_temporary(validated_expr) + } + + /// Creates a temporary to store the result of validating some expression. + fn store_validated_expr_in_temporary( + &mut self, + validated_expr: validated::Expr, ) -> (validated::Expr, validated::Place) { + let origin = self.origins[validated_expr].synthesized(); + let local_variable = self.add( validated::LocalVariableData { name: None, + specifier: None, atomic: Atomic::No, }, - validated::LocalVariableOrigin::Temporary(expr), + validated::LocalVariableOrigin::Temporary(origin.syntax_expr), ); self.scope.insert_temporary(local_variable); - let validated_place = self.add( - validated::PlaceData::LocalVariable(local_variable), - expr.synthesized(), - ); - let validated_expr = self.validate_expr(expr); - let assign_expr = self.add( - validated::ExprData::Assign(validated_place, validated_expr), - expr.synthesized(), + validated::ExprData::AssignTemporary(local_variable, validated_expr), + origin, ); + let validated_place = self.add(validated::PlaceData::LocalVariable(local_variable), origin); (assign_expr, validated_place) } - fn validate_expr_to_value(&mut self, expr: syntax::Expr) -> validated::Expr { - let (assign_expr, place) = self.validate_expr_in_temporary(expr); - let place_expr = self.add(validated::ExprData::Give(place), expr.synthesized()); - self.add( - validated::ExprData::Seq(vec![assign_expr, place_expr]), - expr.synthesized(), - ) - } - fn validate_named_exprs( &mut self, named_exprs: &[syntax::NamedExpr], @@ -688,7 +909,7 @@ impl<'me> Validator<'me> { fn validate_named_expr(&mut self, named_expr: syntax::NamedExpr) -> validated::NamedExpr { let syntax::NamedExprData { name, expr } = named_expr.data(self.syntax_tables()); - let validated_expr = self.validate_expr_to_value(*expr); + let validated_expr = self.reserve_validated_expr(*expr); self.add( validated::NamedExprData { name: *name, @@ -870,6 +1091,12 @@ impl IntoOrigin for LocalVariableOrigin { } fn synthesized(self) -> Self::Origin { - panic!("cannot force local variable origin to be synthesized") + match self { + // temporaries are synthesized local variables, so that's ok + LocalVariableOrigin::Temporary(_) => self, + + // we can't make other variables be synthesized + _ => panic!("cannot force local variable origin to be synthesized"), + } } } diff --git a/dada_tests/gc/stringify.dada b/dada_tests/gc/stringify.dada index 448570fc..3b582584 100644 --- a/dada_tests/gc/stringify.dada +++ b/dada_tests/gc/stringify.dada @@ -1,9 +1,9 @@ -class Point(x, y) +class Point(our x, our y) async fn main() { - p = Point(Point(22, 44), 66) + any p = Point(Point(22, 44), 66) print(p).await - #! OUTPUT my Point\(my Point\(22, 44\), 66\) + #! OUTPUT Point\(our Point\(22, 44\), 66\) print(22 + 44i).await #! OUTPUT 66_i print(22i + 44).await #! OUTPUT 66_i diff --git a/dada_tests/gc/stringify/stdout.ref b/dada_tests/gc/stringify/stdout.ref index 63677fe9..dade2727 100644 --- a/dada_tests/gc/stringify/stdout.ref +++ b/dada_tests/gc/stringify/stdout.ref @@ -1,4 +1,4 @@ -my Point(my Point(22, 44), 66) +my Point(our Point(22, 44), 66) 66_i 66_i 66_u diff --git a/dada_tests/heap-graph/cursor-position.dada b/dada_tests/heap-graph/cursor-position.dada index c00e32e0..968b956d 100644 --- a/dada_tests/heap-graph/cursor-position.dada +++ b/dada_tests/heap-graph/cursor-position.dada @@ -1,11 +1,11 @@ -class Point(x, y) +class Point(our x, our y) async fn main() { - p = Point(22, 44) - #? ^ HeapGraph - q = Point(p, 66) - #? ^ HeapGraph - #? ^ HeapGraph - #? ^ HeapGraph + our p = Point(22, 44) + #? ^ HeapGraph + our q = Point(p, 66) + #? ^ HeapGraph + #? ^ HeapGraph + #? ^ HeapGraph print("Hi").await #! OUTPUT Hi } \ No newline at end of file diff --git a/dada_tests/heap-graph/cursor-position/HeapGraph-0.bir.ref b/dada_tests/heap-graph/cursor-position/HeapGraph-0.bir.ref new file mode 100644 index 00000000..532c653e --- /dev/null +++ b/dada_tests/heap-graph/cursor-position/HeapGraph-0.bir.ref @@ -0,0 +1,244 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + AssignExpr( + temp{6}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{7}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{8}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{1}, + Call( + temp{6}, + [ + temp{7}, + temp{8}, + ], + [ + None, + None, + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{8}, + ), + Expr(2), + ), + ( + Clear( + temp{7}, + ), + Expr(1), + ), + ( + Clear( + temp{6}, + ), + Expr(0), + ), + ( + BreakpoingStart( + "class", + 0, + ), + Expr(4), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(4), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(4), + None, + ), + Expr(4), + ), + ( + AssignExpr( + temp{5}, + (), + ), + Expr(4), + ), + ( + Clear( + temp{5}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{10}, + Class(Id { value: 1 }).reserve, + ), + Expr(5), + ), + ( + AssignExpr( + temp{11}, + p{0}.reserve, + ), + Expr(6), + ), + ( + AssignExpr( + temp{12}, + 66, + ), + Expr(7), + ), + ], + Assign( + temp{3}, + Call( + temp{10}, + [ + temp{11}, + temp{12}, + ], + [ + None, + None, + ], + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + Clear( + temp{12}, + ), + Expr(7), + ), + ( + Clear( + temp{11}, + ), + Expr(6), + ), + ( + Clear( + temp{10}, + ), + Expr(5), + ), + ( + AssignPlace( + q{2}, + temp{3}, + ), + Expr(9), + ), + ( + AssignExpr( + temp{9}, + (), + ), + Expr(9), + ), + ( + Clear( + temp{9}, + ), + Expr(9), + ), + ( + AssignExpr( + temp{14}, + Print.reserve, + ), + Expr(10), + ), + ( + AssignExpr( + temp{15}, + "Hi", + ), + Expr(11), + ), + ], + Assign( + temp{13}, + Call( + temp{14}, + [ + temp{15}, + ], + [ + None, + ], + ), + BasicBlock(3), + ), + ), + BasicBlock(3): BasicBlockData( + [ + ( + Clear( + temp{15}, + ), + Expr(11), + ), + ( + Clear( + temp{14}, + ), + Expr(10), + ), + ], + Assign( + temp{4}, + Await( + temp{13}, + ), + BasicBlock(4), + ), + ), + BasicBlock(4): BasicBlockData( + [ + ( + Clear( + temp{13}, + ), + Expr(12), + ), + ], + Return( + temp{4}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/heap-graph/cursor-position/HeapGraph-0.ref b/dada_tests/heap-graph/cursor-position/HeapGraph-0.ref index 1b7c587b..773f97ee 100644 --- a/dada_tests/heap-graph/cursor-position/HeapGraph-0.ref +++ b/dada_tests/heap-graph/cursor-position/HeapGraph-0.ref @@ -1,4 +1,4 @@ -# Breakpoint: Expr(4) at class:4:5:4:22 +# Breakpoint: Expr(4) at class:4:9:4:26 digraph { node[shape = "note"]; rankdir = "LR"; @@ -13,19 +13,21 @@ digraph { - +
main
p
q
q
>; ]; } afternode0 [ + color = "slategray", + fontcolor = "slategray", label = < - - + +
Point
x: "22"
y: "44"
x: "22"
y: "44"
> ]; - "afterstack":0 -> "afternode0" [label="my", style="solid"]; + "afterstack":0 -> "afternode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; } subgraph cluster_before { label=<before> @@ -38,7 +40,7 @@ digraph { - +
main
p
q
q
>; ]; diff --git a/dada_tests/heap-graph/cursor-position/HeapGraph-1.bir.ref b/dada_tests/heap-graph/cursor-position/HeapGraph-1.bir.ref new file mode 100644 index 00000000..0f3da9ff --- /dev/null +++ b/dada_tests/heap-graph/cursor-position/HeapGraph-1.bir.ref @@ -0,0 +1,246 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + AssignExpr( + temp{6}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{7}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{8}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{1}, + Call( + temp{6}, + [ + temp{7}, + temp{8}, + ], + [ + None, + None, + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{8}, + ), + Expr(2), + ), + ( + Clear( + temp{7}, + ), + Expr(1), + ), + ( + Clear( + temp{6}, + ), + Expr(0), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{5}, + (), + ), + Expr(4), + ), + ( + Clear( + temp{5}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{10}, + Class(Id { value: 1 }).reserve, + ), + Expr(5), + ), + ( + BreakpoingStart( + "class", + 0, + ), + Expr(6), + ), + ( + AssignExpr( + temp{11}, + p{0}.reserve, + ), + Expr(6), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(6), + Some( + temp{11}, + ), + ), + Expr(6), + ), + ( + AssignExpr( + temp{12}, + 66, + ), + Expr(7), + ), + ], + Assign( + temp{3}, + Call( + temp{10}, + [ + temp{11}, + temp{12}, + ], + [ + None, + None, + ], + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + Clear( + temp{12}, + ), + Expr(7), + ), + ( + Clear( + temp{11}, + ), + Expr(6), + ), + ( + Clear( + temp{10}, + ), + Expr(5), + ), + ( + AssignPlace( + q{2}, + temp{3}, + ), + Expr(9), + ), + ( + AssignExpr( + temp{9}, + (), + ), + Expr(9), + ), + ( + Clear( + temp{9}, + ), + Expr(9), + ), + ( + AssignExpr( + temp{14}, + Print.reserve, + ), + Expr(10), + ), + ( + AssignExpr( + temp{15}, + "Hi", + ), + Expr(11), + ), + ], + Assign( + temp{13}, + Call( + temp{14}, + [ + temp{15}, + ], + [ + None, + ], + ), + BasicBlock(3), + ), + ), + BasicBlock(3): BasicBlockData( + [ + ( + Clear( + temp{15}, + ), + Expr(11), + ), + ( + Clear( + temp{14}, + ), + Expr(10), + ), + ], + Assign( + temp{4}, + Await( + temp{13}, + ), + BasicBlock(4), + ), + ), + BasicBlock(4): BasicBlockData( + [ + ( + Clear( + temp{13}, + ), + Expr(12), + ), + ], + Return( + temp{4}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/heap-graph/cursor-position/HeapGraph-1.ref b/dada_tests/heap-graph/cursor-position/HeapGraph-1.ref index ec8f994d..36674338 100644 --- a/dada_tests/heap-graph/cursor-position/HeapGraph-1.ref +++ b/dada_tests/heap-graph/cursor-position/HeapGraph-1.ref @@ -1,4 +1,4 @@ -# Breakpoint: Expr(6) at class:6:15:6:16 +# Breakpoint: Expr(6) at class:6:19:6:20 digraph { node[shape = "note"]; rankdir = "LR"; @@ -12,9 +12,9 @@ digraph { label=< - - - + + +
main
p
q
(in-flight)
p
q
(in-flight)
>; ]; @@ -28,7 +28,8 @@ digraph { y: "44" > ]; - "stack":22 -> "afternode0" [label="my", style="solid"]; + "afterstack":0 -> "afternode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; + "stack":16 -> "afternode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; } subgraph cluster_before { label=<before> @@ -40,8 +41,8 @@ digraph { label=< - - + +
main
p
q
p
q
>; ]; @@ -55,66 +56,7 @@ digraph { y: "44" > ]; - "beforestack":0 -> "beforenode0" [label="my", style="solid"]; - } -} -digraph { - node[shape = "note"]; - rankdir = "LR"; - subgraph cluster_after { - label=<after> - subgraph cluster_afterstack { - label=<stack> - rank="source"; - afterstack[ - shape="none"; - label=< - - - - - -
main
p
q
(in-flight)
- >; - ]; - } - afternode0 [ - color = "slategray", - fontcolor = "slategray", - label = < - - - -
Point
x: "22"
y: "44"
> - ]; - "stack":22 -> "afternode0" [label="my", style="solid"]; - } - subgraph cluster_before { - label=<before> - subgraph cluster_beforestack { - label=<stack> - rank="source"; - beforestack[ - shape="none"; - label=< - - - - -
main
p
q
- >; - ]; - } - beforenode0 [ - color = "slategray", - fontcolor = "slategray", - label = < - - - -
Point
x: "22"
y: "44"
> - ]; - "beforestack":0 -> "beforenode0" [label="my", style="solid"]; + "beforestack":0 -> "beforenode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; } } Hi diff --git a/dada_tests/heap-graph/cursor-position/HeapGraph-2.bir.ref b/dada_tests/heap-graph/cursor-position/HeapGraph-2.bir.ref new file mode 100644 index 00000000..0f3da9ff --- /dev/null +++ b/dada_tests/heap-graph/cursor-position/HeapGraph-2.bir.ref @@ -0,0 +1,246 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + AssignExpr( + temp{6}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{7}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{8}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{1}, + Call( + temp{6}, + [ + temp{7}, + temp{8}, + ], + [ + None, + None, + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{8}, + ), + Expr(2), + ), + ( + Clear( + temp{7}, + ), + Expr(1), + ), + ( + Clear( + temp{6}, + ), + Expr(0), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{5}, + (), + ), + Expr(4), + ), + ( + Clear( + temp{5}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{10}, + Class(Id { value: 1 }).reserve, + ), + Expr(5), + ), + ( + BreakpoingStart( + "class", + 0, + ), + Expr(6), + ), + ( + AssignExpr( + temp{11}, + p{0}.reserve, + ), + Expr(6), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(6), + Some( + temp{11}, + ), + ), + Expr(6), + ), + ( + AssignExpr( + temp{12}, + 66, + ), + Expr(7), + ), + ], + Assign( + temp{3}, + Call( + temp{10}, + [ + temp{11}, + temp{12}, + ], + [ + None, + None, + ], + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + Clear( + temp{12}, + ), + Expr(7), + ), + ( + Clear( + temp{11}, + ), + Expr(6), + ), + ( + Clear( + temp{10}, + ), + Expr(5), + ), + ( + AssignPlace( + q{2}, + temp{3}, + ), + Expr(9), + ), + ( + AssignExpr( + temp{9}, + (), + ), + Expr(9), + ), + ( + Clear( + temp{9}, + ), + Expr(9), + ), + ( + AssignExpr( + temp{14}, + Print.reserve, + ), + Expr(10), + ), + ( + AssignExpr( + temp{15}, + "Hi", + ), + Expr(11), + ), + ], + Assign( + temp{13}, + Call( + temp{14}, + [ + temp{15}, + ], + [ + None, + ], + ), + BasicBlock(3), + ), + ), + BasicBlock(3): BasicBlockData( + [ + ( + Clear( + temp{15}, + ), + Expr(11), + ), + ( + Clear( + temp{14}, + ), + Expr(10), + ), + ], + Assign( + temp{4}, + Await( + temp{13}, + ), + BasicBlock(4), + ), + ), + BasicBlock(4): BasicBlockData( + [ + ( + Clear( + temp{13}, + ), + Expr(12), + ), + ], + Return( + temp{4}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/heap-graph/cursor-position/HeapGraph-2.ref b/dada_tests/heap-graph/cursor-position/HeapGraph-2.ref index ec8f994d..36674338 100644 --- a/dada_tests/heap-graph/cursor-position/HeapGraph-2.ref +++ b/dada_tests/heap-graph/cursor-position/HeapGraph-2.ref @@ -1,4 +1,4 @@ -# Breakpoint: Expr(6) at class:6:15:6:16 +# Breakpoint: Expr(6) at class:6:19:6:20 digraph { node[shape = "note"]; rankdir = "LR"; @@ -12,9 +12,9 @@ digraph { label=< - - - + + +
main
p
q
(in-flight)
p
q
(in-flight)
>; ]; @@ -28,7 +28,8 @@ digraph { y: "44" > ]; - "stack":22 -> "afternode0" [label="my", style="solid"]; + "afterstack":0 -> "afternode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; + "stack":16 -> "afternode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; } subgraph cluster_before { label=<before> @@ -40,8 +41,8 @@ digraph { label=< - - + +
main
p
q
p
q
>; ]; @@ -55,66 +56,7 @@ digraph { y: "44" > ]; - "beforestack":0 -> "beforenode0" [label="my", style="solid"]; - } -} -digraph { - node[shape = "note"]; - rankdir = "LR"; - subgraph cluster_after { - label=<after> - subgraph cluster_afterstack { - label=<stack> - rank="source"; - afterstack[ - shape="none"; - label=< - - - - - -
main
p
q
(in-flight)
- >; - ]; - } - afternode0 [ - color = "slategray", - fontcolor = "slategray", - label = < - - - -
Point
x: "22"
y: "44"
> - ]; - "stack":22 -> "afternode0" [label="my", style="solid"]; - } - subgraph cluster_before { - label=<before> - subgraph cluster_beforestack { - label=<stack> - rank="source"; - beforestack[ - shape="none"; - label=< - - - - -
main
p
q
- >; - ]; - } - beforenode0 [ - color = "slategray", - fontcolor = "slategray", - label = < - - - -
Point
x: "22"
y: "44"
> - ]; - "beforestack":0 -> "beforenode0" [label="my", style="solid"]; + "beforestack":0 -> "beforenode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; } } Hi diff --git a/dada_tests/heap-graph/cursor-position/HeapGraph-3.bir.ref b/dada_tests/heap-graph/cursor-position/HeapGraph-3.bir.ref new file mode 100644 index 00000000..0f3da9ff --- /dev/null +++ b/dada_tests/heap-graph/cursor-position/HeapGraph-3.bir.ref @@ -0,0 +1,246 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + AssignExpr( + temp{6}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{7}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{8}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{1}, + Call( + temp{6}, + [ + temp{7}, + temp{8}, + ], + [ + None, + None, + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{8}, + ), + Expr(2), + ), + ( + Clear( + temp{7}, + ), + Expr(1), + ), + ( + Clear( + temp{6}, + ), + Expr(0), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{5}, + (), + ), + Expr(4), + ), + ( + Clear( + temp{5}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{10}, + Class(Id { value: 1 }).reserve, + ), + Expr(5), + ), + ( + BreakpoingStart( + "class", + 0, + ), + Expr(6), + ), + ( + AssignExpr( + temp{11}, + p{0}.reserve, + ), + Expr(6), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(6), + Some( + temp{11}, + ), + ), + Expr(6), + ), + ( + AssignExpr( + temp{12}, + 66, + ), + Expr(7), + ), + ], + Assign( + temp{3}, + Call( + temp{10}, + [ + temp{11}, + temp{12}, + ], + [ + None, + None, + ], + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + Clear( + temp{12}, + ), + Expr(7), + ), + ( + Clear( + temp{11}, + ), + Expr(6), + ), + ( + Clear( + temp{10}, + ), + Expr(5), + ), + ( + AssignPlace( + q{2}, + temp{3}, + ), + Expr(9), + ), + ( + AssignExpr( + temp{9}, + (), + ), + Expr(9), + ), + ( + Clear( + temp{9}, + ), + Expr(9), + ), + ( + AssignExpr( + temp{14}, + Print.reserve, + ), + Expr(10), + ), + ( + AssignExpr( + temp{15}, + "Hi", + ), + Expr(11), + ), + ], + Assign( + temp{13}, + Call( + temp{14}, + [ + temp{15}, + ], + [ + None, + ], + ), + BasicBlock(3), + ), + ), + BasicBlock(3): BasicBlockData( + [ + ( + Clear( + temp{15}, + ), + Expr(11), + ), + ( + Clear( + temp{14}, + ), + Expr(10), + ), + ], + Assign( + temp{4}, + Await( + temp{13}, + ), + BasicBlock(4), + ), + ), + BasicBlock(4): BasicBlockData( + [ + ( + Clear( + temp{13}, + ), + Expr(12), + ), + ], + Return( + temp{4}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/heap-graph/cursor-position/HeapGraph-3.ref b/dada_tests/heap-graph/cursor-position/HeapGraph-3.ref index ec8f994d..36674338 100644 --- a/dada_tests/heap-graph/cursor-position/HeapGraph-3.ref +++ b/dada_tests/heap-graph/cursor-position/HeapGraph-3.ref @@ -1,4 +1,4 @@ -# Breakpoint: Expr(6) at class:6:15:6:16 +# Breakpoint: Expr(6) at class:6:19:6:20 digraph { node[shape = "note"]; rankdir = "LR"; @@ -12,9 +12,9 @@ digraph { label=< - - - + + +
main
p
q
(in-flight)
p
q
(in-flight)
>; ]; @@ -28,7 +28,8 @@ digraph { y: "44" > ]; - "stack":22 -> "afternode0" [label="my", style="solid"]; + "afterstack":0 -> "afternode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; + "stack":16 -> "afternode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; } subgraph cluster_before { label=<before> @@ -40,8 +41,8 @@ digraph { label=< - - + +
main
p
q
p
q
>; ]; @@ -55,66 +56,7 @@ digraph { y: "44" > ]; - "beforestack":0 -> "beforenode0" [label="my", style="solid"]; - } -} -digraph { - node[shape = "note"]; - rankdir = "LR"; - subgraph cluster_after { - label=<after> - subgraph cluster_afterstack { - label=<stack> - rank="source"; - afterstack[ - shape="none"; - label=< - - - - - -
main
p
q
(in-flight)
- >; - ]; - } - afternode0 [ - color = "slategray", - fontcolor = "slategray", - label = < - - - -
Point
x: "22"
y: "44"
> - ]; - "stack":22 -> "afternode0" [label="my", style="solid"]; - } - subgraph cluster_before { - label=<before> - subgraph cluster_beforestack { - label=<stack> - rank="source"; - beforestack[ - shape="none"; - label=< - - - - -
main
p
q
- >; - ]; - } - beforenode0 [ - color = "slategray", - fontcolor = "slategray", - label = < - - - -
Point
x: "22"
y: "44"
> - ]; - "beforestack":0 -> "beforenode0" [label="my", style="solid"]; + "beforestack":0 -> "beforenode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; } } Hi diff --git a/dada_tests/heap-graph/dag.dada b/dada_tests/heap-graph/dag.dada index f41c7b84..64e5305d 100644 --- a/dada_tests/heap-graph/dag.dada +++ b/dada_tests/heap-graph/dag.dada @@ -1,4 +1,4 @@ -class Point(x, y) +class Point(any x, any y) async fn main() { p = Point(22, 44).share diff --git a/dada_tests/heap-graph/dag/HeapGraph-0.bir.ref b/dada_tests/heap-graph/dag/HeapGraph-0.bir.ref new file mode 100644 index 00000000..ab12875a --- /dev/null +++ b/dada_tests/heap-graph/dag/HeapGraph-0.bir.ref @@ -0,0 +1,193 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + BreakpoingStart( + "class", + 0, + ), + Expr(11), + ), + ( + AssignExpr( + temp{7}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{8}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{9}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{6}, + Call( + temp{7}, + [ + temp{8}, + temp{9}, + ], + [ + None, + None, + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{9}, + ), + Expr(2), + ), + ( + Clear( + temp{8}, + ), + Expr(1), + ), + ( + Clear( + temp{7}, + ), + Expr(0), + ), + ( + AssignExpr( + temp{1}, + temp{6}.share, + ), + Expr(4), + ), + ( + Clear( + temp{6}, + ), + Expr(3), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(5), + ), + ( + AssignExpr( + temp{5}, + (), + ), + Expr(5), + ), + ( + Clear( + temp{5}, + ), + Expr(5), + ), + ( + AssignExpr( + temp{10}, + Class(Id { value: 1 }).reserve, + ), + Expr(6), + ), + ( + AssignExpr( + temp{11}, + p{0}.reserve, + ), + Expr(7), + ), + ( + AssignExpr( + temp{12}, + p{0}.reserve, + ), + Expr(8), + ), + ], + Assign( + temp{3}, + Call( + temp{10}, + [ + temp{11}, + temp{12}, + ], + [ + None, + None, + ], + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + Clear( + temp{12}, + ), + Expr(8), + ), + ( + Clear( + temp{11}, + ), + Expr(7), + ), + ( + Clear( + temp{10}, + ), + Expr(6), + ), + ( + AssignPlace( + q{2}, + temp{3}, + ), + Expr(10), + ), + ( + AssignExpr( + temp{4}, + (), + ), + Expr(10), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(11), + Some( + temp{4}, + ), + ), + Expr(11), + ), + ], + Return( + temp{4}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/heap-graph/dag/HeapGraph-0.ref b/dada_tests/heap-graph/dag/HeapGraph-0.ref index 953e4043..0074a39d 100644 --- a/dada_tests/heap-graph/dag/HeapGraph-0.ref +++ b/dada_tests/heap-graph/dag/HeapGraph-0.ref @@ -13,8 +13,8 @@ digraph { - - + +
main
p
q
(in-flight): "()"
q
(in-flight): "()"
>; ]; @@ -33,10 +33,10 @@ digraph { y: "44" > ]; - "afterstack":0 -> "afternode0" [label="our", style="solid"]; - "afterstack":6 -> "afternode1" [label="my", style="solid"]; - "afternode1":0 -> "afternode0" [label="our", style="solid"]; - "afternode1":1 -> "afternode0" [label="our", style="solid"]; + "afterstack":0 -> "afternode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; + "afterstack":2 -> "afternode1" [label="our leased", style="solid", penwidth=1.0, arrowtype="empty", color="blue"]; + "afternode1":0 -> "afternode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; + "afternode1":1 -> "afternode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; } subgraph cluster_before { label=<before> @@ -49,7 +49,7 @@ digraph { - +
main
p
q
q
>; ]; diff --git a/dada_tests/heap-graph/leased-point.dada b/dada_tests/heap-graph/leased-point.dada index 3249ab61..699a47d2 100644 --- a/dada_tests/heap-graph/leased-point.dada +++ b/dada_tests/heap-graph/leased-point.dada @@ -1,4 +1,4 @@ -class Point(x, y) +class Point(any x, any y) async fn main() { p = Point(22, 44) diff --git a/dada_tests/heap-graph/leased-point/HeapGraph-0.bir.ref b/dada_tests/heap-graph/leased-point/HeapGraph-0.bir.ref new file mode 100644 index 00000000..edbf720a --- /dev/null +++ b/dada_tests/heap-graph/leased-point/HeapGraph-0.bir.ref @@ -0,0 +1,180 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + BreakpoingStart( + "class", + 0, + ), + Expr(11), + ), + ( + AssignExpr( + temp{6}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{7}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{8}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{1}, + Call( + temp{6}, + [ + temp{7}, + temp{8}, + ], + [ + None, + None, + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{8}, + ), + Expr(2), + ), + ( + Clear( + temp{7}, + ), + Expr(1), + ), + ( + Clear( + temp{6}, + ), + Expr(0), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{5}, + (), + ), + Expr(4), + ), + ( + Clear( + temp{5}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{9}, + Class(Id { value: 1 }).reserve, + ), + Expr(5), + ), + ( + AssignExpr( + temp{10}, + p{0}.lease, + ), + Expr(7), + ), + ( + AssignExpr( + temp{11}, + 66, + ), + Expr(8), + ), + ], + Assign( + temp{3}, + Call( + temp{9}, + [ + temp{10}, + temp{11}, + ], + [ + None, + None, + ], + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + Clear( + temp{11}, + ), + Expr(8), + ), + ( + Clear( + temp{10}, + ), + Expr(7), + ), + ( + Clear( + temp{9}, + ), + Expr(5), + ), + ( + AssignPlace( + q{2}, + temp{3}, + ), + Expr(10), + ), + ( + AssignExpr( + temp{4}, + (), + ), + Expr(10), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(11), + Some( + temp{4}, + ), + ), + Expr(11), + ), + ], + Return( + temp{4}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/heap-graph/leased-point/HeapGraph-0.ref b/dada_tests/heap-graph/leased-point/HeapGraph-0.ref index 024cfa25..fcf6b9d1 100644 --- a/dada_tests/heap-graph/leased-point/HeapGraph-0.ref +++ b/dada_tests/heap-graph/leased-point/HeapGraph-0.ref @@ -13,8 +13,8 @@ digraph { - - + +
main
p
q
(in-flight): "()"
q
(in-flight): "()"
>; ]; @@ -33,9 +33,9 @@ digraph { y: "44" > ]; - "afterstack":0 -> "afternode0" [label="my", style="dotted"]; - "afterstack":5 -> "afternode1" [label="my", style="solid"]; - "afternode1":0 -> "afternode0" [label="leased", style="solid"]; + "afterstack":0 -> "afternode0" [label="our leased", style="solid", penwidth=1.0, arrowtype="empty", color="blue"]; + "afterstack":2 -> "afternode1" [label="our leased", style="solid", penwidth=1.0, arrowtype="empty", color="blue"]; + "afternode1":0 -> "afternode0" [label="our leased", style="solid", penwidth=1.0, arrowtype="empty", color="blue"]; } subgraph cluster_before { label=<before> @@ -48,7 +48,7 @@ digraph { - +
main
p
q
q
>; ]; diff --git a/dada_tests/heap-graph/line-end/HeapGraph-0.bir.ref b/dada_tests/heap-graph/line-end/HeapGraph-0.bir.ref new file mode 100644 index 00000000..1e0f1d0d --- /dev/null +++ b/dada_tests/heap-graph/line-end/HeapGraph-0.bir.ref @@ -0,0 +1,78 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + BreakpoingStart( + "async", + 0, + ), + Expr(0), + ), + ( + AssignExpr( + temp{1}, + 22, + ), + Expr(0), + ), + ( + BreakpointEnd( + "async", + 0, + Expr(0), + Some( + temp{1}, + ), + ), + Expr(0), + ), + ( + AssignPlace( + x{0}, + temp{1}, + ), + Expr(1), + ), + ( + AssignExpr( + temp{5}, + (), + ), + Expr(1), + ), + ( + Clear( + temp{5}, + ), + Expr(1), + ), + ( + AssignExpr( + temp{3}, + 44, + ), + Expr(2), + ), + ( + AssignPlace( + y{2}, + temp{3}, + ), + Expr(3), + ), + ( + AssignExpr( + temp{4}, + (), + ), + Expr(3), + ), + ], + Return( + temp{4}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/heap-graph/line-end/HeapGraph-0.ref b/dada_tests/heap-graph/line-end/HeapGraph-0.ref index f3968d66..ab76128f 100644 --- a/dada_tests/heap-graph/line-end/HeapGraph-0.ref +++ b/dada_tests/heap-graph/line-end/HeapGraph-0.ref @@ -14,7 +14,7 @@ digraph { main x y - (in-flight): "22" + (in-flight): "22" >; ]; diff --git a/dada_tests/heap-graph/line-end/HeapGraph-1.bir.ref b/dada_tests/heap-graph/line-end/HeapGraph-1.bir.ref new file mode 100644 index 00000000..b0e5cf49 --- /dev/null +++ b/dada_tests/heap-graph/line-end/HeapGraph-1.bir.ref @@ -0,0 +1,78 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + BreakpoingStart( + "async", + 0, + ), + Expr(4), + ), + ( + AssignExpr( + temp{1}, + 22, + ), + Expr(0), + ), + ( + AssignPlace( + x{0}, + temp{1}, + ), + Expr(1), + ), + ( + AssignExpr( + temp{5}, + (), + ), + Expr(1), + ), + ( + Clear( + temp{5}, + ), + Expr(1), + ), + ( + AssignExpr( + temp{3}, + 44, + ), + Expr(2), + ), + ( + AssignPlace( + y{2}, + temp{3}, + ), + Expr(3), + ), + ( + AssignExpr( + temp{4}, + (), + ), + Expr(3), + ), + ( + BreakpointEnd( + "async", + 0, + Expr(4), + Some( + temp{4}, + ), + ), + Expr(4), + ), + ], + Return( + temp{4}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/heap-graph/line-end/HeapGraph-1.ref b/dada_tests/heap-graph/line-end/HeapGraph-1.ref index 3f565c73..2dcc105b 100644 --- a/dada_tests/heap-graph/line-end/HeapGraph-1.ref +++ b/dada_tests/heap-graph/line-end/HeapGraph-1.ref @@ -14,7 +14,7 @@ digraph { main x: "22" y: "44" - (in-flight): "()" + (in-flight): "()" >; ]; diff --git a/dada_tests/heap-graph/line-start.dada b/dada_tests/heap-graph/line-start.dada index 7f83ee15..c4b7487a 100644 --- a/dada_tests/heap-graph/line-start.dada +++ b/dada_tests/heap-graph/line-start.dada @@ -1,4 +1,4 @@ -class Point(x, y) +class Point(any x, any y) async fn main() { p = Point(22, 44) diff --git a/dada_tests/heap-graph/line-start/HeapGraph-0.bir.ref b/dada_tests/heap-graph/line-start/HeapGraph-0.bir.ref new file mode 100644 index 00000000..933c797e --- /dev/null +++ b/dada_tests/heap-graph/line-start/HeapGraph-0.bir.ref @@ -0,0 +1,180 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + BreakpoingStart( + "class", + 0, + ), + Expr(0), + ), + ( + AssignExpr( + temp{6}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(0), + Some( + temp{6}, + ), + ), + Expr(0), + ), + ( + AssignExpr( + temp{7}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{8}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{1}, + Call( + temp{6}, + [ + temp{7}, + temp{8}, + ], + [ + None, + None, + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{8}, + ), + Expr(2), + ), + ( + Clear( + temp{7}, + ), + Expr(1), + ), + ( + Clear( + temp{6}, + ), + Expr(0), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{5}, + (), + ), + Expr(4), + ), + ( + Clear( + temp{5}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{9}, + Class(Id { value: 1 }).reserve, + ), + Expr(5), + ), + ( + AssignExpr( + temp{10}, + p{0}.reserve, + ), + Expr(6), + ), + ( + AssignExpr( + temp{11}, + 66, + ), + Expr(7), + ), + ], + Assign( + temp{3}, + Call( + temp{9}, + [ + temp{10}, + temp{11}, + ], + [ + None, + None, + ], + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + Clear( + temp{11}, + ), + Expr(7), + ), + ( + Clear( + temp{10}, + ), + Expr(6), + ), + ( + Clear( + temp{9}, + ), + Expr(5), + ), + ( + AssignPlace( + q{2}, + temp{3}, + ), + Expr(9), + ), + ( + AssignExpr( + temp{4}, + (), + ), + Expr(9), + ), + ], + Return( + temp{4}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/heap-graph/line-start/HeapGraph-0.ref b/dada_tests/heap-graph/line-start/HeapGraph-0.ref index 7665feaf..374180a1 100644 --- a/dada_tests/heap-graph/line-start/HeapGraph-0.ref +++ b/dada_tests/heap-graph/line-start/HeapGraph-0.ref @@ -13,8 +13,8 @@ digraph { - - + +
main
p
q
(in-flight)
q
(in-flight)
>; ]; @@ -24,7 +24,7 @@ digraph { fontcolor = "slategray", label = <Point> ]; - "stack":17 -> "afternode0" [label="our", style="solid"]; + "stack":12 -> "afternode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; } subgraph cluster_before { label=<before> @@ -37,52 +37,7 @@ digraph { - -
main
p
q
- >; - ]; - } - } -} -digraph { - node[shape = "note"]; - rankdir = "LR"; - subgraph cluster_after { - label=<after> - subgraph cluster_afterstack { - label=<stack> - rank="source"; - afterstack[ - shape="none"; - label=< - - - - - -
main
p
q
(in-flight)
- >; - ]; - } - afternode0 [ - color = "slategray", - fontcolor = "slategray", - label = <Point> - ]; - "stack":17 -> "afternode0" [label="our", style="solid"]; - } - subgraph cluster_before { - label=<before> - subgraph cluster_beforestack { - label=<stack> - rank="source"; - beforestack[ - shape="none"; - label=< - - - - +
main
p
q
q
>; ]; diff --git a/dada_tests/heap-graph/line-start/HeapGraph-1.bir.ref b/dada_tests/heap-graph/line-start/HeapGraph-1.bir.ref new file mode 100644 index 00000000..b3172366 --- /dev/null +++ b/dada_tests/heap-graph/line-start/HeapGraph-1.bir.ref @@ -0,0 +1,178 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + AssignExpr( + temp{6}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{7}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{8}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{1}, + Call( + temp{6}, + [ + temp{7}, + temp{8}, + ], + [ + None, + None, + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{8}, + ), + Expr(2), + ), + ( + Clear( + temp{7}, + ), + Expr(1), + ), + ( + Clear( + temp{6}, + ), + Expr(0), + ), + ( + BreakpoingStart( + "class", + 0, + ), + Expr(4), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(4), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(4), + None, + ), + Expr(4), + ), + ( + AssignExpr( + temp{5}, + (), + ), + Expr(4), + ), + ( + Clear( + temp{5}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{9}, + Class(Id { value: 1 }).reserve, + ), + Expr(5), + ), + ( + AssignExpr( + temp{10}, + p{0}.reserve, + ), + Expr(6), + ), + ( + AssignExpr( + temp{11}, + 66, + ), + Expr(7), + ), + ], + Assign( + temp{3}, + Call( + temp{9}, + [ + temp{10}, + temp{11}, + ], + [ + None, + None, + ], + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + Clear( + temp{11}, + ), + Expr(7), + ), + ( + Clear( + temp{10}, + ), + Expr(6), + ), + ( + Clear( + temp{9}, + ), + Expr(5), + ), + ( + AssignPlace( + q{2}, + temp{3}, + ), + Expr(9), + ), + ( + AssignExpr( + temp{4}, + (), + ), + Expr(9), + ), + ], + Return( + temp{4}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/heap-graph/line-start/HeapGraph-1.ref b/dada_tests/heap-graph/line-start/HeapGraph-1.ref index bd704103..e6c67792 100644 --- a/dada_tests/heap-graph/line-start/HeapGraph-1.ref +++ b/dada_tests/heap-graph/line-start/HeapGraph-1.ref @@ -13,19 +13,21 @@ digraph { - +
main
p
q
q
>; ]; } afternode0 [ + color = "slategray", + fontcolor = "slategray", label = < - - + +
Point
x: "22"
y: "44"
x: "22"
y: "44"
> ]; - "afterstack":0 -> "afternode0" [label="my", style="solid"]; + "afterstack":0 -> "afternode0" [label="our leased", style="solid", penwidth=1.0, arrowtype="empty", color="blue"]; } subgraph cluster_before { label=<before> @@ -38,7 +40,7 @@ digraph { - +
main
p
q
q
>; ]; diff --git a/dada_tests/heap-graph/mid-increment.dada b/dada_tests/heap-graph/mid-increment.dada index c9b1ce81..1354135d 100644 --- a/dada_tests/heap-graph/mid-increment.dada +++ b/dada_tests/heap-graph/mid-increment.dada @@ -1,8 +1,8 @@ -class Point(x, y) +class Point(any x, any y) async fn main() { - p = Point(22, 44) - q = p.lease + any p = Point(22, 44) + any q = p.lease #? @ +1:10 HeapGraph q.x += 1 print(q).await diff --git a/dada_tests/heap-graph/mid-increment/HeapGraph-0.bir.ref b/dada_tests/heap-graph/mid-increment/HeapGraph-0.bir.ref new file mode 100644 index 00000000..1029b867 --- /dev/null +++ b/dada_tests/heap-graph/mid-increment/HeapGraph-0.bir.ref @@ -0,0 +1,253 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + AssignExpr( + temp{8}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{9}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{10}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{1}, + Call( + temp{8}, + [ + temp{9}, + temp{10}, + ], + [ + None, + None, + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{10}, + ), + Expr(2), + ), + ( + Clear( + temp{9}, + ), + Expr(1), + ), + ( + Clear( + temp{8}, + ), + Expr(0), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{7}, + (), + ), + Expr(4), + ), + ( + Clear( + temp{7}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{3}, + p{0}.lease, + ), + Expr(6), + ), + ( + AssignPlace( + q{2}, + temp{3}, + ), + Expr(7), + ), + ( + AssignExpr( + temp{11}, + (), + ), + Expr(7), + ), + ( + Clear( + temp{11}, + ), + Expr(7), + ), + ( + AssignExpr( + temp{4}, + q{2}.lease, + ), + Expr(8), + ), + ( + AssignExpr( + temp{13}, + temp{4}.x.give, + ), + Expr(9), + ), + ( + AssignExpr( + temp{14}, + 1, + ), + Expr(10), + ), + ( + AssignExpr( + temp{5}, + temp{13} + temp{14}, + ), + Expr(11), + ), + ( + Clear( + temp{14}, + ), + Expr(10), + ), + ( + Clear( + temp{13}, + ), + Expr(9), + ), + ( + BreakpoingStart( + "class", + 0, + ), + Expr(9), + ), + ( + AssignPlace( + temp{4}.x, + temp{5}, + ), + Expr(11), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(11), + None, + ), + Expr(9), + ), + ( + AssignExpr( + temp{12}, + (), + ), + Expr(11), + ), + ( + Clear( + temp{12}, + ), + Expr(11), + ), + ( + AssignExpr( + temp{16}, + Print.reserve, + ), + Expr(12), + ), + ( + AssignExpr( + temp{17}, + q{2}.reserve, + ), + Expr(13), + ), + ], + Assign( + temp{15}, + Call( + temp{16}, + [ + temp{17}, + ], + [ + None, + ], + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + Clear( + temp{17}, + ), + Expr(13), + ), + ( + Clear( + temp{16}, + ), + Expr(12), + ), + ], + Assign( + temp{6}, + Await( + temp{15}, + ), + BasicBlock(3), + ), + ), + BasicBlock(3): BasicBlockData( + [ + ( + Clear( + temp{15}, + ), + Expr(14), + ), + ], + Return( + temp{6}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/heap-graph/mid-increment/HeapGraph-0.ref b/dada_tests/heap-graph/mid-increment/HeapGraph-0.ref index ee5fad0d..f92c9c1c 100644 --- a/dada_tests/heap-graph/mid-increment/HeapGraph-0.ref +++ b/dada_tests/heap-graph/mid-increment/HeapGraph-0.ref @@ -12,24 +12,21 @@ digraph { label=< - - - + +
main
p
q
(in-flight): "22"
p
q
>; ]; } afternode0 [ - color = "slategray", - fontcolor = "slategray", label = < - +
Point
x: "22"
x: "23"
y: "44"
> ]; - "afterstack":0 -> "afternode0" [label="my", style="dotted"]; - "afterstack":5 -> "afternode0" [label="leased", style="solid"]; + "afterstack":0 -> "afternode0" [label="my", style="dotted", penwidth=3.0, arrowtype="normal", color="red"]; + "afterstack":2 -> "afternode0" [label="leased", style="dotted", penwidth=1.0, arrowtype="empty", color="red"]; } subgraph cluster_before { label=<before> @@ -41,23 +38,21 @@ digraph { label=< - - + +
main
p
q
p
q
>; ]; } beforenode0 [ - color = "slategray", - fontcolor = "slategray", label = < - +
Point
x: "22"
x: "22"
y: "44"
> ]; - "beforestack":0 -> "beforenode0" [label="my", style="dotted"]; - "beforestack":5 -> "beforenode0" [label="leased", style="solid"]; + "beforestack":0 -> "beforenode0" [label="my", style="dotted", penwidth=3.0, arrowtype="normal", color="red"]; + "beforestack":2 -> "beforenode0" [label="leased", style="dotted", penwidth=1.0, arrowtype="empty", color="red"]; } } leased Point(23, 44) diff --git a/dada_tests/heap-graph/nested-functions.dada b/dada_tests/heap-graph/nested-functions.dada index 17d1bef9..427e627c 100644 --- a/dada_tests/heap-graph/nested-functions.dada +++ b/dada_tests/heap-graph/nested-functions.dada @@ -1,6 +1,6 @@ #! OUTPUT ANY -class Point(x, y) +class Point(any x, any y) async fn main() { name = "Fellow Dadaist" diff --git a/dada_tests/heap-graph/nested-functions/HeapGraph-0.bir.ref b/dada_tests/heap-graph/nested-functions/HeapGraph-0.bir.ref new file mode 100644 index 00000000..d67fd7cb --- /dev/null +++ b/dada_tests/heap-graph/nested-functions/HeapGraph-0.bir.ref @@ -0,0 +1,392 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + AssignExpr( + temp{1}, + "Fellow Dadaist", + ), + Expr(0), + ), + ( + AssignPlace( + name{0}, + temp{1}, + ), + Expr(1), + ), + ( + AssignExpr( + temp{3}, + (), + ), + Expr(1), + ), + ( + Clear( + temp{3}, + ), + Expr(1), + ), + ( + AssignExpr( + temp{6}, + helper.reserve, + ), + Expr(2), + ), + ], + Assign( + temp{5}, + Call( + temp{6}, + [], + [], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{6}, + ), + Expr(2), + ), + ], + Assign( + temp{4}, + Await( + temp{5}, + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + Clear( + temp{5}, + ), + Expr(3), + ), + ( + Clear( + temp{4}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{9}, + Print.reserve, + ), + Expr(5), + ), + ( + AssignExpr( + temp{10}, + "Hello", + ), + Expr(6), + ), + ], + Assign( + temp{8}, + Call( + temp{9}, + [ + temp{10}, + ], + [ + None, + ], + ), + BasicBlock(3), + ), + ), + BasicBlock(3): BasicBlockData( + [ + ( + Clear( + temp{10}, + ), + Expr(6), + ), + ( + Clear( + temp{9}, + ), + Expr(5), + ), + ], + Assign( + temp{7}, + Await( + temp{8}, + ), + BasicBlock(4), + ), + ), + BasicBlock(4): BasicBlockData( + [ + ( + Clear( + temp{8}, + ), + Expr(7), + ), + ( + Clear( + temp{7}, + ), + Expr(8), + ), + ( + AssignExpr( + temp{12}, + Print.reserve, + ), + Expr(9), + ), + ( + AssignExpr( + temp{13}, + name{0}.reserve, + ), + Expr(10), + ), + ], + Assign( + temp{11}, + Call( + temp{12}, + [ + temp{13}, + ], + [ + None, + ], + ), + BasicBlock(5), + ), + ), + BasicBlock(5): BasicBlockData( + [ + ( + Clear( + temp{13}, + ), + Expr(10), + ), + ( + Clear( + temp{12}, + ), + Expr(9), + ), + ], + Assign( + temp{2}, + Await( + temp{11}, + ), + BasicBlock(6), + ), + ), + BasicBlock(6): BasicBlockData( + [ + ( + Clear( + temp{11}, + ), + Expr(11), + ), + ], + Return( + temp{2}, + ), + ), + }, + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + AssignExpr( + temp{6}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{7}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{8}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{1}, + Call( + temp{6}, + [ + temp{7}, + temp{8}, + ], + [ + None, + None, + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{8}, + ), + Expr(2), + ), + ( + Clear( + temp{7}, + ), + Expr(1), + ), + ( + Clear( + temp{6}, + ), + Expr(0), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{5}, + (), + ), + Expr(4), + ), + ( + Clear( + temp{5}, + ), + Expr(4), + ), + ( + BreakpoingStart( + "class", + 0, + ), + Expr(8), + ), + ( + AssignExpr( + temp{9}, + Class(Id { value: 1 }).reserve, + ), + Expr(5), + ), + ( + AssignExpr( + temp{10}, + p{0}.reserve, + ), + Expr(6), + ), + ( + AssignExpr( + temp{11}, + 66, + ), + Expr(7), + ), + ], + Assign( + temp{3}, + Call( + temp{9}, + [ + temp{10}, + temp{11}, + ], + [ + None, + None, + ], + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + BreakpointEnd( + "class", + 0, + Expr(8), + Some( + temp{3}, + ), + ), + Expr(8), + ), + ( + Clear( + temp{11}, + ), + Expr(7), + ), + ( + Clear( + temp{10}, + ), + Expr(6), + ), + ( + Clear( + temp{9}, + ), + Expr(5), + ), + ( + AssignPlace( + q{2}, + temp{3}, + ), + Expr(9), + ), + ( + AssignExpr( + temp{4}, + (), + ), + Expr(9), + ), + ], + Return( + temp{4}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/heap-graph/nested-functions/HeapGraph-0.ref b/dada_tests/heap-graph/nested-functions/HeapGraph-0.ref index df5826c7..be5c9a45 100644 --- a/dada_tests/heap-graph/nested-functions/HeapGraph-0.ref +++ b/dada_tests/heap-graph/nested-functions/HeapGraph-0.ref @@ -14,21 +14,21 @@ digraph { main name: "Fellow Dadaist" helper - p - q - (in-flight) + p + q + (in-flight) >; ]; } - afternode0 [ + afternode1 [ label = < - +
Point
x
x
y: "66"
> ]; - afternode1 [ + afternode0 [ color = "slategray", fontcolor = "slategray", label = < @@ -37,8 +37,9 @@ digraph {
y: "44"
> ]; - "stack":35 -> "afternode0" [label="my", style="solid"]; - "afternode0":0 -> "afternode1" [label="my", style="solid"]; + "afterstack":14 -> "afternode0" [label="our leased", style="solid", penwidth=1.0, arrowtype="empty", color="blue"]; + "stack":26 -> "afternode1" [label="my", style="solid", penwidth=3.0, arrowtype="normal", color="red"]; + "afternode1":0 -> "afternode0" [label="our leased", style="solid", penwidth=1.0, arrowtype="empty", color="blue"]; } subgraph cluster_before { label=<before> @@ -52,8 +53,8 @@ digraph { main name: "Fellow Dadaist" helper - p - q + p + q >; ]; @@ -67,7 +68,7 @@ digraph { y: "44" > ]; - "beforestack":18 -> "beforenode0" [label="my", style="solid"]; + "beforestack":14 -> "beforenode0" [label="our leased", style="solid", penwidth=1.0, arrowtype="empty", color="blue"]; } } Hello diff --git a/dada_tests/heap-graph/nested-points.dada b/dada_tests/heap-graph/nested-points.dada index 790b7bae..5b1e9cfe 100644 --- a/dada_tests/heap-graph/nested-points.dada +++ b/dada_tests/heap-graph/nested-points.dada @@ -1,5 +1,5 @@ -class Point(x, y) +class Point(any x, any y) async fn main() { p = Point(22, 44) diff --git a/dada_tests/heap-graph/nested-points/HeapGraph-0.bir.ref b/dada_tests/heap-graph/nested-points/HeapGraph-0.bir.ref new file mode 100644 index 00000000..5d1493be --- /dev/null +++ b/dada_tests/heap-graph/nested-points/HeapGraph-0.bir.ref @@ -0,0 +1,180 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + BreakpoingStart( + "class", + 0, + ), + Expr(3), + ), + ( + AssignExpr( + temp{6}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{7}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{8}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{1}, + Call( + temp{6}, + [ + temp{7}, + temp{8}, + ], + [ + None, + None, + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + BreakpointEnd( + "class", + 0, + Expr(3), + Some( + temp{1}, + ), + ), + Expr(3), + ), + ( + Clear( + temp{8}, + ), + Expr(2), + ), + ( + Clear( + temp{7}, + ), + Expr(1), + ), + ( + Clear( + temp{6}, + ), + Expr(0), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{5}, + (), + ), + Expr(4), + ), + ( + Clear( + temp{5}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{9}, + Class(Id { value: 1 }).reserve, + ), + Expr(5), + ), + ( + AssignExpr( + temp{10}, + p{0}.reserve, + ), + Expr(6), + ), + ( + AssignExpr( + temp{11}, + 66, + ), + Expr(7), + ), + ], + Assign( + temp{3}, + Call( + temp{9}, + [ + temp{10}, + temp{11}, + ], + [ + None, + None, + ], + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + Clear( + temp{11}, + ), + Expr(7), + ), + ( + Clear( + temp{10}, + ), + Expr(6), + ), + ( + Clear( + temp{9}, + ), + Expr(5), + ), + ( + AssignPlace( + q{2}, + temp{3}, + ), + Expr(9), + ), + ( + AssignExpr( + temp{4}, + (), + ), + Expr(9), + ), + ], + Return( + temp{4}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/heap-graph/nested-points/HeapGraph-0.ref b/dada_tests/heap-graph/nested-points/HeapGraph-0.ref index 3ff24c05..ea2e5c7d 100644 --- a/dada_tests/heap-graph/nested-points/HeapGraph-0.ref +++ b/dada_tests/heap-graph/nested-points/HeapGraph-0.ref @@ -13,8 +13,8 @@ digraph { - - + +
main
p
q
(in-flight)
q
(in-flight)
>; ]; @@ -26,7 +26,7 @@ digraph { y: "44" > ]; - "stack":17 -> "afternode0" [label="my", style="solid"]; + "stack":12 -> "afternode0" [label="my", style="solid", penwidth=3.0, arrowtype="normal", color="red"]; } subgraph cluster_before { label=<before> @@ -39,7 +39,7 @@ digraph { - +
main
p
q
q
>; ]; diff --git a/dada_tests/heap-graph/nested-points/HeapGraph-1.bir.ref b/dada_tests/heap-graph/nested-points/HeapGraph-1.bir.ref new file mode 100644 index 00000000..b3172366 --- /dev/null +++ b/dada_tests/heap-graph/nested-points/HeapGraph-1.bir.ref @@ -0,0 +1,178 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + AssignExpr( + temp{6}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{7}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{8}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{1}, + Call( + temp{6}, + [ + temp{7}, + temp{8}, + ], + [ + None, + None, + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{8}, + ), + Expr(2), + ), + ( + Clear( + temp{7}, + ), + Expr(1), + ), + ( + Clear( + temp{6}, + ), + Expr(0), + ), + ( + BreakpoingStart( + "class", + 0, + ), + Expr(4), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(4), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(4), + None, + ), + Expr(4), + ), + ( + AssignExpr( + temp{5}, + (), + ), + Expr(4), + ), + ( + Clear( + temp{5}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{9}, + Class(Id { value: 1 }).reserve, + ), + Expr(5), + ), + ( + AssignExpr( + temp{10}, + p{0}.reserve, + ), + Expr(6), + ), + ( + AssignExpr( + temp{11}, + 66, + ), + Expr(7), + ), + ], + Assign( + temp{3}, + Call( + temp{9}, + [ + temp{10}, + temp{11}, + ], + [ + None, + None, + ], + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + Clear( + temp{11}, + ), + Expr(7), + ), + ( + Clear( + temp{10}, + ), + Expr(6), + ), + ( + Clear( + temp{9}, + ), + Expr(5), + ), + ( + AssignPlace( + q{2}, + temp{3}, + ), + Expr(9), + ), + ( + AssignExpr( + temp{4}, + (), + ), + Expr(9), + ), + ], + Return( + temp{4}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/heap-graph/nested-points/HeapGraph-1.ref b/dada_tests/heap-graph/nested-points/HeapGraph-1.ref index 1eec329b..bd1e0304 100644 --- a/dada_tests/heap-graph/nested-points/HeapGraph-1.ref +++ b/dada_tests/heap-graph/nested-points/HeapGraph-1.ref @@ -13,19 +13,21 @@ digraph { - +
main
p
q
q
>; ]; } afternode0 [ + color = "slategray", + fontcolor = "slategray", label = < - - + +
Point
x: "22"
y: "44"
x: "22"
y: "44"
> ]; - "afterstack":0 -> "afternode0" [label="my", style="solid"]; + "afterstack":0 -> "afternode0" [label="our leased", style="solid", penwidth=1.0, arrowtype="empty", color="blue"]; } subgraph cluster_before { label=<before> @@ -38,7 +40,7 @@ digraph { - +
main
p
q
q
>; ]; diff --git a/dada_tests/heap-graph/nested-points/HeapGraph-2.bir.ref b/dada_tests/heap-graph/nested-points/HeapGraph-2.bir.ref new file mode 100644 index 00000000..077b8f40 --- /dev/null +++ b/dada_tests/heap-graph/nested-points/HeapGraph-2.bir.ref @@ -0,0 +1,180 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + AssignExpr( + temp{6}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{7}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{8}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{1}, + Call( + temp{6}, + [ + temp{7}, + temp{8}, + ], + [ + None, + None, + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{8}, + ), + Expr(2), + ), + ( + Clear( + temp{7}, + ), + Expr(1), + ), + ( + Clear( + temp{6}, + ), + Expr(0), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{5}, + (), + ), + Expr(4), + ), + ( + Clear( + temp{5}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{9}, + Class(Id { value: 1 }).reserve, + ), + Expr(5), + ), + ( + BreakpoingStart( + "class", + 0, + ), + Expr(6), + ), + ( + AssignExpr( + temp{10}, + p{0}.reserve, + ), + Expr(6), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(6), + Some( + temp{10}, + ), + ), + Expr(6), + ), + ( + AssignExpr( + temp{11}, + 66, + ), + Expr(7), + ), + ], + Assign( + temp{3}, + Call( + temp{9}, + [ + temp{10}, + temp{11}, + ], + [ + None, + None, + ], + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + Clear( + temp{11}, + ), + Expr(7), + ), + ( + Clear( + temp{10}, + ), + Expr(6), + ), + ( + Clear( + temp{9}, + ), + Expr(5), + ), + ( + AssignPlace( + q{2}, + temp{3}, + ), + Expr(9), + ), + ( + AssignExpr( + temp{4}, + (), + ), + Expr(9), + ), + ], + Return( + temp{4}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/heap-graph/nested-points/HeapGraph-2.ref b/dada_tests/heap-graph/nested-points/HeapGraph-2.ref index 550624ae..b82e0acc 100644 --- a/dada_tests/heap-graph/nested-points/HeapGraph-2.ref +++ b/dada_tests/heap-graph/nested-points/HeapGraph-2.ref @@ -12,9 +12,9 @@ digraph { label=< - - - + + +
main
p
q
(in-flight)
p
q
(in-flight)
>; ]; @@ -28,7 +28,8 @@ digraph { y: "44" > ]; - "stack":17 -> "afternode0" [label="my", style="solid"]; + "afterstack":0 -> "afternode0" [label="our leased", style="solid", penwidth=1.0, arrowtype="empty", color="blue"]; + "stack":12 -> "afternode0" [label="our leased", style="solid", penwidth=1.0, arrowtype="empty", color="blue"]; } subgraph cluster_before { label=<before> @@ -40,8 +41,8 @@ digraph { label=< - - + +
main
p
q
p
q
>; ]; @@ -55,65 +56,6 @@ digraph { y: "44" > ]; - "beforestack":0 -> "beforenode0" [label="my", style="solid"]; - } -} -digraph { - node[shape = "note"]; - rankdir = "LR"; - subgraph cluster_after { - label=<after> - subgraph cluster_afterstack { - label=<stack> - rank="source"; - afterstack[ - shape="none"; - label=< - - - - - -
main
p
q
(in-flight)
- >; - ]; - } - afternode0 [ - color = "slategray", - fontcolor = "slategray", - label = < - - - -
Point
x: "22"
y: "44"
> - ]; - "stack":17 -> "afternode0" [label="my", style="solid"]; - } - subgraph cluster_before { - label=<before> - subgraph cluster_beforestack { - label=<stack> - rank="source"; - beforestack[ - shape="none"; - label=< - - - - -
main
p
q
- >; - ]; - } - beforenode0 [ - color = "slategray", - fontcolor = "slategray", - label = < - - - -
Point
x: "22"
y: "44"
> - ]; - "beforestack":0 -> "beforenode0" [label="my", style="solid"]; + "beforestack":0 -> "beforenode0" [label="our leased", style="solid", penwidth=1.0, arrowtype="empty", color="blue"]; } } diff --git a/dada_tests/heap-graph/past-end/HeapGraph-0.bir.ref b/dada_tests/heap-graph/past-end/HeapGraph-0.bir.ref new file mode 100644 index 00000000..c8c46977 --- /dev/null +++ b/dada_tests/heap-graph/past-end/HeapGraph-0.bir.ref @@ -0,0 +1,72 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + AssignExpr( + temp{2}, + Print.reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{3}, + "I have forced myself to contradict myself\nin order to avoid conforming to my own taste.\n -- Marcel Duchamp", + ), + Expr(1), + ), + ], + Assign( + temp{1}, + Call( + temp{2}, + [ + temp{3}, + ], + [ + None, + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{3}, + ), + Expr(1), + ), + ( + Clear( + temp{2}, + ), + Expr(0), + ), + ], + Assign( + temp{0}, + Await( + temp{1}, + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + Clear( + temp{1}, + ), + Expr(2), + ), + ], + Return( + temp{0}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/heap-graph/rust-thunk/HeapGraph-0.bir.ref b/dada_tests/heap-graph/rust-thunk/HeapGraph-0.bir.ref new file mode 100644 index 00000000..861f75d7 --- /dev/null +++ b/dada_tests/heap-graph/rust-thunk/HeapGraph-0.bir.ref @@ -0,0 +1,90 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + BreakpoingStart( + "async", + 0, + ), + Expr(2), + ), + ( + AssignExpr( + temp{2}, + Print.reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{3}, + "foo", + ), + Expr(1), + ), + ], + Assign( + temp{1}, + Call( + temp{2}, + [ + temp{3}, + ], + [ + None, + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + BreakpointEnd( + "async", + 0, + Expr(2), + Some( + temp{1}, + ), + ), + Expr(2), + ), + ( + Clear( + temp{3}, + ), + Expr(1), + ), + ( + Clear( + temp{2}, + ), + Expr(0), + ), + ], + Assign( + temp{0}, + Await( + temp{1}, + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + Clear( + temp{1}, + ), + Expr(2), + ), + ], + Return( + temp{0}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/heap-graph/rust-thunk/HeapGraph-0.ref b/dada_tests/heap-graph/rust-thunk/HeapGraph-0.ref index cc2b9160..a176819d 100644 --- a/dada_tests/heap-graph/rust-thunk/HeapGraph-0.ref +++ b/dada_tests/heap-graph/rust-thunk/HeapGraph-0.ref @@ -12,7 +12,7 @@ digraph { label=< - +
main
(in-flight)
(in-flight)
>; ]; @@ -23,7 +23,7 @@ digraph { 0: "foo" > ]; - "stack":6 -> "afternode0" [label="my", style="solid"]; + "stack":4 -> "afternode0" [label="my", style="solid", penwidth=3.0, arrowtype="normal", color="red"]; } subgraph cluster_before { label=<before> diff --git a/dada_tests/heap-graph/tutorial-1.dada b/dada_tests/heap-graph/tutorial-1.dada index e9e489ee..17629bb3 100644 --- a/dada_tests/heap-graph/tutorial-1.dada +++ b/dada_tests/heap-graph/tutorial-1.dada @@ -1,4 +1,4 @@ -class Point(x, y) +class Point(any x, any y) async fn main() { p = Point(x: 22, y: 44) diff --git a/dada_tests/heap-graph/tutorial-1/HeapGraph-0.bir.ref b/dada_tests/heap-graph/tutorial-1/HeapGraph-0.bir.ref new file mode 100644 index 00000000..ee2777c5 --- /dev/null +++ b/dada_tests/heap-graph/tutorial-1/HeapGraph-0.bir.ref @@ -0,0 +1,172 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + BreakpoingStart( + "class", + 0, + ), + Expr(0), + ), + ( + AssignExpr( + temp{4}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(0), + Some( + temp{4}, + ), + ), + Expr(0), + ), + ( + AssignExpr( + temp{5}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{6}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{1}, + Call( + temp{4}, + [ + temp{5}, + temp{6}, + ], + [ + Some( + "x", + ), + Some( + "y", + ), + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{6}, + ), + Expr(2), + ), + ( + Clear( + temp{5}, + ), + Expr(1), + ), + ( + Clear( + temp{4}, + ), + Expr(0), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{3}, + (), + ), + Expr(4), + ), + ( + Clear( + temp{3}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{8}, + Print.reserve, + ), + Expr(5), + ), + ( + AssignExpr( + temp{9}, + "The point is FIXME", + ), + Expr(6), + ), + ], + Assign( + temp{7}, + Call( + temp{8}, + [ + temp{9}, + ], + [ + None, + ], + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + Clear( + temp{9}, + ), + Expr(6), + ), + ( + Clear( + temp{8}, + ), + Expr(5), + ), + ], + Assign( + temp{2}, + Await( + temp{7}, + ), + BasicBlock(3), + ), + ), + BasicBlock(3): BasicBlockData( + [ + ( + Clear( + temp{7}, + ), + Expr(7), + ), + ], + Return( + temp{2}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/heap-graph/tutorial-1/HeapGraph-0.ref b/dada_tests/heap-graph/tutorial-1/HeapGraph-0.ref index 916b9936..e5f8c01f 100644 --- a/dada_tests/heap-graph/tutorial-1/HeapGraph-0.ref +++ b/dada_tests/heap-graph/tutorial-1/HeapGraph-0.ref @@ -13,7 +13,7 @@ digraph { - +
main
p
(in-flight)
(in-flight)
>; ]; @@ -23,50 +23,7 @@ digraph { fontcolor = "slategray", label = <Point> ]; - "stack":14 -> "afternode0" [label="our", style="solid"]; - } - subgraph cluster_before { - label=<before> - subgraph cluster_beforestack { - label=<stack> - rank="source"; - beforestack[ - shape="none"; - label=< - - - -
main
p
- >; - ]; - } - } -} -digraph { - node[shape = "note"]; - rankdir = "LR"; - subgraph cluster_after { - label=<after> - subgraph cluster_afterstack { - label=<stack> - rank="source"; - afterstack[ - shape="none"; - label=< - - - - -
main
p
(in-flight)
- >; - ]; - } - afternode0 [ - color = "slategray", - fontcolor = "slategray", - label = <Point> - ]; - "stack":14 -> "afternode0" [label="our", style="solid"]; + "stack":10 -> "afternode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; } subgraph cluster_before { label=<before> diff --git a/dada_tests/heap-graph/tutorial-1/HeapGraph-1.bir.ref b/dada_tests/heap-graph/tutorial-1/HeapGraph-1.bir.ref new file mode 100644 index 00000000..5de2b09f --- /dev/null +++ b/dada_tests/heap-graph/tutorial-1/HeapGraph-1.bir.ref @@ -0,0 +1,172 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + AssignExpr( + temp{4}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + BreakpoingStart( + "class", + 0, + ), + Expr(1), + ), + ( + AssignExpr( + temp{5}, + 22, + ), + Expr(1), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(1), + Some( + temp{5}, + ), + ), + Expr(1), + ), + ( + AssignExpr( + temp{6}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{1}, + Call( + temp{4}, + [ + temp{5}, + temp{6}, + ], + [ + Some( + "x", + ), + Some( + "y", + ), + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{6}, + ), + Expr(2), + ), + ( + Clear( + temp{5}, + ), + Expr(1), + ), + ( + Clear( + temp{4}, + ), + Expr(0), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{3}, + (), + ), + Expr(4), + ), + ( + Clear( + temp{3}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{8}, + Print.reserve, + ), + Expr(5), + ), + ( + AssignExpr( + temp{9}, + "The point is FIXME", + ), + Expr(6), + ), + ], + Assign( + temp{7}, + Call( + temp{8}, + [ + temp{9}, + ], + [ + None, + ], + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + Clear( + temp{9}, + ), + Expr(6), + ), + ( + Clear( + temp{8}, + ), + Expr(5), + ), + ], + Assign( + temp{2}, + Await( + temp{7}, + ), + BasicBlock(3), + ), + ), + BasicBlock(3): BasicBlockData( + [ + ( + Clear( + temp{7}, + ), + Expr(7), + ), + ], + Return( + temp{2}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/heap-graph/tutorial-1/HeapGraph-1.ref b/dada_tests/heap-graph/tutorial-1/HeapGraph-1.ref index 15e2965a..c72b06be 100644 --- a/dada_tests/heap-graph/tutorial-1/HeapGraph-1.ref +++ b/dada_tests/heap-graph/tutorial-1/HeapGraph-1.ref @@ -13,7 +13,7 @@ digraph { - +
main
p
(in-flight): "22"
(in-flight): "22"
>; ]; diff --git a/dada_tests/heap-graph/tutorial-1/HeapGraph-2.bir.ref b/dada_tests/heap-graph/tutorial-1/HeapGraph-2.bir.ref new file mode 100644 index 00000000..1b72a6fb --- /dev/null +++ b/dada_tests/heap-graph/tutorial-1/HeapGraph-2.bir.ref @@ -0,0 +1,170 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + AssignExpr( + temp{4}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{5}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{6}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{1}, + Call( + temp{4}, + [ + temp{5}, + temp{6}, + ], + [ + Some( + "x", + ), + Some( + "y", + ), + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{6}, + ), + Expr(2), + ), + ( + Clear( + temp{5}, + ), + Expr(1), + ), + ( + Clear( + temp{4}, + ), + Expr(0), + ), + ( + BreakpoingStart( + "class", + 0, + ), + Expr(4), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(4), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(4), + None, + ), + Expr(4), + ), + ( + AssignExpr( + temp{3}, + (), + ), + Expr(4), + ), + ( + Clear( + temp{3}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{8}, + Print.reserve, + ), + Expr(5), + ), + ( + AssignExpr( + temp{9}, + "The point is FIXME", + ), + Expr(6), + ), + ], + Assign( + temp{7}, + Call( + temp{8}, + [ + temp{9}, + ], + [ + None, + ], + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + Clear( + temp{9}, + ), + Expr(6), + ), + ( + Clear( + temp{8}, + ), + Expr(5), + ), + ], + Assign( + temp{2}, + Await( + temp{7}, + ), + BasicBlock(3), + ), + ), + BasicBlock(3): BasicBlockData( + [ + ( + Clear( + temp{7}, + ), + Expr(7), + ), + ], + Return( + temp{2}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/heap-graph/tutorial-1/HeapGraph-2.ref b/dada_tests/heap-graph/tutorial-1/HeapGraph-2.ref index 7ad8ec5e..4f8eaaa2 100644 --- a/dada_tests/heap-graph/tutorial-1/HeapGraph-2.ref +++ b/dada_tests/heap-graph/tutorial-1/HeapGraph-2.ref @@ -18,13 +18,15 @@ digraph { ]; } afternode0 [ + color = "slategray", + fontcolor = "slategray", label = < - - + +
Point
x: "22"
y: "44"
x: "22"
y: "44"
> ]; - "afterstack":0 -> "afternode0" [label="my", style="solid"]; + "afterstack":0 -> "afternode0" [label="our leased", style="solid", penwidth=1.0, arrowtype="empty", color="blue"]; } subgraph cluster_before { label=<before> diff --git a/dada_tests/heap-graph/tutorial-1/HeapGraph-3.bir.ref b/dada_tests/heap-graph/tutorial-1/HeapGraph-3.bir.ref new file mode 100644 index 00000000..1b72a6fb --- /dev/null +++ b/dada_tests/heap-graph/tutorial-1/HeapGraph-3.bir.ref @@ -0,0 +1,170 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + AssignExpr( + temp{4}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{5}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{6}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{1}, + Call( + temp{4}, + [ + temp{5}, + temp{6}, + ], + [ + Some( + "x", + ), + Some( + "y", + ), + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{6}, + ), + Expr(2), + ), + ( + Clear( + temp{5}, + ), + Expr(1), + ), + ( + Clear( + temp{4}, + ), + Expr(0), + ), + ( + BreakpoingStart( + "class", + 0, + ), + Expr(4), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(4), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(4), + None, + ), + Expr(4), + ), + ( + AssignExpr( + temp{3}, + (), + ), + Expr(4), + ), + ( + Clear( + temp{3}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{8}, + Print.reserve, + ), + Expr(5), + ), + ( + AssignExpr( + temp{9}, + "The point is FIXME", + ), + Expr(6), + ), + ], + Assign( + temp{7}, + Call( + temp{8}, + [ + temp{9}, + ], + [ + None, + ], + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + Clear( + temp{9}, + ), + Expr(6), + ), + ( + Clear( + temp{8}, + ), + Expr(5), + ), + ], + Assign( + temp{2}, + Await( + temp{7}, + ), + BasicBlock(3), + ), + ), + BasicBlock(3): BasicBlockData( + [ + ( + Clear( + temp{7}, + ), + Expr(7), + ), + ], + Return( + temp{2}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/heap-graph/tutorial-1/HeapGraph-3.ref b/dada_tests/heap-graph/tutorial-1/HeapGraph-3.ref index 7ad8ec5e..4f8eaaa2 100644 --- a/dada_tests/heap-graph/tutorial-1/HeapGraph-3.ref +++ b/dada_tests/heap-graph/tutorial-1/HeapGraph-3.ref @@ -18,13 +18,15 @@ digraph { ]; } afternode0 [ + color = "slategray", + fontcolor = "slategray", label = < - - + +
Point
x: "22"
y: "44"
x: "22"
y: "44"
> ]; - "afterstack":0 -> "afternode0" [label="my", style="solid"]; + "afterstack":0 -> "afternode0" [label="our leased", style="solid", penwidth=1.0, arrowtype="empty", color="blue"]; } subgraph cluster_before { label=<before> diff --git a/dada_tests/heap-graph/tutorial-1/HeapGraph-4.bir.ref b/dada_tests/heap-graph/tutorial-1/HeapGraph-4.bir.ref new file mode 100644 index 00000000..50917a03 --- /dev/null +++ b/dada_tests/heap-graph/tutorial-1/HeapGraph-4.bir.ref @@ -0,0 +1,172 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + AssignExpr( + temp{4}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{5}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{6}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{1}, + Call( + temp{4}, + [ + temp{5}, + temp{6}, + ], + [ + Some( + "x", + ), + Some( + "y", + ), + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{6}, + ), + Expr(2), + ), + ( + Clear( + temp{5}, + ), + Expr(1), + ), + ( + Clear( + temp{4}, + ), + Expr(0), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{3}, + (), + ), + Expr(4), + ), + ( + Clear( + temp{3}, + ), + Expr(4), + ), + ( + BreakpoingStart( + "class", + 0, + ), + Expr(5), + ), + ( + AssignExpr( + temp{8}, + Print.reserve, + ), + Expr(5), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(5), + Some( + temp{8}, + ), + ), + Expr(5), + ), + ( + AssignExpr( + temp{9}, + "The point is FIXME", + ), + Expr(6), + ), + ], + Assign( + temp{7}, + Call( + temp{8}, + [ + temp{9}, + ], + [ + None, + ], + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + Clear( + temp{9}, + ), + Expr(6), + ), + ( + Clear( + temp{8}, + ), + Expr(5), + ), + ], + Assign( + temp{2}, + Await( + temp{7}, + ), + BasicBlock(3), + ), + ), + BasicBlock(3): BasicBlockData( + [ + ( + Clear( + temp{7}, + ), + Expr(7), + ), + ], + Return( + temp{2}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/heap-graph/tutorial-1/HeapGraph-4.ref b/dada_tests/heap-graph/tutorial-1/HeapGraph-4.ref index 73951588..0683cc4c 100644 --- a/dada_tests/heap-graph/tutorial-1/HeapGraph-4.ref +++ b/dada_tests/heap-graph/tutorial-1/HeapGraph-4.ref @@ -13,7 +13,7 @@ digraph { - +
main
p
(in-flight): "print"
(in-flight): "print"
>; ]; @@ -27,7 +27,7 @@ digraph { y: "44" > ]; - "afterstack":0 -> "afternode0" [label="my", style="solid"]; + "afterstack":0 -> "afternode0" [label="our leased", style="solid", penwidth=1.0, arrowtype="empty", color="blue"]; } subgraph cluster_before { label=<before> @@ -53,64 +53,7 @@ digraph { y: "44" > ]; - "beforestack":0 -> "beforenode0" [label="my", style="solid"]; - } -} -digraph { - node[shape = "note"]; - rankdir = "LR"; - subgraph cluster_after { - label=<after> - subgraph cluster_afterstack { - label=<stack> - rank="source"; - afterstack[ - shape="none"; - label=< - - - - -
main
p
(in-flight): "print"
- >; - ]; - } - afternode0 [ - color = "slategray", - fontcolor = "slategray", - label = < - - - -
Point
x: "22"
y: "44"
> - ]; - "afterstack":0 -> "afternode0" [label="my", style="solid"]; - } - subgraph cluster_before { - label=<before> - subgraph cluster_beforestack { - label=<stack> - rank="source"; - beforestack[ - shape="none"; - label=< - - - -
main
p
- >; - ]; - } - beforenode0 [ - color = "slategray", - fontcolor = "slategray", - label = < - - - -
Point
x: "22"
y: "44"
> - ]; - "beforestack":0 -> "beforenode0" [label="my", style="solid"]; + "beforestack":0 -> "beforenode0" [label="our leased", style="solid", penwidth=1.0, arrowtype="empty", color="blue"]; } } The point is FIXME diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-give-10.dada b/dada_tests/permissions/dyn_tutorial/tutorial-give-10.dada index 79ffd8c4..6b0359fe 100644 --- a/dada_tests/permissions/dyn_tutorial/tutorial-give-10.dada +++ b/dada_tests/permissions/dyn_tutorial/tutorial-give-10.dada @@ -1,10 +1,10 @@ -class Point(x, y) +class Point(any x, any y) async fn main() { - p = Point(x: 22, y: 44) - #? ^ HeapGraph - q = p - #? ^ HeapGraph + any p = Point(x: 22, y: 44) + #? ^ HeapGraph + any q = p + #? ^ HeapGraph x = p.x #! ^ RUN ERROR your lease to this object was cancelled diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-give-10/HeapGraph-0.bir.ref b/dada_tests/permissions/dyn_tutorial/tutorial-give-10/HeapGraph-0.bir.ref new file mode 100644 index 00000000..dc754fc3 --- /dev/null +++ b/dada_tests/permissions/dyn_tutorial/tutorial-give-10/HeapGraph-0.bir.ref @@ -0,0 +1,131 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + AssignExpr( + temp{6}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{7}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{8}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{1}, + Call( + temp{6}, + [ + temp{7}, + temp{8}, + ], + [ + Some( + "x", + ), + Some( + "y", + ), + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{8}, + ), + Expr(2), + ), + ( + Clear( + temp{7}, + ), + Expr(1), + ), + ( + Clear( + temp{6}, + ), + Expr(0), + ), + ( + BreakpoingStart( + "class", + 0, + ), + Expr(4), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(4), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(4), + None, + ), + Expr(4), + ), + ( + AssignExpr( + temp{5}, + (), + ), + Expr(4), + ), + ( + Clear( + temp{5}, + ), + Expr(4), + ), + ( + AssignPlace( + q{2}, + p{0}, + ), + Expr(6), + ), + ( + AssignPlace( + x{3}, + p{0}.x, + ), + Expr(9), + ), + ( + AssignExpr( + temp{4}, + (), + ), + Expr(9), + ), + ], + Return( + temp{4}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-give-10/HeapGraph-0.ref b/dada_tests/permissions/dyn_tutorial/tutorial-give-10/HeapGraph-0.ref index 75b9e6f3..b111409f 100644 --- a/dada_tests/permissions/dyn_tutorial/tutorial-give-10/HeapGraph-0.ref +++ b/dada_tests/permissions/dyn_tutorial/tutorial-give-10/HeapGraph-0.ref @@ -1,4 +1,4 @@ -# Breakpoint: Expr(4) at class:4:5:4:28 +# Breakpoint: Expr(4) at class:4:9:4:32 digraph { node[shape = "note"]; rankdir = "LR"; @@ -13,20 +13,22 @@ digraph { - - + +
main
p
q
x
q
x
>; ]; } afternode0 [ + color = "slategray", + fontcolor = "slategray", label = < - - + +
Point
x: "22"
y: "44"
x: "22"
y: "44"
> ]; - "afterstack":0 -> "afternode0" [label="my", style="solid"]; + "afterstack":0 -> "afternode0" [label="my", style="solid", penwidth=3.0, arrowtype="normal", color="red"]; } subgraph cluster_before { label=<before> @@ -39,8 +41,8 @@ digraph { - - + +
main
p
q
x
q
x
>; ]; @@ -50,9 +52,9 @@ digraph { Error: your lease to this object was cancelled ╭─[class:9:9] │ - 6 │     q = p -  · ┬ -  · ╰── lease was cancelled here + 6 │     any q = p +  · ──┬── +  · ╰──── lease was cancelled here 9 │     x = p.x  · ┬  · ╰── cancelled lease used here diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-give-10/HeapGraph-1.bir.ref b/dada_tests/permissions/dyn_tutorial/tutorial-give-10/HeapGraph-1.bir.ref new file mode 100644 index 00000000..e0ed58e0 --- /dev/null +++ b/dada_tests/permissions/dyn_tutorial/tutorial-give-10/HeapGraph-1.bir.ref @@ -0,0 +1,131 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + AssignExpr( + temp{6}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{7}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{8}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{1}, + Call( + temp{6}, + [ + temp{7}, + temp{8}, + ], + [ + Some( + "x", + ), + Some( + "y", + ), + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{8}, + ), + Expr(2), + ), + ( + Clear( + temp{7}, + ), + Expr(1), + ), + ( + Clear( + temp{6}, + ), + Expr(0), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{5}, + (), + ), + Expr(4), + ), + ( + Clear( + temp{5}, + ), + Expr(4), + ), + ( + BreakpoingStart( + "class", + 0, + ), + Expr(6), + ), + ( + AssignPlace( + q{2}, + p{0}, + ), + Expr(6), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(6), + None, + ), + Expr(6), + ), + ( + AssignPlace( + x{3}, + p{0}.x, + ), + Expr(9), + ), + ( + AssignExpr( + temp{4}, + (), + ), + Expr(9), + ), + ], + Return( + temp{4}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-give-10/HeapGraph-1.ref b/dada_tests/permissions/dyn_tutorial/tutorial-give-10/HeapGraph-1.ref index 4e24d27e..6fa944bd 100644 --- a/dada_tests/permissions/dyn_tutorial/tutorial-give-10/HeapGraph-1.ref +++ b/dada_tests/permissions/dyn_tutorial/tutorial-give-10/HeapGraph-1.ref @@ -1,4 +1,4 @@ -# Breakpoint: Expr(6) at class:6:5:6:10 +# Breakpoint: Expr(6) at class:6:9:6:14 digraph { node[shape = "note"]; rankdir = "LR"; @@ -13,8 +13,8 @@ digraph { - - + +
main
p
q
x
q
x
>; ]; @@ -28,7 +28,7 @@ digraph { y: "44" > ]; - "afterstack":5 -> "afternode0" [label="my", style="solid"]; + "afterstack":2 -> "afternode0" [label="my", style="solid", penwidth=3.0, arrowtype="normal", color="red"]; } subgraph cluster_before { label=<before> @@ -41,8 +41,8 @@ digraph { - - + +
main
p
q
x
q
x
>; ]; @@ -56,15 +56,15 @@ digraph { y: "44" > ]; - "beforestack":0 -> "beforenode0" [label="my", style="solid"]; + "beforestack":0 -> "beforenode0" [label="my", style="solid", penwidth=3.0, arrowtype="normal", color="red"]; } } Error: your lease to this object was cancelled ╭─[class:9:9] │ - 6 │     q = p -  · ┬ -  · ╰── lease was cancelled here + 6 │     any q = p +  · ──┬── +  · ╰──── lease was cancelled here 9 │     x = p.x  · ┬  · ╰── cancelled lease used here diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-lease-10.dada b/dada_tests/permissions/dyn_tutorial/tutorial-lease-10.dada index da7ed458..da8deb2e 100644 --- a/dada_tests/permissions/dyn_tutorial/tutorial-lease-10.dada +++ b/dada_tests/permissions/dyn_tutorial/tutorial-lease-10.dada @@ -1,8 +1,8 @@ -class Point(x, y) +class Point(any x, any y) async fn main() { - p = Point(x: 22, y: 44) - q = p.lease + any p = Point(x: 22, y: 44) + any q = p.lease q.x += 1 #? ^ HeapGraph #? ^ HeapGraph diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-lease-10/HeapGraph-0.bir.ref b/dada_tests/permissions/dyn_tutorial/tutorial-lease-10/HeapGraph-0.bir.ref new file mode 100644 index 00000000..66028078 --- /dev/null +++ b/dada_tests/permissions/dyn_tutorial/tutorial-lease-10/HeapGraph-0.bir.ref @@ -0,0 +1,259 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + AssignExpr( + temp{8}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{9}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{10}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{1}, + Call( + temp{8}, + [ + temp{9}, + temp{10}, + ], + [ + Some( + "x", + ), + Some( + "y", + ), + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{10}, + ), + Expr(2), + ), + ( + Clear( + temp{9}, + ), + Expr(1), + ), + ( + Clear( + temp{8}, + ), + Expr(0), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{7}, + (), + ), + Expr(4), + ), + ( + Clear( + temp{7}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{3}, + p{0}.lease, + ), + Expr(6), + ), + ( + AssignPlace( + q{2}, + temp{3}, + ), + Expr(7), + ), + ( + AssignExpr( + temp{11}, + (), + ), + Expr(7), + ), + ( + Clear( + temp{11}, + ), + Expr(7), + ), + ( + AssignExpr( + temp{4}, + q{2}.lease, + ), + Expr(8), + ), + ( + AssignExpr( + temp{13}, + temp{4}.x.give, + ), + Expr(9), + ), + ( + BreakpoingStart( + "class", + 0, + ), + Expr(10), + ), + ( + AssignExpr( + temp{14}, + 1, + ), + Expr(10), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(10), + Some( + temp{14}, + ), + ), + Expr(10), + ), + ( + AssignExpr( + temp{5}, + temp{13} + temp{14}, + ), + Expr(11), + ), + ( + Clear( + temp{14}, + ), + Expr(10), + ), + ( + Clear( + temp{13}, + ), + Expr(9), + ), + ( + AssignPlace( + temp{4}.x, + temp{5}, + ), + Expr(11), + ), + ( + AssignExpr( + temp{12}, + (), + ), + Expr(11), + ), + ( + Clear( + temp{12}, + ), + Expr(11), + ), + ( + AssignExpr( + temp{16}, + Print.reserve, + ), + Expr(12), + ), + ( + AssignExpr( + temp{17}, + p{0}.x.reserve, + ), + Expr(14), + ), + ], + Assign( + temp{15}, + Call( + temp{16}, + [ + temp{17}, + ], + [ + None, + ], + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + Clear( + temp{17}, + ), + Expr(14), + ), + ( + Clear( + temp{16}, + ), + Expr(12), + ), + ], + Assign( + temp{6}, + Await( + temp{15}, + ), + BasicBlock(3), + ), + ), + BasicBlock(3): BasicBlockData( + [ + ( + Clear( + temp{15}, + ), + Expr(15), + ), + ], + Return( + temp{6}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-lease-10/HeapGraph-0.ref b/dada_tests/permissions/dyn_tutorial/tutorial-lease-10/HeapGraph-0.ref index 793939d0..dff533e4 100644 --- a/dada_tests/permissions/dyn_tutorial/tutorial-lease-10/HeapGraph-0.ref +++ b/dada_tests/permissions/dyn_tutorial/tutorial-lease-10/HeapGraph-0.ref @@ -13,7 +13,7 @@ digraph { - +
main
p
q
q
(in-flight): "1"
>; @@ -28,8 +28,8 @@ digraph { y: "44" > ]; - "afterstack":0 -> "afternode0" [label="my", style="dotted"]; - "afterstack":5 -> "afternode0" [label="leased", style="solid"]; + "afterstack":0 -> "afternode0" [label="my", style="dotted", penwidth=3.0, arrowtype="normal", color="red"]; + "afterstack":2 -> "afternode0" [label="leased", style="dotted", penwidth=1.0, arrowtype="empty", color="red"]; } subgraph cluster_before { label=<before> @@ -42,7 +42,7 @@ digraph { - +
main
p
q
q
>; ]; @@ -56,8 +56,8 @@ digraph { y: "44" > ]; - "beforestack":0 -> "beforenode0" [label="my", style="dotted"]; - "beforestack":5 -> "beforenode0" [label="leased", style="solid"]; + "beforestack":0 -> "beforenode0" [label="my", style="dotted", penwidth=3.0, arrowtype="normal", color="red"]; + "beforestack":2 -> "beforenode0" [label="leased", style="dotted", penwidth=1.0, arrowtype="empty", color="red"]; } } 23 diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-lease-10/HeapGraph-1.bir.ref b/dada_tests/permissions/dyn_tutorial/tutorial-lease-10/HeapGraph-1.bir.ref new file mode 100644 index 00000000..dc0e0a70 --- /dev/null +++ b/dada_tests/permissions/dyn_tutorial/tutorial-lease-10/HeapGraph-1.bir.ref @@ -0,0 +1,257 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + AssignExpr( + temp{8}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{9}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{10}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{1}, + Call( + temp{8}, + [ + temp{9}, + temp{10}, + ], + [ + Some( + "x", + ), + Some( + "y", + ), + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{10}, + ), + Expr(2), + ), + ( + Clear( + temp{9}, + ), + Expr(1), + ), + ( + Clear( + temp{8}, + ), + Expr(0), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{7}, + (), + ), + Expr(4), + ), + ( + Clear( + temp{7}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{3}, + p{0}.lease, + ), + Expr(6), + ), + ( + AssignPlace( + q{2}, + temp{3}, + ), + Expr(7), + ), + ( + AssignExpr( + temp{11}, + (), + ), + Expr(7), + ), + ( + Clear( + temp{11}, + ), + Expr(7), + ), + ( + AssignExpr( + temp{4}, + q{2}.lease, + ), + Expr(8), + ), + ( + AssignExpr( + temp{13}, + temp{4}.x.give, + ), + Expr(9), + ), + ( + AssignExpr( + temp{14}, + 1, + ), + Expr(10), + ), + ( + AssignExpr( + temp{5}, + temp{13} + temp{14}, + ), + Expr(11), + ), + ( + Clear( + temp{14}, + ), + Expr(10), + ), + ( + Clear( + temp{13}, + ), + Expr(9), + ), + ( + BreakpoingStart( + "class", + 0, + ), + Expr(11), + ), + ( + AssignPlace( + temp{4}.x, + temp{5}, + ), + Expr(11), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(11), + None, + ), + Expr(11), + ), + ( + AssignExpr( + temp{12}, + (), + ), + Expr(11), + ), + ( + Clear( + temp{12}, + ), + Expr(11), + ), + ( + AssignExpr( + temp{16}, + Print.reserve, + ), + Expr(12), + ), + ( + AssignExpr( + temp{17}, + p{0}.x.reserve, + ), + Expr(14), + ), + ], + Assign( + temp{15}, + Call( + temp{16}, + [ + temp{17}, + ], + [ + None, + ], + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + Clear( + temp{17}, + ), + Expr(14), + ), + ( + Clear( + temp{16}, + ), + Expr(12), + ), + ], + Assign( + temp{6}, + Await( + temp{15}, + ), + BasicBlock(3), + ), + ), + BasicBlock(3): BasicBlockData( + [ + ( + Clear( + temp{15}, + ), + Expr(15), + ), + ], + Return( + temp{6}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-lease-10/HeapGraph-1.ref b/dada_tests/permissions/dyn_tutorial/tutorial-lease-10/HeapGraph-1.ref index 6310a5ed..b8be6f1b 100644 --- a/dada_tests/permissions/dyn_tutorial/tutorial-lease-10/HeapGraph-1.ref +++ b/dada_tests/permissions/dyn_tutorial/tutorial-lease-10/HeapGraph-1.ref @@ -1,122 +1,4 @@ # Breakpoint: Expr(11) at class:6:5:6:13 -digraph { - node[shape = "note"]; - rankdir = "LR"; - subgraph cluster_after { - label=<after> - subgraph cluster_afterstack { - label=<stack> - rank="source"; - afterstack[ - shape="none"; - label=< - - - - - -
main
p
q
(in-flight): "22"
- >; - ]; - } - afternode0 [ - color = "slategray", - fontcolor = "slategray", - label = < - - - -
Point
x: "22"
y: "44"
> - ]; - "afterstack":0 -> "afternode0" [label="my", style="dotted"]; - "afterstack":5 -> "afternode0" [label="leased", style="solid"]; - } - subgraph cluster_before { - label=<before> - subgraph cluster_beforestack { - label=<stack> - rank="source"; - beforestack[ - shape="none"; - label=< - - - - -
main
p
q
- >; - ]; - } - beforenode0 [ - color = "slategray", - fontcolor = "slategray", - label = < - - - -
Point
x: "22"
y: "44"
> - ]; - "beforestack":0 -> "beforenode0" [label="my", style="dotted"]; - "beforestack":5 -> "beforenode0" [label="leased", style="solid"]; - } -} -digraph { - node[shape = "note"]; - rankdir = "LR"; - subgraph cluster_after { - label=<after> - subgraph cluster_afterstack { - label=<stack> - rank="source"; - afterstack[ - shape="none"; - label=< - - - - - -
main
p
q
(in-flight): "23"
- >; - ]; - } - afternode0 [ - label = < - - - -
Point
x: "23"
y: "44"
> - ]; - "afterstack":0 -> "afternode0" [label="my", style="dotted"]; - "afterstack":5 -> "afternode0" [label="leased", style="solid"]; - } - subgraph cluster_before { - label=<before> - subgraph cluster_beforestack { - label=<stack> - rank="source"; - beforestack[ - shape="none"; - label=< - - - - -
main
p
q
- >; - ]; - } - beforenode0 [ - label = < - - - -
Point
x: "22"
y: "44"
> - ]; - "beforestack":0 -> "beforenode0" [label="my", style="dotted"]; - "beforestack":5 -> "beforenode0" [label="leased", style="solid"]; - } -} digraph { node[shape = "note"]; rankdir = "LR"; @@ -131,7 +13,7 @@ digraph { - +
main
p
q
q
>; ]; @@ -139,12 +21,12 @@ digraph { afternode0 [ label = < - +
Point
x: "23"
x: "23"
y: "44"
> ]; - "afterstack":0 -> "afternode0" [label="my", style="dotted"]; - "afterstack":5 -> "afternode0" [label="leased", style="solid"]; + "afterstack":0 -> "afternode0" [label="my", style="dotted", penwidth=3.0, arrowtype="normal", color="red"]; + "afterstack":2 -> "afternode0" [label="leased", style="dotted", penwidth=1.0, arrowtype="empty", color="red"]; } subgraph cluster_before { label=<before> @@ -157,7 +39,7 @@ digraph { - +
main
p
q
q
>; ]; @@ -165,12 +47,12 @@ digraph { beforenode0 [ label = < - +
Point
x: "22"
x: "22"
y: "44"
> ]; - "beforestack":0 -> "beforenode0" [label="my", style="dotted"]; - "beforestack":5 -> "beforenode0" [label="leased", style="solid"]; + "beforestack":0 -> "beforenode0" [label="my", style="dotted", penwidth=3.0, arrowtype="normal", color="red"]; + "beforestack":2 -> "beforenode0" [label="leased", style="dotted", penwidth=1.0, arrowtype="empty", color="red"]; } } 23 diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-lease-20.dada b/dada_tests/permissions/dyn_tutorial/tutorial-lease-20.dada index 2d8f417b..81857374 100644 --- a/dada_tests/permissions/dyn_tutorial/tutorial-lease-20.dada +++ b/dada_tests/permissions/dyn_tutorial/tutorial-lease-20.dada @@ -1,9 +1,9 @@ -class Point(x, y) +class Point(any x, any y) async fn main() { - p = Point(x: 22, y: 44) - q = p.lease - r = q.lease + any p = Point(x: 22, y: 44) + any q = p.lease + any r = q.lease r.x += 1 #? ^ HeapGraph # diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-lease-20/HeapGraph-0.bir.ref b/dada_tests/permissions/dyn_tutorial/tutorial-lease-20/HeapGraph-0.bir.ref new file mode 100644 index 00000000..97822820 --- /dev/null +++ b/dada_tests/permissions/dyn_tutorial/tutorial-lease-20/HeapGraph-0.bir.ref @@ -0,0 +1,284 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + AssignExpr( + temp{10}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{11}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{12}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{1}, + Call( + temp{10}, + [ + temp{11}, + temp{12}, + ], + [ + Some( + "x", + ), + Some( + "y", + ), + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{12}, + ), + Expr(2), + ), + ( + Clear( + temp{11}, + ), + Expr(1), + ), + ( + Clear( + temp{10}, + ), + Expr(0), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{9}, + (), + ), + Expr(4), + ), + ( + Clear( + temp{9}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{3}, + p{0}.lease, + ), + Expr(6), + ), + ( + AssignPlace( + q{2}, + temp{3}, + ), + Expr(7), + ), + ( + AssignExpr( + temp{13}, + (), + ), + Expr(7), + ), + ( + Clear( + temp{13}, + ), + Expr(7), + ), + ( + AssignExpr( + temp{5}, + q{2}.lease, + ), + Expr(9), + ), + ( + AssignPlace( + r{4}, + temp{5}, + ), + Expr(10), + ), + ( + AssignExpr( + temp{14}, + (), + ), + Expr(10), + ), + ( + Clear( + temp{14}, + ), + Expr(10), + ), + ( + AssignExpr( + temp{6}, + r{4}.lease, + ), + Expr(11), + ), + ( + AssignExpr( + temp{16}, + temp{6}.x.give, + ), + Expr(12), + ), + ( + AssignExpr( + temp{17}, + 1, + ), + Expr(13), + ), + ( + AssignExpr( + temp{7}, + temp{16} + temp{17}, + ), + Expr(14), + ), + ( + Clear( + temp{17}, + ), + Expr(13), + ), + ( + Clear( + temp{16}, + ), + Expr(12), + ), + ( + BreakpoingStart( + "class", + 0, + ), + Expr(14), + ), + ( + AssignPlace( + temp{6}.x, + temp{7}, + ), + Expr(14), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(14), + None, + ), + Expr(14), + ), + ( + AssignExpr( + temp{15}, + (), + ), + Expr(14), + ), + ( + Clear( + temp{15}, + ), + Expr(14), + ), + ( + AssignExpr( + temp{19}, + Print.reserve, + ), + Expr(15), + ), + ( + AssignExpr( + temp{20}, + p{0}.x.reserve, + ), + Expr(17), + ), + ], + Assign( + temp{18}, + Call( + temp{19}, + [ + temp{20}, + ], + [ + None, + ], + ), + BasicBlock(2), + ), + ), + BasicBlock(2): BasicBlockData( + [ + ( + Clear( + temp{20}, + ), + Expr(17), + ), + ( + Clear( + temp{19}, + ), + Expr(15), + ), + ], + Assign( + temp{8}, + Await( + temp{18}, + ), + BasicBlock(3), + ), + ), + BasicBlock(3): BasicBlockData( + [ + ( + Clear( + temp{18}, + ), + Expr(18), + ), + ], + Return( + temp{8}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-lease-20/HeapGraph-0.ref b/dada_tests/permissions/dyn_tutorial/tutorial-lease-20/HeapGraph-0.ref index 11dfe7ed..ac2203da 100644 --- a/dada_tests/permissions/dyn_tutorial/tutorial-lease-20/HeapGraph-0.ref +++ b/dada_tests/permissions/dyn_tutorial/tutorial-lease-20/HeapGraph-0.ref @@ -1,130 +1,4 @@ # Breakpoint: Expr(14) at class:7:5:7:13 -digraph { - node[shape = "note"]; - rankdir = "LR"; - subgraph cluster_after { - label=<after> - subgraph cluster_afterstack { - label=<stack> - rank="source"; - afterstack[ - shape="none"; - label=< - - - - - - -
main
p
q
r
(in-flight): "22"
- >; - ]; - } - afternode0 [ - color = "slategray", - fontcolor = "slategray", - label = < - - - -
Point
x: "22"
y: "44"
> - ]; - "afterstack":0 -> "afternode0" [label="my", style="dotted"]; - "afterstack":5 -> "afternode0" [label="leased", style="dotted"]; - "afterstack":7 -> "afternode0" [label="leased", style="solid"]; - } - subgraph cluster_before { - label=<before> - subgraph cluster_beforestack { - label=<stack> - rank="source"; - beforestack[ - shape="none"; - label=< - - - - - -
main
p
q
r
- >; - ]; - } - beforenode0 [ - color = "slategray", - fontcolor = "slategray", - label = < - - - -
Point
x: "22"
y: "44"
> - ]; - "beforestack":0 -> "beforenode0" [label="my", style="dotted"]; - "beforestack":5 -> "beforenode0" [label="leased", style="dotted"]; - "beforestack":7 -> "beforenode0" [label="leased", style="solid"]; - } -} -digraph { - node[shape = "note"]; - rankdir = "LR"; - subgraph cluster_after { - label=<after> - subgraph cluster_afterstack { - label=<stack> - rank="source"; - afterstack[ - shape="none"; - label=< - - - - - - -
main
p
q
r
(in-flight): "23"
- >; - ]; - } - afternode0 [ - label = < - - - -
Point
x: "23"
y: "44"
> - ]; - "afterstack":0 -> "afternode0" [label="my", style="dotted"]; - "afterstack":5 -> "afternode0" [label="leased", style="dotted"]; - "afterstack":7 -> "afternode0" [label="leased", style="solid"]; - } - subgraph cluster_before { - label=<before> - subgraph cluster_beforestack { - label=<stack> - rank="source"; - beforestack[ - shape="none"; - label=< - - - - - -
main
p
q
r
- >; - ]; - } - beforenode0 [ - label = < - - - -
Point
x: "22"
y: "44"
> - ]; - "beforestack":0 -> "beforenode0" [label="my", style="dotted"]; - "beforestack":5 -> "beforenode0" [label="leased", style="dotted"]; - "beforestack":7 -> "beforenode0" [label="leased", style="solid"]; - } -} digraph { node[shape = "note"]; rankdir = "LR"; @@ -139,8 +13,8 @@ digraph { - - + +
main
p
q
r
q
r
>; ]; @@ -148,13 +22,13 @@ digraph { afternode0 [ label = < - +
Point
x: "23"
x: "23"
y: "44"
> ]; - "afterstack":0 -> "afternode0" [label="my", style="dotted"]; - "afterstack":5 -> "afternode0" [label="leased", style="dotted"]; - "afterstack":7 -> "afternode0" [label="leased", style="solid"]; + "afterstack":0 -> "afternode0" [label="my", style="dotted", penwidth=3.0, arrowtype="normal", color="red"]; + "afterstack":2 -> "afternode0" [label="leased", style="dotted", penwidth=1.0, arrowtype="empty", color="red"]; + "afterstack":4 -> "afternode0" [label="leased", style="dotted", penwidth=1.0, arrowtype="empty", color="red"]; } subgraph cluster_before { label=<before> @@ -167,8 +41,8 @@ digraph { - - + +
main
p
q
r
q
r
>; ]; @@ -176,13 +50,13 @@ digraph { beforenode0 [ label = < - +
Point
x: "22"
x: "22"
y: "44"
> ]; - "beforestack":0 -> "beforenode0" [label="my", style="dotted"]; - "beforestack":5 -> "beforenode0" [label="leased", style="dotted"]; - "beforestack":7 -> "beforenode0" [label="leased", style="solid"]; + "beforestack":0 -> "beforenode0" [label="my", style="dotted", penwidth=3.0, arrowtype="normal", color="red"]; + "beforestack":2 -> "beforenode0" [label="leased", style="dotted", penwidth=1.0, arrowtype="empty", color="red"]; + "beforestack":4 -> "beforenode0" [label="leased", style="dotted", penwidth=1.0, arrowtype="empty", color="red"]; } } 23 diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-lease-30.dada b/dada_tests/permissions/dyn_tutorial/tutorial-lease-30.dada index 623c36f8..dbbf3549 100644 --- a/dada_tests/permissions/dyn_tutorial/tutorial-lease-30.dada +++ b/dada_tests/permissions/dyn_tutorial/tutorial-lease-30.dada @@ -1,8 +1,8 @@ -class Point(x, y) +class Point(any x, any y) async fn main() { - p = Point(x: 22, y: 44) - q = p.lease + any p = Point(x: 22, y: 44) + any q = p.lease q.x += 1 x = p.x #? ^ HeapGraph diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-lease-30/HeapGraph-0.bir.ref b/dada_tests/permissions/dyn_tutorial/tutorial-lease-30/HeapGraph-0.bir.ref new file mode 100644 index 00000000..5ccc1210 --- /dev/null +++ b/dada_tests/permissions/dyn_tutorial/tutorial-lease-30/HeapGraph-0.bir.ref @@ -0,0 +1,218 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + AssignExpr( + temp{10}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{11}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{12}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{1}, + Call( + temp{10}, + [ + temp{11}, + temp{12}, + ], + [ + Some( + "x", + ), + Some( + "y", + ), + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{12}, + ), + Expr(2), + ), + ( + Clear( + temp{11}, + ), + Expr(1), + ), + ( + Clear( + temp{10}, + ), + Expr(0), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{9}, + (), + ), + Expr(4), + ), + ( + Clear( + temp{9}, + ), + Expr(4), + ), + ( + AssignExpr( + temp{3}, + p{0}.lease, + ), + Expr(6), + ), + ( + AssignPlace( + q{2}, + temp{3}, + ), + Expr(7), + ), + ( + AssignExpr( + temp{13}, + (), + ), + Expr(7), + ), + ( + Clear( + temp{13}, + ), + Expr(7), + ), + ( + AssignExpr( + temp{4}, + q{2}.lease, + ), + Expr(8), + ), + ( + AssignExpr( + temp{15}, + temp{4}.x.give, + ), + Expr(9), + ), + ( + AssignExpr( + temp{16}, + 1, + ), + Expr(10), + ), + ( + AssignExpr( + temp{5}, + temp{15} + temp{16}, + ), + Expr(11), + ), + ( + Clear( + temp{16}, + ), + Expr(10), + ), + ( + Clear( + temp{15}, + ), + Expr(9), + ), + ( + AssignPlace( + temp{4}.x, + temp{5}, + ), + Expr(11), + ), + ( + AssignExpr( + temp{14}, + (), + ), + Expr(11), + ), + ( + Clear( + temp{14}, + ), + Expr(11), + ), + ( + BreakpoingStart( + "class", + 0, + ), + Expr(14), + ), + ( + AssignPlace( + x{6}, + p{0}.x, + ), + Expr(14), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(14), + None, + ), + Expr(14), + ), + ( + AssignPlace( + x{7}, + q{2}.x, + ), + Expr(17), + ), + ( + AssignExpr( + temp{8}, + (), + ), + Expr(17), + ), + ], + Return( + temp{8}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-lease-30/HeapGraph-0.ref b/dada_tests/permissions/dyn_tutorial/tutorial-lease-30/HeapGraph-0.ref index 9d8def11..c43088dd 100644 --- a/dada_tests/permissions/dyn_tutorial/tutorial-lease-30/HeapGraph-0.ref +++ b/dada_tests/permissions/dyn_tutorial/tutorial-lease-30/HeapGraph-0.ref @@ -13,9 +13,9 @@ digraph { - - - + + +
main
p
q
x: "23"
x
q
x: "23"
x
>; ]; @@ -29,7 +29,7 @@ digraph { y: "44" > ]; - "afterstack":0 -> "afternode0" [label="my", style="solid"]; + "afterstack":0 -> "afternode0" [label="my", style="solid", penwidth=3.0, arrowtype="normal", color="red"]; } subgraph cluster_before { label=<before> @@ -42,9 +42,9 @@ digraph { - + + -
main
p
q
q
x
x
x
>; ]; @@ -58,16 +58,16 @@ digraph { y: "44" > ]; - "beforestack":0 -> "beforenode0" [label="my", style="dotted"]; - "beforestack":5 -> "beforenode0" [label="leased", style="solid"]; + "beforestack":0 -> "beforenode0" [label="my", style="dotted", penwidth=3.0, arrowtype="normal", color="red"]; + "beforestack":2 -> "beforenode0" [label="leased", style="dotted", penwidth=1.0, arrowtype="empty", color="red"]; } } Error: your lease to this object was cancelled ╭─[class:9:9] │ - 7 │     x = p.x -  · ─┬─ -  · ╰─── lease was cancelled here + 7 │     x = p.x +  · ───┬─── +  · ╰───── lease was cancelled here 9 │     x = q.x  · ┬  · ╰── cancelled lease used here diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-share-10.dada b/dada_tests/permissions/dyn_tutorial/tutorial-share-10.dada index 91dd783e..9b6fe3dc 100644 --- a/dada_tests/permissions/dyn_tutorial/tutorial-share-10.dada +++ b/dada_tests/permissions/dyn_tutorial/tutorial-share-10.dada @@ -1,4 +1,4 @@ -class Point(x, y) +class Point(any x, any y) async fn main() { p = Point(x: 22, y: 44).share diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-share-10/HeapGraph-0.bir.ref b/dada_tests/permissions/dyn_tutorial/tutorial-share-10/HeapGraph-0.bir.ref new file mode 100644 index 00000000..01d654ff --- /dev/null +++ b/dada_tests/permissions/dyn_tutorial/tutorial-share-10/HeapGraph-0.bir.ref @@ -0,0 +1,158 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + AssignExpr( + temp{9}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{10}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{11}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{8}, + Call( + temp{9}, + [ + temp{10}, + temp{11}, + ], + [ + Some( + "x", + ), + Some( + "y", + ), + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{11}, + ), + Expr(2), + ), + ( + Clear( + temp{10}, + ), + Expr(1), + ), + ( + Clear( + temp{9}, + ), + Expr(0), + ), + ( + AssignExpr( + temp{1}, + temp{8}.share, + ), + Expr(4), + ), + ( + Clear( + temp{8}, + ), + Expr(3), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(5), + ), + ( + AssignExpr( + temp{7}, + (), + ), + Expr(5), + ), + ( + Clear( + temp{7}, + ), + Expr(5), + ), + ( + BreakpoingStart( + "class", + 0, + ), + Expr(7), + ), + ( + AssignPlace( + q{2}, + p{0}, + ), + Expr(7), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(7), + None, + ), + Expr(7), + ), + ( + AssignPlace( + x{3}, + p{0}.x, + ), + Expr(10), + ), + ( + AssignPlace( + x{4}, + q{2}.x, + ), + Expr(13), + ), + ( + AssignPlace( + x{5}, + p{0}.x, + ), + Expr(16), + ), + ( + AssignExpr( + temp{6}, + (), + ), + Expr(16), + ), + ], + Return( + temp{6}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-share-10/HeapGraph-0.ref b/dada_tests/permissions/dyn_tutorial/tutorial-share-10/HeapGraph-0.ref index bac4f7a0..55a194cd 100644 --- a/dada_tests/permissions/dyn_tutorial/tutorial-share-10/HeapGraph-0.ref +++ b/dada_tests/permissions/dyn_tutorial/tutorial-share-10/HeapGraph-0.ref @@ -13,10 +13,10 @@ digraph { - - - - + + + +
main
p
q
x
x
x
q
x
x
x
>; ]; @@ -30,8 +30,8 @@ digraph { y: "44" > ]; - "afterstack":0 -> "afternode0" [label="our", style="solid"]; - "afterstack":6 -> "afternode0" [label="our", style="solid"]; + "afterstack":0 -> "afternode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; + "afterstack":2 -> "afternode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; } subgraph cluster_before { label=<before> @@ -44,10 +44,10 @@ digraph { - - - - + + + +
main
p
q
x
x
x
q
x
x
x
>; ]; @@ -61,6 +61,6 @@ digraph { y: "44" > ]; - "beforestack":0 -> "beforenode0" [label="our", style="solid"]; + "beforestack":0 -> "beforenode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; } } diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-share-20.dada b/dada_tests/permissions/dyn_tutorial/tutorial-share-20.dada index 5f252ee4..60cf8739 100644 --- a/dada_tests/permissions/dyn_tutorial/tutorial-share-20.dada +++ b/dada_tests/permissions/dyn_tutorial/tutorial-share-20.dada @@ -1,4 +1,4 @@ -class Point(x, y) +class Point(any x, any y) async fn main() { p = Point(x: 22, y: 44).share diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-share-20/HeapGraph-0.bir.ref b/dada_tests/permissions/dyn_tutorial/tutorial-share-20/HeapGraph-0.bir.ref new file mode 100644 index 00000000..907d0742 --- /dev/null +++ b/dada_tests/permissions/dyn_tutorial/tutorial-share-20/HeapGraph-0.bir.ref @@ -0,0 +1,239 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + BreakpoingStart( + "class", + 0, + ), + Expr(15), + ), + ( + AssignExpr( + temp{11}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{12}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{13}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{10}, + Call( + temp{11}, + [ + temp{12}, + temp{13}, + ], + [ + Some( + "x", + ), + Some( + "y", + ), + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{13}, + ), + Expr(2), + ), + ( + Clear( + temp{12}, + ), + Expr(1), + ), + ( + Clear( + temp{11}, + ), + Expr(0), + ), + ( + AssignExpr( + temp{1}, + temp{10}.share, + ), + Expr(4), + ), + ( + Clear( + temp{10}, + ), + Expr(3), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(5), + ), + ( + AssignExpr( + temp{9}, + (), + ), + Expr(5), + ), + ( + Clear( + temp{9}, + ), + Expr(5), + ), + ( + AssignExpr( + temp{15}, + p{0}.give, + ), + Expr(6), + ), + ( + AssignExpr( + temp{3}, + temp{15}.share, + ), + Expr(7), + ), + ( + Clear( + temp{15}, + ), + Expr(6), + ), + ( + AssignPlace( + q{2}, + temp{3}, + ), + Expr(8), + ), + ( + AssignExpr( + temp{14}, + (), + ), + Expr(8), + ), + ( + Clear( + temp{14}, + ), + Expr(8), + ), + ( + AssignExpr( + temp{17}, + q{2}.give, + ), + Expr(9), + ), + ( + AssignExpr( + temp{5}, + temp{17}.share, + ), + Expr(10), + ), + ( + Clear( + temp{17}, + ), + Expr(9), + ), + ( + AssignPlace( + r{4}, + temp{5}, + ), + Expr(11), + ), + ( + AssignExpr( + temp{16}, + (), + ), + Expr(11), + ), + ( + Clear( + temp{16}, + ), + Expr(11), + ), + ( + AssignExpr( + temp{18}, + r{4}.give, + ), + Expr(12), + ), + ( + AssignExpr( + temp{7}, + temp{18}.share, + ), + Expr(13), + ), + ( + Clear( + temp{18}, + ), + Expr(12), + ), + ( + AssignPlace( + s{6}, + temp{7}, + ), + Expr(14), + ), + ( + AssignExpr( + temp{8}, + (), + ), + Expr(14), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(15), + Some( + temp{8}, + ), + ), + Expr(15), + ), + ], + Return( + temp{8}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-share-20/HeapGraph-0.ref b/dada_tests/permissions/dyn_tutorial/tutorial-share-20/HeapGraph-0.ref index d9857363..32bf420e 100644 --- a/dada_tests/permissions/dyn_tutorial/tutorial-share-20/HeapGraph-0.ref +++ b/dada_tests/permissions/dyn_tutorial/tutorial-share-20/HeapGraph-0.ref @@ -13,10 +13,10 @@ digraph { - - - - + + + +
main
p
q
r
s
(in-flight): "()"
q
r
s
(in-flight): "()"
>; ]; @@ -28,10 +28,10 @@ digraph { y: "44" > ]; - "afterstack":0 -> "afternode0" [label="our", style="solid"]; - "afterstack":6 -> "afternode0" [label="our", style="solid"]; - "afterstack":8 -> "afternode0" [label="our", style="solid"]; - "afterstack":10 -> "afternode0" [label="our", style="solid"]; + "afterstack":0 -> "afternode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; + "afterstack":2 -> "afternode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; + "afterstack":4 -> "afternode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; + "afterstack":6 -> "afternode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; } subgraph cluster_before { label=<before> @@ -44,9 +44,9 @@ digraph { - - - + + +
main
p
q
r
s
q
r
s
>; ]; diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-share-30.dada b/dada_tests/permissions/dyn_tutorial/tutorial-share-30.dada index a7439dab..4490d74e 100644 --- a/dada_tests/permissions/dyn_tutorial/tutorial-share-30.dada +++ b/dada_tests/permissions/dyn_tutorial/tutorial-share-30.dada @@ -1,4 +1,4 @@ -class Point(x, y) +class Point(any x, any y) async fn main() { p = Point(x: 22, y: 44).share diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-share-30/HeapGraph-0.bir.ref b/dada_tests/permissions/dyn_tutorial/tutorial-share-30/HeapGraph-0.bir.ref new file mode 100644 index 00000000..a56e921a --- /dev/null +++ b/dada_tests/permissions/dyn_tutorial/tutorial-share-30/HeapGraph-0.bir.ref @@ -0,0 +1,166 @@ +[ + bir::Bir { + start_basic_block: BasicBlock(0), + BasicBlock(0): BasicBlockData( + [ + ( + BreakpoingStart( + "class", + 0, + ), + Expr(11), + ), + ( + AssignExpr( + temp{8}, + Class(Id { value: 1 }).reserve, + ), + Expr(0), + ), + ( + AssignExpr( + temp{9}, + 22, + ), + Expr(1), + ), + ( + AssignExpr( + temp{10}, + 44, + ), + Expr(2), + ), + ], + Assign( + temp{7}, + Call( + temp{8}, + [ + temp{9}, + temp{10}, + ], + [ + Some( + "x", + ), + Some( + "y", + ), + ], + ), + BasicBlock(1), + ), + ), + BasicBlock(1): BasicBlockData( + [ + ( + Clear( + temp{10}, + ), + Expr(2), + ), + ( + Clear( + temp{9}, + ), + Expr(1), + ), + ( + Clear( + temp{8}, + ), + Expr(0), + ), + ( + AssignExpr( + temp{1}, + temp{7}.share, + ), + Expr(4), + ), + ( + Clear( + temp{7}, + ), + Expr(3), + ), + ( + AssignPlace( + p{0}, + temp{1}, + ), + Expr(5), + ), + ( + AssignExpr( + temp{6}, + (), + ), + Expr(5), + ), + ( + Clear( + temp{6}, + ), + Expr(5), + ), + ( + AssignExpr( + temp{3}, + p{0}.give, + ), + Expr(7), + ), + ( + AssignPlace( + q{2}, + temp{3}, + ), + Expr(8), + ), + ( + AssignExpr( + temp{11}, + (), + ), + Expr(8), + ), + ( + Clear( + temp{11}, + ), + Expr(8), + ), + ( + AssignPlace( + r{4}, + q{2}, + ), + Expr(10), + ), + ( + AssignExpr( + temp{5}, + (), + ), + Expr(10), + ), + ( + BreakpointEnd( + "class", + 0, + Expr(11), + Some( + temp{5}, + ), + ), + Expr(11), + ), + ], + Return( + temp{5}, + ), + ), + }, +] \ No newline at end of file diff --git a/dada_tests/permissions/dyn_tutorial/tutorial-share-30/HeapGraph-0.ref b/dada_tests/permissions/dyn_tutorial/tutorial-share-30/HeapGraph-0.ref index c26ce821..4a90431f 100644 --- a/dada_tests/permissions/dyn_tutorial/tutorial-share-30/HeapGraph-0.ref +++ b/dada_tests/permissions/dyn_tutorial/tutorial-share-30/HeapGraph-0.ref @@ -13,9 +13,9 @@ digraph { - - - + + +
main
p
q
r
(in-flight): "()"
q
r
(in-flight): "()"
>; ]; @@ -27,9 +27,9 @@ digraph { y: "44" > ]; - "afterstack":0 -> "afternode0" [label="our", style="solid"]; - "afterstack":6 -> "afternode0" [label="our", style="solid"]; - "afterstack":8 -> "afternode0" [label="our", style="solid"]; + "afterstack":0 -> "afternode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; + "afterstack":2 -> "afternode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; + "afterstack":4 -> "afternode0" [label="our", style="solid", penwidth=3.0, arrowtype="normal", color="blue"]; } subgraph cluster_before { label=<before> @@ -42,8 +42,8 @@ digraph { - - + +
main
p
q
r
q
r
>; ]; diff --git a/dada_tests/permissions/exhaustive/give-var-field-my.dada b/dada_tests/permissions/exhaustive/give-var-field-my.dada index 3473c630..7960f8fc 100644 --- a/dada_tests/permissions/exhaustive/give-var-field-my.dada +++ b/dada_tests/permissions/exhaustive/give-var-field-my.dada @@ -1,11 +1,11 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { # Here we have an owned point, but in a shared field - p = Pair(Pair(22, 44), 66) + any p = Pair(Pair(22, 44), 66) # Giving that makes a my result - q = p.a.give + any q = p.a.give print(p).await #! OUTPUT my Pair\(\(expired\), 66\) print(q).await #! OUTPUT my Pair\(22, 44\) diff --git a/dada_tests/permissions/exhaustive/give-var-leased-shared.dada b/dada_tests/permissions/exhaustive/give-var-leased-shared.dada index 5dfecb3a..8f8705dd 100644 --- a/dada_tests/permissions/exhaustive/give-var-leased-shared.dada +++ b/dada_tests/permissions/exhaustive/give-var-leased-shared.dada @@ -1,4 +1,4 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { # FIXME: Debatable when the underlying pair should be freed. diff --git a/dada_tests/permissions/exhaustive/give-var-leased.dada b/dada_tests/permissions/exhaustive/give-var-leased.dada index 2bd928cf..4bbd8fd6 100644 --- a/dada_tests/permissions/exhaustive/give-var-leased.dada +++ b/dada_tests/permissions/exhaustive/give-var-leased.dada @@ -1,10 +1,15 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { - # FIXME: Debatable when the underlying pair should be freed. - p = Pair(22, 44).lease - q = p.give - print(p).await #! OUTPUT leased Pair\(22, 44\) + any p = Pair(22, 44).lease + any q = p.give # Giving a leased thing: subleases + + # Accessing `q`: ok print(q).await #! OUTPUT leased Pair\(22, 44\) + + # Accessing `p`: ok, but cancels sublease (to `q`) print(p).await #! OUTPUT leased Pair\(22, 44\) + + # Accessing `q` again: error + print(q).await #! RUN ERROR your lease to this object was cancelled } \ No newline at end of file diff --git a/dada_tests/permissions/exhaustive/give-var-leased/stdout.ref b/dada_tests/permissions/exhaustive/give-var-leased/stdout.ref index f8a5a90e..52946d17 100644 --- a/dada_tests/permissions/exhaustive/give-var-leased/stdout.ref +++ b/dada_tests/permissions/exhaustive/give-var-leased/stdout.ref @@ -1,3 +1,2 @@ leased Pair(22, 44) leased Pair(22, 44) -leased Pair(22, 44) diff --git a/dada_tests/permissions/exhaustive/give-var-my.dada b/dada_tests/permissions/exhaustive/give-var-my.dada index cba27909..5b8d8bc9 100644 --- a/dada_tests/permissions/exhaustive/give-var-my.dada +++ b/dada_tests/permissions/exhaustive/give-var-my.dada @@ -1,8 +1,8 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { - p = Pair(22, 44) - q = p.give + any p = Pair(22, 44) + any q = p.give print(p).await #! RUN ERROR your lease to this object was cancelled } \ No newline at end of file diff --git a/dada_tests/permissions/exhaustive/give-var-our.dada b/dada_tests/permissions/exhaustive/give-var-our.dada index 69d6c3a5..5dfb91ae 100644 --- a/dada_tests/permissions/exhaustive/give-var-our.dada +++ b/dada_tests/permissions/exhaustive/give-var-our.dada @@ -1,4 +1,4 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { p = Pair(22, 44).share diff --git a/dada_tests/permissions/exhaustive/lease-var-field-my.dada b/dada_tests/permissions/exhaustive/lease-var-field-my.dada index 58a318de..0b0db51f 100644 --- a/dada_tests/permissions/exhaustive/lease-var-field-my.dada +++ b/dada_tests/permissions/exhaustive/lease-var-field-my.dada @@ -1,8 +1,8 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { - p = Pair(Pair(22, 44), 66) - q = p.a.lease + any p = Pair(Pair(22, 44), 66) + any q = p.a.lease print(q).await #! OUTPUT leased Pair\(22, 44\) print(p).await #! OUTPUT my Pair\(my Pair\(22, 44\), 66\) print(q).await #! RUN ERROR your lease to this object was cancelled diff --git a/dada_tests/permissions/exhaustive/lease-var-lease-shared.dada b/dada_tests/permissions/exhaustive/lease-var-lease-shared.dada index 26ad0f8e..1b26dfa1 100644 --- a/dada_tests/permissions/exhaustive/lease-var-lease-shared.dada +++ b/dada_tests/permissions/exhaustive/lease-var-lease-shared.dada @@ -1,4 +1,4 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { p = Pair(22, 44).lease.share diff --git a/dada_tests/permissions/exhaustive/lease-var-my.dada b/dada_tests/permissions/exhaustive/lease-var-my.dada index 6d1f6969..80e82cea 100644 --- a/dada_tests/permissions/exhaustive/lease-var-my.dada +++ b/dada_tests/permissions/exhaustive/lease-var-my.dada @@ -1,8 +1,8 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { - p = Pair(22, 44) - q = p.lease + any p = Pair(22, 44) + any q = p.lease print(q).await #! OUTPUT leased Pair\(22, 44\) print(p).await #! OUTPUT my Pair\(22, 44\) print(q).await #! RUN ERROR your lease to this object was cancelled diff --git a/dada_tests/permissions/exhaustive/lease-var-our.dada b/dada_tests/permissions/exhaustive/lease-var-our.dada index bdcf003d..e0b9bbd2 100644 --- a/dada_tests/permissions/exhaustive/lease-var-our.dada +++ b/dada_tests/permissions/exhaustive/lease-var-our.dada @@ -1,9 +1,9 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { p = Pair(22, 44).share q = p.lease - print(q).await #! OUTPUT our leased Pair\(22, 44\) + print(q).await #! OUTPUT our Pair\(22, 44\) print(p).await #! OUTPUT our Pair\(22, 44\) - print(q).await #! OUTPUT our leased Pair\(22, 44\) + print(q).await #! OUTPUT our Pair\(22, 44\) } \ No newline at end of file diff --git a/dada_tests/permissions/exhaustive/lease-var-our/stdout.ref b/dada_tests/permissions/exhaustive/lease-var-our/stdout.ref index 864a7cd9..6b44d38d 100644 --- a/dada_tests/permissions/exhaustive/lease-var-our/stdout.ref +++ b/dada_tests/permissions/exhaustive/lease-var-our/stdout.ref @@ -1,3 +1,3 @@ -our leased Pair(22, 44) our Pair(22, 44) -our leased Pair(22, 44) +our Pair(22, 44) +our Pair(22, 44) diff --git a/dada_tests/permissions/exhaustive/share-var-field-my.dada b/dada_tests/permissions/exhaustive/share-var-field-my.dada index 06f2550b..145f72d8 100644 --- a/dada_tests/permissions/exhaustive/share-var-field-my.dada +++ b/dada_tests/permissions/exhaustive/share-var-field-my.dada @@ -1,8 +1,8 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { - p = Pair(Pair(22, 44), 66) - q = p.a.share - print(p).await #! OUTPUT my Pair\(our Pair\(22, 44\), 66\) + any p = Pair(Pair(22, 44), 66) + any q = p.a.share + print(p).await #! OUTPUT my Pair\(\(expired\), 66\) print(q).await #! OUTPUT our Pair\(22, 44\) } \ No newline at end of file diff --git a/dada_tests/permissions/exhaustive/share-var-field-my/stdout.ref b/dada_tests/permissions/exhaustive/share-var-field-my/stdout.ref index ed860c3d..232ce596 100644 --- a/dada_tests/permissions/exhaustive/share-var-field-my/stdout.ref +++ b/dada_tests/permissions/exhaustive/share-var-field-my/stdout.ref @@ -1,2 +1,2 @@ -my Pair(our Pair(22, 44), 66) +my Pair((expired), 66) our Pair(22, 44) diff --git a/dada_tests/permissions/exhaustive/share-var-leased.dada b/dada_tests/permissions/exhaustive/share-var-leased.dada index f97a9c3d..0906897e 100644 --- a/dada_tests/permissions/exhaustive/share-var-leased.dada +++ b/dada_tests/permissions/exhaustive/share-var-leased.dada @@ -1,8 +1,17 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { - p = Pair(22, 44).lease - q = p.share - print(p).await #! OUTPUT leased Pair\(22, 44\) + any p = Pair(22, 44).lease + + # Sharing a leased thing: creates a shared sublease + any q = p.share + + # Accessing `q`: ok print(q).await #! OUTPUT our leased Pair\(22, 44\) + + # Accessing `p`: ok, but cancels subleases + print(p).await #! OUTPUT leased Pair\(22, 44\) + + # Accessing `q` again: error + print(q).await #! RUN ERROR your lease to this object was cancelled } \ No newline at end of file diff --git a/dada_tests/permissions/exhaustive/share-var-leased/stdout.ref b/dada_tests/permissions/exhaustive/share-var-leased/stdout.ref index a5798c41..71906f6a 100644 --- a/dada_tests/permissions/exhaustive/share-var-leased/stdout.ref +++ b/dada_tests/permissions/exhaustive/share-var-leased/stdout.ref @@ -1,2 +1,2 @@ -leased Pair(22, 44) our leased Pair(22, 44) +leased Pair(22, 44) diff --git a/dada_tests/permissions/exhaustive/share-var-my.dada b/dada_tests/permissions/exhaustive/share-var-my.dada index a6ffadf7..492c7072 100644 --- a/dada_tests/permissions/exhaustive/share-var-my.dada +++ b/dada_tests/permissions/exhaustive/share-var-my.dada @@ -1,8 +1,8 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { - p = Pair(22, 44) - q = p.share - print(p).await #! OUTPUT our Pair\(22, 44\) + any p = Pair(22, 44) + any q = p.share print(q).await #! OUTPUT our Pair\(22, 44\) + print(p).await #! RUN ERROR your lease to this object was cancelled } \ No newline at end of file diff --git a/dada_tests/permissions/exhaustive/share-var-my/stdout.ref b/dada_tests/permissions/exhaustive/share-var-my/stdout.ref index f1cc663b..2aa36059 100644 --- a/dada_tests/permissions/exhaustive/share-var-my/stdout.ref +++ b/dada_tests/permissions/exhaustive/share-var-my/stdout.ref @@ -1,2 +1 @@ our Pair(22, 44) -our Pair(22, 44) diff --git a/dada_tests/permissions/exhaustive/share-var-our.dada b/dada_tests/permissions/exhaustive/share-var-our.dada index 9e51eaf2..143044db 100644 --- a/dada_tests/permissions/exhaustive/share-var-our.dada +++ b/dada_tests/permissions/exhaustive/share-var-our.dada @@ -1,4 +1,4 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { p = Pair(22, 44).share diff --git a/dada_tests/permissions/house-parties/house-parties-are-not-enough.dada b/dada_tests/permissions/house-parties/house-parties-are-not-enough.dada new file mode 100644 index 00000000..a060ddc7 --- /dev/null +++ b/dada_tests/permissions/house-parties/house-parties-are-not-enough.dada @@ -0,0 +1,30 @@ +# Test which shows that the "house party" rule, +# which permits the lessor to read a leased thing +# if that thing is subleased to a shared lease, +# isn't enough to truly capture the flexibility of +# Rust's shared borrows. + +class Accumulator(my list) +class List() + +fn get_list(accumulator) # -> +{ + accumulator.list.share +} + +async fn main() { + # ```rust + # let a = Accumulator::new(vec![]); + # let l1 = get_list(&a); + # let l2 = get_list(&a); + # // works fine! + # ``` + + my a = Accumulator(list: List()) + l1 = get_list(a) + l2 = get_list(a) + print(l2).await #! OUTPUT List\(\) + print(l1).await #! RUN ERROR your lease to this object was cancelled + print(l2).await + #! FIXME: House parties are not enough to express Rust patterns +} diff --git a/dada_tests/permissions/house-parties/house-parties-are-not-enough/compiler-output.ref b/dada_tests/permissions/house-parties/house-parties-are-not-enough/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/permissions/house-parties/house-parties-are-not-enough/stdout.ref b/dada_tests/permissions/house-parties/house-parties-are-not-enough/stdout.ref new file mode 100644 index 00000000..81444e49 --- /dev/null +++ b/dada_tests/permissions/house-parties/house-parties-are-not-enough/stdout.ref @@ -0,0 +1 @@ +our leased List() diff --git a/dada_tests/permissions/house-parties/house-parties-are-not-fair-to-the-tenant.dada b/dada_tests/permissions/house-parties/house-parties-are-not-fair-to-the-tenant.dada new file mode 100644 index 00000000..cb99a893 --- /dev/null +++ b/dada_tests/permissions/house-parties/house-parties-are-not-fair-to-the-tenant.dada @@ -0,0 +1,41 @@ +# Test that is intended to show the dangerous +# of the "house party" rule. If we allow the +# lessor to read without violating the tenant's +# lease, that could permit the lessor to mutate +# atomic fields, but the tenant was assuming it +# had unique access to those fields. +# +# It turns out that, lacking a combined +# "shared-lease" operation, and not having implemented +# atomic fields etc, it's hard to demonstrate this danger. =) +# But I'm pretty sure it's there. + +class Accumulator(my atomic list) +class List() + +fn foo(leased accumulator) # -> +{ + accumulator.list.lease.share +} + +async fn main() { + my a = Accumulator(list: List()) + + # get a shared lease to the list, + # but it is still owned by `a` + l = foo(a) + + # share `a`, which currently revokes + # the lease `a`, and hence `l` + # becomes inaccessible + our s = a + + print(l).await #! RUN ERROR your lease to this object was cancelled + + atomic { + # can still modify `s.list`, but only + # in an atomic section + s.list := List() + #! FIXME: atomic writes not implemented + } +} diff --git a/dada_tests/permissions/house-parties/house-parties-are-not-fair-to-the-tenant/compiler-output.ref b/dada_tests/permissions/house-parties/house-parties-are-not-fair-to-the-tenant/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/permissions/house-parties/house-parties-are-not-fair-to-the-tenant/stdout.ref b/dada_tests/permissions/house-parties/house-parties-are-not-fair-to-the-tenant/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/permissions/patterns/pattern-lease-my.dada b/dada_tests/permissions/patterns/pattern-lease-my.dada index dfe46559..af263f38 100644 --- a/dada_tests/permissions/patterns/pattern-lease-my.dada +++ b/dada_tests/permissions/patterns/pattern-lease-my.dada @@ -1,4 +1,4 @@ -class Point(x, y) +class Point(any x, any y) # Test what happens when we lease a "my" thing `p` # and then destroy `p`. The lease should be canceled. diff --git a/dada_tests/permissions/patterns/pattern-lease-our.dada b/dada_tests/permissions/patterns/pattern-lease-our.dada index e5bf9f7f..60f7f1ad 100644 --- a/dada_tests/permissions/patterns/pattern-lease-our.dada +++ b/dada_tests/permissions/patterns/pattern-lease-our.dada @@ -1,13 +1,11 @@ -class Point(x, y) +class Point(any x, any y) # Test what happens when we lease an "our" thing `p` -# and then drop `p`. Just as with any owned thing, -# the memory for `p` is freed and hence the lease -# is invalid. +# and then drop `p`. Since leasing an `our` thing +# just clones it, the result is valid. async fn main() { r = callee() - #! ^^^^^^^^ RUN ERROR your lease to this object was cancelled data = r.x } diff --git a/dada_tests/permissions/revokation/house-party.dada b/dada_tests/permissions/revokation/house-party.dada index 979b79bf..760b72e9 100644 --- a/dada_tests/permissions/revokation/house-party.dada +++ b/dada_tests/permissions/revokation/house-party.dada @@ -11,9 +11,9 @@ class Character(name) # not need to cancel guest. async fn main() { - owner = Character("Achilles") - lessee = owner.lease - guest = lessee.share + any owner = Character("Achilles") + any lessee = owner.lease + any guest = lessee.share print(owner.name).await #! OUTPUT Achilles print(lessee.name).await #! OUTPUT Achilles print(guest.name).await #! OUTPUT Achilles diff --git a/dada_tests/permissions/revokation/overwrite-lease-share.dada b/dada_tests/permissions/revokation/overwrite-lease-share.dada index cb870c58..8cfa050d 100644 --- a/dada_tests/permissions/revokation/overwrite-lease-share.dada +++ b/dada_tests/permissions/revokation/overwrite-lease-share.dada @@ -1,10 +1,10 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { - pair1 = Pair(22, 44) - pair2 = Pair(pair1.lease.share, 66) + any pair1 = Pair(22, 44) + any pair2 = Pair(pair1.lease.share, 66) - p = pair2.a.lease + any p = pair2.a.lease pair2.a := Pair(23, 45) diff --git a/dada_tests/permissions/revokation/overwrite-leased.dada b/dada_tests/permissions/revokation/overwrite-leased.dada index eba26173..b79137ac 100644 --- a/dada_tests/permissions/revokation/overwrite-leased.dada +++ b/dada_tests/permissions/revokation/overwrite-leased.dada @@ -1,10 +1,10 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { - pair1 = Pair(22, 44) - pair2 = Pair(pair1.lease, 66) + any pair1 = Pair(22, 44) + any pair2 = Pair(pair1.lease, 66) - p = pair2.a.lease + any p = pair2.a.lease # Writing to `pair.a` causes the lease from `pair2.a` # to be unreachable. However, the owned value that backs diff --git a/dada_tests/permissions/revokation/overwrite-our-leased.dada b/dada_tests/permissions/revokation/overwrite-our-leased.dada index 48eb5ad3..c491dc01 100644 --- a/dada_tests/permissions/revokation/overwrite-our-leased.dada +++ b/dada_tests/permissions/revokation/overwrite-our-leased.dada @@ -1,14 +1,14 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { - pair1 = Pair(22, 44).share - pair2 = Pair(pair1.lease, 66) + any pair1 = Pair(22, 44).share + any pair2 = Pair(pair1.lease, 66) - p = pair2.a.lease + any p = pair2.a.lease # Writing to `pair2.a` overwrites the shared # lease, but that doesn't cancel it. pair2.a := Pair(23, 45) - print(p).await #! OUTPUT our leased Pair\(22, 44\) + print(p).await #! OUTPUT our Pair\(22, 44\) } \ No newline at end of file diff --git a/dada_tests/permissions/revokation/overwrite-our-leased/stdout.ref b/dada_tests/permissions/revokation/overwrite-our-leased/stdout.ref index 50c48401..2aa36059 100644 --- a/dada_tests/permissions/revokation/overwrite-our-leased/stdout.ref +++ b/dada_tests/permissions/revokation/overwrite-our-leased/stdout.ref @@ -1 +1 @@ -our leased Pair(22, 44) +our Pair(22, 44) diff --git a/dada_tests/permissions/revokation/overwrite-our-shared.dada b/dada_tests/permissions/revokation/overwrite-our-shared.dada index d131c381..bc059b70 100644 --- a/dada_tests/permissions/revokation/overwrite-our-shared.dada +++ b/dada_tests/permissions/revokation/overwrite-our-shared.dada @@ -1,10 +1,10 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { - pair = Pair(Pair(22, 44).share, 66) + any pair = Pair(Pair(22, 44).share, 66) # `p` becomes an independent handle on the same shared pair - p = pair.a.share + any p = pair.a.share # `p` is not disturbed by this write pair.a := Pair(23, 45) diff --git a/dada_tests/permissions/revokation/overwrite-owned.dada b/dada_tests/permissions/revokation/overwrite-owned.dada index f9f7ef07..23fbd4e2 100644 --- a/dada_tests/permissions/revokation/overwrite-owned.dada +++ b/dada_tests/permissions/revokation/overwrite-owned.dada @@ -1,9 +1,9 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { - pair = Pair(Pair(22, 44), 66) + any pair = Pair(Pair(22, 44), 66) - p = pair.a.lease + any p = pair.a.lease # This write causes the `Pair(22, 44)` # to have no owner. It gets collected by the GC... diff --git a/dada_tests/permissions/revokation/overwrite-shared-separate-root.dada b/dada_tests/permissions/revokation/overwrite-shared-separate-root.dada new file mode 100644 index 00000000..408e8fec --- /dev/null +++ b/dada_tests/permissions/revokation/overwrite-shared-separate-root.dada @@ -0,0 +1,16 @@ +class Pair(any a, any b) + +async fn main() { + any temp = Pair(22, 44).share + any pair = Pair(temp, 66) + # ^^^^ + # Temp is shared, so this clones + + # Leasing from `pair.a` creates a third owner. + any p = pair.a.lease + + # Overwriting `pair.a` removes one handle to + # the shared pair, but `p` is unaffected. + pair.a := Pair(23, 45) + print(p).await #! OUTPUT our Pair\(22, 44\) +} \ No newline at end of file diff --git a/dada_tests/permissions/revokation/overwrite-shared-separate-root/compiler-output.ref b/dada_tests/permissions/revokation/overwrite-shared-separate-root/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/permissions/revokation/overwrite-shared-separate-root/stdout.ref b/dada_tests/permissions/revokation/overwrite-shared-separate-root/stdout.ref new file mode 100644 index 00000000..2aa36059 --- /dev/null +++ b/dada_tests/permissions/revokation/overwrite-shared-separate-root/stdout.ref @@ -0,0 +1 @@ +our Pair(22, 44) diff --git a/dada_tests/permissions/revokation/overwrite-shared.dada b/dada_tests/permissions/revokation/overwrite-shared.dada index c57fe03a..49eb1f15 100644 --- a/dada_tests/permissions/revokation/overwrite-shared.dada +++ b/dada_tests/permissions/revokation/overwrite-shared.dada @@ -1,18 +1,15 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { - pair = Pair(Pair(22, 44).share, 66) + any pair = Pair(Pair(22, 44).share, 66) # ^^^^^^^^^^^^^^^^^^ - # This introduces a (jointly owned) temporary. + # This created a jointly owned value but + # puts `pair` is the sole owner of it. - # Leasing that jointly owned temporary produces - # a shared lease. - p = pair.a.lease + # Leasing from `pair.a` clones it, since `pair.a` is "our" + any p = pair.a.lease - # Overwriting `pair.a` does not destroy the temporary - # behind it. + # Since `p` is owned, overwriting `pair.a` has no effect on it. pair.a := Pair(23, 45) - - # So we can still access `p` - print(p).await #! OUTPUT our leased Pair\(22, 44\) + print(p).await #! OUTPUT our Pair\(22, 44\) } \ No newline at end of file diff --git a/dada_tests/permissions/revokation/overwrite-shared/stdout.ref b/dada_tests/permissions/revokation/overwrite-shared/stdout.ref index 50c48401..2aa36059 100644 --- a/dada_tests/permissions/revokation/overwrite-shared/stdout.ref +++ b/dada_tests/permissions/revokation/overwrite-shared/stdout.ref @@ -1 +1 @@ -our leased Pair(22, 44) +our Pair(22, 44) diff --git a/dada_tests/permissions/revokation/scoped-exit-atomic-temporary.dada b/dada_tests/permissions/revokation/scoped-exit-atomic-temporary.dada index 8401bdd3..61219668 100644 --- a/dada_tests/permissions/revokation/scoped-exit-atomic-temporary.dada +++ b/dada_tests/permissions/revokation/scoped-exit-atomic-temporary.dada @@ -1,4 +1,4 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { p = 0 diff --git a/dada_tests/permissions/revokation/scoped-exit-if-false-temporary.dada b/dada_tests/permissions/revokation/scoped-exit-if-false-temporary.dada index 91fb950a..c8822773 100644 --- a/dada_tests/permissions/revokation/scoped-exit-if-false-temporary.dada +++ b/dada_tests/permissions/revokation/scoped-exit-if-false-temporary.dada @@ -1,4 +1,4 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { p = 0 diff --git a/dada_tests/permissions/revokation/scoped-exit-if-true-temporary.dada b/dada_tests/permissions/revokation/scoped-exit-if-true-temporary.dada index 8317692a..6562cd0a 100644 --- a/dada_tests/permissions/revokation/scoped-exit-if-true-temporary.dada +++ b/dada_tests/permissions/revokation/scoped-exit-if-true-temporary.dada @@ -1,4 +1,4 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { p = 0 diff --git a/dada_tests/permissions/revokation/scoped-exit-while-iteration-named.dada b/dada_tests/permissions/revokation/scoped-exit-while-iteration-named.dada index 93b21ea1..077dfcc2 100644 --- a/dada_tests/permissions/revokation/scoped-exit-while-iteration-named.dada +++ b/dada_tests/permissions/revokation/scoped-exit-while-iteration-named.dada @@ -2,7 +2,7 @@ # by a named variable scoped to a loop body # that is carried across iterations. -class Pair(a, b) +class Pair(any a, any b) async fn main() { p = 0 diff --git a/dada_tests/permissions/revokation/scoped-exit-while-iteration-temporary.dada b/dada_tests/permissions/revokation/scoped-exit-while-iteration-temporary.dada index 9b4f5aa4..71e8c625 100644 --- a/dada_tests/permissions/revokation/scoped-exit-while-iteration-temporary.dada +++ b/dada_tests/permissions/revokation/scoped-exit-while-iteration-temporary.dada @@ -2,7 +2,7 @@ # by a temporary scoped to a loop body # that is carried across iterations. -class Pair(a, b) +class Pair(any a, any b) async fn main() { p = 0 diff --git a/dada_tests/permissions/revokation/scoped-exit-while-named.dada b/dada_tests/permissions/revokation/scoped-exit-while-named.dada index 8b741372..dee5e2d7 100644 --- a/dada_tests/permissions/revokation/scoped-exit-while-named.dada +++ b/dada_tests/permissions/revokation/scoped-exit-while-named.dada @@ -1,4 +1,4 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { p = 0 diff --git a/dada_tests/permissions/revokation/scoped-exit-while-temporary.dada b/dada_tests/permissions/revokation/scoped-exit-while-temporary.dada index e823cc99..b556478e 100644 --- a/dada_tests/permissions/revokation/scoped-exit-while-temporary.dada +++ b/dada_tests/permissions/revokation/scoped-exit-while-temporary.dada @@ -1,4 +1,4 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { p = 0 diff --git a/dada_tests/permissions/revokation/write-field-of-leased.dada b/dada_tests/permissions/revokation/write-field-of-leased.dada index 14d8a3e1..bca385bf 100644 --- a/dada_tests/permissions/revokation/write-field-of-leased.dada +++ b/dada_tests/permissions/revokation/write-field-of-leased.dada @@ -1,10 +1,10 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { - p = Pair(22, 44).lease + any p = Pair(22, 44).lease # we now have a shared lease on `p` - q = p.share + any q = p.share # mutating field of `p` cancels our shared lease p.a := 23 diff --git a/dada_tests/permissions/shared-data-is-immutable/write-shared-field.dada b/dada_tests/permissions/shared-data-is-immutable/write-shared-field.dada index 9caaa999..f0594b9c 100644 --- a/dada_tests/permissions/shared-data-is-immutable/write-shared-field.dada +++ b/dada_tests/permissions/shared-data-is-immutable/write-shared-field.dada @@ -1,4 +1,4 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { pair = Pair(22, 44).share diff --git a/dada_tests/permissions/shared-data-is-immutable/write-shared-traverse.dada b/dada_tests/permissions/shared-data-is-immutable/write-shared-traverse.dada index 61273314..9c7efbf5 100644 --- a/dada_tests/permissions/shared-data-is-immutable/write-shared-traverse.dada +++ b/dada_tests/permissions/shared-data-is-immutable/write-shared-traverse.dada @@ -1,7 +1,7 @@ -class Pair(a, b) +class Pair(any a, any b) async fn main() { - pair = Pair(Pair(22, 44), 66).share + any pair = Pair(Pair(22, 44), 66).share # Here the *immediate* pair (`Pair(22, 44)`) was never shared, # but it is stored in a pair that *is* shared. diff --git a/dada_tests/reservations/our-to-our-leased-assign-field.dada b/dada_tests/reservations/our-to-our-leased-assign-field.dada new file mode 100644 index 00000000..c1fb2483 --- /dev/null +++ b/dada_tests/reservations/our-to-our-leased-assign-field.dada @@ -0,0 +1,18 @@ +class Point(any a, any b) + +class OurLeased(our leased f) + +async fn main() { + our p = Point(22, 44) # create a shared point `(22, 44)` + my q = OurLeased(p) # `q.f` becomes 2nd owner of `(22, 44)` + print(q.lease).await #! OUTPUT OurLeased\(our Point\(22, 44\)\) + + p := Point(44, 66) # `p` is shared owner of `(44, 66)` + q.f := p # `q.f` becomes 2nd owner of `(44, 66)` + print(q.lease).await #! OUTPUT OurLeased\(our Point\(44, 66\)\) + p := Point(11, 55) # overwriting `p` doesn't invalidate `q.f` + + print(q.lease).await #! OUTPUT OurLeased\(our Point\(44, 66\)\) + print(p.lease).await #! OUTPUT Point\(11, 55\) + print(q.lease).await #! OUTPUT OurLeased\(our Point\(44, 66\)\) +} \ No newline at end of file diff --git a/dada_tests/reservations/our-to-our-leased-assign-field/compiler-output.ref b/dada_tests/reservations/our-to-our-leased-assign-field/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/reservations/our-to-our-leased-assign-field/stdout.ref b/dada_tests/reservations/our-to-our-leased-assign-field/stdout.ref new file mode 100644 index 00000000..f5398274 --- /dev/null +++ b/dada_tests/reservations/our-to-our-leased-assign-field/stdout.ref @@ -0,0 +1,5 @@ +leased OurLeased(our Point(22, 44)) +leased OurLeased(our Point(44, 66)) +leased OurLeased(our Point(44, 66)) +our Point(11, 55) +leased OurLeased(our Point(44, 66)) diff --git a/dada_tests/reservations/our-to-our-leased-assign.dada b/dada_tests/reservations/our-to-our-leased-assign.dada new file mode 100644 index 00000000..8cc0b9c4 --- /dev/null +++ b/dada_tests/reservations/our-to-our-leased-assign.dada @@ -0,0 +1,22 @@ +class Point(any a, any b) + +async fn main() { + our p = Point(22, 44) + + # leasing an "our" thing becomes a second + # owner (lessors are always exclusive) + our leased q = p + print(q).await #! OUTPUT Point\(22, 44\) + + # reassigning `p` does not invalidate `q`. + p := Point(44, 66) + print(q).await #! OUTPUT Point\(22, 44\) + + # reassigning `q` creates a second owner for the `(44, 66)` point + q := p + + # reassigning `p`, again, does not invalidate `q` + p := Point(33, 55) + print(p).await #! OUTPUT Point\(33, 55\) + print(q).await #! OUTPUT Point\(44, 66\) +} \ No newline at end of file diff --git a/dada_tests/reservations/our-to-our-leased-assign/compiler-output.ref b/dada_tests/reservations/our-to-our-leased-assign/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/reservations/our-to-our-leased-assign/stdout.ref b/dada_tests/reservations/our-to-our-leased-assign/stdout.ref new file mode 100644 index 00000000..c90cad39 --- /dev/null +++ b/dada_tests/reservations/our-to-our-leased-assign/stdout.ref @@ -0,0 +1,4 @@ +our Point(22, 44) +our Point(22, 44) +our Point(33, 55) +our Point(44, 66) diff --git a/dada_tests/reservations/our-to-our-leased-field.dada b/dada_tests/reservations/our-to-our-leased-field.dada new file mode 100644 index 00000000..216a5502 --- /dev/null +++ b/dada_tests/reservations/our-to-our-leased-field.dada @@ -0,0 +1,15 @@ +class Point(any a, any b) + +class OurLeased(our leased f) + +async fn main() { + our p = Point(22, 44) # create `(22, 44)` with shared ownership + print(p.lease).await #! OUTPUT Point\(22, 44\) + my q = OurLeased(p) # `OurLeased` takes 2nd ownership of `(22, 44)` + print(q.lease).await #! OUTPUT OurLeased\(our Point\(22, 44\)\) + p := Point(44, 66) # reassigning `p` doesn't invalidate `q.f` + + print(q.lease).await #! OUTPUT OurLeased\(our Point\(22, 44\)\) + print(p.lease).await #! OUTPUT Point\(44, 66\) + print(q.lease).await #! OUTPUT OurLeased\(our Point\(22, 44\)\) +} \ No newline at end of file diff --git a/dada_tests/reservations/our-to-our-leased-field/compiler-output.ref b/dada_tests/reservations/our-to-our-leased-field/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/reservations/our-to-our-leased-field/stdout.ref b/dada_tests/reservations/our-to-our-leased-field/stdout.ref new file mode 100644 index 00000000..6190d68a --- /dev/null +++ b/dada_tests/reservations/our-to-our-leased-field/stdout.ref @@ -0,0 +1,5 @@ +our Point(22, 44) +leased OurLeased(our Point(22, 44)) +leased OurLeased(our Point(22, 44)) +our Point(44, 66) +leased OurLeased(our Point(22, 44)) diff --git a/dada_tests/reservations/our-to-our-leased-var.dada b/dada_tests/reservations/our-to-our-leased-var.dada new file mode 100644 index 00000000..0a7da87b --- /dev/null +++ b/dada_tests/reservations/our-to-our-leased-var.dada @@ -0,0 +1,10 @@ +class Point(any a, any b) + +async fn main() { + our p = Point(22, 44) + our leased q = p # `q` becomes 2nd owner of `(22, 44)` + p := Point(44, 66) # reassigning `p` has no effect on `q` + + print(p).await #! OUTPUT Point\(44, 66\) + print(q).await #! OUTPUT Point\(22, 44\) +} \ No newline at end of file diff --git a/dada_tests/reservations/our-to-our-leased-var/compiler-output.ref b/dada_tests/reservations/our-to-our-leased-var/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/reservations/our-to-our-leased-var/stdout.ref b/dada_tests/reservations/our-to-our-leased-var/stdout.ref new file mode 100644 index 00000000..e82e7332 --- /dev/null +++ b/dada_tests/reservations/our-to-our-leased-var/stdout.ref @@ -0,0 +1,2 @@ +our Point(44, 66) +our Point(22, 44) diff --git a/dada_tests/specifier/field-leased-mode-leases.dada b/dada_tests/specifier/field-leased-mode-leases.dada new file mode 100644 index 00000000..519b88e8 --- /dev/null +++ b/dada_tests/specifier/field-leased-mode-leases.dada @@ -0,0 +1,17 @@ +class Modes( + leased l + our o +) + +class Widget( + our name +) + +async fn main() { + my w1 = Widget("w1") + my w2 = Widget("w2") + my p = Modes(w1, w2) + print(p.l).await #! OUTPUT Widget\(w1\) + print(w1).await #! OUTPUT Widget\(w1\) + print(p.l).await #! RUN ERROR your lease to this object was cancelled +} diff --git a/dada_tests/specifier/field-leased-mode-leases/compiler-output.ref b/dada_tests/specifier/field-leased-mode-leases/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/field-leased-mode-leases/stdout.ref b/dada_tests/specifier/field-leased-mode-leases/stdout.ref new file mode 100644 index 00000000..58ba5a3b --- /dev/null +++ b/dada_tests/specifier/field-leased-mode-leases/stdout.ref @@ -0,0 +1,2 @@ +leased Widget(w1) +my Widget(w1) diff --git a/dada_tests/specifier/field-our-mode-moves.dada b/dada_tests/specifier/field-our-mode-moves.dada new file mode 100644 index 00000000..350686f4 --- /dev/null +++ b/dada_tests/specifier/field-our-mode-moves.dada @@ -0,0 +1,15 @@ +class Modes( + leased l + our o +) + +class Widget( + our name +) + +async fn main() { + my w1 = Widget("w1") + my w2 = Widget("w2") + my p = Modes(w1, w2) + print(w2).await #! RUN ERROR your lease to this object was cancelled +} diff --git a/dada_tests/specifier/field-our-mode-moves/compiler-output.ref b/dada_tests/specifier/field-our-mode-moves/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/field-our-mode-moves/stdout.ref b/dada_tests/specifier/field-our-mode-moves/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/leased-from-rvalue-assign-in-loop.dada b/dada_tests/specifier/leased-from-rvalue-assign-in-loop.dada new file mode 100644 index 00000000..af22aff5 --- /dev/null +++ b/dada_tests/specifier/leased-from-rvalue-assign-in-loop.dada @@ -0,0 +1,20 @@ +class Point(our x, our y) + +async fn main() { + # Leases a temporary that lives as long as `p` + leased p = Point(22, 44) + + i = 0 + while i < 1 { + print(p).await #! OUTPUT Point\(22, 44\) + + # Creates a temporary here, which will expire + # when we exit the loop, and leases it to `p` + p := Point(44, 66) + print(p).await #! OUTPUT Point\(44, 66\) + i += 1 + } + + # The value stored in `p` has expired + print(p).await #! RUN ERROR your lease to this object was cancelled +} \ No newline at end of file diff --git a/dada_tests/specifier/leased-from-rvalue-assign-in-loop/compiler-output.ref b/dada_tests/specifier/leased-from-rvalue-assign-in-loop/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/leased-from-rvalue-assign-in-loop/stdout.ref b/dada_tests/specifier/leased-from-rvalue-assign-in-loop/stdout.ref new file mode 100644 index 00000000..8b7043a7 --- /dev/null +++ b/dada_tests/specifier/leased-from-rvalue-assign-in-loop/stdout.ref @@ -0,0 +1,2 @@ +leased Point(22, 44) +leased Point(44, 66) diff --git a/dada_tests/specifier/leased-from-rvalue.dada b/dada_tests/specifier/leased-from-rvalue.dada new file mode 100644 index 00000000..a597937b --- /dev/null +++ b/dada_tests/specifier/leased-from-rvalue.dada @@ -0,0 +1,10 @@ +class Point(our x, our y) + +async fn main() { + leased p = Point(22, 44) + leased q = p + print(q).await #! OUTPUT Point\(22, 44\) + print(p).await #! OUTPUT Point\(22, 44\) + p := Point(44, 66) + print(q).await #! RUN ERROR your lease to this object was cancelled +} \ No newline at end of file diff --git a/dada_tests/specifier/leased-from-rvalue/compiler-output.ref b/dada_tests/specifier/leased-from-rvalue/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/leased-from-rvalue/stdout.ref b/dada_tests/specifier/leased-from-rvalue/stdout.ref new file mode 100644 index 00000000..871cee0f --- /dev/null +++ b/dada_tests/specifier/leased-from-rvalue/stdout.ref @@ -0,0 +1,2 @@ +leased Point(22, 44) +leased Point(22, 44) diff --git a/dada_tests/specifier/leased-from-shared-rvalue-assign-in-loop.dada b/dada_tests/specifier/leased-from-shared-rvalue-assign-in-loop.dada new file mode 100644 index 00000000..7edc1b49 --- /dev/null +++ b/dada_tests/specifier/leased-from-shared-rvalue-assign-in-loop.dada @@ -0,0 +1,23 @@ +class Point(our x, our y) + +async fn main() { + # Leasing an `our` value just takes ownership + # of it, so `p` becomes (shared) owner of this + # point here. + our leased p = Point(22, 44).share + + i = 0 + while i < 1 { + print(p).await #! OUTPUT Point\(22, 44\) + + # Leasing an `our` value just takes ownership + # of it, so `p` becomes (shared) owner of this + # point here. + p := Point(44, 66).share + print(p).await #! OUTPUT Point\(44, 66\) + i += 1 + } + + # p is (shared) owner, so still valid. + print(p).await #! OUTPUT Point\(44, 66\) +} \ No newline at end of file diff --git a/dada_tests/specifier/leased-from-shared-rvalue-assign-in-loop/compiler-output.ref b/dada_tests/specifier/leased-from-shared-rvalue-assign-in-loop/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/leased-from-shared-rvalue-assign-in-loop/stdout.ref b/dada_tests/specifier/leased-from-shared-rvalue-assign-in-loop/stdout.ref new file mode 100644 index 00000000..f604265c --- /dev/null +++ b/dada_tests/specifier/leased-from-shared-rvalue-assign-in-loop/stdout.ref @@ -0,0 +1,3 @@ +our Point(22, 44) +our Point(44, 66) +our Point(44, 66) diff --git a/dada_tests/specifier/local-my.dada b/dada_tests/specifier/local-my.dada new file mode 100644 index 00000000..f011f1f6 --- /dev/null +++ b/dada_tests/specifier/local-my.dada @@ -0,0 +1,13 @@ +class Pair(any a, any b) + +async fn main() { + my pair = Pair(22, 44) + print(pair.lease).await #! OUTPUT Pair\(22, 44\) + + our pair1 = pair + our pair2 = pair1 + print(pair1).await #! OUTPUT Pair\(22, 44\) + print(pair2).await #! OUTPUT Pair\(22, 44\) + + print(pair).await #! RUN ERROR your lease to this object was cancelled +} \ No newline at end of file diff --git a/dada_tests/specifier/local-my/compiler-output.ref b/dada_tests/specifier/local-my/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/local-my/stdout.ref b/dada_tests/specifier/local-my/stdout.ref new file mode 100644 index 00000000..765d2df4 --- /dev/null +++ b/dada_tests/specifier/local-my/stdout.ref @@ -0,0 +1,3 @@ +leased Pair(22, 44) +our Pair(22, 44) +our Pair(22, 44) diff --git a/dada_tests/specifier/need-any-got-leased.dada b/dada_tests/specifier/need-any-got-leased.dada new file mode 100644 index 00000000..b3621221 --- /dev/null +++ b/dada_tests/specifier/need-any-got-leased.dada @@ -0,0 +1,5 @@ +class Point() + +async fn main() { + any p = Point().lease +} \ No newline at end of file diff --git a/dada_tests/specifier/need-any-got-leased/compiler-output.ref b/dada_tests/specifier/need-any-got-leased/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-any-got-leased/stdout.ref b/dada_tests/specifier/need-any-got-leased/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-any-got-my.dada b/dada_tests/specifier/need-any-got-my.dada new file mode 100644 index 00000000..bf888ccd --- /dev/null +++ b/dada_tests/specifier/need-any-got-my.dada @@ -0,0 +1,5 @@ +class Point() + +async fn main() { + any p = Point() +} \ No newline at end of file diff --git a/dada_tests/specifier/need-any-got-my/compiler-output.ref b/dada_tests/specifier/need-any-got-my/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-any-got-my/stdout.ref b/dada_tests/specifier/need-any-got-my/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-any-got-our-leased.dada b/dada_tests/specifier/need-any-got-our-leased.dada new file mode 100644 index 00000000..5b4702d6 --- /dev/null +++ b/dada_tests/specifier/need-any-got-our-leased.dada @@ -0,0 +1,5 @@ +class Point() + +async fn main() { + any p = Point().lease.share +} \ No newline at end of file diff --git a/dada_tests/specifier/need-any-got-our-leased/compiler-output.ref b/dada_tests/specifier/need-any-got-our-leased/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-any-got-our-leased/stdout.ref b/dada_tests/specifier/need-any-got-our-leased/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-any-got-our.dada b/dada_tests/specifier/need-any-got-our.dada new file mode 100644 index 00000000..82ae94a5 --- /dev/null +++ b/dada_tests/specifier/need-any-got-our.dada @@ -0,0 +1,5 @@ +class Point() + +async fn main() { + any p = Point().share +} \ No newline at end of file diff --git a/dada_tests/specifier/need-any-got-our/compiler-output.ref b/dada_tests/specifier/need-any-got-our/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-any-got-our/stdout.ref b/dada_tests/specifier/need-any-got-our/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-leased-got-leased.dada b/dada_tests/specifier/need-leased-got-leased.dada new file mode 100644 index 00000000..e373cb65 --- /dev/null +++ b/dada_tests/specifier/need-leased-got-leased.dada @@ -0,0 +1,5 @@ +class Point() + +async fn main() { + leased p = Point().lease +} \ No newline at end of file diff --git a/dada_tests/specifier/need-leased-got-leased/compiler-output.ref b/dada_tests/specifier/need-leased-got-leased/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-leased-got-leased/stdout.ref b/dada_tests/specifier/need-leased-got-leased/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-leased-got-my-lvalue.dada b/dada_tests/specifier/need-leased-got-my-lvalue.dada new file mode 100644 index 00000000..9c9492b6 --- /dev/null +++ b/dada_tests/specifier/need-leased-got-my-lvalue.dada @@ -0,0 +1,11 @@ +class Point() + +async fn main() { + my p = Point() + leased q = p + + # Check that the leased value can be used until `p` is used again + print(q).await #! OUTPUT Point\(\) + print(p).await #! OUTPUT Point\(\) + print(q).await #! RUN ERROR your lease to this object was cancelled +} \ No newline at end of file diff --git a/dada_tests/specifier/need-leased-got-my-lvalue/compiler-output.ref b/dada_tests/specifier/need-leased-got-my-lvalue/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-leased-got-my-lvalue/stdout.ref b/dada_tests/specifier/need-leased-got-my-lvalue/stdout.ref new file mode 100644 index 00000000..f912844d --- /dev/null +++ b/dada_tests/specifier/need-leased-got-my-lvalue/stdout.ref @@ -0,0 +1,2 @@ +leased Point() +my Point() diff --git a/dada_tests/specifier/need-leased-got-my.dada b/dada_tests/specifier/need-leased-got-my.dada new file mode 100644 index 00000000..18846312 --- /dev/null +++ b/dada_tests/specifier/need-leased-got-my.dada @@ -0,0 +1,5 @@ +class Point() + +async fn main() { + leased p = Point() +} \ No newline at end of file diff --git a/dada_tests/specifier/need-leased-got-my/compiler-output.ref b/dada_tests/specifier/need-leased-got-my/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-leased-got-my/stdout.ref b/dada_tests/specifier/need-leased-got-my/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-leased-got-our-leased.dada b/dada_tests/specifier/need-leased-got-our-leased.dada new file mode 100644 index 00000000..82f0591d --- /dev/null +++ b/dada_tests/specifier/need-leased-got-our-leased.dada @@ -0,0 +1,5 @@ +class Point() + +async fn main() { + leased p = Point().lease.share #! RUN ERROR more permissions needed +} \ No newline at end of file diff --git a/dada_tests/specifier/need-leased-got-our-leased/compiler-output.ref b/dada_tests/specifier/need-leased-got-our-leased/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-leased-got-our-leased/stdout.ref b/dada_tests/specifier/need-leased-got-our-leased/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-leased-got-our.dada b/dada_tests/specifier/need-leased-got-our.dada new file mode 100644 index 00000000..ed46c80c --- /dev/null +++ b/dada_tests/specifier/need-leased-got-our.dada @@ -0,0 +1,5 @@ +class Point() + +async fn main() { + leased p = Point().share #! RUN ERROR more permissions needed +} \ No newline at end of file diff --git a/dada_tests/specifier/need-leased-got-our/compiler-output.ref b/dada_tests/specifier/need-leased-got-our/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-leased-got-our/stdout.ref b/dada_tests/specifier/need-leased-got-our/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-my-got-leased.dada b/dada_tests/specifier/need-my-got-leased.dada new file mode 100644 index 00000000..e655e184 --- /dev/null +++ b/dada_tests/specifier/need-my-got-leased.dada @@ -0,0 +1,5 @@ +class Point() + +async fn main() { + my p = Point().lease #! RUN ERROR more permissions needed +} \ No newline at end of file diff --git a/dada_tests/specifier/need-my-got-leased/compiler-output.ref b/dada_tests/specifier/need-my-got-leased/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-my-got-leased/stdout.ref b/dada_tests/specifier/need-my-got-leased/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-my-got-my.dada b/dada_tests/specifier/need-my-got-my.dada new file mode 100644 index 00000000..f7faa919 --- /dev/null +++ b/dada_tests/specifier/need-my-got-my.dada @@ -0,0 +1,5 @@ +class Point() + +async fn main() { + my p = Point() +} \ No newline at end of file diff --git a/dada_tests/specifier/need-my-got-my/compiler-output.ref b/dada_tests/specifier/need-my-got-my/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-my-got-my/stdout.ref b/dada_tests/specifier/need-my-got-my/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-my-got-our-leased.dada b/dada_tests/specifier/need-my-got-our-leased.dada new file mode 100644 index 00000000..7d803503 --- /dev/null +++ b/dada_tests/specifier/need-my-got-our-leased.dada @@ -0,0 +1,5 @@ +class Point() + +async fn main() { + my p = Point().lease.share #! RUN ERROR more permissions needed +} \ No newline at end of file diff --git a/dada_tests/specifier/need-my-got-our-leased/compiler-output.ref b/dada_tests/specifier/need-my-got-our-leased/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-my-got-our-leased/stdout.ref b/dada_tests/specifier/need-my-got-our-leased/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-my-got-our.dada b/dada_tests/specifier/need-my-got-our.dada new file mode 100644 index 00000000..596d277f --- /dev/null +++ b/dada_tests/specifier/need-my-got-our.dada @@ -0,0 +1,5 @@ +class Point() + +async fn main() { + my p = Point().share #! RUN ERROR more permissions needed +} \ No newline at end of file diff --git a/dada_tests/specifier/need-my-got-our/compiler-output.ref b/dada_tests/specifier/need-my-got-our/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-my-got-our/stdout.ref b/dada_tests/specifier/need-my-got-our/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-our-got-leased.dada b/dada_tests/specifier/need-our-got-leased.dada new file mode 100644 index 00000000..f2ae08c6 --- /dev/null +++ b/dada_tests/specifier/need-our-got-leased.dada @@ -0,0 +1,5 @@ +class Point() + +async fn main() { + our p = Point().lease #! RUN ERROR more permissions needed +} \ No newline at end of file diff --git a/dada_tests/specifier/need-our-got-leased/compiler-output.ref b/dada_tests/specifier/need-our-got-leased/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-our-got-leased/stdout.ref b/dada_tests/specifier/need-our-got-leased/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-our-got-my.dada b/dada_tests/specifier/need-our-got-my.dada new file mode 100644 index 00000000..970d88be --- /dev/null +++ b/dada_tests/specifier/need-our-got-my.dada @@ -0,0 +1,5 @@ +class Point() + +async fn main() { + our p = Point() +} \ No newline at end of file diff --git a/dada_tests/specifier/need-our-got-my/compiler-output.ref b/dada_tests/specifier/need-our-got-my/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-our-got-my/stdout.ref b/dada_tests/specifier/need-our-got-my/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-our-got-our-leased.dada b/dada_tests/specifier/need-our-got-our-leased.dada new file mode 100644 index 00000000..8f6967d9 --- /dev/null +++ b/dada_tests/specifier/need-our-got-our-leased.dada @@ -0,0 +1,5 @@ +class Point() + +async fn main() { + our p = Point().lease.share #! RUN ERROR more permissions needed +} \ No newline at end of file diff --git a/dada_tests/specifier/need-our-got-our-leased/compiler-output.ref b/dada_tests/specifier/need-our-got-our-leased/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-our-got-our-leased/stdout.ref b/dada_tests/specifier/need-our-got-our-leased/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-our-got-our.dada b/dada_tests/specifier/need-our-got-our.dada new file mode 100644 index 00000000..c6c3c66c --- /dev/null +++ b/dada_tests/specifier/need-our-got-our.dada @@ -0,0 +1,5 @@ +class Point() + +async fn main() { + our p = Point().share +} \ No newline at end of file diff --git a/dada_tests/specifier/need-our-got-our/compiler-output.ref b/dada_tests/specifier/need-our-got-our/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-our-got-our/stdout.ref b/dada_tests/specifier/need-our-got-our/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-our-leased-got-leased.dada b/dada_tests/specifier/need-our-leased-got-leased.dada new file mode 100644 index 00000000..189b450e --- /dev/null +++ b/dada_tests/specifier/need-our-leased-got-leased.dada @@ -0,0 +1,5 @@ +class Point() + +async fn main() { + our leased p = Point().lease +} \ No newline at end of file diff --git a/dada_tests/specifier/need-our-leased-got-leased/compiler-output.ref b/dada_tests/specifier/need-our-leased-got-leased/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-our-leased-got-leased/stdout.ref b/dada_tests/specifier/need-our-leased-got-leased/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-our-leased-got-my.dada b/dada_tests/specifier/need-our-leased-got-my.dada new file mode 100644 index 00000000..59e86c11 --- /dev/null +++ b/dada_tests/specifier/need-our-leased-got-my.dada @@ -0,0 +1,5 @@ +class Point() + +async fn main() { + our leased p = Point() +} \ No newline at end of file diff --git a/dada_tests/specifier/need-our-leased-got-my/compiler-output.ref b/dada_tests/specifier/need-our-leased-got-my/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-our-leased-got-my/stdout.ref b/dada_tests/specifier/need-our-leased-got-my/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-our-leased-got-our-leased.dada b/dada_tests/specifier/need-our-leased-got-our-leased.dada new file mode 100644 index 00000000..e5ca7ce6 --- /dev/null +++ b/dada_tests/specifier/need-our-leased-got-our-leased.dada @@ -0,0 +1,5 @@ +class Point() + +async fn main() { + our leased p = Point().lease.share +} \ No newline at end of file diff --git a/dada_tests/specifier/need-our-leased-got-our-leased/compiler-output.ref b/dada_tests/specifier/need-our-leased-got-our-leased/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-our-leased-got-our-leased/stdout.ref b/dada_tests/specifier/need-our-leased-got-our-leased/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-our-leased-got-our.dada b/dada_tests/specifier/need-our-leased-got-our.dada new file mode 100644 index 00000000..758fff6a --- /dev/null +++ b/dada_tests/specifier/need-our-leased-got-our.dada @@ -0,0 +1,5 @@ +class Point() + +async fn main() { + our leased p = Point().share +} \ No newline at end of file diff --git a/dada_tests/specifier/need-our-leased-got-our/compiler-output.ref b/dada_tests/specifier/need-our-leased-got-our/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/need-our-leased-got-our/stdout.ref b/dada_tests/specifier/need-our-leased-got-our/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/our-leased-from-shared-rvalue-assign-in-loop.dada b/dada_tests/specifier/our-leased-from-shared-rvalue-assign-in-loop.dada new file mode 100644 index 00000000..7edc1b49 --- /dev/null +++ b/dada_tests/specifier/our-leased-from-shared-rvalue-assign-in-loop.dada @@ -0,0 +1,23 @@ +class Point(our x, our y) + +async fn main() { + # Leasing an `our` value just takes ownership + # of it, so `p` becomes (shared) owner of this + # point here. + our leased p = Point(22, 44).share + + i = 0 + while i < 1 { + print(p).await #! OUTPUT Point\(22, 44\) + + # Leasing an `our` value just takes ownership + # of it, so `p` becomes (shared) owner of this + # point here. + p := Point(44, 66).share + print(p).await #! OUTPUT Point\(44, 66\) + i += 1 + } + + # p is (shared) owner, so still valid. + print(p).await #! OUTPUT Point\(44, 66\) +} \ No newline at end of file diff --git a/dada_tests/specifier/our-leased-from-shared-rvalue-assign-in-loop/compiler-output.ref b/dada_tests/specifier/our-leased-from-shared-rvalue-assign-in-loop/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/our-leased-from-shared-rvalue-assign-in-loop/stdout.ref b/dada_tests/specifier/our-leased-from-shared-rvalue-assign-in-loop/stdout.ref new file mode 100644 index 00000000..f604265c --- /dev/null +++ b/dada_tests/specifier/our-leased-from-shared-rvalue-assign-in-loop/stdout.ref @@ -0,0 +1,3 @@ +our Point(22, 44) +our Point(44, 66) +our Point(44, 66) diff --git a/dada_tests/specifier/our-leased-got-my-then-copy.dada b/dada_tests/specifier/our-leased-got-my-then-copy.dada new file mode 100644 index 00000000..2b69071a --- /dev/null +++ b/dada_tests/specifier/our-leased-got-my-then-copy.dada @@ -0,0 +1,7 @@ +class Point(our x, our y) + +async fn main() { + p = Point(22, 33) + q = p + print(p).await #! OUTPUT Point\(22, 33\) +} diff --git a/dada_tests/specifier/our-leased-got-my-then-copy/compiler-output.ref b/dada_tests/specifier/our-leased-got-my-then-copy/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/our-leased-got-my-then-copy/stdout.ref b/dada_tests/specifier/our-leased-got-my-then-copy/stdout.ref new file mode 100644 index 00000000..dce04907 --- /dev/null +++ b/dada_tests/specifier/our-leased-got-my-then-copy/stdout.ref @@ -0,0 +1 @@ +our leased Point(22, 33) diff --git a/dada_tests/specifier/temporary-lifetime/call-argument.dada b/dada_tests/specifier/temporary-lifetime/call-argument.dada new file mode 100644 index 00000000..32ed566b --- /dev/null +++ b/dada_tests/specifier/temporary-lifetime/call-argument.dada @@ -0,0 +1,18 @@ +class Object(any data) + +async fn main() { + any o = lease_me(Object(22)).data + #! RUN ERROR your lease to this object was cancelled + # + # What happens here: + # * the `Object(22)` is stored into a temporary that is dropped as soon + # as the call completes. + # + #! FIXME: This seems kind of annoying! + + print(o).await +} + +fn lease_me(leased p) { + p +} \ No newline at end of file diff --git a/dada_tests/specifier/temporary-lifetime/call-argument/compiler-output.ref b/dada_tests/specifier/temporary-lifetime/call-argument/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/temporary-lifetime/call-argument/stdout.ref b/dada_tests/specifier/temporary-lifetime/call-argument/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/temporary-lifetime/if-then-else-leased.dada b/dada_tests/specifier/temporary-lifetime/if-then-else-leased.dada new file mode 100644 index 00000000..6a24dcd5 --- /dev/null +++ b/dada_tests/specifier/temporary-lifetime/if-then-else-leased.dada @@ -0,0 +1,12 @@ +class Object(any data) + +async fn main() { + any o = if true { Object(true).lease } else { Object(false).lease } + #! RUN ERROR your lease to this object was cancelled + # + # What happens here: + # * `Object(true).lease` is equivalent to `{ any o = Object(true); o.lease }` + # * that variable `o` is dropped as we exit the `if-then-else` + + print(o).await +} diff --git a/dada_tests/specifier/temporary-lifetime/if-then-else-leased/compiler-output.ref b/dada_tests/specifier/temporary-lifetime/if-then-else-leased/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/temporary-lifetime/if-then-else-leased/stdout.ref b/dada_tests/specifier/temporary-lifetime/if-then-else-leased/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/temporary-lifetime/if-then-else-owned.dada b/dada_tests/specifier/temporary-lifetime/if-then-else-owned.dada new file mode 100644 index 00000000..1f4e57f2 --- /dev/null +++ b/dada_tests/specifier/temporary-lifetime/if-then-else-owned.dada @@ -0,0 +1,7 @@ +class Object(any data) + +async fn main() { + # This is equivalent to `if { .. } else { .. }.lease`. + leased o = if true { Object(true) } else { Object(false) } + print(o).await #! OUTPUT Object\(true\) +} diff --git a/dada_tests/specifier/temporary-lifetime/if-then-else-owned/compiler-output.ref b/dada_tests/specifier/temporary-lifetime/if-then-else-owned/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/specifier/temporary-lifetime/if-then-else-owned/stdout.ref b/dada_tests/specifier/temporary-lifetime/if-then-else-owned/stdout.ref new file mode 100644 index 00000000..2b84166d --- /dev/null +++ b/dada_tests/specifier/temporary-lifetime/if-then-else-owned/stdout.ref @@ -0,0 +1 @@ +leased Object(true) diff --git a/dada_tests/validate/assign-to-class.dada b/dada_tests/validate/assign-to-class.dada new file mode 100644 index 00000000..8c983cfe --- /dev/null +++ b/dada_tests/validate/assign-to-class.dada @@ -0,0 +1,7 @@ +class Foo() + +async fn main() { + if false { + Foo := 22 #! ERROR you can only assign to local variables or fields + } +} \ No newline at end of file diff --git a/dada_tests/validate/assign-to-class/compiler-output.ref b/dada_tests/validate/assign-to-class/compiler-output.ref new file mode 100644 index 00000000..2c5c66ee --- /dev/null +++ b/dada_tests/validate/assign-to-class/compiler-output.ref @@ -0,0 +1,7 @@ +Error: you can only assign to local variables or fields, not classes like `Foo` + ╭─[dada_tests/validate/assign-to-class.dada:5:9] + │ + 5 │ Foo := 22 #! ERROR you can only assign to local variables or fields + · ─┬─ + · ╰─── here +───╯ diff --git a/dada_tests/validate/assign-to-class/stdout.ref b/dada_tests/validate/assign-to-class/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/validate/op-eq/lhs_field.dada b/dada_tests/validate/op-eq/lhs_field.dada new file mode 100644 index 00000000..8e951777 --- /dev/null +++ b/dada_tests/validate/op-eq/lhs_field.dada @@ -0,0 +1,7 @@ +class Point(our x, our y) + +async fn main() { + my p = Point(22, 44) + p.x += 1 + print(p).await #! OUTPUT Point\(23, 44\) +} diff --git a/dada_tests/validate/op-eq/lhs_field/compiler-output.ref b/dada_tests/validate/op-eq/lhs_field/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/validate/op-eq/lhs_field/stdout.ref b/dada_tests/validate/op-eq/lhs_field/stdout.ref new file mode 100644 index 00000000..e56b542f --- /dev/null +++ b/dada_tests/validate/op-eq/lhs_field/stdout.ref @@ -0,0 +1 @@ +my Point(23, 44) diff --git a/dada_tests/validate/op-eq/lhs_field_of_func_call.dada b/dada_tests/validate/op-eq/lhs_field_of_func_call.dada new file mode 100644 index 00000000..36e2f76b --- /dev/null +++ b/dada_tests/validate/op-eq/lhs_field_of_func_call.dada @@ -0,0 +1,12 @@ +class Point(our x, our y) + +async fn main() { + my p = Point(22, 44) + test(p).await.x += 1 + print(p).await #! OUTPUT Point\(23, 44\) +} + +async fn test(leased p) { + print("Hi").await #! OUTPUT Hi + p +} \ No newline at end of file diff --git a/dada_tests/validate/op-eq/lhs_field_of_func_call/compiler-output.ref b/dada_tests/validate/op-eq/lhs_field_of_func_call/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/validate/op-eq/lhs_field_of_func_call/stdout.ref b/dada_tests/validate/op-eq/lhs_field_of_func_call/stdout.ref new file mode 100644 index 00000000..b20f3eae --- /dev/null +++ b/dada_tests/validate/op-eq/lhs_field_of_func_call/stdout.ref @@ -0,0 +1,2 @@ +Hi +my Point(23, 44) diff --git a/dada_tests/validate/op-eq/lhs_func_call.dada b/dada_tests/validate/op-eq/lhs_func_call.dada new file mode 100644 index 00000000..ffe132ff --- /dev/null +++ b/dada_tests/validate/op-eq/lhs_func_call.dada @@ -0,0 +1,14 @@ +class Point(our x, our y) + +async fn main() { + my p = Point(22, 44) + test(p) += 1 + #! ERROR you can only assign to local variables and fields, not arbitrary expressions + #! RUN ERROR compilation error encountered + print(p).await +} + +async fn test(leased p) { + print("Hi").await + p +} \ No newline at end of file diff --git a/dada_tests/validate/op-eq/lhs_func_call/compiler-output.ref b/dada_tests/validate/op-eq/lhs_func_call/compiler-output.ref new file mode 100644 index 00000000..bd07c50a --- /dev/null +++ b/dada_tests/validate/op-eq/lhs_func_call/compiler-output.ref @@ -0,0 +1,7 @@ +Error: you can only assign to local variables and fields, not arbitrary expressions + ╭─[dada_tests/validate/op-eq/lhs_func_call.dada:5:5] + │ + 5 │ test(p) += 1 + · ───┬─── + · ╰───── here +───╯ diff --git a/dada_tests/validate/op-eq/lhs_func_call/stdout.ref b/dada_tests/validate/op-eq/lhs_func_call/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/validate/op-eq/lhs_local_variable.dada b/dada_tests/validate/op-eq/lhs_local_variable.dada new file mode 100644 index 00000000..b0d5ca6f --- /dev/null +++ b/dada_tests/validate/op-eq/lhs_local_variable.dada @@ -0,0 +1,8 @@ +class Point(our x, our y) + +async fn main() { + our x = 22 + x += 1 + my p = Point(x, 44) + print(p).await #! OUTPUT Point\(23, 44\) +} diff --git a/dada_tests/validate/op-eq/lhs_local_variable/compiler-output.ref b/dada_tests/validate/op-eq/lhs_local_variable/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/validate/op-eq/lhs_local_variable/stdout.ref b/dada_tests/validate/op-eq/lhs_local_variable/stdout.ref new file mode 100644 index 00000000..e56b542f --- /dev/null +++ b/dada_tests/validate/op-eq/lhs_local_variable/stdout.ref @@ -0,0 +1 @@ +my Point(23, 44) diff --git a/dada_tests/validate/op-eq/lhs_shared_field.dada b/dada_tests/validate/op-eq/lhs_shared_field.dada new file mode 100644 index 00000000..8f05e94f --- /dev/null +++ b/dada_tests/validate/op-eq/lhs_shared_field.dada @@ -0,0 +1,7 @@ +class Point(our x, our y) + +async fn main() { + our p = Point(22, 44) + p.x += 1 #! RUN ERROR cannot write to shared fields + print(p).await +} diff --git a/dada_tests/validate/op-eq/lhs_shared_field/compiler-output.ref b/dada_tests/validate/op-eq/lhs_shared_field/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/validate/op-eq/lhs_shared_field/stdout.ref b/dada_tests/validate/op-eq/lhs_shared_field/stdout.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/validate/op-eq/lhs_shared_field_of_func_call.dada b/dada_tests/validate/op-eq/lhs_shared_field_of_func_call.dada new file mode 100644 index 00000000..0b544471 --- /dev/null +++ b/dada_tests/validate/op-eq/lhs_shared_field_of_func_call.dada @@ -0,0 +1,14 @@ +class Point(our x, our y) + +async fn main() { + our p = Point(22, 44) + # Test that we execute `test(p)` (and hence see its output) + # before we detect the error here + test(p).await.x += 1 #! RUN ERROR cannot write to shared fields + print(p).await +} + +async fn test(our leased p) { + print("Hi").await #! OUTPUT Hi + p +} \ No newline at end of file diff --git a/dada_tests/validate/op-eq/lhs_shared_field_of_func_call/compiler-output.ref b/dada_tests/validate/op-eq/lhs_shared_field_of_func_call/compiler-output.ref new file mode 100644 index 00000000..e69de29b diff --git a/dada_tests/validate/op-eq/lhs_shared_field_of_func_call/stdout.ref b/dada_tests/validate/op-eq/lhs_shared_field_of_func_call/stdout.ref new file mode 100644 index 00000000..b14df644 --- /dev/null +++ b/dada_tests/validate/op-eq/lhs_shared_field_of_func_call/stdout.ref @@ -0,0 +1 @@ +Hi