Changeset - e914af987c85
[Not reviewed]
0 7 0
MH - 4 years ago 2021-05-31 14:30:29
contact@maxhenger.nl
fix string-related bugs
7 files changed with 176 insertions and 63 deletions:
0 comments (0 inline, 0 general)
src/protocol/eval/store.rs
Show inline comments
 
@@ -170,24 +170,25 @@ impl Store {
 
        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),
 
            _ => 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);
 
        }
 
    }
src/protocol/eval/value.rs
Show inline comments
 
@@ -132,24 +132,25 @@ impl Value {
 
            Value::SInt16(v) => *v as i64,
 
            Value::SInt32(v) => *v as i64,
 
            Value::SInt64(v) => *v as i64,
 
            _ => unreachable!("called as_signed_integer on {:?}", self)
 
        }
 
    }
 

	
 
    /// Returns the heap position associated with the value. If the value
 
    /// doesn't store anything in the heap then we return `None`.
 
    pub(crate) fn get_heap_pos(&self) -> Option<HeapPos> {
 
        match self {
 
            Value::Message(v) => Some(*v),
 
            Value::String(v) => Some(*v),
 
            Value::Array(v) => Some(*v),
 
            Value::Union(_, v) => Some(*v),
 
            Value::Struct(v) => Some(*v),
 
            _ => None
 
        }
 
    }
 
}
 

	
 
/// When providing arguments to a new component, or when transferring values
 
/// from one component's store to a newly instantiated component, one has to
 
/// transfer stack and heap values. This `ValueGroup` represents such a
 
/// temporary group of values with potential heap allocations.
src/protocol/parser/pass_typing.rs
Show inline comments
 
@@ -58,25 +58,25 @@ use crate::protocol::parser::token_parsing::*;
 
use super::visitor::{
 
    STMT_BUFFER_INIT_CAPACITY,
 
    EXPR_BUFFER_INIT_CAPACITY,
 
    Ctx,
 
    Visitor,
 
    VisitorResult
 
};
 

	
 
const VOID_TEMPLATE: [InferenceTypePart; 1] = [ InferenceTypePart::Void ];
 
const MESSAGE_TEMPLATE: [InferenceTypePart; 2] = [ InferenceTypePart::Message, InferenceTypePart::UInt8 ];
 
const BOOL_TEMPLATE: [InferenceTypePart; 1] = [ InferenceTypePart::Bool ];
 
const CHARACTER_TEMPLATE: [InferenceTypePart; 1] = [ InferenceTypePart::Character ];
 
const STRING_TEMPLATE: [InferenceTypePart; 1] = [ InferenceTypePart::String ];
 
const STRING_TEMPLATE: [InferenceTypePart; 2] = [ InferenceTypePart::String, InferenceTypePart::Character ];
 
const NUMBERLIKE_TEMPLATE: [InferenceTypePart; 1] = [ InferenceTypePart::NumberLike ];
 
const INTEGERLIKE_TEMPLATE: [InferenceTypePart; 1] = [ InferenceTypePart::IntegerLike ];
 
const ARRAY_TEMPLATE: [InferenceTypePart; 2] = [ InferenceTypePart::Array, InferenceTypePart::Unknown ];
 
const SLICE_TEMPLATE: [InferenceTypePart; 2] = [ InferenceTypePart::Slice, InferenceTypePart::Unknown ];
 
const ARRAYLIKE_TEMPLATE: [InferenceTypePart; 2] = [ InferenceTypePart::ArrayLike, InferenceTypePart::Unknown ];
 

	
 
/// TODO: @performance Turn into PartialOrd+Ord to simplify checks
 
#[derive(Debug, Clone, Eq, PartialEq)]
 
pub(crate) enum InferenceTypePart {
 
    // When we infer types of AST elements that support polymorphic arguments,
 
    // then we might have the case that multiple embedded types depend on the
 
