Changeset - 7f9b23076d66
[Not reviewed]
0 6 0
mh - 4 years ago 2021-12-15 16:56:29
contact@maxhenger.nl
Add some tests for tuple member access
6 files changed with 118 insertions and 4 deletions:
0 comments (0 inline, 0 general)
src/protocol/eval/executor.rs
Show inline comments
 
@@ -107,774 +107,780 @@ impl Frame {
 
        match &heap[id] {
 
            Expression::Assignment(expr) => {
 
                self.serialize_expression(heap, expr.left);
 
                self.serialize_expression(heap, expr.right);
 
            },
 
            Expression::Binding(expr) => {
 
                self.serialize_expression(heap, expr.bound_to);
 
                self.serialize_expression(heap, expr.bound_from);
 
            },
 
            Expression::Conditional(expr) => {
 
                self.serialize_expression(heap, expr.test);
 
            },
 
            Expression::Binary(expr) => {
 
                self.serialize_expression(heap, expr.left);
 
                self.serialize_expression(heap, expr.right);
 
            },
 
            Expression::Unary(expr) => {
 
                self.serialize_expression(heap, expr.expression);
 
            },
 
            Expression::Indexing(expr) => {
 
                self.serialize_expression(heap, expr.index);
 
                self.serialize_expression(heap, expr.subject);
 
            },
 
            Expression::Slicing(expr) => {
 
                self.serialize_expression(heap, expr.from_index);
 
                self.serialize_expression(heap, expr.to_index);
 
                self.serialize_expression(heap, expr.subject);
 
            },
 
            Expression::Select(expr) => {
 
                self.serialize_expression(heap, expr.subject);
 
            },
 
            Expression::Literal(expr) => {
 
                // Here we only care about literals that have subexpressions
 
                match &expr.value {
 
                    Literal::Null | Literal::True | Literal::False |
 
                    Literal::Character(_) | Literal::String(_) |
 
                    Literal::Integer(_) | Literal::Enum(_) => {
 
                        // No subexpressions
 
                    },
 
                    Literal::Struct(literal) => {
 
                        // Note: fields expressions are evaluated in programmer-
 
                        // specified order. But struct construction expects them
 
                        // in type-defined order. I might want to come back to
 
                        // this.
 
                        let mut _num_pushed = 0;
 
                        for want_field_idx in 0..literal.fields.len() {
 
                            for field in &literal.fields {
 
                                if field.field_idx == want_field_idx {
 
                                    _num_pushed += 1;
 
                                    self.expr_stack.push_back(ExprInstruction::PushValToFront);
 
                                    self.serialize_expression(heap, field.value);
 
                                }
 
                            }
 
                        }
 
                        debug_assert_eq!(_num_pushed, literal.fields.len())
 
                    },
 
                    Literal::Union(literal) => {
 
                        for value_expr_id in &literal.values {
 
                            self.expr_stack.push_back(ExprInstruction::PushValToFront);
 
                            self.serialize_expression(heap, *value_expr_id);
 
                        }
 
                    },
 
                    Literal::Array(value_expr_ids) => {
 
                        for value_expr_id in value_expr_ids {
 
                            self.expr_stack.push_back(ExprInstruction::PushValToFront);
 
                            self.serialize_expression(heap, *value_expr_id);
 
                        }
 
                    },
 
                    Literal::Tuple(value_expr_ids) => {
 
                        for value_expr_id in value_expr_ids {
 
                            self.expr_stack.push_back(ExprInstruction::PushValToFront);
 
                            self.serialize_expression(heap, *value_expr_id);
 
                        }
 
                    }
 
                }
 
            },
 
            Expression::Cast(expr) => {
 
                self.serialize_expression(heap, expr.subject);
 
            }
 
            Expression::Call(expr) => {
 
                for arg_expr_id in &expr.arguments {
 
                    self.expr_stack.push_back(ExprInstruction::PushValToFront);
 
                    self.serialize_expression(heap, *arg_expr_id);
 
                }
 
            },
 
            Expression::Variable(_expr) => {
 
                // No subexpressions
 
            }
 
        }
 
    }
 
}
 

	
 
type EvalResult = Result<EvalContinuation, EvalError>;
 

	
 
#[derive(Debug)]
 
pub enum EvalContinuation {
 
    // Returned in both sync and non-sync modes
 
    Stepping,
 
    // Returned only in sync mode
 
    BranchInconsistent,
 
    SyncBlockEnd,
 
    NewFork,
 
    BlockFires(PortId),
 
    BlockGet(PortId),
 
    Put(PortId, ValueGroup),
 
    // Returned only in non-sync mode
 
    ComponentTerminated,
 
    SyncBlockStart,
 
    NewComponent(DefinitionId, i32, ValueGroup),
 
    NewChannel,
 
}
 

	
 
// Note: cloning is fine, methinks. cloning all values and the heap regions then
 
// we end up with valid "pointers" to heap regions.
 
#[derive(Debug, Clone)]
 
pub struct Prompt {
 
    pub(crate) frames: Vec<Frame>,
 
    pub(crate) store: Store,
 
}
 

	
 
impl Prompt {
 
    pub fn new(_types: &TypeTable, heap: &Heap, def: DefinitionId, monomorph_idx: i32, args: ValueGroup) -> Self {
 
        let mut prompt = Self{
 
            frames: Vec::new(),
 
            store: Store::new(),
 
        };
 

	
 
        // Maybe do typechecking in the future?
 
        let new_frame = Frame::new(heap, def, monomorph_idx);
 
        let max_stack_size = new_frame.max_stack_size;
 
        prompt.frames.push(new_frame);
 
        args.into_store(&mut prompt.store);
 
        prompt.store.reserve_stack(max_stack_size);
 

	
 
        prompt
 
    }
 

	
 
    /// Big 'ol function right here. Didn't want to break it up unnecessarily.
 
    /// It consists of, in sequence: executing any expressions that should be
 
    /// executed before the next statement can be evaluated, then a section that
 
    /// performs debug printing, and finally a section that takes the next
 
    /// statement and executes it. If the statement requires any expressions to
 
    /// be evaluated, then they will be added such that the next time `step` is
 
    /// called, all of these expressions are indeed evaluated.
 
