diff --git a/src/protocol/parser/pass_typing.rs b/src/protocol/parser/pass_typing.rs index 78a48d4fd57c4119c169cdbc1927f0766d2ca50a..6f40f02bbff94ca6d26ba694fb95e1c6d2e0958b 100644 --- a/src/protocol/parser/pass_typing.rs +++ b/src/protocol/parser/pass_typing.rs @@ -862,7 +862,7 @@ enum InferenceRule { SelectStructField(InferenceRuleSelectStructField), SelectTupleMember(InferenceRuleSelectTupleMember), LiteralStruct(InferenceRuleLiteralStruct), - LiteralEnum(InferenceRuleLiteralEnum), + LiteralEnum, LiteralUnion(InferenceRuleLiteralUnion), LiteralArray(InferenceRuleLiteralArray), LiteralTuple(InferenceRuleLiteralTuple), @@ -881,6 +881,10 @@ impl InferenceRule { union_cast_method_impl!(as_slicing_expr, InferenceRuleSlicingExpr, InferenceRule::SlicingExpr); union_cast_method_impl!(as_select_struct_field, InferenceRuleSelectStructField, InferenceRule::SelectStructField); union_cast_method_impl!(as_select_tuple_member, InferenceRuleSelectTupleMember, InferenceRule::SelectTupleMember); + union_cast_method_impl!(as_literal_struct, InferenceRuleLiteralStruct, InferenceRule::LiteralStruct); + union_cast_method_impl!(as_literal_union, InferenceRuleLiteralUnion, InferenceRule::LiteralUnion); + union_cast_method_impl!(as_literal_array, InferenceRuleLiteralArray, InferenceRule::LiteralArray); + union_cast_method_impl!(as_literal_tuple, InferenceRuleLiteralTuple, InferenceRule::LiteralTuple); } struct InferenceRuleTemplate { @@ -975,10 +979,6 @@ struct InferenceRuleLiteralStruct { element_indices: Vec, } -struct InferenceRuleLiteralEnum { - -} - struct InferenceRuleLiteralUnion { element_indices: Vec } @@ -2382,6 +2382,8 @@ impl PassTyping { if progress_subject_1 || progress_subject_2 { self.queue_node(subject_index); } if progress_field_1 || progress_field_2 { self.queue_node_parent(node_index); } + poly_progress_section.forget(); + return Ok(()) } @@ -2428,12 +2430,206 @@ impl PassTyping { } }; + // If here then we at least have the tuple size. Now check if the + // index doesn't exceed that size. + if tuple_member_index >= tuple_size as u64 { + let select_expr_span = ctx.heap[node.expr_id].full_span(); + return Err(ParseError::new_error_at_span( + &ctx.module().source, select_expr_span, format!( + "element index {} is out of bounds, tuple has {} elements", + tuple_member_index, tuple_size + ) + )); + } + + // Within bounds, set index on the type inference node + let node = &mut self.infer_nodes[node_index]; + node.field_or_monomorph_index = tuple_member_index as i32; + } + + // If here then we know we can use `tuple_member_index`. We need to keep + // computing the offset to the subtype, as its value changes during + // inference + let subject_type = &self.infer_nodes[subject_index].expr_type; + let mut selected_member_start_index = 1; // start just after the InferenceTypeElement::Tuple + for _ in 0..tuple_member_index { + selected_member_start_index = InferenceType::find_subtree_end_idx(&subject_type.parts, selected_member_start_index); + } + + let (progress_member, progress_subject) = self.apply_equal2_constraint( + ctx, node_index, node_index, 0, subject_index, selected_member_start_idx + )?; + + if progress_member { self.queue_node_parent(node_index); } + if progress_subject { self.queue_node(subject_index); } + + return Ok(()); + } + + fn progress_inference_rule_literal_struct(&mut self, ctx: &Ctx, node_index: InferNodeIndex) -> Result<(), ParseError> { + let node = &self.infer_nodes[node_index]; + let rule = node.inference_rule.as_literal_struct(); + + // For each of the fields in the literal struct, apply the type equality + // constraint. If the literal is polymorphic, then we try to progress + // their types during this process + let mut poly_progress_section = self.poly_progress_buffer.start_section(); + for (field_index, field_node_index) in rule.element_indices.iter().copied().enumerate() { + let field_expr_id = self.infer_nodes[field_node_index].expr_id; + let (_, progress_field) = self.apply_polydata_equal2_constraint( + ctx, node_index, field_expr_id, "struct field's", + PolyDataTypeIndex::Associated(field_index), 0, + field_node_index, 0, &mut poly_progress_section + )?; + + if progress_field { self.queue_node(field_node_index); } + } + + // Now we do the same thing for the struct literal expression (the type + // of the struct itself). + let (_, progress_literal_1) = self.apply_polydata_equal2_constraint( + ctx, node_index, node.expr_id, "struct literal's", + PolyDataTypeIndex::Returned, 0, node_index, 0, &mut poly_progress_section + )?; + + // And the other way around: if any of our polymorphic variables are + // more specific then they were before, then we forward that information + // back to our struct/fields. + for (field_index, field_node_index) in rule.element_indices.iter().copied().enumerate() { + let progress_field = self.apply_polydata_polyvar_constraint( + ctx, node_index, PolyDataTypeIndex::Associated(field_index), + field_node_index, &poly_progress_section + ); + if progress_field { self.queue_node(field_node_index); } } + let progress_literal_2 = self.apply_polydata_polyvar_constraint( + ctx, node_index, PolyDataTypeIndex::Returned, + node_index, &poly_progress_section + ); + + if progress_literal_1 || progress_literal_2 { self.queue_node_parent(node_index); } + + poly_progress_section.forget(); + return Ok(()) } + fn progress_inference_rule_literal_enum(&mut self, ctx: &Ctx, node_index: InferNodeIndex) -> Result<(), ParseError> { + let node = &self.infer_nodes[node_index]; + let rule = node.inference_rule.as_literal_enum(); + + let mut poly_progress_section = self.poly_progress_buffer.start_section(); + + // An enum literal type is simply, well, the enum's type. However, it + // might still have polymorphic variables, hence the use of `PolyData`. + let (_, progress_literal_1) = self.apply_polydata_equal2_constraint( + ctx, node_index, node.expr_id, "enum literal's", + PolyDataTypeIndex::Returned, 0, node_index, 0, &mut poly_progress_section + )?; + + let progress_literal_2 = self.apply_polydata_polyvar_constraint( + ctx, node_index, PolyDataTypeIndex::Returned, node_index, &poly_progress_section + ); + + if progress_literal_1 || progress_literal_2 { self.queue_node_parent(node_index); } + + poly_progress_section.forget(); + return Ok(()); + } + + fn progress_inference_rule_literal_union(&mut self, ctx: &Ctx, node_index: InferNodeIndex) -> Result<(), ParseError> { + let node = &self.infer_nodes[node_index]; + let rule = node.inference_rule.as_literal_union(); + + // Infer type of any embedded values in the union variant. At the same + // time progress the polymorphic variables associated with the union. + let mut poly_progress_section = self.poly_progress_buffer.start_section(); + + for (embedded_index, embedded_node_index) in rule.element_indices.iter().copied().enumerate() { + let embedded_node_expr_id = self.infer_nodes[embedded_node_index].expr_id; + let (_, progress_embedded) = self.apply_polydata_equal2_constraint( + ctx, node_index, embedded_node_expr_id, "embedded value's", + PolyDataTypeIndex::Associated(embedded_index), 0, + embedded_node_index, 0, &mut poly_progress_section + )?; + + if progress_embedded { self.queue_node(embedded_node_index); } + } + + let (_, progress_literal_1) = self.apply_polydata_equal2_constraint( + ctx, node_index, node.expr_id, "union's", + PolyDataTypeIndex::Returned, 0, node_index, 0, &mut poly_progress_section + )?; + + // Propagate progress in the polymorphic variables to the expressions + // that constitute the union literal. + for (embedded_index, embedded_node_index) in rule.element_indices.iter().copied().enumerate() { + let progress_embedded = self.apply_polydata_polyvar_constraint( + ctx, node_index, PolyDataTypeIndex::Associated(embedded_index), + embedded_node_index, &poly_progress_section + ); + + if progress_embedded { self.queue_node(embedded_node_index); } + } + + let progress_literal_2 = self.apply_polydata_polyvar_constraint( + ctx, node_index, PolyDataTypeIndex::Returned, node_index, &poly_progress_section + ); + + if progress_literal_1 || progress_literal_2 { self.queue_node_parent(node_index); } + + poly_progress_section.forget(); + return Ok(()); + } + + fn progress_inference_rule_literal_array(&mut self, ctx: &Ctx, node_index: InferNodeIndex) -> Result<(), ParseError> { + let node = &self.infer_nodes[node_index]; + let rule = node.inference_rule.as_literal_array(); + + // Apply equality rule to all of the elements that form the array + let argument_node_indices = self.index_buffer.start_section_initialized(&rule.element_indices); + let mut argument_progress_section = self.bool_buffer.start_section(); + self.apply_equal_n_constraint(ctx, node_index, &argument_node_indices, &mut argument_progress_section)?; + + debug_assert_eq!(argument_node_indices.len(), argument_progress_section.len()); + for argument_index in 0..argument_node_indices.len() { + let argument_node_index = argument_node_indices[argument_index]; + let progress = argument_progress_section[argument_index]; + + if progress { self.queue_node(argument_node_index); } + } + + // If elements are of type `T`, then the array is of type `Array`, so: + let mut progress_literal = self.apply_template_constraint(ctx, node_index, &ARRAY_TEMPLATE)?; + if argument_node_indices.len() != 0 { + let argument_node_index = argument_node_indices[0]; + let (progress_literal_inner, progress_argument) = self.apply_equal2_constraint( + ctx, node_index, node_index, 1, argument_node_index, 0 + )?; + + progress_literal = progress_literal || progress_literal_inner; + + // It is possible that the `Array` has a more progress `T` then + // the arguments. So in the case we progress our argument type we + // simply queue this rule again + if progress_argument { self.queue_expr(node_index); } + } + + argument_node_indices.forget(); + argument_progress_section.forget(); + + if progress_literal { self.queue_node_parent(node_index); } + return Ok(()); + } + + fn progress_inference_rule_literal_tuple(&mut self, ctx: &Ctx, node_index: InferNodeIndex) -> Result<(), ParseError> { + let node = &self.infer_nodes[node_index]; + let rule = node.inference_rule.as_literal_tuple(); + + } + fn progress_template(&mut self, ctx: &Ctx, node_index: InferNodeIndex, application: InferenceRuleTemplateApplication, template: &[InferenceTypePart]) -> Result { use InferenceRuleTemplateApplication as TA; @@ -4150,15 +4346,15 @@ impl PassTyping { /// Applies equal constraint to N consecutive expressions. The returned /// `progress` vec will contain which expressions were progressed and will - /// have length N - // If you ever + /// have length N. fn apply_equal_n_constraint( - &mut self, ctx: &Ctx, expr_id: ExpressionId, - args: &ScopedSection, progress: &mut ScopedSection + &mut self, ctx: &Ctx, outer_node_index: InferNodeIndex, + arguments: &ScopedSection, progress: &mut ScopedSection ) -> Result<(), ParseError> { - // Early exit + // Depending on the argument perform an early exit. This simplifies + // later logic debug_assert_eq!(progress.len(), 0); - match args.len() { + match arguments.len() { 0 => { // nothing to progress return Ok(()) @@ -4175,50 +4371,46 @@ impl PassTyping { } } - // 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_copied(); - 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.infer_nodes[last_expr_idx as usize].expr_type; - let next_type: *mut _ = &mut self.infer_nodes[next_expr_idx as usize].expr_type; - - let res = unsafe { - InferenceType::infer_subtrees_for_both_types(last_type, 0, next_type, 0) - }; + // We'll start doing pairwise inference for all of the inference nodes + // (node[0] with node[1], then node[1] with node[2], then node[2] ..., + // etc.), so when we're at the end we have `node[N-1]` as the most + // progressed type. + let mut last_index_requiring_inference = 0; - if res == DualInferenceResult::Incompatible { - return Err(self.construct_arg_type_error(ctx, expr_id, last_arg_id, next_arg_id)); - } + for prev_argument_index in 0..arguments.len() - 1 { + let next_argument_index = prev_argument_index + 1; - 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(); + let prev_node_index = arguments[prev_argument_index]; + let next_node_index = arguments[next_argument_index]; + let (prev_progress, next_progress) = self.apply_equal2_constraint( + ctx, outer_node_index, prev_node_index, 0, next_node_index, 0 + )?; - last_arg_id = next_arg_id; - lhs_arg_idx += 1; + if prev_progress { + // Previous node is progress, so every type in front of it needs + // to be reinferred. + progress[prev_argument_index] = true; + last_index_requiring_inference = prev_argument_index; + } + progress[next_argument_index] = next_progress; } - // 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[last_arg_id].get_unique_id_in_definition(); - let last_type: *mut _ = &mut self.infer_nodes[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.infer_nodes[other_arg_expr_idx as usize].expr_type; - unsafe{ - (*arg_type).replace_subtree(0, &(*last_type).parts); + // Apply inference using the most progressed type (the last one) to the + // ones that did not obtain this information during the inference + // process. + let last_argument_node_index = arguments[arguments.len() - 1]; + let last_argument_type: *mut _ = &mut self.infer_nodes[last_argument_node_index].expr_type; + + for argument_index in 0..last_index_requiring_inference { + // We can cheat, we know the LHS is less specific than the right + // hand side, so: + let argument_node_index = arguments[argument_index]; + let argument_type = &mut self.infer_nodes[argument_node_index].expr_type; + unsafe { + // safety: we're dealing with different vectors, so cannot alias + argument_type.replace_subtree(0, &(*last_argument_type).parts); } - progress[arg_idx] = true; + progress[argument_index] = true; } return Ok(());