    // polymorphic type (e.g. func bla(T a, T[] b) -> T[][]). If we can infer
 
@@ -146,108 +146,80 @@ impl InferenceTypePart {
 
        }
 
    }
 

	
 
    fn is_concrete_integer(&self) -> bool {
 
        use InferenceTypePart as ITP;
 
        match self {
 
            ITP::UInt8 | ITP::UInt16 | ITP::UInt32 | ITP::UInt64 |
 
            ITP::SInt8 | ITP::SInt16 | ITP::SInt32 | ITP::SInt64 => true,
 
            _ => false,
 
        }
 
    }
 

	
 
    fn is_concrete_msg_array_or_slice(&self) -> bool {
 
    fn is_concrete_arraylike(&self) -> bool {
 
        use InferenceTypePart as ITP;
 
        match self {
 
            ITP::Array | ITP::Slice | ITP::Message => true,
 
            ITP::Array | ITP::Slice | ITP::String | ITP::Message => true,
 
            _ => false,
 
        }
 
    }
 

	
 
    fn is_concrete_port(&self) -> bool {
 
        use InferenceTypePart as ITP;
 
        match self {
 
            ITP::Input | ITP::Output => true,
 
            _ => false,
 
        }
 
    }
 

	
 
    /// Checks if a part is less specific than the argument. Only checks for 
 
    /// single-part inference (i.e. not the replacement of an `Unknown` variant 
 
    /// with the argument)
 
    fn may_be_inferred_from(&self, arg: &InferenceTypePart) -> bool {
 
        use InferenceTypePart as ITP;
 

	
 
        (*self == ITP::IntegerLike && arg.is_concrete_integer()) ||
 
        (*self == ITP::NumberLike && (arg.is_concrete_number() || *arg == ITP::IntegerLike)) ||
 
        (*self == ITP::ArrayLike && arg.is_concrete_msg_array_or_slice()) ||
 
        (*self == ITP::ArrayLike && arg.is_concrete_arraylike()) ||
 
        (*self == ITP::PortLike && arg.is_concrete_port())
 
    }
 

	
 
    /// Checks if a part is more specific
 

	
 
    /// Returns the change in "iteration depth" when traversing this particular
 
    /// part. The iteration depth is used to traverse the tree in a linear 
 
    /// fashion. It is basically `number_of_subtypes - 1`
 
    fn depth_change(&self) -> i32 {
 
        use InferenceTypePart as ITP;
 
        match &self {
 
            ITP::Unknown | ITP::NumberLike | ITP::IntegerLike |
 
            ITP::Void | ITP::Bool |
 
            ITP::UInt8 | ITP::UInt16 | ITP::UInt32 | ITP::UInt64 |
 
            ITP::SInt8 | ITP::SInt16 | ITP::SInt32 | ITP::SInt64 |
 
            ITP::Character | ITP::String => {
 
            ITP::Character => {
 
                -1
 
            },
 
            ITP::Marker(_) |
 
            ITP::ArrayLike | ITP::Message | ITP::Array | ITP::Slice |
 
            ITP::PortLike | ITP::Input | ITP::Output => {
 
            ITP::PortLike | ITP::Input | ITP::Output | ITP::String => {
 
                // One subtype, so do not modify depth
 
                0
 
            },
 
            ITP::Instance(_, num_args) => {
 
                (*num_args as i32) - 1
 
            }
 
        }
 
    }
 
}
 

	
 
impl From<ConcreteTypePart> for InferenceTypePart {
 
    fn from(v: ConcreteTypePart) -> InferenceTypePart {
 
        use ConcreteTypePart as CTP;
 
        use InferenceTypePart as ITP;
 

	
 
        match v {
 
            CTP::Void => ITP::Void,
 
            CTP::Message => ITP::Message,
 
            CTP::Bool => ITP::Bool,
 
            CTP::UInt8 => ITP::UInt8,
 
            CTP::UInt16 => ITP::UInt16,
 
            CTP::UInt32 => ITP::UInt32,
 
            CTP::UInt64 => ITP::UInt64,
 
            CTP::SInt8 => ITP::SInt8,
 
            CTP::SInt16 => ITP::SInt16,
 
            CTP::SInt32 => ITP::SInt32,
 
            CTP::SInt64 => ITP::SInt64,
 
            CTP::Character => ITP::Character,
 
            CTP::String => ITP::String,
 
            CTP::Array => ITP::Array,
 
            CTP::Slice => ITP::Slice,
 
            CTP::Input => ITP::Input,
 
            CTP::Output => ITP::Output,
 
            CTP::Instance(id, num) => ITP::Instance(id, num),
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone)]
 
struct InferenceType {
 
    has_marker: bool,
 
    is_done: bool,
 
    parts: Vec<InferenceTypePart>,
 
}
 

	
 
impl InferenceType {
 
    /// Generates a new InferenceType. The two boolean flags will be checked in
 
    /// debug mode.
 
    fn new(has_marker: bool, is_done: bool, parts: Vec<InferenceTypePart>) -> Self {
 
        if cfg!(debug_assertions) {
 
@@ -303,25 +275,25 @@ impl InferenceType {
 
        let mut depth = 1;
 
        let mut idx = start_idx;
 

	
 
        while idx < parts.len() {
 
            depth += parts[idx].depth_change();
 
            if depth == 0 {
 
                return idx + 1;
 
            }
 
            idx += 1;
 
        }
 

	
 
        // If here, then the inference type is malformed
 
        unreachable!();
 
        unreachable!("Malformed type: {:?}", parts);
 
    }
 

	
 
    /// Call that attempts to infer the part at `to_infer.parts[to_infer_idx]` 
 
    /// using the subtree at `template.parts[template_idx]`. Will return 
 
    /// `Some(depth_change_due_to_traversal)` if type inference has been 
 
    /// applied. In this case the indices will also be modified to point to the 
 
    /// next part in both templates. If type inference has not (or: could not) 
 
    /// be applied then `None` will be returned. Note that this might mean that 
 
    /// the types are incompatible.
 
    ///
 
    /// As this is a helper functions, some assumptions: the parts are not 
 
    /// exactly equal, and neither of them contains a marker. Also: only the
 
@@ -616,25 +588,31 @@ impl InferenceType {
 
                ITP::Void => CTP::Void,
 
                ITP::Message => CTP::Message,
 
                ITP::Bool => CTP::Bool,
 
                ITP::UInt8 => CTP::UInt8,
 
                ITP::UInt16 => CTP::UInt16,
 
                ITP::UInt32 => CTP::UInt32,
 
                ITP::UInt64 => CTP::UInt64,
 
                ITP::SInt8 => CTP::SInt8,
 
                ITP::SInt16 => CTP::SInt16,
 
                ITP::SInt32 => CTP::SInt32,
 
                ITP::SInt64 => CTP::SInt64,
 
                ITP::Character => CTP::Character,
 
                ITP::String => CTP::String,
 
                ITP::String => {
 
                    // Inferred type has a 'char' subtype to simplify array
 
                    // checking, we remove it here.
 
                    debug_assert_eq!(self.parts[idx + 1], InferenceTypePart::Character);
 
                    idx += 1;
 
                    CTP::String
 
                },
 
                ITP::Array => CTP::Array,
 
                ITP::Slice => CTP::Slice,
 
                ITP::Input => CTP::Input,
 
                ITP::Output => CTP::Output,
 
                ITP::Instance(id, num) => CTP::Instance(*id, *num),
 
            };
 

	
 
            concrete_type.parts.push(converted_part);
 
            idx += 1;
 
        }
 
    }
 

	
 
@@ -666,25 +644,28 @@ impl InferenceType {
 
            }
 
            ITP::Void => buffer.push_str("void"),
 
            ITP::Bool => buffer.push_str(KW_TYPE_BOOL_STR),
 
            ITP::UInt8 => buffer.push_str(KW_TYPE_UINT8_STR),
 
            ITP::UInt16 => buffer.push_str(KW_TYPE_UINT16_STR),
 
            ITP::UInt32 => buffer.push_str(KW_TYPE_UINT32_STR),
 
            ITP::UInt64 => buffer.push_str(KW_TYPE_UINT64_STR),
 
            ITP::SInt8 => buffer.push_str(KW_TYPE_SINT8_STR),
 
            ITP::SInt16 => buffer.push_str(KW_TYPE_SINT16_STR),
 
            ITP::SInt32 => buffer.push_str(KW_TYPE_SINT32_STR),
 
            ITP::SInt64 => buffer.push_str(KW_TYPE_SINT64_STR),
 
            ITP::Character => buffer.push_str(KW_TYPE_CHAR_STR),
 
            ITP::String => buffer.push_str(KW_TYPE_STRING_STR),
 
            ITP::String => {
 
                buffer.push_str(KW_TYPE_STRING_STR);
 
                idx += 1; // skip the 'char' subtype
 
            },
 
            ITP::Message => {
 
                buffer.push_str(KW_TYPE_MESSAGE_STR);
 
                buffer.push('<');
 
                idx = Self::write_display_name(buffer, heap, parts, idx + 1);
 
                buffer.push('>');
 
            },
 
            ITP::Array => {
 
                idx = Self::write_display_name(buffer, heap, parts, idx + 1);
 
                buffer.push_str("[]");
 
            },
 
            ITP::Slice => {
 
                idx = Self::write_display_name(buffer, heap, parts, idx + 1);
 
@@ -1032,27 +1013,27 @@ impl Visitor for PassTyping {
 
    fn visit_function_definition(&mut self, ctx: &mut Ctx, id: FunctionDefinitionId) -> VisitorResult {
 
        self.definition_type = DefinitionType::Function(id);
 

	
 
        let func_def = &ctx.heap[id];
 
        debug_assert_eq!(func_def.poly_vars.len(), self.poly_vars.len(), "function polyvars do not match imposed polyvars");
 

	
 
        debug_log!("{}", "-".repeat(50));
 
        debug_log!("Visiting function '{}': {}", func_def.identifier.value.as_str(), id.0.index);
 
        if debug_log_enabled!() {
 
            debug_log!("Polymorphic variables:");
 
            for (_idx, poly_var) in self.poly_vars.iter().enumerate() {
 
                let mut infer_type_parts = Vec::new();
 
                for concrete_part in &poly_var.parts {
 
                    infer_type_parts.push(InferenceTypePart::from(*concrete_part));
 
                }
 
                Self::determine_inference_type_from_concrete_type(
 
                    &mut infer_type_parts, &poly_var.parts
 
                );
 
                let _infer_type = InferenceType::new(false, true, infer_type_parts);
 
                debug_log!(" - [{:03}] {:?}", _idx, _infer_type.display_name(&ctx.heap));
 
            }
 
        }
 
        debug_log!("{}", "-".repeat(50));
 

	
 
        // Reserve data for expression types
 
        debug_assert!(self.expr_types.is_empty());
 
        self.expr_types.resize(func_def.num_expressions_in_body as usize, Default::default());
 

	
 
        // Visit parameters
 
        for param_id in func_def.parameters.clone() {
 
@@ -1717,34 +1698,56 @@ impl PassTyping {
 
        let expr = &ctx.heap[id];
 
        let arg1_id = expr.left;
 
        let arg2_id = expr.right;
 

	
 
        debug_log!("Binary expr '{:?}': {}", expr.operation, upcast_id.index);
 
        debug_log!(" * Before:");
 
        debug_log!("   - Arg1 type: {}", self.debug_get_display_name(ctx, arg1_id));
 
        debug_log!("   - Arg2 type: {}", self.debug_get_display_name(ctx, arg2_id));
 
        debug_log!("   - Expr type: {}", self.debug_get_display_name(ctx, upcast_id));
 

	
 
        let (progress_expr, progress_arg1, progress_arg2) = match expr.operation {
 
            BO::Concatenate => {
 
                // Arguments may be arrays/slices, output is always an array
 
                let progress_expr = self.apply_template_constraint(ctx, upcast_id, &ARRAY_TEMPLATE)?;
 
                let progress_arg1 = self.apply_template_constraint(ctx, arg1_id, &ARRAYLIKE_TEMPLATE)?;
 
                let progress_arg2 = self.apply_template_constraint(ctx, arg2_id, &ARRAYLIKE_TEMPLATE)?;
 
                // Two cases: if one of the arguments or the output type is a
 
                // string, then all must be strings. Otherwise the arguments
 
                // must be arraylike and the output will be a array.
 
                let (expr_is_str, expr_is_not_str) = self.type_is_certainly_or_certainly_not_string(ctx, upcast_id);
 
                let (arg1_is_str, arg1_is_not_str) = self.type_is_certainly_or_certainly_not_string(ctx, arg1_id);
 
                let (arg2_is_str, arg2_is_not_str) = self.type_is_certainly_or_certainly_not_string(ctx, arg2_id);
 

	
 
                let someone_is_str = expr_is_str || arg1_is_str || arg2_is_str;
 
                let someone_is_not_str = expr_is_not_str || arg1_is_not_str || arg2_is_not_str;
 

	
 
                // Note: this statement is an expression returning the progression bools
 
                if someone_is_str {
 
                    // One of the arguments is a string, then all must be strings
 
                    self.apply_equal3_constraint(ctx, upcast_id, arg1_id, arg2_id, 0)?
 
                } else {
 
                    let progress_expr = if someone_is_not_str {
 
                        // Output must be a normal array
 
                        self.apply_template_constraint(ctx, upcast_id, &ARRAY_TEMPLATE)?
 
                    } else {
 
                        // Output may still be anything
 
                        self.apply_template_constraint(ctx, upcast_id, &ARRAYLIKE_TEMPLATE)?
 
                    };
 

	
 
                    let progress_arg1 = self.apply_template_constraint(ctx, arg1_id, &ARRAYLIKE_TEMPLATE)?;
 
                    let progress_arg2 = self.apply_template_constraint(ctx, arg2_id, &ARRAYLIKE_TEMPLATE)?;
 

	
 
                // If they're all arraylike, then we want the subtype to match
 
                let (subtype_expr, subtype_arg1, subtype_arg2) =
 
                    self.apply_equal3_constraint(ctx, upcast_id, arg1_id, arg2_id, 1)?;
 
                    // If they're all arraylike, then we want the subtype to match
 
                    let (subtype_expr, subtype_arg1, subtype_arg2) =
 
                        self.apply_equal3_constraint(ctx, upcast_id, arg1_id, arg2_id, 1)?;
 

	
 
                (progress_expr || subtype_expr, progress_arg1 || subtype_arg1, progress_arg2 || subtype_arg2)
 
                    (progress_expr || subtype_expr, progress_arg1 || subtype_arg1, progress_arg2 || subtype_arg2)
 
                }
 
            },
 
            BO::LogicalAnd => {
 
                // Forced boolean on all
 
                let progress_expr = self.apply_forced_constraint(ctx, upcast_id, &BOOL_TEMPLATE)?;
 
                let progress_arg1 = self.apply_forced_constraint(ctx, upcast_id, &BOOL_TEMPLATE)?;
 
                let progress_arg2 = self.apply_forced_constraint(ctx, upcast_id, &BOOL_TEMPLATE)?;
 

	
 
                (progress_expr, progress_arg1, progress_arg2)
 
            },
 
            BO::LogicalOr => {
 
                // Forced boolean on all
 
                let progress_expr = self.apply_forced_constraint(ctx, upcast_id, &BOOL_TEMPLATE)?;
 
@@ -1889,37 +1892,54 @@ impl PassTyping {
 
        debug_log!("Slicing expr: {}", upcast_id.index);
 
        debug_log!(" * Before:");
 
        debug_log!("   - Subject type: {}", self.debug_get_display_name(ctx, subject_id));
 
        debug_log!("   - FromIdx type: {}", self.debug_get_display_name(ctx, from_id));
 
        debug_log!("   - ToIdx   type: {}", self.debug_get_display_name(ctx, to_id));
 
        debug_log!("   - Expr    type: {}", self.debug_get_display_name(ctx, upcast_id));
 

	
 
        // Make sure subject is arraylike and indices are of equal integerlike
 
        let progress_subject_base = self.apply_template_constraint(ctx, subject_id, &ARRAYLIKE_TEMPLATE)?;
 
        let progress_idx_base = self.apply_template_constraint(ctx, from_id, &INTEGERLIKE_TEMPLATE)?;
 
        let (progress_from, progress_to) = self.apply_equal2_constraint(ctx, upcast_id, from_id, 0, to_id, 0)?;
 

	
 
        // Make sure if output is of Slice<T> then subject is Array<T>
 
        let progress_expr_base = self.apply_template_constraint(ctx, upcast_id, &SLICE_TEMPLATE)?;
 
        let (progress_expr, progress_subject) =
 
            self.apply_equal2_constraint(ctx, upcast_id, upcast_id, 1, subject_id, 1)?;
 
        let (progress_expr, progress_subject) = match self.type_is_certainly_or_certainly_not_string(ctx, subject_id) {
 
            (true, _) => {
 
                // Certainly a string
 
                (self.apply_forced_constraint(ctx, upcast_id, &STRING_TEMPLATE)?, false)
 
            },
 
            (_, true) => {
 
                // Certainly not a string
 
                let progress_expr_base = self.apply_template_constraint(ctx, upcast_id, &SLICE_TEMPLATE)?;
 
                let (progress_expr, progress_subject) =
 
                    self.apply_equal2_constraint(ctx, upcast_id, upcast_id, 1, subject_id, 1)?;
 

	
 
                (progress_expr_base || progress_expr, progress_subject)
 
            },
 
            _ => {
 
                // Could be anything, at least attempt to progress subtype
 
                let progress_expr_base = self.apply_template_constraint(ctx, upcast_id, &ARRAYLIKE_TEMPLATE)?;
 
                let (progress_expr, progress_subject) =
 
                    self.apply_equal2_constraint(ctx, upcast_id, upcast_id, 1, subject_id, 1)?;
 

	
 
                (progress_expr_base || progress_expr, progress_subject)
 
            }
 
        };
 

	
 
        debug_log!(" * After:");
 
        debug_log!("   - Subject type [{}]: {}", progress_subject_base || progress_subject, self.debug_get_display_name(ctx, subject_id));
 
        debug_log!("   - FromIdx type [{}]: {}", progress_idx_base || progress_from, self.debug_get_display_name(ctx, from_id));
 
        debug_log!("   - ToIdx   type [{}]: {}", progress_idx_base || progress_to, self.debug_get_display_name(ctx, to_id));
 
        debug_log!("   - Expr    type [{}]: {}", progress_expr, self.debug_get_display_name(ctx, upcast_id));
 

	
 
        if progress_expr_base || progress_expr { self.queue_expr_parent(ctx, upcast_id); }
 
        if progress_expr { self.queue_expr_parent(ctx, upcast_id); }
 
        if progress_subject_base || progress_subject { self.queue_expr(ctx, subject_id); }
 
        if progress_idx_base || progress_from { self.queue_expr(ctx, from_id); }
 
        if progress_idx_base || progress_to { self.queue_expr(ctx, to_id); }
 

	
 
        Ok(())
 
    }
 

	
 
    fn progress_select_expr(&mut self, ctx: &mut Ctx, id: SelectExpressionId) -> Result<(), ParseError> {
 
        let upcast_id = id.upcast();
 
        
 
        debug_log!("Select expr: {}", upcast_id.index);
 
        debug_log!(" * Before:");
 
@@ -2671,24 +2691,40 @@ impl PassTyping {
 
    fn queue_expr_parent(&mut self, ctx: &Ctx, expr_id: ExpressionId) {
 
        if let ExpressionParent::Expression(parent_expr_id, _) = &ctx.heap[expr_id].parent() {
 
            let expr_idx = ctx.heap[*parent_expr_id].get_unique_id_in_definition();
 
            self.expr_queued.push_back(expr_idx);
 
        }
 
    }
 

	
 
    fn queue_expr(&mut self, ctx: &Ctx, expr_id: ExpressionId) {
 
        let expr_idx = ctx.heap[expr_id].get_unique_id_in_definition();
 
        self.expr_queued.push_back(expr_idx);
 
    }
 

	
 

	
 
    // first returned is certainly string, second is certainly not
 
    fn type_is_certainly_or_certainly_not_string(&self, ctx: &Ctx, expr_id: ExpressionId) -> (bool, bool) {
 
        let expr_idx = ctx.heap[expr_id].get_unique_id_in_definition();
 
        let expr_type = &self.expr_types[expr_idx as usize].expr_type;
 
        if expr_type.is_done {
 
            if expr_type.parts[0] == InferenceTypePart::String {
 
                return (true, false);
 
            } else {
 
                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),
 
@@ -3403,63 +3439,99 @@ impl PassTyping {
 
                    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); },
 
                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::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());
 

	
 
                        for concrete_part in &self.poly_vars[poly_arg_idx as usize].parts {
 
                            infer_type.push(ITP::from(*concrete_part));
 
                        }
 
                        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;
 
                        infer_type.push(ITP::Marker(poly_arg_idx));
 
                        infer_type.push(ITP::Unknown)
 
                    }
 
                },
 
                PTV::Definition(definition_id, num_embedded) => {
 
                    infer_type.push(ITP::Instance(*definition_id, *num_embedded));
 
                }
 
            }
 
        }
 

	
 
        InferenceType::new(has_markers, !has_inferred, infer_type)
 
    }
 

	
 
    /// Determines the inference type from an already concrete type. Applies the
 
    /// various type "hacks" inside the type inferencer.
 
    fn determine_inference_type_from_concrete_type(parser_type: &mut Vec<InferenceTypePart>, concrete_type: &[ConcreteTypePart]) {
 
        use InferenceTypePart as ITP;
 
        use ConcreteTypePart as CTP;
 

	
 
        for concrete_part in concrete_type {
 
            match concrete_part {
 
                CTP::Void => parser_type.push(ITP::Void),
 
                CTP::Message => parser_type.push(ITP::Message),
 
                CTP::Bool => parser_type.push(ITP::Bool),
 
                CTP::UInt8 => parser_type.push(ITP::UInt8),
 
                CTP::UInt16 => parser_type.push(ITP::UInt16),
 
                CTP::UInt32 => parser_type.push(ITP::UInt32),
 
                CTP::UInt64 => parser_type.push(ITP::UInt64),
 
                CTP::SInt8 => parser_type.push(ITP::SInt8),
 
                CTP::SInt16 => parser_type.push(ITP::SInt16),
 
                CTP::SInt32 => parser_type.push(ITP::SInt32),
 
                CTP::SInt64 => parser_type.push(ITP::SInt64),
 
                CTP::Character => parser_type.push(ITP::Character),
 
                CTP::String => {
 
                    parser_type.push(ITP::String);
 
                    parser_type.push(ITP::Character)
 
                },
 
                CTP::Array => parser_type.push(ITP::Array),
 
                CTP::Slice => parser_type.push(ITP::Slice),
 
                CTP::Input => parser_type.push(ITP::Input),
 
                CTP::Output => parser_type.push(ITP::Output),
 
                CTP::Instance(id, num) => parser_type.push(ITP::Instance(*id, *num)),
 
            }
 
        }
 
    }
 

	
 
    /// Construct an error when an expression's type does not match. This
 
    /// happens if we infer the expression type from its arguments (e.g. the
 
    /// expression type of an addition operator is the type of the arguments)
 
    /// But the expression type was already set due to our parent (e.g. an
 
    /// "if statement" or a "logical not" always expecting a boolean)
 
    fn construct_expr_type_error(
 
        &self, ctx: &Ctx, expr_id: ExpressionId, arg_id: ExpressionId
 
    ) -> ParseError {
 
        // TODO: Expand and provide more meaningful information for humans
 
        let expr = &ctx.heap[expr_id];
 
        let arg_expr = &ctx.heap[arg_id];
 
        let expr_idx = expr.get_unique_id_in_definition();
 
@@ -3760,25 +3832,25 @@ mod tests {
 
    use super::*;
 
    use crate::protocol::arena::Id;
 
    use InferenceTypePart as ITP;
 
    use InferenceType as IT;
 

	
 
    #[test]
 
    fn test_single_part_inference() {
 
        // lhs argument inferred from rhs
 
        let pairs = [
 
            (ITP::NumberLike, ITP::UInt8),
 
            (ITP::IntegerLike, ITP::SInt32),
 
            (ITP::Unknown, ITP::UInt64),
 
            (ITP::Unknown, ITP::String)
 
            (ITP::Unknown, ITP::Bool)
 
        ];
 
        for (lhs, rhs) in pairs.iter() {
 
            // Using infer-both
 
            let mut lhs_type = IT::new(false, false, vec![lhs.clone()]);
 
            let mut rhs_type = IT::new(false, true, vec![rhs.clone()]);
 
            let result = unsafe{ IT::infer_subtrees_for_both_types(
 
                &mut lhs_type, 0, &mut rhs_type, 0
 
            ) };
 
            assert_eq!(DualInferenceResult::First, result);
 
            assert_eq!(lhs_type.parts, rhs_type.parts);
 

	
 
            // Using infer-single
 
@@ -3787,25 +3859,25 @@ mod tests {
 
            let result = IT::infer_subtree_for_single_type(
 
                &mut lhs_type, 0, &rhs_type.parts, 0, false
 
            );
 
            assert_eq!(SingleInferenceResult::Modified, result);
 
            assert_eq!(lhs_type.parts, rhs_type.parts);
 
        }
 
    }
 

	
 
    #[test]
 
    fn test_multi_part_inference() {
 
        let pairs = [
 
            (vec![ITP::ArrayLike, ITP::NumberLike], vec![ITP::Slice, ITP::SInt8]),
 
            (vec![ITP::Unknown], vec![ITP::Input, ITP::Array, ITP::String]),
 
            (vec![ITP::Unknown], vec![ITP::Input, ITP::Array, ITP::String, ITP::Character]),
 
            (vec![ITP::PortLike, ITP::SInt32], vec![ITP::Input, ITP::SInt32]),
 
            (vec![ITP::Unknown], vec![ITP::Output, ITP::SInt32]),
 
            (
 
                vec![ITP::Instance(Id::new(0), 2), ITP::Input, ITP::Unknown, ITP::Output, ITP::Unknown],
 
                vec![ITP::Instance(Id::new(0), 2), ITP::Input, ITP::Array, ITP::SInt32, ITP::Output, ITP::SInt32]
 
            )
 
        ];
 

	
 
        for (lhs, rhs) in pairs.iter() {
 
            let mut lhs_type = IT::new(false, false, lhs.clone());
 
            let mut rhs_type = IT::new(false, true, rhs.clone());
 
            let result = unsafe{ IT::infer_subtrees_for_both_types(
src/protocol/parser/pass_validation_linking.rs
Show inline comments
 
@@ -703,24 +703,31 @@ impl Visitor for PassValidationLinking {
 
        self.visit_expr(ctx, index_expr_id)?;
 

	
 
        self.must_be_assignable = old_assignable;
 
        self.expr_parent = old_expr_parent;
 

	
 
        Ok(())
 
    }
 

	
 
    fn visit_slicing_expr(&mut self, ctx: &mut Ctx, id: SlicingExpressionId) -> VisitorResult {
 
        let upcast_id = id.upcast();
 
        let slicing_expr = &mut ctx.heap[id];
 

	
 
        if let Some(span) = self.must_be_assignable {
 
            // TODO: @Slicing
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module.source, span, "assignment to slices should be valid in the final language, but is currently not implemented"
 
            ));
 
        }
 

	
 
        let subject_expr_id = slicing_expr.subject;
 
        let from_expr_id = slicing_expr.from_index;
 
        let to_expr_id = slicing_expr.to_index;
 

	
 
        let old_expr_parent = self.expr_parent;
 
        slicing_expr.parent = old_expr_parent;
 
        slicing_expr.unique_id_in_definition = self.next_expr_index;
 
        self.next_expr_index += 1;
 

	
 
        self.expr_parent = ExpressionParent::Expression(upcast_id, 0);
 
        self.visit_expr(ctx, subject_expr_id)?;
 

	
src/protocol/parser/token_parsing.rs
Show inline comments
 
@@ -385,28 +385,32 @@ pub(crate) fn consume_string_literal(
 
    if Some(TokenKind::String) != iter.next() {
 
        return Err(ParseError::new_error_str_at_pos(source, iter.last_valid_pos(), "expected a string literal"));
 
    }
 

	
 
    buffer.clear();
 
    let span = iter.next_span();
 
    iter.consume();
 

	
 
    let text = source.section_at_span(span);
 
    if !text.is_ascii() {
 
        return Err(ParseError::new_error_str_at_span(source, span, "expected an ASCII string literal"));
 
    }
 
    buffer.reserve(text.len());
 

	
 
    debug_assert_eq!(text[0], b'"'); // here as kind of a reminder: the span includes the bounding quotation marks
 
    debug_assert_eq!(text[text.len() - 1], b'"');
 

	
 
    buffer.reserve(text.len() - 2);
 

	
 
    let mut was_escape = false;
 
    for idx in 0..text.len() {
 
    for idx in 1..text.len() - 1 {
 
        let cur = text[idx];
 
        if cur != b'\\' {
 
            if was_escape {
 
                let to_push = parse_escaped_character(source, span, cur)?;
 
                buffer.push(to_push);
 
            } else {
 
                buffer.push(cur as char);
 
            }
 
            was_escape = false;
 
        } else {
 
            was_escape = true;
 
        }
src/protocol/tests/eval_operators.rs
Show inline comments
 
@@ -135,13 +135,41 @@ fn test_binary_integer_operators() {
 
    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_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
 
@@ -452,40 +452,40 @@ fn test_explicit_polymorph_argument() {
 
    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 mismatch", "
 
    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 mismatch", "
 
    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
0 comments (0 inline, 0 general)