    pub(crate) fn step(&mut self, types: &TypeTable, heap: &Heap, modules: &[Module], ctx: &mut impl RunContext) -> EvalResult {
 
        // Helper function to transfer multiple values from the expression value
 
        // array into a heap region (e.g. constructing arrays or structs).
 
        fn transfer_expression_values_front_into_heap(cur_frame: &mut Frame, store: &mut Store, num_values: usize) -> HeapPos {
 
            let heap_pos = store.alloc_heap();
 

	
 
            // Do the transformation first (because Rust...)
 
            for val_idx in 0..num_values {
 
                cur_frame.expr_values[val_idx] = store.read_take_ownership(cur_frame.expr_values[val_idx].clone());
 
            }
 

	
 
            // And now transfer to the heap region
 
            let values = &mut store.heap_regions[heap_pos as usize].values;
 
            debug_assert!(values.is_empty());
 
            values.reserve(num_values);
 
            for _ in 0..num_values {
 
                values.push(cur_frame.expr_values.pop_front().unwrap());
 
            }
 

	
 
            heap_pos
 
        }
 

	
 
        // Helper function to make sure that an index into an aray is valid.
 
        fn array_inclusive_index_is_invalid(store: &Store, array_heap_pos: u32, idx: i64) -> bool {
 
            let array_len = store.heap_regions[array_heap_pos as usize].values.len();
 
            return idx < 0 || idx >= array_len as i64;
 
        }
 

	
 
        fn array_exclusive_index_is_invalid(store: &Store, array_heap_pos: u32, idx: i64) -> bool {
 
            let array_len = store.heap_regions[array_heap_pos as usize].values.len();
 
            return idx < 0 || idx > array_len as i64;
 
        }
 

	
 
        fn construct_array_error(prompt: &Prompt, modules: &[Module], heap: &Heap, expr_id: ExpressionId, heap_pos: u32, idx: i64) -> EvalError {
 
            let array_len = prompt.store.heap_regions[heap_pos as usize].values.len();
 
            return EvalError::new_error_at_expr(
 
                prompt, modules, heap, expr_id,
 
                format!("index {} is out of bounds: array length is {}", idx, array_len)
 
            )
 
        }
 

	
 
        // Checking if we're at the end of execution
 
        let cur_frame = self.frames.last_mut().unwrap();
 
        if cur_frame.position.is_invalid() {
 
            if heap[cur_frame.definition].is_function() {
 
                todo!("End of function without return, return an evaluation error");
 
            }
 
            return Ok(EvalContinuation::ComponentTerminated);
 
        }
 

	
 
        debug_log!("Taking step in '{}'", heap[cur_frame.definition].identifier().value.as_str());
 

	
 
        // Execute all pending expressions
 
        while !cur_frame.expr_stack.is_empty() {
 
            let next = cur_frame.expr_stack.pop_back().unwrap();
 
            debug_log!("Expr stack: {:?}", next);
 
            match next {
 
                ExprInstruction::PushValToFront => {
 
                    cur_frame.expr_values.rotate_right(1);
 
                },
 
                ExprInstruction::EvalExpr(expr_id) => {
 
                    let expr = &heap[expr_id];
 
                    match expr {
 
                        Expression::Assignment(expr) => {
 
                            let to = cur_frame.expr_values.pop_back().unwrap().as_ref();
 
                            let rhs = cur_frame.expr_values.pop_back().unwrap();
 

	
 
                            // Note: although not pretty, the assignment operator takes ownership
 
                            // of the right-hand side value when possible. So we do not drop the
 
                            // rhs's optionally owned heap data.
 
                            let rhs = self.store.read_take_ownership(rhs);
 
                            apply_assignment_operator(&mut self.store, to, expr.operation, rhs);
 
                        },
 
                        Expression::Binding(_expr) => {
 
                            let bind_to = cur_frame.expr_values.pop_back().unwrap();
 
                            let bind_from = cur_frame.expr_values.pop_back().unwrap();
 
                            let bind_to_heap_pos = bind_to.get_heap_pos();
 
                            let bind_from_heap_pos = bind_from.get_heap_pos();
 

	
 
                            let result = apply_binding_operator(&mut self.store, bind_to, bind_from);
 
                            self.store.drop_value(bind_to_heap_pos);
 
                            self.store.drop_value(bind_from_heap_pos);
 
                            cur_frame.expr_values.push_back(Value::Bool(result));
 
                        },
 
                        Expression::Conditional(expr) => {
 
                            // Evaluate testing expression, then extend the
 
                            // expression stack with the appropriate expression
 
                            let test_result = cur_frame.expr_values.pop_back().unwrap().as_bool();
 
                            if test_result {
 
                                cur_frame.serialize_expression(heap, expr.true_expression);
 
                            } else {
 
                                cur_frame.serialize_expression(heap, expr.false_expression);
 
                            }
 
                        },
 
                        Expression::Binary(expr) => {
 
                            let lhs = cur_frame.expr_values.pop_back().unwrap();
 
                            let rhs = cur_frame.expr_values.pop_back().unwrap();
 
                            let result = apply_binary_operator(&mut self.store, &lhs, expr.operation, &rhs);
 
                            cur_frame.expr_values.push_back(result);
 
                            self.store.drop_value(lhs.get_heap_pos());
 
                            self.store.drop_value(rhs.get_heap_pos());
 
                        },
 
                        Expression::Unary(expr) => {
 
                            let val = cur_frame.expr_values.pop_back().unwrap();
 
                            let result = apply_unary_operator(&mut self.store, expr.operation, &val);
 
                            cur_frame.expr_values.push_back(result);
 
                            self.store.drop_value(val.get_heap_pos());
 
                        },
 
                        Expression::Indexing(_expr) => {
 
                            // Evaluate index. Never heap allocated so we do
 
                            // not have to drop it.
 
                            let index = cur_frame.expr_values.pop_back().unwrap();
 
                            let index = self.store.maybe_read_ref(&index);
 

	
 
                            debug_assert!(index.is_integer());
 
                            let index = if index.is_signed_integer() {
 
                                index.as_signed_integer() as i64
 
                            } else {
 
                                index.as_unsigned_integer() as i64
 
                            };
 

	
 
                            let subject = cur_frame.expr_values.pop_back().unwrap();
 

	
 
                            let (deallocate_heap_pos, value_to_push) = match subject {
 
                                Value::Ref(value_ref) => {
 
                                    // Our expression stack value is a reference to something that
 
                                    // exists in the normal stack/heap. We don't want to deallocate
 
                                    // this thing. Rather we want to return a reference to it.
 
                                    let subject = self.store.read_ref(value_ref);
 
                                    let subject_heap_pos = match subject {
 
                                        Value::String(v) => *v,
 
                                        Value::Array(v) => *v,
 
                                        Value::Message(v) => *v,
 
                                        _ => unreachable!(),
 
                                    };
 

	
 
                                    if array_inclusive_index_is_invalid(&self.store, subject_heap_pos, index) {
 
                                        return Err(construct_array_error(self, modules, heap, expr_id, subject_heap_pos, index));
 
                                    }
 

	
 
                                    (None, Value::Ref(ValueId::Heap(subject_heap_pos, index as u32)))
 
                                },
 
                                _ => {
 
                                    // Our value lives on the expression stack, hence we need to
 
                                    // clone whatever we're referring to. Then drop the subject.
 
                                    let subject_heap_pos = match &subject {
 
                                        Value::String(v) => *v,
 
                                        Value::Array(v) => *v,
 
                                        Value::Message(v) => *v,
 
                                        _ => unreachable!(),
 
                                    };
 

	
 
                                    if array_inclusive_index_is_invalid(&self.store, subject_heap_pos, index) {
 
                                        return Err(construct_array_error(self, modules, heap, expr_id, subject_heap_pos, index));
 
                                    }
 

	
 
                                    let subject_indexed = Value::Ref(ValueId::Heap(subject_heap_pos, index as u32));
 
                                    (Some(subject_heap_pos), self.store.clone_value(subject_indexed))
 
                                },
 
                            };
 

	
 
                            cur_frame.expr_values.push_back(value_to_push);
 
                            self.store.drop_value(deallocate_heap_pos);
 
                        },
 
                        Expression::Slicing(expr) => {
 
                            // Evaluate indices
 
                            let from_index = cur_frame.expr_values.pop_back().unwrap();
 
                            let from_index = self.store.maybe_read_ref(&from_index);
 
                            let to_index = cur_frame.expr_values.pop_back().unwrap();
 
                            let to_index = self.store.maybe_read_ref(&to_index);
 

	
 
                            debug_assert!(from_index.is_integer() && to_index.is_integer());
 
                            let from_index = if from_index.is_signed_integer() {
 
                                from_index.as_signed_integer()
 
                            } else {
 
                                from_index.as_unsigned_integer() as i64
 
                            };
 
                            let to_index = if to_index.is_signed_integer() {
 
                                to_index.as_signed_integer()
 
                            } else {
 
                                to_index.as_unsigned_integer() as i64
 
                            };
 

	
 
                            // Dereference subject if needed
 
                            let subject = cur_frame.expr_values.pop_back().unwrap();
 
                            let deref_subject = self.store.maybe_read_ref(&subject);
 

	
 
                            // Slicing needs to produce a copy anyway (with the
 
                            // current evaluator implementation)
 
                            enum ValueKind{ Array, String, Message }
 
                            let (value_kind, array_heap_pos) = match deref_subject {
 
                                Value::Array(v) => (ValueKind::Array, *v),
 
                                Value::String(v) => (ValueKind::String, *v),
 
                                Value::Message(v) => (ValueKind::Message, *v),
 
                                _ => unreachable!()
 
                            };
 

	
 
                            if array_inclusive_index_is_invalid(&self.store, array_heap_pos, from_index) {
 
                                return Err(construct_array_error(self, modules, heap, expr.from_index, array_heap_pos, from_index));
 
                            }
 
                            if array_exclusive_index_is_invalid(&self.store, array_heap_pos, to_index) {
 
                                return Err(construct_array_error(self, modules, heap, expr.to_index, array_heap_pos, to_index));
 
                            }
 

	
 
                            // Again: would love to push directly, but rust...
 
                            let new_heap_pos = self.store.alloc_heap();
 
                            debug_assert!(self.store.heap_regions[new_heap_pos as usize].values.is_empty());
 
                            if to_index > from_index {
 
                                let from_index = from_index as usize;
 
                                let to_index = to_index as usize;
 
                                let mut values = Vec::with_capacity(to_index - from_index);
 
                                for idx in from_index..to_index {
 
                                    let value = self.store.heap_regions[array_heap_pos as usize].values[idx].clone();
 
                                    values.push(self.store.clone_value(value));
 
                                }
 

	
 
                                self.store.heap_regions[new_heap_pos as usize].values = values;
 

	
 
                            } // else: empty range
 

	
 
                            cur_frame.expr_values.push_back(match value_kind {
 
                                ValueKind::Array => Value::Array(new_heap_pos),
 
                                ValueKind::String => Value::String(new_heap_pos),
 
                                ValueKind::Message => Value::Message(new_heap_pos),
 
                            });
 

	
 
                            // Dropping the original subject, because we don't
 
                            // want to drop something on the stack
 
                            self.store.drop_value(subject.get_heap_pos());
 
                        },
 
                        Expression::Select(expr) => {
 
                            let subject= cur_frame.expr_values.pop_back().unwrap();
 
                            let mono_data = types.get_procedure_monomorph(cur_frame.monomorph_idx);
 
                            let field_idx = mono_data.expr_data[expr.unique_id_in_definition as usize].field_or_monomorph_idx as u32;
 

	
 
                            // Note: same as above: clone if value lives on expr stack, simply
 
                            // refer to it if it already lives on the stack/heap.
 
                            let (deallocate_heap_pos, value_to_push) = match subject {
 
                                Value::Ref(value_ref) => {
 
                                    let subject = self.store.read_ref(value_ref);
 
                                    let subject_heap_pos = subject.as_struct();
 
                                    let subject_heap_pos = match expr.kind {
 
                                        SelectKind::StructField(_) => subject.as_struct(),
 
                                        SelectKind::TupleMember(_) => subject.as_tuple(),
 
                                    };
 

	
 
                                    (None, Value::Ref(ValueId::Heap(subject_heap_pos, field_idx)))
 
                                },
 
                                _ => {
 
                                    let subject_heap_pos = subject.as_struct();
 
                                    let subject_heap_pos = match expr.kind {
 
                                        SelectKind::StructField(_) => subject.as_struct(),
 
                                        SelectKind::TupleMember(_) => subject.as_tuple(),
 
                                    };
 
                                    let subject_indexed = Value::Ref(ValueId::Heap(subject_heap_pos, field_idx));
 
                                    (Some(subject_heap_pos), self.store.clone_value(subject_indexed))
 
                                },
 
                            };
 

	
 
                            cur_frame.expr_values.push_back(value_to_push);
 
                            self.store.drop_value(deallocate_heap_pos);
 
                        },
 
                        Expression::Literal(expr) => {
 
                            let value = match &expr.value {
 
                                Literal::Null => Value::Null,
 
                                Literal::True => Value::Bool(true),
 
                                Literal::False => Value::Bool(false),
 
                                Literal::Character(lit_value) => Value::Char(*lit_value),
 
                                Literal::String(lit_value) => {
 
                                    let heap_pos = self.store.alloc_heap();
 
                                    let values = &mut self.store.heap_regions[heap_pos as usize].values;
 
                                    let value = lit_value.as_str();
 
                                    debug_assert!(values.is_empty());
 
                                    values.reserve(value.len());
 
                                    for character in value.as_bytes() {
 
                                        debug_assert!(character.is_ascii());
 
                                        values.push(Value::Char(*character as char));
 
                                    }
 
                                    Value::String(heap_pos)
 
                                }
 
                                Literal::Integer(lit_value) => {
 
                                    use ConcreteTypePart as CTP;
 
                                    let def_types = types.get_procedure_monomorph(cur_frame.monomorph_idx);
 
                                    let concrete_type = &def_types.expr_data[expr.unique_id_in_definition as usize].expr_type;
 

	
 
                                    debug_assert_eq!(concrete_type.parts.len(), 1);
 
                                    match concrete_type.parts[0] {
 
                                        CTP::UInt8  => Value::UInt8(lit_value.unsigned_value as u8),
 
                                        CTP::UInt16 => Value::UInt16(lit_value.unsigned_value as u16),
 
                                        CTP::UInt32 => Value::UInt32(lit_value.unsigned_value as u32),
 
                                        CTP::UInt64 => Value::UInt64(lit_value.unsigned_value as u64),
 
                                        CTP::SInt8  => Value::SInt8(lit_value.unsigned_value as i8),
 
                                        CTP::SInt16 => Value::SInt16(lit_value.unsigned_value as i16),
 
                                        CTP::SInt32 => Value::SInt32(lit_value.unsigned_value as i32),
 
                                        CTP::SInt64 => Value::SInt64(lit_value.unsigned_value as i64),
 
                                        _ => unreachable!("got concrete type {:?} for integer literal at expr {:?}", concrete_type, expr_id),
 
                                    }
 
                                }
 
                                Literal::Struct(lit_value) => {
 
                                    let heap_pos = transfer_expression_values_front_into_heap(
 
                                        cur_frame, &mut self.store, lit_value.fields.len()
 
                                    );
 
                                    Value::Struct(heap_pos)
 
                                }
 
                                Literal::Enum(lit_value) => {
 
                                    Value::Enum(lit_value.variant_idx as i64)
 
                                }
 
                                Literal::Union(lit_value) => {
 
                                    let heap_pos = transfer_expression_values_front_into_heap(
 
                                        cur_frame, &mut self.store, lit_value.values.len()
 
                                    );
 
                                    Value::Union(lit_value.variant_idx as i64, heap_pos)
 
                                }
 
                                Literal::Array(lit_value) => {
 
                                    let heap_pos = transfer_expression_values_front_into_heap(
 
                                        cur_frame, &mut self.store, lit_value.len()
 
                                    );
 
                                    Value::Array(heap_pos)
 
                                }
 
                                Literal::Tuple(lit_value) => {
 
                                    let heap_pos = transfer_expression_values_front_into_heap(
 
                                        cur_frame, &mut self.store, lit_value.len()
 
                                    );
 
                                    Value::Tuple(heap_pos)
 
                                }
 
                            };
 

	
 
                            cur_frame.expr_values.push_back(value);
 
                        },
 
                        Expression::Cast(expr) => {
 
                            let mono_data = types.get_procedure_monomorph(cur_frame.monomorph_idx);
 
                            let output_type = &mono_data.expr_data[expr.unique_id_in_definition as usize].expr_type;
 

	
 
                            // Typechecking reduced this to two cases: either we
 
                            // have casting noop (same types), or we're casting
 
                            // between integer/bool/char types.
 
                            let subject = cur_frame.expr_values.pop_back().unwrap();
 
                            match apply_casting(&mut self.store, output_type, &subject) {
 
                                Ok(value) => cur_frame.expr_values.push_back(value),
 
                                Err(msg) => {
 
                                    return Err(EvalError::new_error_at_expr(self, modules, heap, expr.this.upcast(), msg));
 
                                }
 
                            }
 

	
 
                            self.store.drop_value(subject.get_heap_pos());
 
                        }
 
                        Expression::Call(expr) => {
 
                            // If we're dealing with a builtin we don't do any
 
                            // fancy shenanigans at all, just push the result.
 
                            match expr.method {
 
                                Method::Get => {
 
                                    let value = cur_frame.expr_values.pop_front().unwrap();
 
                                    let value = self.store.maybe_read_ref(&value).clone();
 

	
 
                                    let port_id = if let Value::Input(port_id) = value {
 
                                        port_id
 
                                    } else {
 
                                        unreachable!("executor calling 'get' on value {:?}", value)
 
                                    };
 

	
 
                                    match ctx.performed_get(port_id) {
 
                                        Some(result) => {
 
                                            // We have the result. Merge the `ValueGroup` with the
 
                                            // stack/heap storage.
 
                                            debug_assert_eq!(result.values.len(), 1);
 
                                            result.into_stack(&mut cur_frame.expr_values, &mut self.store);
 
                                        },
 
                                        None => {
 
                                            // Don't have the result yet, prepare the expression to
 
                                            // get run again after we've received a message.
 
                                            cur_frame.expr_values.push_front(value.clone());
 
                                            cur_frame.expr_stack.push_back(ExprInstruction::EvalExpr(expr_id));
 
                                            return Ok(EvalContinuation::BlockGet(port_id));
 
                                        }
 
                                    }
 
                                },
 
                                Method::Put => {
 
                                    let port_value = cur_frame.expr_values.pop_front().unwrap();
 
                                    let deref_port_value = self.store.maybe_read_ref(&port_value).clone();
 

	
 
                                    let port_id = if let Value::Output(port_id) = deref_port_value {
 
                                        port_id
 
                                    } else {
 
                                        unreachable!("executor calling 'put' on value {:?}", deref_port_value)
 
                                    };
 

	
 
                                    let msg_value = cur_frame.expr_values.pop_front().unwrap();
 
                                    let deref_msg_value = self.store.maybe_read_ref(&msg_value).clone();
 

	
 
                                    if ctx.performed_put(port_id) {
 
                                        // We're fine, deallocate in case the expression value stack
 
                                        // held an owned value
 
                                        self.store.drop_value(msg_value.get_heap_pos());
 
                                    } else {
 
                                        // Prepare to execute again
 
                                        cur_frame.expr_values.push_front(msg_value);
 
                                        cur_frame.expr_values.push_front(port_value);
 
                                        cur_frame.expr_stack.push_back(ExprInstruction::EvalExpr(expr_id));
 
                                        let value_group = ValueGroup::from_store(&self.store, &[deref_msg_value]);
 
                                        return Ok(EvalContinuation::Put(port_id, value_group));
 
                                    }
 
                                },
 
                                Method::Fires => {
 
                                    let port_value = cur_frame.expr_values.pop_front().unwrap();
 
                                    let port_value_deref = self.store.maybe_read_ref(&port_value).clone();
 

	
 
                                    let port_id = match port_value_deref {
 
                                        Value::Input(port_id) => port_id,
 
                                        Value::Output(port_id) => port_id,
 
                                        _ => unreachable!("executor calling 'fires' on value {:?}", port_value_deref),
 
                                    };
 

	
 
                                    match ctx.fires(port_id) {
 
                                        None => {
 
                                            cur_frame.expr_values.push_front(port_value);
 
                                            cur_frame.expr_stack.push_back(ExprInstruction::EvalExpr(expr_id));
 
                                            return Ok(EvalContinuation::BlockFires(port_id));
 
                                        },
 
                                        Some(value) => {
 
                                            cur_frame.expr_values.push_back(value);
 
                                        }
 
                                    }
 
                                },
 
                                Method::Create => {
 
                                    let length_value = cur_frame.expr_values.pop_front().unwrap();
 
                                    let length_value = self.store.maybe_read_ref(&length_value);
 
                                    let length = if length_value.is_signed_integer() {
 
                                        let length_value = length_value.as_signed_integer();
 
                                        if length_value < 0 {
 
                                            return Err(EvalError::new_error_at_expr(
 
                                                self, modules, heap, expr_id,
 
                                                format!("got length '{}', can only create a message with a non-negative length", length_value)
 
                                            ));
 
                                        }
 

	
 
                                        length_value as u64
 
                                    } else {
 
                                        debug_assert!(length_value.is_unsigned_integer());
 
                                        length_value.as_unsigned_integer()
 
                                    };
 

	
 
                                    let heap_pos = self.store.alloc_heap();
 
                                    let values = &mut self.store.heap_regions[heap_pos as usize].values;
 
                                    debug_assert!(values.is_empty());
 
                                    values.resize(length as usize, Value::UInt8(0));
 
                                    cur_frame.expr_values.push_back(Value::Message(heap_pos));
 
                                },
 
                                Method::Length => {
 
                                    let value = cur_frame.expr_values.pop_front().unwrap();
 
                                    let value_heap_pos = value.get_heap_pos();
 
                                    let value = self.store.maybe_read_ref(&value);
 

	
 
                                    let heap_pos = match value {
 
                                        Value::Array(pos) => *pos,
 
                                        Value::String(pos) => *pos,
 
                                        _ => unreachable!("length(...) on {:?}", value),
 
                                    };
 

	
 
                                    let len = self.store.heap_regions[heap_pos as usize].values.len();
 

	
 
                                    // TODO: @PtrInt
 
                                    cur_frame.expr_values.push_back(Value::UInt32(len as u32));
 
                                    self.store.drop_value(value_heap_pos);
 
                                },
 
                                Method::Assert => {
 
                                    let value = cur_frame.expr_values.pop_front().unwrap();
 
                                    let value = self.store.maybe_read_ref(&value).clone();
 
                                    if !value.as_bool() {
 
                                        return Ok(EvalContinuation::BranchInconsistent)
 
                                    }
 
                                },
 
                                Method::Print => {
 
                                    // Convert the runtime-variant of a string
 
                                    // into an actual string.
 
                                    let value = cur_frame.expr_values.pop_front().unwrap();
 
                                    let value_heap_pos = value.as_string();
 
                                    let elements = &self.store.heap_regions[value_heap_pos as usize].values;
 

	
 
                                    let mut message = String::with_capacity(elements.len());
 
                                    for element in elements {
 
                                        message.push(element.as_char());
 
                                    }
 

	
 
                                    // Drop the heap-allocated value from the
 
                                    // store
 
                                    self.store.drop_heap_pos(value_heap_pos);
 
                                    println!("{}", message);
 
                                },
 
                                Method::UserComponent => {
 
                                    // This is actually handled by the evaluation
 
                                    // of the statement.
 
                                    debug_assert_eq!(heap[expr.definition].parameters().len(), cur_frame.expr_values.len());
 
                                    debug_assert_eq!(heap[cur_frame.position].as_new().expression, expr.this)
 
                                },
 
                                Method::UserFunction => {
 
                                    // Push a new frame. Note that all expressions have
 
                                    // been pushed to the front, so they're in the order
 
                                    // of the definition.
 
                                    let num_args = expr.arguments.len();
 

	
 
                                    // Determine stack boundaries
 
                                    let cur_stack_boundary = self.store.cur_stack_boundary;
 
                                    let new_stack_boundary = self.store.stack.len();
 

	
 
                                    // Push new boundary and function arguments for new frame
 
                                    self.store.stack.push(Value::PrevStackBoundary(cur_stack_boundary as isize));
 
                                    for _ in 0..num_args {
 
                                        let argument = self.store.read_take_ownership(cur_frame.expr_values.pop_front().unwrap());
 
                                        self.store.stack.push(argument);
 
                                    }
 

	
 
                                    // Determine the monomorph index of the function we're calling
 
                                    let mono_data = types.get_procedure_monomorph(cur_frame.monomorph_idx);
 
                                    let call_data = &mono_data.expr_data[expr.unique_id_in_definition as usize];
 

	
 
                                    // Push the new frame and reserve its stack size
 
                                    let new_frame = Frame::new(heap, expr.definition, call_data.field_or_monomorph_idx);
 
                                    let new_stack_size = new_frame.max_stack_size;
 
                                    self.frames.push(new_frame);
 
                                    self.store.cur_stack_boundary = new_stack_boundary;
 
                                    self.store.reserve_stack(new_stack_size);
 

	
 
                                    // To simplify the logic a little bit we will now
 
                                    // return and ask our caller to call us again
 
                                    return Ok(EvalContinuation::Stepping);
 
                                },
 
                            }
 
                        },
 
                        Expression::Variable(expr) => {
 
                            let variable = &heap[expr.declaration.unwrap()];
 
                            let ref_value = if expr.used_as_binding_target {
 
                                Value::Binding(variable.unique_id_in_scope as StackPos)
 
                            } else {
 
                                Value::Ref(ValueId::Stack(variable.unique_id_in_scope as StackPos))
 
                            };
 
                            cur_frame.expr_values.push_back(ref_value);
 
                        }
 
                    }
 
                }
 
            }
 
        }
 

	
 
        debug_log!("Frame [{:?}] at {:?}", cur_frame.definition, cur_frame.position);
 
        if debug_enabled!() {
 
            debug_log!("Expression value stack (size = {}):", cur_frame.expr_values.len());
 
            for (_stack_idx, _stack_val) in cur_frame.expr_values.iter().enumerate() {
 
                debug_log!("  [{:03}] {:?}", _stack_idx, _stack_val);
 
            }
 

	
 
            debug_log!("Stack (size = {}):", self.store.stack.len());
 
            for (_stack_idx, _stack_val) in self.store.stack.iter().enumerate() {
 
                debug_log!("  [{:03}] {:?}", _stack_idx, _stack_val);
 
            }
 

	
 
            debug_log!("Heap:");
 
            for (_heap_idx, _heap_region) in self.store.heap_regions.iter().enumerate() {
 
                let _is_free = self.store.free_regions.iter().any(|idx| *idx as usize == _heap_idx);
 
                debug_log!("  [{:03}] in_use: {}, len: {}, vals: {:?}", _heap_idx, !_is_free, _heap_region.values.len(), &_heap_region.values);
 
            }
 
        }
 
        // No (more) expressions to evaluate. So evaluate statement (that may
 
        // depend on the result on the last evaluated expression(s))
 
        let stmt = &heap[cur_frame.position];
 
        let return_value = match stmt {
 
            Statement::Block(stmt) => {
 
                debug_assert!(stmt.statements.is_empty() || stmt.next == stmt.statements[0]);
 
                cur_frame.position = stmt.next;
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::EndBlock(stmt) => {
 
                let block = &heap[stmt.start_block];
 
                self.store.clear_stack(block.first_unique_id_in_scope as usize);
 
                cur_frame.position = stmt.next;
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::Local(stmt) => {
 
                match stmt {
 
                    LocalStatement::Memory(stmt) => {
 
                        let variable = &heap[stmt.variable];
 
                        self.store.write(ValueId::Stack(variable.unique_id_in_scope as u32), Value::Unassigned);
 

	
 
                        cur_frame.position = stmt.next;
 
                        Ok(EvalContinuation::Stepping)
 
                    },
 
                    LocalStatement::Channel(stmt) => {
 
                        // Need to create a new channel by requesting it from
 
                        // the runtime.
 
                        match ctx.created_channel() {
 
                            None => {
 
                                // No channel is pending. So request one
 
                                Ok(EvalContinuation::NewChannel)
 
                            },
 
                            Some((put_port, get_port)) => {
 
                                self.store.write(ValueId::Stack(heap[stmt.from].unique_id_in_scope as u32), put_port);
 
                                self.store.write(ValueId::Stack(heap[stmt.to].unique_id_in_scope as u32), get_port);
 
                                cur_frame.position = stmt.next;
 
                                Ok(EvalContinuation::Stepping)
 
                            }
 
                        }
 
                    }
 
                }
 
            },
 
            Statement::Labeled(stmt) => {
 
                cur_frame.position = stmt.body;
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::If(stmt) => {
 
                debug_assert_eq!(cur_frame.expr_values.len(), 1, "expected one expr value for if statement");
 
                let test_value = cur_frame.expr_values.pop_back().unwrap();
 
                let test_value = self.store.maybe_read_ref(&test_value).as_bool();
 
                if test_value {
 
                    cur_frame.position = stmt.true_body.upcast();
 
                } else if let Some(false_body) = stmt.false_body {
 
                    cur_frame.position = false_body.upcast();
 
                } else {
 
                    // Not true, and no false body
 
                    cur_frame.position = stmt.end_if.upcast();
 
                }
 

	
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::EndIf(stmt) => {
 
                cur_frame.position = stmt.next;
 
                Ok(EvalContinuation::Stepping)
 
            },
 
            Statement::While(stmt) => {
 
                debug_assert_eq!(cur_frame.expr_values.len(), 1, "expected one expr value for while statement");
 
                let test_value = cur_frame.expr_values.pop_back().unwrap();
 
                let test_value = self.store.maybe_read_ref(&test_value).as_bool();
 
                if test_value {
 
                    cur_frame.position = stmt.body.upcast();
 
                } else {
 
                    cur_frame.position = stmt.end_while.upcast();
 
                }
 

	
 
                Ok(EvalContinuation::Stepping)
src/protocol/eval/store.rs
Show inline comments
 

	
 
use std::collections::VecDeque;
 

	
 
use super::value::{Value, ValueId, HeapPos};
 

	
 
#[derive(Debug, Clone)]
 
pub(crate) struct HeapAllocation {
 
    pub values: Vec<Value>,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub(crate) struct Store {
 
    // The stack where variables/parameters are stored. Note that this is a
 
    // non-shrinking stack. So it may be filled with garbage.
 
    pub(crate) stack: Vec<Value>,
 
    // Represents the place in the stack where we find the `PrevStackBoundary`
 
    // value containing the previous stack boundary. This is so we can pop from
 
    // the stack after function calls.
 
    pub(crate) cur_stack_boundary: usize,
 
    // A rather ridiculous simulated heap, but this allows us to "allocate"
 
    // things that occupy more then one stack slot.
 
    pub(crate) heap_regions: Vec<HeapAllocation>,
 
    pub(crate) free_regions: VecDeque<HeapPos>,
 
}
 

	
 
impl Store {
 
    pub(crate) fn new() -> Self {
 
        let mut store = Self{
 
            stack: Vec::with_capacity(64),
 
            cur_stack_boundary: 0,
 
            heap_regions: Vec::new(),
 
            free_regions: VecDeque::new(),
 
        };
 

	
 
        store.stack.push(Value::PrevStackBoundary(-1));
 
        store
 
    }
 

	
 
    /// Resizes(!) the stack to fit the required number of values. Any
 
    /// unallocated slots are initialized to `Unassigned`. The specified stack
 
    /// index is exclusive.
 
    pub(crate) fn reserve_stack(&mut self, unique_stack_idx: u32) {
 
        let new_size = self.cur_stack_boundary + unique_stack_idx as usize + 1;
 
        if new_size > self.stack.len() {
 
            self.stack.resize(new_size, Value::Unassigned);
 
        }
 
    }
 

	
 
    /// Clears values on the stack and removes their heap allocations when
 
    /// applicable. The specified index itself will also be cleared (so if you
 
    /// specify 0 all values in the frame will be destroyed)
 
    pub(crate) fn clear_stack(&mut self, unique_stack_idx: usize) {
 
        let new_size = self.cur_stack_boundary + unique_stack_idx + 1;
 
        for idx in new_size..self.stack.len() {
 
            let heap_pos = self.stack[idx].get_heap_pos();
 
            self.drop_value(heap_pos);
 

	
 
            // TODO: @remove, somewhat temporarily not clearing pure stack
 
            //  values for testing purposes.
 
            if heap_pos.is_some() {
 
                self.stack[idx] = Value::Unassigned;
 
            }
 
        }
 
    }
 

	
 
    /// Reads a value and takes ownership. This is different from a move because
 
    /// the value might indirectly reference stack/heap values. For these kinds
 
    /// values we will actually return a cloned value.
 
    pub(crate) fn read_take_ownership(&mut self, value: Value) -> Value {
 
        match value {
 
            Value::Ref(ValueId::Stack(pos)) => {
 
                let abs_pos = self.cur_stack_boundary + 1 + pos as usize;
 
                return self.clone_value(self.stack[abs_pos].clone());
 
            },
 
            Value::Ref(ValueId::Heap(heap_pos, value_idx)) => {
 
                let heap_pos = heap_pos as usize;
 
                let value_idx = value_idx as usize;
 
                return self.clone_value(self.heap_regions[heap_pos].values[value_idx].clone());
 
            },
 
            _ => value
 
        }
 
    }
 

	
 
    /// Reads a value from a specific address. The value is always copied, hence
 
    /// if the value ends up not being written, one should call `drop_value` on
 
    /// it.
 
    pub(crate) fn read_copy(&mut self, address: ValueId) -> Value {
 
        match address {
 
            ValueId::Stack(pos) => {
 
                let cur_pos = self.cur_stack_boundary + 1 + pos as usize;
 
                return self.clone_value(self.stack[cur_pos].clone());
 
            },
 
            ValueId::Heap(heap_pos, region_idx) => {
 
                return self.clone_value(self.heap_regions[heap_pos as usize].values[region_idx as usize].clone())
 
            }
 
        }
 
    }
 

	
 
    /// Potentially reads a reference value. The supplied `Value` might not
 
    /// actually live in the store's stack or heap, but live on the expression
 
    /// stack. Generally speaking you only want to call this if the value comes
 
    /// from the expression stack due to borrowing issues.
 
    pub(crate) fn maybe_read_ref<'a>(&'a self, value: &'a Value) -> &'a Value {
 
        match value {
 
            Value::Ref(value_id) => self.read_ref(*value_id),
 
            _ => value,
 
        }
 
    }
 

	
 
    /// Returns an immutable reference to the value pointed to by an address
 
    pub(crate) fn read_ref(&self, address: ValueId) -> &Value {
 
        match address {
 
            ValueId::Stack(pos) => {
 
                let cur_pos = self.cur_stack_boundary + 1 + pos as usize;
 
                return &self.stack[cur_pos];
 
            },
 
            ValueId::Heap(heap_pos, region_idx) => {
 
                return &self.heap_regions[heap_pos as usize].values[region_idx as usize];
 
            }
 
        }
 
    }
 

	
 
    /// Returns a mutable reference to the value pointed to by an address
 
    pub(crate) fn read_mut_ref(&mut self, address: ValueId) -> &mut Value {
 
        match address {
 
            ValueId::Stack(pos) => {
 
                let cur_pos = self.cur_stack_boundary + 1 + pos as usize;
 
                return &mut self.stack[cur_pos];
 
            },
 
            ValueId::Heap(heap_pos, region_idx) => {
 
                return &mut self.heap_regions[heap_pos as usize].values[region_idx as usize];
 
            }
 
        }
 
    }
 

	
 
    /// Writes a value
 
    pub(crate) fn write(&mut self, address: ValueId, value: Value) {
 
        match address {
 
            ValueId::Stack(pos) => {
 
                let cur_pos = self.cur_stack_boundary + 1 + pos as usize;
 
                self.drop_value(self.stack[cur_pos].get_heap_pos());
 
                self.stack[cur_pos] = value;
 
            },
 
            ValueId::Heap(heap_pos, region_idx) => {
 
                let heap_pos = heap_pos as usize;
 
                let region_idx = region_idx as usize;
 
                self.drop_value(self.heap_regions[heap_pos].values[region_idx].get_heap_pos());
 
                self.heap_regions[heap_pos].values[region_idx] = value
 
            }
 
        }
 
    }
 

	
 
    /// This thing takes a cloned Value, because of borrowing issues (which is
 
    /// either a direct value, or might contain an index to a heap value), but
 
    /// should be treated by the programmer as a reference (i.e. don't call
 
    /// `drop_value(thing)` after calling `clone_value(thing.clone())`.
 
    pub(crate) fn clone_value(&mut self, value: Value) -> Value {
 
        // Quickly check if the value is not on the heap
 
        let source_heap_pos = value.get_heap_pos();
 
        if source_heap_pos.is_none() {
 
            // We can do a trivial copy, unless we're dealing with a value
 
            // reference
 
            return match value {
 
                Value::Ref(ValueId::Stack(stack_pos)) => {
 
                    let abs_stack_pos = self.cur_stack_boundary + stack_pos as usize + 1;
 
                    self.clone_value(self.stack[abs_stack_pos].clone())
 
                },
 
                Value::Ref(ValueId::Heap(heap_pos, val_idx)) => {
 
                    self.clone_value(self.heap_regions[heap_pos as usize].values[val_idx as usize].clone())
 
                },
 
                _ => value,
 
            };
 
        }
 

	
 
        // Value does live on heap, copy it
 
        let source_heap_pos = source_heap_pos.unwrap() as usize;
 
        let target_heap_pos = self.alloc_heap();
 
        let target_heap_pos_usize = target_heap_pos as usize;
 

	
 
        let num_values = self.heap_regions[source_heap_pos].values.len();
 
        for value_idx in 0..num_values {
 
            let cloned = self.clone_value(self.heap_regions[source_heap_pos].values[value_idx].clone());
 
            self.heap_regions[target_heap_pos_usize].values.push(cloned);
 
        }
 

	
 
        match value {
 
            Value::Message(_) => Value::Message(target_heap_pos),
 
            Value::String(_) => Value::String(target_heap_pos),
 
            Value::Array(_) => Value::Array(target_heap_pos),
 
            Value::Union(tag, _) => Value::Union(tag, target_heap_pos),
 
            Value::Struct(_) => Value::Struct(target_heap_pos),
 
            Value::Tuple(_) => Value::Tuple(target_heap_pos),
 
            _ => unreachable!("performed clone_value on heap, but {:?} is not a heap value", value),
 
        }
 
    }
 

	
 
    pub(crate) fn drop_value(&mut self, value: Option<HeapPos>) {
 
        if let Some(heap_pos) = value {
 
            self.drop_heap_pos(heap_pos);
 
        }
 
    }
 

	
 
    pub(crate) fn drop_heap_pos(&mut self, heap_pos: HeapPos) {
 
        let num_values = self.heap_regions[heap_pos as usize].values.len();
 
        for value_idx in 0..num_values {
 
            if let Some(other_heap_pos) = self.heap_regions[heap_pos as usize].values[value_idx].get_heap_pos() {
 
                self.drop_heap_pos(other_heap_pos);
 
            }
 
        }
 

	
 
        self.heap_regions[heap_pos as usize].values.clear();
 
        self.free_regions.push_back(heap_pos);
 
    }
 

	
 
    pub(crate) fn alloc_heap(&mut self) -> HeapPos {
 
        if self.free_regions.is_empty() {
 
            let idx = self.heap_regions.len() as HeapPos;
 
            self.heap_regions.push(HeapAllocation{ values: Vec::new() });
 
            return idx;
 
        } else {
 
            let idx = self.free_regions.pop_back().unwrap();
 
            return idx;
 
        }
 
    }
 
}
 
\ No newline at end of file
src/protocol/parser/pass_typing.rs
Show inline comments
 
@@ -2920,769 +2920,772 @@ impl PassTyping {
 
                return (false, true);
 
            }
 
        }
 

	
 
        (false, false)
 
    }
 

	
 
    /// Applies a template type constraint: the type associated with the
 
    /// supplied expression will be molded into the provided `template`. But
 
    /// will be considered valid if the template could've been molded into the
 
    /// expression type as well. Hence the template may be fully specified (e.g.
 
    /// a bool) or contain "inference" variables (e.g. an array of T)
 
    fn apply_template_constraint(
 
        &mut self, ctx: &Ctx, expr_id: ExpressionId, template: &[InferenceTypePart]
 
    ) -> Result<bool, ParseError> {
 
        let expr_idx = ctx.heap[expr_id].get_unique_id_in_definition(); // TODO: @Temp
 
        let expr_type = &mut self.expr_types[expr_idx as usize].expr_type;
 
        match InferenceType::infer_subtree_for_single_type(expr_type, 0, template, 0, false) {
 
            SingleInferenceResult::Modified => Ok(true),
 
            SingleInferenceResult::Unmodified => Ok(false),
 
            SingleInferenceResult::Incompatible => Err(
 
                self.construct_template_type_error(ctx, expr_id, template)
 
            )
 
        }
 
    }
 

	
 
    fn apply_template_constraint_to_types(
 
        to_infer: *mut InferenceType, to_infer_start_idx: usize,
 
        template: &[InferenceTypePart], template_start_idx: usize
 
    ) -> Result<bool, ()> {
 
        match InferenceType::infer_subtree_for_single_type(
 
            unsafe{ &mut *to_infer }, to_infer_start_idx,
 
            template, template_start_idx, false
 
        ) {
 
            SingleInferenceResult::Modified => Ok(true),
 
            SingleInferenceResult::Unmodified => Ok(false),
 
            SingleInferenceResult::Incompatible => Err(()),
 
        }
 
    }
 

	
 
    /// Applies a forced constraint: the supplied expression's type MUST be
 
    /// inferred from the template, the other way around is considered invalid.
 
    fn apply_forced_constraint(
 
        &mut self, ctx: &Ctx, expr_id: ExpressionId, template: &[InferenceTypePart]
 
    ) -> Result<bool, ParseError> {
 
        let expr_idx = ctx.heap[expr_id].get_unique_id_in_definition();
 
        let expr_type = &mut self.expr_types[expr_idx as usize].expr_type;
 
        match InferenceType::infer_subtree_for_single_type(expr_type, 0, template, 0, true) {
 
            SingleInferenceResult::Modified => Ok(true),
 
            SingleInferenceResult::Unmodified => Ok(false),
 
            SingleInferenceResult::Incompatible => Err(
 
                self.construct_template_type_error(ctx, expr_id, template)
 
            )
 
        }
 
    }
 

	
 
    /// Applies a type constraint that expects the two provided types to be
 
    /// equal. We attempt to make progress in inferring the types. If the call
 
    /// is successful then the composition of all types are made equal.
 
    /// The "parent" `expr_id` is provided to construct errors.
 
    fn apply_equal2_constraint(
 
        &mut self, ctx: &Ctx, expr_id: ExpressionId,
 
        arg1_id: ExpressionId, arg1_start_idx: usize,
 
        arg2_id: ExpressionId, arg2_start_idx: usize
 
    ) -> Result<(bool, bool), ParseError> {
 
        let arg1_expr_idx = ctx.heap[arg1_id].get_unique_id_in_definition(); // TODO: @Temp
 
        let arg2_expr_idx = ctx.heap[arg2_id].get_unique_id_in_definition();
 
        let arg1_type: *mut _ = &mut self.expr_types[arg1_expr_idx as usize].expr_type;
 
        let arg2_type: *mut _ = &mut self.expr_types[arg2_expr_idx as usize].expr_type;
 

	
 
        let infer_res = unsafe{ InferenceType::infer_subtrees_for_both_types(
 
            arg1_type, arg1_start_idx,
 
            arg2_type, arg2_start_idx
 
        ) };
 
        if infer_res == DualInferenceResult::Incompatible {
 
            return Err(self.construct_arg_type_error(ctx, expr_id, arg1_id, arg2_id));
 
        }
 

	
 
        Ok((infer_res.modified_lhs(), infer_res.modified_rhs()))
 
    }
 

	
 
    /// Applies an equal2 constraint between a signature type (e.g. a function
 
    /// argument or struct field) and an expression whose type should match that
 
    /// expression. If we make progress on the signature, then we try to see if
 
    /// any of the embedded polymorphic types can be progressed.
 
    ///
 
    /// `outer_expr_id` is the main expression we're progressing (e.g. a 
 
    /// function call), while `expr_id` is the embedded expression we're 
 
    /// matching against the signature. `expression_type` and 
 
    /// `expression_start_idx` belong to `expr_id`.
 
    fn apply_equal2_signature_constraint(
 
        ctx: &Ctx, outer_expr_id: ExpressionId, expr_id: Option<ExpressionId>,
 
        polymorph_data: &mut ExtraData, polymorph_progress: &mut HashSet<u32>,
 
        signature_type: *mut InferenceType, signature_start_idx: usize,
 
        expression_type: *mut InferenceType, expression_start_idx: usize
 
    ) -> Result<(bool, bool), ParseError> {
 
        // Safety: all pointers distinct
 

	
 
        // Infer the signature and expression type
 
        let infer_res = unsafe { 
 
            InferenceType::infer_subtrees_for_both_types(
 
                signature_type, signature_start_idx,
 
                expression_type, expression_start_idx
 
            ) 
 
        };
 

	
 
        if infer_res == DualInferenceResult::Incompatible {
 
            // TODO: Check if I still need to use this
 
            let outer_span = ctx.heap[outer_expr_id].full_span();
 
            let (span_name, span) = match expr_id {
 
                Some(expr_id) => ("argument's", ctx.heap[expr_id].full_span()),
 
                None => ("type's", outer_span)
 
            };
 
            let (signature_display_type, expression_display_type) = unsafe { (
 
                (&*signature_type).display_name(&ctx.heap),
 
                (&*expression_type).display_name(&ctx.heap)
 
            ) };
 

	
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module().source, outer_span,
 
                "failed to fully resolve the types of this expression"
 
            ).with_info_at_span(
 
                &ctx.module().source, span, format!(
 
                    "because the {} signature has been resolved to '{}', but the expression has been resolved to '{}'",
 
                    span_name, signature_display_type, expression_display_type
 
                )
 
            ));
 
        }
 

	
 
        // Try to see if we can progress any of the polymorphic variables
 
        let progress_sig = infer_res.modified_lhs();
 
        let progress_expr = infer_res.modified_rhs();
 

	
 
        if progress_sig {
 
            let signature_type = unsafe{&mut *signature_type};
 
            debug_assert!(
 
                signature_type.has_marker,
 
                "made progress on signature type, but it doesn't have a marker"
 
            );
 
            for (poly_idx, poly_section) in signature_type.marker_iter() {
 
                let polymorph_type = &mut polymorph_data.poly_vars[poly_idx as usize];
 
                match Self::apply_template_constraint_to_types(
 
                    polymorph_type, 0, poly_section, 0
 
                ) {
 
                    Ok(true) => { polymorph_progress.insert(poly_idx); },
 
                    Ok(false) => {},
 
                    Err(()) => { return Err(Self::construct_poly_arg_error(ctx, polymorph_data, outer_expr_id))}
 
                }
 
            }
 
        }
 
        Ok((progress_sig, progress_expr))
 
    }
 

	
 
    /// Applies equal2 constraints on the signature type for each of the 
 
    /// polymorphic variables. If the signature type is progressed then we 
 
    /// progress the expression type as well.
 
    ///
 
    /// This function assumes that the polymorphic variables have already been
 
    /// progressed as far as possible by calling 
 
    /// `apply_equal2_signature_constraint`. As such, we expect to not encounter
 
    /// any errors.
 
    ///
 
    /// This function returns true if the expression's type has been progressed
 
    fn apply_equal2_polyvar_constraint(
 
        polymorph_data: &ExtraData, _polymorph_progress: &HashSet<u32>,
 
        signature_type: *mut InferenceType, expr_type: *mut InferenceType
 
    ) -> bool {
 
        // Safety: all pointers should be distinct
 
        //         polymorph_data containers may not be modified
 
        let signature_type = unsafe{&mut *signature_type};
 
        let expr_type = unsafe{&mut *expr_type};
 

	
 
        // Iterate through markers in signature type to try and make progress
 
        // on the polymorphic variable        
 
        let mut seek_idx = 0;
 
        let mut modified_sig = false;
 
        
 
        while let Some((poly_idx, start_idx)) = signature_type.find_marker(seek_idx) {
 
            let end_idx = InferenceType::find_subtree_end_idx(&signature_type.parts, start_idx);
 
            // if polymorph_progress.contains(&poly_idx) {
 
                // Need to match subtrees
 
                let polymorph_type = &polymorph_data.poly_vars[poly_idx as usize];
 
                let modified_at_marker = Self::apply_template_constraint_to_types(
 
                    signature_type, start_idx, 
 
                    &polymorph_type.parts, 0
 
                ).expect("no failure when applying polyvar constraints");
 

	
 
                modified_sig = modified_sig || modified_at_marker;
 
            // }
 

	
 
            seek_idx = end_idx;
 
        }
 

	
 
        // If we made any progress on the signature's type, then we also need to
 
        // apply it to the expression that is supposed to match the signature.
 
        if modified_sig {
 
            match InferenceType::infer_subtree_for_single_type(
 
                expr_type, 0, &signature_type.parts, 0, true
 
            ) {
 
                SingleInferenceResult::Modified => true,
 
                SingleInferenceResult::Unmodified => false,
 
                SingleInferenceResult::Incompatible =>
 
                    unreachable!("encountered failure while reapplying modified signature to expression after polyvar inference")
 
            }
 
        } else {
 
            false
 
        }
 
    }
 

	
 
    /// Applies a type constraint that expects all three provided types to be
 
    /// equal. In case we can make progress in inferring the types then we
 
    /// attempt to do so. If the call is successful then the composition of all
 
    /// types is made equal.
 
    fn apply_equal3_constraint(
 
        &mut self, ctx: &Ctx, expr_id: ExpressionId,
 
        arg1_id: ExpressionId, arg2_id: ExpressionId,
 
        start_idx: usize
 
    ) -> Result<(bool, bool, bool), ParseError> {
 
        // Safety: all points are unique
 
        //         containers may not be modified
 
        let expr_expr_idx = ctx.heap[expr_id].get_unique_id_in_definition(); // TODO: @Temp
 
        let arg1_expr_idx = ctx.heap[arg1_id].get_unique_id_in_definition();
 
        let arg2_expr_idx = ctx.heap[arg2_id].get_unique_id_in_definition();
 

	
 
        let expr_type: *mut _ = &mut self.expr_types[expr_expr_idx as usize].expr_type;
 
        let arg1_type: *mut _ = &mut self.expr_types[arg1_expr_idx as usize].expr_type;
 
        let arg2_type: *mut _ = &mut self.expr_types[arg2_expr_idx as usize].expr_type;
 

	
 
        let expr_res = unsafe{
 
            InferenceType::infer_subtrees_for_both_types(expr_type, start_idx, arg1_type, start_idx)
 
        };
 
        if expr_res == DualInferenceResult::Incompatible {
 
            return Err(self.construct_expr_type_error(ctx, expr_id, arg1_id));
 
        }
 

	
 
        let args_res = unsafe{
 
            InferenceType::infer_subtrees_for_both_types(arg1_type, start_idx, arg2_type, start_idx) };
 
        if args_res == DualInferenceResult::Incompatible {
 
            return Err(self.construct_arg_type_error(ctx, expr_id, arg1_id, arg2_id));
 
        }
 

	
 
        // If all types are compatible, but the second call caused the arg1_type
 
        // to be expanded, then we must also assign this to expr_type.
 
        let mut progress_expr = expr_res.modified_lhs();
 
        let mut progress_arg1 = expr_res.modified_rhs();
 
        let progress_arg2 = args_res.modified_rhs();
 

	
 
        if args_res.modified_lhs() { 
 
            unsafe {
 
                let end_idx = InferenceType::find_subtree_end_idx(&(*arg2_type).parts, start_idx);
 
                let subtree = &((*arg2_type).parts[start_idx..end_idx]);
 
                (*expr_type).replace_subtree(start_idx, subtree);
 
            }
 
            progress_expr = true;
 
            progress_arg1 = true;
 
        }
 

	
 
        Ok((progress_expr, progress_arg1, progress_arg2))
 
    }
 

	
 
    // TODO: @optimize Since we only deal with a single type this might be done
 
    //  a lot more efficiently, methinks (disregarding the allocations here)
 
    fn apply_equal_n_constraint(
 
        &mut self, ctx: &Ctx, expr_id: ExpressionId, args: &[ExpressionId],
 
    ) -> Result<Vec<bool>, ParseError> {
 
        // Early exit
 
        match args.len() {
 
            0 => return Ok(vec!()),         // nothing to progress
 
            1 => return Ok(vec![false]),    // only one type, so nothing to infer
 
            _ => {}
 
        }
 

	
 
        let mut progress = Vec::new(); // TODO: @Performance
 
        progress.resize(args.len(), false);
 

	
 
        // Do pairwise inference, keep track of the last entry we made progress
 
        // on. Once done we need to update everything to the most-inferred type.
 
        let mut arg_iter = args.iter();
 
        let mut last_arg_id = *arg_iter.next().unwrap();
 
        let mut last_lhs_progressed = 0;
 
        let mut lhs_arg_idx = 0;
 

	
 
        while let Some(next_arg_id) = arg_iter.next() {
 
            let last_expr_idx = ctx.heap[last_arg_id].get_unique_id_in_definition(); // TODO: @Temp
 
            let next_expr_idx = ctx.heap[*next_arg_id].get_unique_id_in_definition();
 
            let last_type: *mut _ = &mut self.expr_types[last_expr_idx as usize].expr_type;
 
            let next_type: *mut _ = &mut self.expr_types[next_expr_idx as usize].expr_type;
 

	
 
            let res = unsafe {
 
                InferenceType::infer_subtrees_for_both_types(last_type, 0, next_type, 0)
 
            };
 

	
 
            if res == DualInferenceResult::Incompatible {
 
                return Err(self.construct_arg_type_error(ctx, expr_id, last_arg_id, *next_arg_id));
 
            }
 

	
 
            if res.modified_lhs() {
 
                // We re-inferred something on the left hand side, so everything
 
                // up until now should be re-inferred.
 
                progress[lhs_arg_idx] = true;
 
                last_lhs_progressed = lhs_arg_idx;
 
            }
 
            progress[lhs_arg_idx + 1] = res.modified_rhs();
 

	
 
            last_arg_id = *next_arg_id;
 
            lhs_arg_idx += 1;
 
        }
 

	
 
        // Re-infer everything. Note that we do not need to re-infer the type
 
        // exactly at `last_lhs_progressed`, but only everything up to it.
 
        let last_arg_expr_idx = ctx.heap[*args.last().unwrap()].get_unique_id_in_definition();
 
        let last_type: *mut _ = &mut self.expr_types[last_arg_expr_idx as usize].expr_type;
 
        for arg_idx in 0..last_lhs_progressed {
 
            let other_arg_expr_idx = ctx.heap[args[arg_idx]].get_unique_id_in_definition();
 
            let arg_type: *mut _ = &mut self.expr_types[other_arg_expr_idx as usize].expr_type;
 
            unsafe{
 
                (*arg_type).replace_subtree(0, &(*last_type).parts);
 
            }
 
            progress[arg_idx] = true;
 
        }
 

	
 
        Ok(progress)
 
    }
 

	
 
    /// Determines the `InferenceType` for the expression based on the
 
    /// expression parent. Note that if the parent is another expression, we do
 
    /// not take special action, instead we let parent expressions fix the type
 
    /// of subexpressions before they have a chance to call this function.
 
    fn insert_initial_expr_inference_type(
 
        &mut self, ctx: &mut Ctx, expr_id: ExpressionId
 
    ) -> Result<(), ParseError> {
 
        use ExpressionParent as EP;
 
        use InferenceTypePart as ITP;
 

	
 
        let expr = &ctx.heap[expr_id];
 
        let inference_type = match expr.parent() {
 
            EP::None =>
 
                // Should have been set by linker
 
                unreachable!(),
 
            EP::ExpressionStmt(_) =>
 
                // Determined during type inference
 
                InferenceType::new(false, false, vec![ITP::Unknown]),
 
            EP::Expression(parent_id, idx_in_parent) => {
 
                // If we are the test expression of a conditional expression,
 
                // then we must resolve to a boolean
 
                let is_conditional = if let Expression::Conditional(_) = &ctx.heap[*parent_id] {
 
                    true
 
                } else {
 
                    false
 
                };
 

	
 
                if is_conditional && *idx_in_parent == 0 {
 
                    InferenceType::new(false, true, vec![ITP::Bool])
 
                } else {
 
                    InferenceType::new(false, false, vec![ITP::Unknown])
 
                }
 
            },
 
            EP::If(_) | EP::While(_) =>
 
                // Must be a boolean
 
                InferenceType::new(false, true, vec![ITP::Bool]),
 
            EP::Return(_) =>
 
                // Must match the return type of the function
 
                if let DefinitionType::Function(func_id) = self.definition_type {
 
                    debug_assert_eq!(ctx.heap[func_id].return_types.len(), 1);
 
                    let returned = &ctx.heap[func_id].return_types[0];
 
                    self.determine_inference_type_from_parser_type_elements(&returned.elements, true)
 
                } else {
 
                    // Cannot happen: definition always set upon body traversal
 
                    // and "return" calls in components are illegal.
 
                    unreachable!();
 
                },
 
            EP::New(_) =>
 
                // Must be a component call, which we assign a "Void" return
 
                // type
 
                InferenceType::new(false, true, vec![ITP::Void]),
 
        };
 

	
 
        let infer_expr = &mut self.expr_types[expr.get_unique_id_in_definition() as usize];
 
        let needs_extra_data = match expr {
 
            Expression::Call(_) => true,
 
            Expression::Literal(expr) => match expr.value {
 
                Literal::Enum(_) | Literal::Union(_) | Literal::Struct(_) => true,
 
                _ => false,
 
            },
 
            Expression::Select(_) => true,
 
            Expression::Select(expr) => match expr.kind {
 
                SelectKind::StructField(_) => true,
 
                SelectKind::TupleMember(_) => false,
 
            },
 
            _ => false,
 
        };
 

	
 
        if infer_expr.expr_id.is_invalid() {
 
            // Nothing is set yet
 
            infer_expr.expr_type = inference_type;
 
            infer_expr.expr_id = expr_id;
 
            if needs_extra_data {
 
                let extra_idx = self.extra_data.len() as i32;
 
                self.extra_data.push(ExtraData::default());
 
                infer_expr.extra_data_idx = extra_idx;
 
            }
 
        } else {
 
            // We already have an entry
 
            debug_assert!(false, "does this ever happen?");
 
            if let SingleInferenceResult::Incompatible = InferenceType::infer_subtree_for_single_type(
 
                &mut infer_expr.expr_type, 0, &inference_type.parts, 0, false
 
            ) {
 
                return Err(self.construct_expr_type_error(ctx, expr_id, expr_id));
 
            }
 

	
 
            debug_assert!((infer_expr.extra_data_idx != -1) == needs_extra_data);
 
        }
 

	
 
        Ok(())
 
    }
 

	
 
    fn insert_initial_call_polymorph_data(
 
        &mut self, ctx: &mut Ctx, call_id: CallExpressionId
 
    ) {
 
        // Note: the polymorph variables may be partially specified and may
 
        // contain references to the wrapping definition's (i.e. the proctype
 
        // we are currently visiting) polymorphic arguments.
 
        //
 
        // The arguments of the call may refer to polymorphic variables in the
 
        // definition of the function we're calling, not of the wrapping
 
        // definition. We insert markers in these inferred types to be able to
 
        // map them back and forth to the polymorphic arguments of the function
 
        // we are calling.
 
        let call = &ctx.heap[call_id];
 
        let extra_data_idx = self.expr_types[call.unique_id_in_definition as usize].extra_data_idx; // TODO: @Temp
 
        debug_assert!(extra_data_idx != -1, "insert initial call polymorph data, no preallocated ExtraData");
 

	
 
        // Handle the polymorphic arguments (if there are any)
 
        let num_poly_args = call.parser_type.elements[0].variant.num_embedded();
 
        let mut poly_args = Vec::with_capacity(num_poly_args);
 
        for embedded_elements in call.parser_type.iter_embedded(0) {
 
            poly_args.push(self.determine_inference_type_from_parser_type_elements(embedded_elements, true));
 
        }
 

	
 
        // Handle the arguments and return types
 
        let definition = &ctx.heap[call.definition];
 
        let (parameters, returned) = match definition {
 
            Definition::Component(definition) => {
 
                debug_assert_eq!(poly_args.len(), definition.poly_vars.len());
 
                (&definition.parameters, None)
 
            },
 
            Definition::Function(definition) => {
 
                debug_assert_eq!(poly_args.len(), definition.poly_vars.len());
 
                (&definition.parameters, Some(&definition.return_types))
 
            },
 
            Definition::Struct(_) | Definition::Enum(_) | Definition::Union(_) => {
 
                unreachable!("insert_initial_call_polymorph data for non-procedure type");
 
            },
 
        };
 

	
 
        let mut parameter_types = Vec::with_capacity(parameters.len());
 
        for parameter_id in parameters.clone().into_iter() { // TODO: @Performance @Now
 
            let param = &ctx.heap[parameter_id];
 
            parameter_types.push(self.determine_inference_type_from_parser_type_elements(&param.parser_type.elements, false));
 
        }
 

	
 
        let return_type = match returned {
 
            None => {
 
                // Component, so returns a "Void"
 
                InferenceType::new(false, true, vec![InferenceTypePart::Void])
 
            },
 
            Some(returned) => {
 
                debug_assert_eq!(returned.len(), 1); // TODO: @ReturnTypes
 
                let returned = &returned[0];
 
                self.determine_inference_type_from_parser_type_elements(&returned.elements, false)
 
            }
 
        };
 

	
 
        self.extra_data[extra_data_idx as usize] = ExtraData{
 
            expr_id: call_id.upcast(),
 
            definition_id: call.definition,
 
            poly_vars: poly_args,
 
            embedded: parameter_types,
 
            returned: return_type
 
        };
 
    }
 

	
 
    fn insert_initial_struct_polymorph_data(
 
        &mut self, ctx: &mut Ctx, lit_id: LiteralExpressionId,
 
    ) {
 
        use InferenceTypePart as ITP;
 
        let literal = &ctx.heap[lit_id];
 
        let extra_data_idx = self.expr_types[literal.unique_id_in_definition as usize].extra_data_idx; // TODO: @Temp
 
        debug_assert!(extra_data_idx != -1, "initial struct polymorph data, but no preallocated ExtraData");
 
        let literal = ctx.heap[lit_id].value.as_struct();
 

	
 
        // Handle polymorphic arguments
 
        let num_embedded = literal.parser_type.elements[0].variant.num_embedded();
 
        let mut total_num_poly_parts = 0;
 
        let mut poly_args = Vec::with_capacity(num_embedded);
 

	
 
        for embedded_elements in literal.parser_type.iter_embedded(0) {
 
            let poly_type = self.determine_inference_type_from_parser_type_elements(embedded_elements, true);
 
            total_num_poly_parts += poly_type.parts.len();
 
            poly_args.push(poly_type);
 
        }
 

	
 
        // Handle parser types on struct definition
 
        let defined_type = ctx.types.get_base_definition(&literal.definition).unwrap();
 
        let struct_type = defined_type.definition.as_struct();
 
        debug_assert_eq!(poly_args.len(), defined_type.poly_vars.len());
 

	
 
        // Note: programmer is capable of specifying fields in a struct literal
 
        // in a different order than on the definition. We take the literal-
 
        // specified order to be leading.
 
        let mut embedded_types = Vec::with_capacity(struct_type.fields.len());
 
        for lit_field in literal.fields.iter() {
 
            let def_field = &struct_type.fields[lit_field.field_idx];
 
            let inference_type = self.determine_inference_type_from_parser_type_elements(&def_field.parser_type.elements, false);
 
            embedded_types.push(inference_type);
 
        }
 

	
 
        // Return type is the struct type itself, with the appropriate 
 
        // polymorphic variables. So:
 
        // - 1 part for definition
 
        // - N_poly_arg marker parts for each polymorphic argument
 
        // - all the parts for the currently known polymorphic arguments 
 
        let parts_reserved = 1 + poly_args.len() + total_num_poly_parts;
 
        let mut parts = Vec::with_capacity(parts_reserved);
 
        parts.push(ITP::Instance(literal.definition, poly_args.len() as u32));
 
        let mut return_type_done = true;
 
        for (poly_var_idx, poly_var) in poly_args.iter().enumerate() {
 
            if !poly_var.is_done { return_type_done = false; }
 

	
 
            parts.push(ITP::Marker(poly_var_idx as u32));
 
            parts.extend(poly_var.parts.iter().cloned());
 
        }
 

	
 
        debug_assert_eq!(parts.len(), parts_reserved);
 
        let return_type = InferenceType::new(!poly_args.is_empty(), return_type_done, parts);
 

	
 
        self.extra_data[extra_data_idx as usize] = ExtraData{
 
            expr_id: lit_id.upcast(),
 
            definition_id: literal.definition,
 
            poly_vars: poly_args,
 
            embedded: embedded_types,
 
            returned: return_type,
 
        };
 
    }
 

	
 
    /// Inserts the extra polymorphic data struct for enum expressions. These
 
    /// can never be determined from the enum itself, but may be inferred from
 
    /// the use of the enum.
 
    fn insert_initial_enum_polymorph_data(
 
        &mut self, ctx: &Ctx, lit_id: LiteralExpressionId
 
    ) {
 
        use InferenceTypePart as ITP;
 
        let literal = &ctx.heap[lit_id];
 
        let extra_data_idx = self.expr_types[literal.unique_id_in_definition as usize].extra_data_idx; // TODO: @Temp
 
        debug_assert!(extra_data_idx != -1, "initial enum polymorph data, but no preallocated ExtraData");
 
        let literal = ctx.heap[lit_id].value.as_enum();
 

	
 
        // Handle polymorphic arguments to the enum
 
        let num_poly_args = literal.parser_type.elements[0].variant.num_embedded();
 
        let mut total_num_poly_parts = 0;
 
        let mut poly_args = Vec::with_capacity(num_poly_args);
 

	
 
        for embedded_elements in literal.parser_type.iter_embedded(0) {
 
            let poly_type = self.determine_inference_type_from_parser_type_elements(embedded_elements, true);
 
            total_num_poly_parts += poly_type.parts.len();
 
            poly_args.push(poly_type);
 
        }
 

	
 
        // Handle enum type itself
 
        let parts_reserved = 1 + poly_args.len() + total_num_poly_parts;
 
        let mut parts = Vec::with_capacity(parts_reserved);
 
        parts.push(ITP::Instance(literal.definition, poly_args.len() as u32));
 
        let mut enum_type_done = true;
 
        for (poly_var_idx, poly_var) in poly_args.iter().enumerate() {
 
            if !poly_var.is_done { enum_type_done = false; }
 

	
 
            parts.push(ITP::Marker(poly_var_idx as u32));
 
            parts.extend(poly_var.parts.iter().cloned());
 
        }
 

	
 
        debug_assert_eq!(parts.len(), parts_reserved);
 
        let enum_type = InferenceType::new(!poly_args.is_empty(), enum_type_done, parts);
 

	
 
        self.extra_data[extra_data_idx as usize] = ExtraData{
 
            expr_id: lit_id.upcast(),
 
            definition_id: literal.definition,
 
            poly_vars: poly_args,
 
            embedded: Vec::new(),
 
            returned: enum_type,
 
        };
 
    }
 

	
 
    /// Inserts the extra polymorphic data struct for unions. The polymorphic
 
    /// arguments may be partially determined from embedded values in the union.
 
    fn insert_initial_union_polymorph_data(
 
        &mut self, ctx: &Ctx, lit_id: LiteralExpressionId
 
    ) {
 
        use InferenceTypePart as ITP;
 
        let literal = &ctx.heap[lit_id];
 
        let extra_data_idx = self.expr_types[literal.unique_id_in_definition as usize].extra_data_idx; // TODO: @Temp
 
        debug_assert!(extra_data_idx != -1, "initial union polymorph data, but no preallocated ExtraData");
 
        let literal = ctx.heap[lit_id].value.as_union();
 

	
 
        // Construct the polymorphic variables
 
        let num_poly_args = literal.parser_type.elements[0].variant.num_embedded();
 
        let mut total_num_poly_parts = 0;
 
        let mut poly_args = Vec::with_capacity(num_poly_args);
 

	
 
        for embedded_elements in literal.parser_type.iter_embedded(0) {
 
            let poly_type = self.determine_inference_type_from_parser_type_elements(embedded_elements, true);
 
            total_num_poly_parts += poly_type.parts.len();
 
            poly_args.push(poly_type);
 
        }
 

	
 
        // Handle any of the embedded values in the variant, if specified
 
        let definition_id = literal.definition;
 
        let type_definition = ctx.types.get_base_definition(&definition_id).unwrap();
 
        let union_definition = type_definition.definition.as_union();
 
        debug_assert_eq!(poly_args.len(), type_definition.poly_vars.len());
 

	
 
        let variant_definition = &union_definition.variants[literal.variant_idx];
 
        debug_assert_eq!(variant_definition.embedded.len(), literal.values.len());
 

	
 
        let mut embedded = Vec::with_capacity(variant_definition.embedded.len());
 
        for embedded_parser_type in &variant_definition.embedded {
 
            let inference_type = self.determine_inference_type_from_parser_type_elements(&embedded_parser_type.elements, false);
 
            embedded.push(inference_type);
 
        }
 

	
 
        // Handle the type of the union itself
 
        let parts_reserved = 1 + poly_args.len() + total_num_poly_parts;
 
        let mut parts = Vec::with_capacity(parts_reserved);
 
        parts.push(ITP::Instance(definition_id, poly_args.len() as u32));
 
        let mut union_type_done = true;
 
        for (poly_var_idx, poly_var) in poly_args.iter().enumerate() {
 
            if !poly_var.is_done { union_type_done = false; }
 

	
 
            parts.push(ITP::Marker(poly_var_idx as u32));
 
            parts.extend(poly_var.parts.iter().cloned());
 
        }
 

	
 
        debug_assert_eq!(parts_reserved, parts.len());
 
        let union_type = InferenceType::new(!poly_args.is_empty(), union_type_done, parts);
 

	
 
        self.extra_data[extra_data_idx as usize] = ExtraData{
 
            expr_id: lit_id.upcast(),
 
            definition_id: literal.definition,
 
            poly_vars: poly_args,
 
            embedded,
 
            returned: union_type
 
        };
 
    }
 

	
 
    /// Inserts the extra polymorphic data struct. Assumes that the select
 
    /// expression's referenced (definition_id, field_idx) has been resolved.
 
    fn insert_initial_select_polymorph_data(
 
        &mut self, ctx: &Ctx, select_id: SelectExpressionId, struct_def_id: DefinitionId
 
    ) {
 
        use InferenceTypePart as ITP;
 

	
 
        // Retrieve relevant data
 
        let expr = &ctx.heap[select_id];
 
        let expr_type = &self.expr_types[expr.unique_id_in_definition as usize];
 
        let field_idx = expr_type.field_or_monomorph_idx as usize;
 
        let extra_data_idx = expr_type.extra_data_idx; // TODO: @Temp
 
        debug_assert!(extra_data_idx != -1, "initial select polymorph data, but no preallocated ExtraData");
 

	
 
        let definition = ctx.heap[struct_def_id].as_struct();
 

	
 
        // Generate initial polyvar types and struct type
 
        // TODO: @Performance: we can immediately set the polyvars of the subject's struct type
 
        let num_poly_vars = definition.poly_vars.len();
 
        let mut poly_vars = Vec::with_capacity(num_poly_vars);
 
        let struct_parts_reserved = 1 + 2 * num_poly_vars;
 
        let mut struct_parts = Vec::with_capacity(struct_parts_reserved);
 
        struct_parts.push(ITP::Instance(struct_def_id, num_poly_vars as u32));
 

	
 
        for poly_idx in 0..num_poly_vars {
 
            poly_vars.push(InferenceType::new(true, false, vec![
 
                ITP::Marker(poly_idx as u32), ITP::Unknown,
 
            ]));
 
            struct_parts.push(ITP::Marker(poly_idx as u32));
 
            struct_parts.push(ITP::Unknown);
 
        }
 
        debug_assert_eq!(struct_parts.len(), struct_parts_reserved);
 

	
 
        // Generate initial field type
 
        let field_type = self.determine_inference_type_from_parser_type_elements(&definition.fields[field_idx].parser_type.elements, false);
 
        self.extra_data[extra_data_idx as usize] = ExtraData{
 
            expr_id: select_id.upcast(),
 
            definition_id: struct_def_id,
 
            poly_vars,
 
            embedded: vec![InferenceType::new(num_poly_vars != 0, num_poly_vars == 0, struct_parts)],
 
            returned: field_type
 
        };
 
    }
 

	
 
    /// Determines the initial InferenceType from the provided ParserType. This
 
    /// may be called with two kinds of intentions:
 
    /// 1. To resolve a ParserType within the body of a function, or on
 
    ///     polymorphic arguments to calls/instantiations within that body. This
 
    ///     means that the polymorphic variables are known and can be replaced
 
    ///     with the monomorph we're instantiating.
 
    /// 2. To resolve a ParserType on a called function's definition or on
 
    ///     an instantiated datatype's members. This means that the polymorphic
 
    ///     arguments inside those ParserTypes refer to the polymorphic
 
    ///     variables in the called/instantiated type's definition.
 
    /// In the second case we place InferenceTypePart::Marker instances such
 
    /// that we can perform type inference on the polymorphic variables.
 
    fn determine_inference_type_from_parser_type_elements(
 
        &mut self, elements: &[ParserTypeElement],
 
        use_definitions_known_poly_args: bool
 
    ) -> InferenceType {
 
        use ParserTypeVariant as PTV;
 
        use InferenceTypePart as ITP;
 

	
 
        let mut infer_type = Vec::with_capacity(elements.len());
 
        let mut has_inferred = false;
 
        let mut has_markers = false;
 

	
 
        for element in elements {
 
            match &element.variant {
 
                // Compiler-only types
 
                PTV::Void => { infer_type.push(ITP::Void); },
 
                PTV::InputOrOutput => { infer_type.push(ITP::PortLike); has_inferred = true },
 
                PTV::ArrayLike => { infer_type.push(ITP::ArrayLike); has_inferred = true },
 
                PTV::IntegerLike => { infer_type.push(ITP::IntegerLike); has_inferred = true },
 
                // Builtins
 
                PTV::Message => {
 
                    // TODO: @types Remove the Message -> Byte hack at some point...
 
                    infer_type.push(ITP::Message);
 
                    infer_type.push(ITP::UInt8);
 
                },
 
                PTV::Bool => { infer_type.push(ITP::Bool); },
 
                PTV::UInt8 => { infer_type.push(ITP::UInt8); },
 
                PTV::UInt16 => { infer_type.push(ITP::UInt16); },
 
                PTV::UInt32 => { infer_type.push(ITP::UInt32); },
 
                PTV::UInt64 => { infer_type.push(ITP::UInt64); },
 
                PTV::SInt8 => { infer_type.push(ITP::SInt8); },
 
                PTV::SInt16 => { infer_type.push(ITP::SInt16); },
 
                PTV::SInt32 => { infer_type.push(ITP::SInt32); },
 
                PTV::SInt64 => { infer_type.push(ITP::SInt64); },
 
                PTV::Character => { infer_type.push(ITP::Character); },
 
                PTV::String => {
 
                    infer_type.push(ITP::String);
 
                    infer_type.push(ITP::Character);
 
                },
 
                // Special markers
 
                PTV::IntegerLiteral => { unreachable!("integer literal type on variable type"); },
 
                PTV::Inferred => {
 
                    infer_type.push(ITP::Unknown);
 
                    has_inferred = true;
 
                },
 
                // With nested types
 
                PTV::Array => { infer_type.push(ITP::Array); },
 
                PTV::Input => { infer_type.push(ITP::Input); },
 
                PTV::Output => { infer_type.push(ITP::Output); },
 
                PTV::Tuple(num_embedded) => { infer_type.push(ITP::Tuple(*num_embedded)); },
 
                PTV::PolymorphicArgument(belongs_to_definition, poly_arg_idx) => {
 
                    let poly_arg_idx = *poly_arg_idx;
 
                    if use_definitions_known_poly_args {
 
                        // Refers to polymorphic argument on procedure we're currently processing.
 
                        // This argument is already known.
 
                        debug_assert_eq!(*belongs_to_definition, self.definition_type.definition_id());
 
                        debug_assert!((poly_arg_idx as usize) < self.poly_vars.len());
 

	
 
                        Self::determine_inference_type_from_concrete_type(
 
                            &mut infer_type, &self.poly_vars[poly_arg_idx as usize].parts
 
                        );
 
                    } else {
 
                        // Polymorphic argument has to be inferred
 
                        has_markers = true;
 
                        has_inferred = true;
src/protocol/tests/eval_operators.rs
Show inline comments
 
use super::*;
 

	
 
#[test]
 
fn test_assignment_operators() {
 
    fn construct_source(value_type: &str, value_initial: &str, value_op: &str) -> String {
 
        return format!(
 
            "func foo() -> {} {{
 
                {} value = {};
 
                value {};
 
                return value;
 
            }}",
 
            value_type, value_type, value_initial, value_op
 
        );
 
    }
 

	
 
    fn perform_test(name: &str, source: String, expected_value: Value) {
 
        Tester::new_single_source_expect_ok(name, source)
 
            .for_function("foo", move |f| {
 
                f.call_ok(Some(expected_value));
 
            });
 
    }
 

	
 
    perform_test(
 
        "set",
 
        construct_source("u32", "1", "= 5"),
 
        Value::UInt32(5)
 
    );
 

	
 
    perform_test(
 
        "multiplied",
 
        construct_source("u32", "2", "*= 4"),
 
        Value::UInt32(8)
 
    );
 

	
 
    perform_test(
 
        "divided",
 
        construct_source("u32", "8", "/= 4"),
 
        Value::UInt32(2)
 
    );
 

	
 
    perform_test(
 
        "remained",
 
        construct_source("u32", "8", "%= 3"),
 
        Value::UInt32(2)
 
    );
 

	
 
    perform_test(
 
        "added",
 
        construct_source("u32", "2", "+= 4"),
 
        Value::UInt32(6)
 
    );
 

	
 
    perform_test(
 
        "subtracted",
 
        construct_source("u32", "6", "-= 4"),
 
        Value::UInt32(2)
 
    );
 

	
 
    perform_test(
 
        "shifted left",
 
        construct_source("u32", "2", "<<= 2"),
 
        Value::UInt32(8)
 
    );
 

	
 
    perform_test(
 
        "shifted right",
 
        construct_source("u32", "8", ">>= 2"),
 
        Value::UInt32(2)
 
    );
 

	
 
    perform_test(
 
        "bitwise and",
 
        construct_source("u32", "15", "&= 35"),
 
        Value::UInt32(3)
 
    );
 

	
 
    perform_test(
 
        "bitwise xor",
 
        construct_source("u32", "3", "^= 7"),
 
        Value::UInt32(4)
 
    );
 

	
 
    perform_test(
 
        "bitwise or",
 
        construct_source("u32", "12", "|= 3"),
 
        Value::UInt32(15)
 
    );
 
}
 

	
 
#[test]
 
fn test_binary_integer_operators() {
 
    fn construct_source(value_type: &str, code: &str) -> String {
 
        format!("
 
        func foo() -> {} {{
 
            {}
 
        }}
 
        ", value_type, code)
 
    }
 

	
 
    fn perform_test(test_name: &str, value_type: &str, code: &str, expected_value: Value) {
 
        Tester::new_single_source_expect_ok(test_name, construct_source(value_type, code))
 
            .for_function("foo", move |f| {
 
                f.call_ok(Some(expected_value));
 
            });
 
    }
 

	
 
    perform_test(
 
        "bitwise_or", "u16",
 
        "auto a = 3; return a | 4;", Value::UInt16(7)
 
    );
 
    perform_test(
 
        "bitwise_xor", "u16",
 
        "auto a = 3; return a ^ 7;", Value::UInt16(4)
 
    );
 
    perform_test(
 
        "bitwise and", "u16",
 
        "auto a = 0b110011; return a & 0b011110;", Value::UInt16(0b010010)
 
    );
 
    perform_test(
 
        "shift left", "u16",
 
        "auto a = 0x0F; return a << 4;", Value::UInt16(0xF0)
 
    );
 
    perform_test(
 
        "shift right", "u64",
 
        "auto a = 0xF0; return a >> 4;", Value::UInt64(0x0F)
 
    );
 
    perform_test(
 
        "add", "u32",
 
        "auto a = 5; return a + 5;", Value::UInt32(10)
 
    );
 
    perform_test(
 
        "subtract", "u32",
 
        "auto a = 3; return a - 3;", Value::UInt32(0)
 
    );
 
    perform_test(
 
        "multiply", "u8",
 
        "auto a = 2 * 2; return a * 2 * 2;", Value::UInt8(16)
 
    );
 
    perform_test(
 
        "divide", "u8",
 
        "auto a = 32 / 2; return a / 2 / 2;", Value::UInt8(4)
 
    );
 
    perform_test(
 
        "remainder", "u16",
 
        "auto a = 29; return a % 3;", Value::UInt16(2)
 
    );
 
}
 

	
 
#[test]
 
fn test_tuple_operators() {
 
fn test_tuple_comparison_operators() {
 
    Tester::new_single_source_expect_ok("tuple equality", "
 
    func test_func() -> bool {
 
        auto a1 = (8, 16, 32);
 
        (u8, u16, u32) a2 = (8, 16, 32);
 
        auto b1 = ();
 
        () b2 = ();
 

	
 
        return a1 == a2 && a2 == (8, 16, 32) && b1 == b2 && b2 == ();
 
    }
 
    ").for_function("test_func", |f| { f
 
        .call_ok(Some(Value::Bool(true)));
 
    });
 

	
 
    Tester::new_single_source_expect_ok("tuple inequality", "
 
    func test_func() -> bool {
 
        auto a = (8, 16, 32);
 
        (u8, u16, u32) a_same = (8, 16, 32);
 
        auto a_diff = (0b111, 0b1111, 0b11111);
 
        auto b = ();
 
        return
 
            !(a != a_same) &&
 
            a != a_diff &&
 
            a != (8, 16, 320) &&
 
            !(b != ());
 
    }
 
    ").for_function("test_func", |f| { f
 
        .call_ok(Some(Value::Bool(true)));
 
    });
 
}
 

	
 
#[test]
 
fn test_tuple_select_operator() {
 
    Tester::new_single_source_expect_ok("tuple member assignment", "
 
    func test() -> bool {
 
        auto tuple = (0, 1, 0, 1);
 
        tuple.0 = cast<u8>(1);
 
        tuple.1 = cast<u16>(2);
 
        tuple.2 = cast<u32>(3);
 
        tuple.3 = cast<u64>(4);
 
        return cast(tuple.0) + cast(tuple.1) + tuple.2 + cast(tuple.3) == 10;
 
    }
 
    ").for_function("test", |f| { f
 
        .for_variable("tuple", |v| { v.assert_concrete_type("(u8,u16,u32,u64)"); })
 
        .call_ok(Some(Value::Bool(true)));
 
    });
 

	
 
    Tester::new_single_source_expect_ok("tuple polymorph member access", "
 
    func sum_variant_a<A,B,C,D>((A,B,C,D) v) -> C {
 
        return cast(v.0) + cast(v.1) + v.2 + cast(v.3);
 
    }
 
    func sum_variant_b<A,B,C,D>((A,B,C,D) i) -> B {
 
        (A,B,C,D) c = (0,0,0,0);
 
        c.0 = i.0; c.1 = i.1; c.2 = i.2; c.3 = i.3;
 
        return cast(c.0) + c.1 + cast(c.2) + cast(c.3);
 
    }
 
    func test() -> bool {
 
        (u8,u16,u32,u64) tuple = (1, 2, 3, 4);
 
        return sum_variant_a(tuple) == 10 && sum_variant_b(tuple) == 10;
 
    }
 
    ").for_function("test", |f| { f
 
        .call_ok(Some(Value::Bool(true)));
 
    });
 
}
 

	
 
#[test]
 
fn test_string_operators() {
 
    Tester::new_single_source_expect_ok("string concatenation", "
 
func create_concatenated(string left, string right) -> string {
 
    return left @ \", but also \" @ right;
 
}
 
func perform_concatenate(string left, string right) -> string {
 
    left @= \", but also \";
 
    left @= right;
 
    return left;
 
}
 
func foo() -> bool {
 
    auto left = \"Darth Vader\";
 
    auto right = \"Anakin Skywalker\";
 
    auto res1 = create_concatenated(left, right);
 
    auto res2 = perform_concatenate(left, right);
 
    auto expected = \"Darth Vader, but also Anakin Skywalker\";
 

	
 
    return
 
        res1 == expected &&
 
        res2 == \"Darth Vader, but also Anakin Skywalker\" &&
 
        res1 != \"This kind of thing\" && res2 != \"Another likewise kind of thing\";
 
}
 
    ").for_function("foo", |f| { f
 
        .call_ok(Some(Value::Bool(true)));
 
    });
 
}
 
\ No newline at end of file
src/protocol/tests/parser_inference.rs
Show inline comments
 
/// parser_inference.rs
 
///
 
/// Simple tests for the type inferences
 

	
 
use super::*;
 

	
 
#[test]
 
fn test_integer_inference() {
 
    Tester::new_single_source_expect_ok(
 
        "by arguments",
 
        "
 
        func call(u8 b, u16 s, u32 i, u64 l) -> u32 {
 
            auto b2 = b;
 
            auto s2 = s;
 
            auto i2 = i;
 
            auto l2 = l;
 
            return i2;
 
        }
 
        "
 
    ).for_function("call", |f| { f
 
        .for_variable("b2", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("u8");
 
        })
 
        .for_variable("s2", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("u16");
 
        })
 
        .for_variable("i2", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("u32");
 
        })
 
        .for_variable("l2", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("u64");
 
        });
 
    });
 

	
 
    Tester::new_single_source_expect_ok(
 
        "by assignment",
 
        "
 
        func call() -> u32 {
 
            u8 b1 = 0; u16 s1 = 0; u32 i1 = 0; u64 l1 = 0;
 
            auto b2 = b1;
 
            auto s2 = s1;
 
            auto i2 = i1;
 
            auto l2 = l1;
 
            return 0;
 
        }"
 
    ).for_function("call", |f| { f
 
        .for_variable("b2", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("u8");
 
        })
 
        .for_variable("s2", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("u16");
 
        })
 
        .for_variable("i2", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("u32");
 
        })
 
        .for_variable("l2", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("u64");
 
        });
 
    });
 
}
 

	
 
#[test]
 
fn test_binary_expr_inference() {
 
    Tester::new_single_source_expect_ok(
 
        "compatible types",
 
        "func call() -> s32 {
 
            s8 b0 = 0;
 
            s8 b1 = 1;
 
            s16 s0 = 0;
 
            s16 s1 = 1;
 
            s32 i0 = 0;
 
            s32 i1 = 1;
 
            s64 l0 = 0;
 
            s64 l1 = 1;
 
            auto b = b0 + b1;
 
            auto s = s0 + s1;
 
            auto i = i0 + i1;
 
            auto l = l0 + l1;
 
            return i;
 
        }"
 
    ).for_function("call", |f| { f
 
        .for_expression_by_source(
 
            "b0 + b1", "+", 
 
            |e| { e.assert_concrete_type("s8"); }
 
        )
 
        .for_expression_by_source(
 
            "s0 + s1", "+", 
 
            |e| { e.assert_concrete_type("s16"); }
 
        )
 
        .for_expression_by_source(
 
            "i0 + i1", "+", 
 
            |e| { e.assert_concrete_type("s32"); }
 
        )
 
        .for_expression_by_source(
 
            "l0 + l1", "+", 
 
            |e| { e.assert_concrete_type("s64"); }
 
        );
 
    });
 

	
 
    Tester::new_single_source_expect_err(
 
        "incompatible types", 
 
        "func call() -> s32 {
 
            s8 b = 0;
 
            s64 l = 1;
 
            auto r = b + l;
 
            return 0;
 
        }"
 
    ).error(|e| { e
 
        .assert_ctx_has(0, "b + l")
 
        .assert_msg_has(0, "cannot apply")
 
        .assert_occurs_at(0, "+")
 
        .assert_msg_has(1, "has type 's8'")
 
        .assert_msg_has(2, "has type 's64'");
 
    });
 
}
 

	
 
#[test]
 
fn test_tuple_inference() {
 
    Tester::new_single_source_expect_ok(
 
        "from tuple to variable",
 
        "
 
        func test() -> u32 {
 
            (u8,u16,u32,u64) tuple = (1, 2, 3, 4);
 
            auto a = tuple.0;
 
            auto b = tuple.1;
 
            auto c = tuple.2;
 
            auto d = tuple.3;
 
            return cast(a) + cast(b) + c + cast(d);
 
        }
 
        "
 
    ).for_function("test", |f| { f
 
        .for_variable("a", |v| { v.assert_concrete_type("u8"); })
 
        .for_variable("b", |v| { v.assert_concrete_type("u16"); })
 
        .for_variable("c", |v| { v.assert_concrete_type("u32"); })
 
        .for_variable("d", |v| { v.assert_concrete_type("u64"); })
 
        .call_ok(Some(Value::UInt32(10)));
 
    });
 

	
 
    Tester::new_single_source_expect_ok(
 
        "from variable to tuple",
 
        "
 
        func test() -> u32 {
 
            auto tuple = (1, 2, 3, 4);
 
            u8  a = tuple.0;
 
            u16 b = tuple.1;
 
            u32 c = tuple.2;
 
            u64 d = tuple.3;
 
            return cast(a) + cast(b) + c + cast(d);
 
        }
 
        "
 
    ).for_function("test", |f| { f
 
        .for_variable("tuple", |v| { v.assert_concrete_type("(u8,u16,u32,u64)"); })
 
        .call_ok(Some(Value::UInt32(10)));
 
    });
 
}
 

	
 
#[test]
 
fn test_struct_inference() {
 
    Tester::new_single_source_expect_ok(
 
        "by function calls",
 
        "
 
        struct Pair<T1, T2>{ T1 first, T2 second }
 
        func construct<T1, T2>(T1 first, T2 second) -> Pair<T1, T2> {
 
            return Pair{ first: first, second: second };
 
        }
 
        func fix_t1<T2>(Pair<s8, T2> arg) -> s32 { return 0; }
 
        func fix_t2<T1>(Pair<T1, s32> arg) -> s32 { return 0; }
 
        func test() -> s32 {
 
            auto first = 0;
 
            auto second = 1;
 
            auto pair = construct(first, second);
 
            fix_t1(pair);
 
            fix_t2(pair);
 
            return 0;
 
        }
 
        "
 
    ).for_function("test", |f| { f
 
        .for_variable("first", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("s8");
 
        })
 
        .for_variable("second", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("s32");
 
        })
 
        .for_variable("pair", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("Pair<s8,s32>");
 
        });
 
    });
 

	
 
    Tester::new_single_source_expect_ok(
 
        "by field access",
 
        "
 
        struct Pair<T1, T2>{ T1 first, T2 second }
 
        func construct<T1, T2>(T1 first, T2 second) -> Pair<T1, T2> {
 
            return Pair{ first: first, second: second };
 
        }
 
        func test() -> s32 {
 
            auto first = 0;
 
            auto second = 1;
 
            auto pair = construct(first, second);
 
            s8 assign_first = 0;
 
            s64 assign_second = 1;
 
            pair.first = assign_first;
 
            pair.second = assign_second;
 
            return 0;
 
        }
 
        "
 
    ).for_function("test", |f| { f
 
        .for_variable("first", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("s8");
 
        })
 
        .for_variable("second", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("s64");
 
        })
 
        .for_variable("pair", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("Pair<s8,s64>");
 
        });
 
    });
 

	
 
    Tester::new_single_source_expect_ok(
 
        "by nested field access",
 
        "
 
        struct Node<T1, T2>{ T1 l, T2 r }
 
        func construct<T1, T2>(T1 l, T2 r) -> Node<T1, T2> {
 
            return Node{ l: l, r: r };
 
        }
 
        func fix_poly<T>(Node<T, T> a) -> s32 { return 0; }
 
        func test() -> s32 {
 
            s8 assigned = 0;
 
            auto thing = construct(assigned, construct(0, 1));
 
            fix_poly(thing.r);
 
            thing.r.r = assigned;
 
            return 0;
 
        }
 
        ",
 
    ).for_function("test", |f| { f
 
        .for_variable("thing", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("Node<s8,Node<s8,s8>>");
 
        });
 
    });
 
}
 

	
 
#[test]
 
fn test_enum_inference() {
 
    Tester::new_single_source_expect_ok(
 
        "no polymorphic vars",
 
        "
 
        enum Choice { A, B }
 
        func test_instances() -> s32 {
 
            auto foo = Choice::A;
 
            auto bar = Choice::B;
 
            return 0;
 
        }
 
        "
 
    ).for_function("test_instances", |f| { f
 
        .for_variable("foo", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("Choice");
 
        })
 
        .for_variable("bar", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("Choice");
 
        });
 
    });
 

	
 
    Tester::new_single_source_expect_ok(
 
        "one polymorphic var",
 
        "
 
        enum Choice<T>{
 
            A,
 
            B,
 
        }
 
        func fix_as_s8(Choice<s8> arg) -> s32 { return 0; }
 
        func fix_as_s32(Choice<s32> arg) -> s32 { return 0; }
 
        func test_instances() -> s32 {
 
            auto choice_s8 = Choice::A;
 
            auto choice_s32_1 = Choice::B;
 
            Choice<auto> choice_s32_2 = Choice::B;
 
            fix_as_s8(choice_s8);
 
            fix_as_s32(choice_s32_1);
 
            return fix_as_s32(choice_s32_2);
 
        }
 
        "
 
    ).for_function("test_instances", |f| { f
 
        .for_variable("choice_s8", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("Choice<s8>");
 
        })
 
        .for_variable("choice_s32_1", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("Choice<s32>");
 
        })
 
        .for_variable("choice_s32_2", |v| { v
 
            .assert_parser_type("Choice<auto>")
 
            .assert_concrete_type("Choice<s32>");
 
        });
 
    });
 

	
 
    Tester::new_single_source_expect_ok(
 
        "two polymorphic vars",
 
        "
 
        enum Choice<T1, T2>{ A, B, }
 
        func fix_t1<T>(Choice<s8, T> arg) -> s32 { return 0; }
 
        func fix_t2<T>(Choice<T, s32> arg) -> s32 { return 0; }
 
        func test_instances() -> s32 {
 
            Choice<s8, auto> choice1 = Choice::A;
 
            Choice<auto, s32> choice2 = Choice::A;
 
            Choice<auto, auto> choice3 = Choice::B;
 
            auto choice4 = Choice::B;
 
            fix_t1(choice1); fix_t1(choice2); fix_t1(choice3); fix_t1(choice4);
 
            fix_t2(choice1); fix_t2(choice2); fix_t2(choice3); fix_t2(choice4);
 
            return 0;
 
        }
 
        "
 
    ).for_function("test_instances", |f| { f
 
        .for_variable("choice1", |v| { v
 
            .assert_parser_type("Choice<s8,auto>")
 
            .assert_concrete_type("Choice<s8,s32>");
 
        })
 
        .for_variable("choice2", |v| { v
 
            .assert_parser_type("Choice<auto,s32>")
 
            .assert_concrete_type("Choice<s8,s32>");
 
        })
 
        .for_variable("choice3", |v| { v
 
            .assert_parser_type("Choice<auto,auto>")
 
            .assert_concrete_type("Choice<s8,s32>");
 
        })
 
        .for_variable("choice4", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("Choice<s8,s32>");
 
        });
 
    });
 
}
 

	
 
#[test]
 
fn test_failed_variable_inference() {
 
    Tester::new_single_source_expect_err(
 
        "variable assignment mismatch",
 
        "
 
        func call() -> u32 {
 
            u16 val16 = 0;
 
            u32 val32 = 0;
 
            auto a = val16;
 
            a = val32;
 
            return 0;
 
        }
 
        "
 
    ).error(|e| { e
 
        .assert_num(2)
 
        .assert_msg_has(0, "type 'u16'")
 
        .assert_msg_has(1, "type 'u32'");
 
    });
 
}
 

	
 
#[test]
 
fn test_failed_polymorph_inference() {
 
    Tester::new_single_source_expect_err(
 
        "function call inference mismatch",
 
        "
 
        func poly<T>(T a, T b) -> s32 { return 0; }
 
        func call() -> s32 {
 
            s8 first_arg = 5;
 
            s64 second_arg = 2;
 
            return poly(first_arg, second_arg);
 
        }
 
        "
 
    ).error(|e| { e
 
        .assert_num(3)
 
        .assert_ctx_has(0, "poly(first_arg, second_arg)")
 
        .assert_occurs_at(0, "poly")
 
        .assert_msg_has(0, "Conflicting type for polymorphic variable 'T'")
 
        .assert_occurs_at(1, "second_arg")
 
        .assert_msg_has(1, "inferred it to 's64'")
 
        .assert_occurs_at(2, "first_arg")
 
        .assert_msg_has(2, "inferred it to 's8'");
 
    });
 

	
 
    Tester::new_single_source_expect_err(
 
        "struct literal inference mismatch",
 
        "
 
        struct Pair<T>{ T first, T second }
 
        func call() -> s32 {
 
            s8 first_arg = 5;
 
            s64 second_arg = 2;
 
            auto pair = Pair{ first: first_arg, second: second_arg };
 
            return 3;
 
        }
 
        "
 
    ).error(|e| { e
 
        .assert_num(3)
 
        .assert_ctx_has(0, "Pair{ first: first_arg, second: second_arg }")
 
        .assert_occurs_at(0, "Pair{")
 
        .assert_msg_has(0, "Conflicting type for polymorphic variable 'T'")
 
        .assert_occurs_at(1, "second_arg")
 
        .assert_msg_has(1, "inferred it to 's64'")
 
        .assert_occurs_at(2, "first_arg")
 
        .assert_msg_has(2, "inferred it to 's8'");
 
    });
 

	
 
    // Cannot really test literal inference error, but this comes close
 
    Tester::new_single_source_expect_err(
 
        "enum literal inference mismatch",
 
        "
 
        enum Uninteresting<T>{ Variant }
 
        func fix_t<T>(Uninteresting<T> arg) -> s32 { return 0; }
 
        func call() -> s32 {
 
            auto a = Uninteresting::Variant;
 
            fix_t<s8>(a);
 
            fix_t<s32>(a);
 
            return 4;
 
        }
 
        "
 
    ).error(|e| { e
 
        .assert_num(2)
 
        .assert_msg_has(0, "type 'Uninteresting<s8>'")
 
        .assert_msg_has(1, "type 'Uninteresting<s32>'");
 
    });
 

	
 
    Tester::new_single_source_expect_err(
 
        "field access inference mismatch",
 
        "
 
        struct Holder<Shazam>{ Shazam a }
 
        func call() -> s32 {
 
            s8 to_hold = 0;
 
            auto holder = Holder{ a: to_hold };
 
            return holder.a;
 
        }
 
        "
 
    ).error(|e| { e
 
        .assert_num(3)
 
        .assert_ctx_has(0, "holder.a")
 
        .assert_occurs_at(0, "holder.a")
 
        .assert_msg_has(0, "Conflicting type for polymorphic variable 'Shazam'")
 
        .assert_msg_has(1, "inferred it to 's8'")
 
        .assert_msg_has(2, "inferred it to 's32'");
 
    });
 

	
 
    // Silly regression test
 
    Tester::new_single_source_expect_err(
 
        "nested field access inference mismatch",
 
        "
 
        struct Node<T1, T2>{ T1 l, T2 r }
 
        func construct<T1, T2>(T1 l, T2 r) -> Node<T1, T2> { return Node{ l: l, r: r }; }
 
        func fix_poly<T>(Node<T, T> a) -> s32 { return 0; }
 
        func test() -> s32 {
 
            s8 assigned = 0;
 
            s64 another = 1;
 
            auto thing = construct(assigned, construct(another, 1));
 
            fix_poly(thing.r);
 
            thing.r.r = assigned;
 
            return 0;
 
        }
 
        ",
 
    );
 
}
 

	
 

	
 
#[test]
 
fn test_explicit_polymorph_argument() {
 
    // Failed because array was put at same type depth as u32. So interpreted
 
    // as a function with two polymorphic arguments
 
    Tester::new_single_source_expect_ok("explicit with array", "
 
    func foo<T>(T a, T b) -> T {
 
        return a @ b;
 
    }
 
    func test() -> u32 {
 
        return foo<u32[]>({1}, {2})[1];
 
    }").for_function("test", |f| { f
 
        .call_ok(Some(Value::UInt32(2)));
 
    });
 

	
 
    Tester::new_single_source_expect_err("multi-explicit with array", "
 
    func foo<A, B, C>(A a, B b) -> C {
 
        return (a @ b)[1];
 
    }
 
    func test() -> u32 {
 
        return foo<u32[], u32[], u32, u32>({1}, {2});
 
    }").error(|e| { e
 
        .assert_num(1)
 
        .assert_occurs_at(0, "foo<u32")
 
        .assert_msg_has(0, "expected 3")
 
        .assert_msg_has(0, "4 were provided");
 
    });
 

	
 
    // Failed because type inferencer did not construct polymorph errors by
 
    // considering that argument/return types failed against explicitly
 
    // specified polymorphic arguments
 
    Tester::new_single_source_expect_err("explicit polymorph argument mismatch", "
 
    func foo<T>(T a, T b) -> T { return a + b; }
 
    struct Bar<A, B>{A a, B b}
 
    func test() -> u32 {
 
        return foo<Bar<u32, u64>[]>(5, 6);
 
    }").error(|e| { e
 
        .assert_num(2)
 
        .assert_occurs_at(0, "foo<Bar")
 
        .assert_msg_has(0, "'T' of 'foo'")
 
        .assert_occurs_at(1, "5, ")
 
        .assert_msg_has(1, "has type 'Bar<u32, u64>[]")
 
        .assert_msg_has(1, "inferred it to 'integerlike'");
 
    });
 

	
 
    // Similar to above, now for return type
 
    Tester::new_single_source_expect_err("explicit polymorph return mismatch", "
 
    func foo<T>(T a, T b) -> T { return a + b; }
 
    func test() -> u32 {
 
        return foo<u64>(5, 6);
 
    }").error(|e| { e
 
        .assert_num(2)
 
        .assert_occurs_at(0, "foo<u64")
 
        .assert_msg_has(0, "'T' of 'foo'")
 
        .assert_occurs_at(1, "foo<u64")
 
        .assert_msg_has(1, "has type 'u64'")
 
        .assert_msg_has(1, "return type inferred it to 'u32'");
 
    });
 
}
 
\ No newline at end of file
src/protocol/tests/parser_validation.rs
Show inline comments
 
@@ -95,405 +95,437 @@ fn test_incorrect_struct_instance() {
 
        .assert_num(2)
 
        .assert_occurs_at(0, "a }")
 
        .assert_msg_has(0, "defined more than once")
 
        .assert_occurs_at(1, "a, ")
 
        .assert_msg_has(1, "other struct field");
 
    });
 

	
 
    Tester::new_single_source_expect_err(
 
        "reused field in instance",
 
        "
 
        struct Foo{ s32 a, s32 b }
 
        func bar() -> s32 {
 
            auto foo = Foo{ a: 5, a: 3 };
 
            return 0;
 
        }
 
        "
 
    ).error(|e| { e
 
        .assert_occurs_at(0, "a: 3")
 
        .assert_msg_has(0, "field is specified more than once");
 
    });
 

	
 
    Tester::new_single_source_expect_err(
 
        "missing field",
 
        "
 
        struct Foo { s32 a, s32 b }
 
        func bar() -> s32 {
 
            auto foo = Foo{ a: 2 };
 
            return 0;
 
        }
 
        "
 
    ).error(|e| { e
 
        .assert_occurs_at(0, "Foo{")
 
        .assert_msg_has(0, "'b' is missing");
 
    });
 

	
 
    Tester::new_single_source_expect_err(
 
        "missing fields",
 
        "
 
        struct Foo { s32 a, s32 b, s32 c }
 
        func bar() -> s32 {
 
            auto foo = Foo{ a: 2 };
 
            return 0;
 
        }
 
        "
 
    ).error(|e| { e
 
        .assert_occurs_at(0, "Foo{")
 
        .assert_msg_has(0, "[b, c] are missing");
 
    });
 
}
 

	
 
#[test]
 
fn test_correct_enum_instance() {
 
    Tester::new_single_source_expect_ok(
 
        "single variant",
 
        "
 
        enum Foo { A }
 
        func bar() -> Foo { return Foo::A; }
 
        "
 
    );
 

	
 
    Tester::new_single_source_expect_ok(
 
        "multiple variants",
 
        "
 
        enum Foo { A=15, B = 0xF }
 
        func bar() -> Foo { auto a = Foo::A; return Foo::B; }
 
        "
 
    );
 

	
 
    Tester::new_single_source_expect_ok(
 
        "explicit single polymorph",
 
        "
 
        enum Foo<T>{ A }
 
        func bar() -> Foo<s32> { return Foo::A; }
 
        "
 
    );
 

	
 
    Tester::new_single_source_expect_ok(
 
        "explicit multi-polymorph",
 
        "
 
        enum Foo<A, B>{ A, B }
 
        func bar() -> Foo<s8, s32> { return Foo::B; }
 
        "
 
    );
 
}
 

	
 
#[test]
 
fn test_incorrect_enum_instance() {
 
    Tester::new_single_source_expect_err(
 
        "variant name reuse",
 
        "
 
        enum Foo { A, A }
 
        func bar() -> Foo { return Foo::A; }
 
        "
 
    ).error(|e| { e
 
        .assert_num(2)
 
        .assert_occurs_at(0, "A }")
 
        .assert_msg_has(0, "defined more than once")
 
        .assert_occurs_at(1, "A, ")
 
        .assert_msg_has(1, "other enum variant is defined here");
 
    });
 

	
 
    Tester::new_single_source_expect_err(
 
        "undefined variant",
 
        "
 
        enum Foo { A }
 
        func bar() -> Foo { return Foo::B; }
 
        "
 
    ).error(|e| { e
 
        .assert_num(1)
 
        .assert_msg_has(0, "variant 'B' does not exist on the enum 'Foo'");
 
    });
 
}
 

	
 
#[test]
 
fn test_correct_union_instance() {
 
    Tester::new_single_source_expect_ok(
 
        "single tag",
 
        "
 
        union Foo { A }
 
        func bar() -> Foo { return Foo::A; }
 
        "
 
    );
 

	
 
    Tester::new_single_source_expect_ok(
 
        "multiple tags",
 
        "
 
        union Foo { A, B }
 
        func bar() -> Foo { return Foo::B; }
 
        "
 
    );
 

	
 
    Tester::new_single_source_expect_ok(
 
        "single embedded",
 
        "
 
        union Foo { A(s32) }
 
        func bar() -> Foo { return Foo::A(5); }
 
        "
 
    );
 

	
 
    Tester::new_single_source_expect_ok(
 
        "multiple embedded",
 
        "
 
        union Foo { A(s32), B(s8) }
 
        func bar() -> Foo { return Foo::B(2); }
 
        "
 
    );
 

	
 
    Tester::new_single_source_expect_ok(
 
        "multiple values in embedded",
 
        "
 
        union Foo { A(s32, s8) }
 
        func bar() -> Foo { return Foo::A(0, 2); }
 
        "
 
    );
 

	
 
    Tester::new_single_source_expect_ok(
 
        "mixed tag/embedded",
 
        "
 
        union OptionInt { None, Some(s32) }
 
        func bar() -> OptionInt { return OptionInt::Some(3); }
 
        "
 
    );
 

	
 
    Tester::new_single_source_expect_ok(
 
        "single polymorphic var",
 
        "
 
        union Option<T> { None, Some(T) }
 
        func bar() -> Option<s32> { return Option::Some(3); }"
 
    );
 

	
 
    Tester::new_single_source_expect_ok(
 
        "multiple polymorphic vars",
 
        "
 
        union Result<T, E> { Ok(T), Err(E), }
 
        func bar() -> Result<s32, s8> { return Result::Ok(3); }
 
        "
 
    );
 

	
 
    Tester::new_single_source_expect_ok(
 
        "multiple polymorphic in one variant",
 
        "
 
        union MaybePair<T1, T2>{ None, Some(T1, T2) }
 
        func bar() -> MaybePair<s8, s32> { return MaybePair::Some(1, 2); }
 
        "
 
    );
 
}
 

	
 
#[test]
 
fn test_incorrect_union_instance() {
 
    Tester::new_single_source_expect_err(
 
        "tag-variant name reuse",
 
        "
 
        union Foo{ A, A }
 
        "
 
    ).error(|e| { e
 
        .assert_num(2)
 
        .assert_occurs_at(0, "A }")
 
        .assert_msg_has(0, "union variant is defined more than once")
 
        .assert_occurs_at(1, "A, ")
 
        .assert_msg_has(1, "other union variant");
 
    });
 

	
 
    Tester::new_single_source_expect_err(
 
        "embedded-variant name reuse",
 
        "
 
        union Foo{ A(s32), A(s8) }
 
        "
 
    ).error(|e| { e 
 
        .assert_num(2)
 
        .assert_occurs_at(0, "A(s8)")
 
        .assert_msg_has(0, "union variant is defined more than once")
 
        .assert_occurs_at(1, "A(s32)")
 
        .assert_msg_has(1, "other union variant");
 
    });
 

	
 
    Tester::new_single_source_expect_err(
 
        "undefined variant",
 
        "
 
        union Silly{ Thing(s8) }
 
        func bar() -> Silly { return Silly::Undefined(5); }
 
        "
 
    ).error(|e| { e
 
        .assert_msg_has(0, "variant 'Undefined' does not exist on the union 'Silly'");
 
    });
 

	
 
    Tester::new_single_source_expect_err(
 
        "using tag instead of embedded",
 
        "
 
        union Foo{ A(s32) }
 
        func bar() -> Foo { return Foo::A; }
 
        "
 
    ).error(|e| { e
 
        .assert_msg_has(0, "variant 'A' of union 'Foo' expects 1 embedded values, but 0 were");
 
    });
 

	
 
    Tester::new_single_source_expect_err(
 
        "using embedded instead of tag",
 
        "
 
        union Foo{ A }
 
        func bar() -> Foo { return Foo::A(3); }
 
        "
 
    ).error(|e| { e 
 
        .assert_msg_has(0, "The variant 'A' of union 'Foo' expects 0");
 
    });
 

	
 
    Tester::new_single_source_expect_err(
 
        "wrong embedded value",
 
        "
 
        union Foo{ A(s32) }
 
        func bar() -> Foo { return Foo::A(false); }
 
        "
 
    ).error(|e| { e
 
        .assert_occurs_at(0, "Foo::A")
 
        .assert_msg_has(0, "failed to fully resolve")
 
        .assert_occurs_at(1, "false")
 
        .assert_msg_has(1, "has been resolved to 's32'")
 
        .assert_msg_has(1, "has been resolved to 'bool'");
 
    });
 
}
 

	
 
#[test]
 
fn test_correct_tuple_members() {
 
    // Tuples with zero members
 
    Tester::new_single_source_expect_ok(
 
        "single zero-tuple",
 
        "struct Foo{ () bar }"
 
    ).for_struct("Foo", |s| { s
 
        .for_field("bar", |f| { f.assert_parser_type("()"); })
 
        .assert_size_alignment("Foo", 0, 1);
 
    });
 

	
 
    Tester::new_single_source_expect_ok(
 
        "triple zero-tuple",
 
        "struct Foo{ () bar, () baz, () qux }"
 
    ).for_struct("Foo", |s| { s
 
        .assert_size_alignment("Foo", 0, 1);
 
    });
 

	
 
    // Tuples with one member (which are elided, because due to ambiguity
 
    // between a one-tuple literal and a parenthesized expression, we're not
 
    // going to be able to construct one-tuples).
 
    Tester::new_single_source_expect_ok(
 
        "single elided one-tuple",
 
        "struct Foo{ (u32) bar }"
 
    ).for_struct("Foo", |s| { s
 
        .for_field("bar", |f| { f.assert_parser_type("u32"); })
 
        .assert_size_alignment("Foo", 4, 4);
 
    });
 

	
 
    Tester::new_single_source_expect_ok(
 
        "triple elided one-tuple",
 
        "struct Foo{ (u8) bar, (u16) baz, (u32) qux }"
 
    ).for_struct("Foo", |s| { s
 
        .assert_size_alignment("Foo", 8, 4);
 
    });
 

	
 
    // Tuples with three members
 
    Tester::new_single_source_expect_ok(
 
        "single three-tuple",
 
        "struct Foo{ (u8, u16, u32) bar }"
 
    ).for_struct("Foo", |s| { s
 
        .for_field("bar", |f| { f.assert_parser_type("(u8,u16,u32)"); })
 
        .assert_size_alignment("Foo", 8, 4);
 
    });
 

	
 
    Tester::new_single_source_expect_ok(
 
        "double three-tuple",
 
        "struct Foo{ (u8,u16,u32,) bar, (s8,s16,s32,) baz }"
 
    ).for_struct("Foo", |s| { s
 
        .for_field("bar", |f| { f.assert_parser_type("(u8,u16,u32)"); })
 
        .for_field("baz", |f| { f.assert_parser_type("(s8,s16,s32)"); })
 
        .assert_size_alignment("Foo", 16, 4);
 
    });
 
}
 

	
 
#[test]
 
fn test_incorrect_tuple_member() {
 
    // Test not really necessary, but hey, what's a test between friends
 
    Tester::new_single_source_expect_err(
 
        "unknown tuple member",
 
        "struct Foo{ (u32, u32, u32, YouThirstySchmoo) field }"
 
    ).error(|e| { e
 
        .assert_num(1)
 
        .assert_msg_has(0, "unknown type")
 
        .assert_occurs_at(0, "YouThirstySchmoo");
 
    });
 
}
 

	
 
#[test]
 
fn test_correct_tuple_polymorph_args() {
 
    Tester::new_single_source_expect_ok(
 
        "single tuple arg",
 
        "
 
        union Option<T>{ Some(T), None }
 
        func thing() -> u32 {
 
            auto a = Option<()>::None;
 
            auto b = Option<(u32, u64)>::None;
 
            auto c = Option<(Option<(u8, s8)>, Option<(s8, u8)>)>::None;
 
            return 0;
 
        }
 
        "
 
    ).for_union("Option", |u| { u
 
        .assert_has_monomorph("Option<()>")
 
        .assert_has_monomorph("Option<(u32,u64)>")
 
        .assert_has_monomorph("Option<(Option<(u8,s8)>,Option<(s8,u8)>)>")
 
        .assert_size_alignment("Option<()>", 1, 1, 0, 0)
 
        .assert_size_alignment("Option<(u32,u64)>", 24, 8, 0, 0) // (u32, u64) becomes size 16, alignment 8. Hence union tag is aligned to 8
 
        .assert_size_alignment("Option<(Option<(u8,s8)>,Option<(s8,u8)>)>", 7, 1, 0, 0); // inner unions are size 3, alignment 1. Two of those with a tag is size 7
 
    });
 
}
 

	
 
#[test]
 
fn test_incorrect_tuple_polymorph_args() {
 
    // Do some mismatching brackets. I don't know what else to test
 
    Tester::new_single_source_expect_err(
 
        "mismatch angle bracket",
 
        "
 
        union Option<T>{ Some(T), None }
 
        func f() -> u32 {
 
            auto a = Option<(u32>)::None;
 
            return 0;
 
        }"
 
    ).error(|e| { e
 
        .assert_num(2)
 
        .assert_msg_has(0, "closing '>'").assert_occurs_at(0, ">)::None")
 
        .assert_msg_has(1, "match this '('").assert_occurs_at(1, "(u32>");
 
    });
 

	
 
    Tester::new_single_source_expect_err(
 
        "wrongly placed angle",
 
        "
 
        union O<T>{ S(T), N }
 
        func f() -> u32 {
 
            auto a = O<(<u32>)>::None;
 
            return 0;
 
        }
 
        "
 
    ).error(|e| { e
 
        .assert_num(1)
 
        .assert_msg_has(0, "expected typename")
 
        .assert_occurs_at(0, "<u32");
 
    });
 
}
 

	
 
#[test]
 
fn test_incorrect_tuple_member_access() {
 
    Tester::new_single_source_expect_err(
 
        "zero-tuple",
 
        "func foo() -> () { () a = (); auto b = a.0; return a; }"
 
    ).error(|e| { e
 
        .assert_num(1)
 
        .assert_msg_has(0, "out of bounds")
 
        .assert_occurs_at(0, "a.0");
 
    });
 

	
 
    // Make the type checker do some shenanigans before we can decide the tuple
 
    // type.
 
    Tester::new_single_source_expect_err(
 
        "sized tuple",
 
        "
 
        func determinator<A,B>((A,B,A) v) -> B { return v.1; }
 
        func tester() -> u64 {
 
            auto v = (0,1,2);
 
            u32 a_u32 = 5;
 
            v.2 = a_u32;
 
            v.8 = 5;
 
            return determinator(v);
 
        }
 
        "
 
    ).error(|e| { e
 
        .assert_num(1)
 
        .assert_msg_has(0, "out of bounds")
 
        .assert_occurs_at(0, "v.8");
 
    });
 
}
 

	
 
#[test]
 
fn test_polymorph_array_types() {
 
    Tester::new_single_source_expect_ok(
 
        "array of polymorph in struct",
 
        "
 
        struct Foo<T> { T[] hello }
 
        struct Bar { Foo<u32>[] world }
 
        "
 
    ).for_struct("Bar", |s| { s
 
        .for_field("world", |f| { f.assert_parser_type("Foo<u32>[]"); });
 
    });
 

	
 
    Tester::new_single_source_expect_ok(
 
        "array of port in struct",
 
        "
 
        struct Bar { in<u32>[] inputs }
 
        "
 
    ).for_struct("Bar", |s| { s
 
        .for_field("inputs", |f| { f.assert_parser_type("in<u32>[]"); });
 
    });
 
}
 
\ No newline at end of file
0 comments (0 inline, 0 general)