From 8c870bc3cff3e6b98840c5ace3705150988b2d39 2021-12-15 16:21:15 From: mh Date: 2021-12-15 16:21:15 Subject: [PATCH] Implement tuple element selection --- diff --git a/src/protocol/ast.rs b/src/protocol/ast.rs index 83ae094fbfd5a4aad116353aef5f60c9c8c6c1f5..95195aef473b46c535fb2b034a712da10bf953ca 100644 --- a/src/protocol/ast.rs +++ b/src/protocol/ast.rs @@ -1647,6 +1647,12 @@ pub struct SlicingExpression { pub unique_id_in_definition: i32, } +#[derive(Debug, Clone)] +pub enum SelectKind { + StructField(Identifier), + TupleMember(u64), // u64 is overkill, but space is taken up by `StructField` variant anyway +} + #[derive(Debug, Clone)] pub struct SelectExpression { pub this: SelectExpressionId, @@ -1654,7 +1660,7 @@ pub struct SelectExpression { pub operator_span: InputSpan, // of the '.' pub full_span: InputSpan, // includes subject and field pub subject: ExpressionId, - pub field_name: Identifier, + pub kind: SelectKind, // Validator/Linker pub parent: ExpressionParent, pub unique_id_in_definition: i32, diff --git a/src/protocol/ast_printer.rs b/src/protocol/ast_printer.rs index ba45acba976625fff9d116b837774d4cd276901e..6bdc0dd1171d61344c781fe57b0ee3e296f35098 100644 --- a/src/protocol/ast_printer.rs +++ b/src/protocol/ast_printer.rs @@ -649,7 +649,15 @@ impl ASTWriter { self.kv(indent2).with_s_key("Subject"); self.write_expr(heap, expr.subject, indent3); - self.kv(indent2).with_s_key("Field").with_identifier_val(&expr.field_name); + match &expr.kind { + SelectKind::StructField(field_name) => { + self.kv(indent2).with_s_key("StructField").with_identifier_val(field_name); + }, + SelectKind::TupleMember(member_index) => { + self.kv(indent2).with_s_key("TupleMember").with_disp_val(member_index); + }, + } + self.kv(indent2).with_s_key("Parent") .with_custom_val(|v| write_expression_parent(v, &expr.parent)); }, diff --git a/src/protocol/parser/pass_definitions.rs b/src/protocol/parser/pass_definitions.rs index ab2b18ad81aa014acecabc50db0c7b131aa6c945..0436245808a05644b981900a7705735ba1f7457b 100644 --- a/src/protocol/parser/pass_definitions.rs +++ b/src/protocol/parser/pass_definitions.rs @@ -1317,15 +1317,38 @@ impl PassDefinitions { )); } } else { + // Can be a select expression for struct fields, or a select + // for a tuple element. debug_assert_eq!(token, TokenKind::Dot); let subject = result; - let field_name = consume_ident_interned(&module.source, iter, ctx)?; - let full_span = InputSpan::from_positions( - ctx.heap[subject].full_span().begin, field_name.span.end - ); + let next = iter.next(); + let (select_kind, full_span) = if Some(TokenKind::Integer) == next { + // Tuple member + let (index, index_span) = consume_integer_literal(&module.source, iter, &mut self.buffer)?; + let full_span = InputSpan::from_positions( + ctx.heap[subject].full_span().begin, index_span.end + ); + + (SelectKind::TupleMember(index), full_span) + } else if Some(TokenKind::Ident) == next { + // Struct field + let field_name = consume_ident_interned(&module.source, iter, ctx)?; + + let full_span = InputSpan::from_positions( + ctx.heap[subject].full_span().begin, field_name.span.end + ); + + (SelectKind::StructField(field_name), full_span) + } else { + return Err(ParseError::new_error_str_at_pos( + &module.source, iter.last_valid_pos(), "unexpected token: expected integer or identifier" + )); + }; + result = ctx.heap.alloc_select_expression(|this| SelectExpression{ - this, operator_span, full_span, subject, field_name, + this, operator_span, full_span, subject, + kind: select_kind, parent: ExpressionParent::None, unique_id_in_definition: -1, }).upcast(); diff --git a/src/protocol/parser/pass_typing.rs b/src/protocol/parser/pass_typing.rs index ba38491017f0c0a32f7ecb48c41de6711f5f4475..1ccb132cc8fa8ba49c3e8f6a2f696258c8c88c1f 100644 --- a/src/protocol/parser/pass_typing.rs +++ b/src/protocol/parser/pass_typing.rs @@ -2039,7 +2039,7 @@ impl PassTyping { let infer_expr = &self.expr_types[expr_idx as usize]; let extra_idx = infer_expr.extra_data_idx; - fn determine_inference_type_instance<'a>(types: &'a TypeTable, infer_type: &InferenceType) -> Result, ()> { + fn try_get_definition_id_from_inference_type<'a>(types: &'a TypeTable, infer_type: &InferenceType) -> Result, ()> { for part in &infer_type.parts { if part.is_marker() || !part.is_concrete() { continue; @@ -2064,118 +2064,198 @@ impl PassTyping { Ok(None) } - if infer_expr.field_or_monomorph_idx < 0 { - // We don't know the field or the definition it is pointing to yet - // Not yet known, check if we can determine it - let subject_type = &self.expr_types[subject_expr_idx as usize].expr_type; - let type_def = determine_inference_type_instance(&ctx.types, subject_type); + fn try_get_tuple_size_from_inference_type(infer_type: &InferenceType) -> Result, ()> { + for part in &infer_type.parts { + if part.is_marker() || !part.is_concrete() { + continue; + } - match type_def { - Ok(Some(type_def)) => { - // Subject type is known, check if it is a - // struct and the field exists on the struct - let struct_def = if let DefinedTypeVariant::Struct(struct_def) = &type_def.definition { - struct_def - } else { - return Err(ParseError::new_error_at_span( - &ctx.module().source, select_expr.field_name.span, format!( - "Can only apply field access to structs, got a subject of type '{}'", - subject_type.display_name(&ctx.heap) - ) - )); - }; + if let InferenceTypePart::Tuple(size) = part { + return Ok(Some(*size)); + } else { + // Expected a tuple + return Err(()); + } + } - let mut struct_def_id = None; + // Type is not "defined enough" yet + Ok(None) + } - for (field_def_idx, field_def) in struct_def.fields.iter().enumerate() { - if field_def.identifier == select_expr.field_name { - // Set field definition and index - let infer_expr = &mut self.expr_types[expr_idx as usize]; - infer_expr.field_or_monomorph_idx = field_def_idx as i32; - struct_def_id = Some(type_def.ast_definition); - break; + let (progress_subject, progress_expr) = match &select_expr.kind { + SelectKind::StructField(field_name) => { + // Handle select of a struct's field + if infer_expr.field_or_monomorph_idx < 0 { + // We don't know the field or the definition it is pointing to yet + // Not yet known, check if we can determine it + let subject_type = &self.expr_types[subject_expr_idx as usize].expr_type; + let type_def = try_get_definition_id_from_inference_type(&ctx.types, subject_type); + + match type_def { + Ok(Some(type_def)) => { + // Subject type is known, check if it is a + // struct and the field exists on the struct + let struct_def = if let DefinedTypeVariant::Struct(struct_def) = &type_def.definition { + struct_def + } else { + return Err(ParseError::new_error_at_span( + &ctx.module().source, field_name.span, format!( + "Can only apply field access to structs, got a subject of type '{}'", + subject_type.display_name(&ctx.heap) + ) + )); + }; + + let mut struct_def_id = None; + + for (field_def_idx, field_def) in struct_def.fields.iter().enumerate() { + if field_def.identifier == *field_name { + // Set field definition and index + let infer_expr = &mut self.expr_types[expr_idx as usize]; + infer_expr.field_or_monomorph_idx = field_def_idx as i32; + struct_def_id = Some(type_def.ast_definition); + break; + } + } + + if struct_def_id.is_none() { + let ast_struct_def = ctx.heap[type_def.ast_definition].as_struct(); + return Err(ParseError::new_error_at_span( + &ctx.module().source, field_name.span, format!( + "this field does not exist on the struct '{}'", + ast_struct_def.identifier.value.as_str() + ) + )) + } + + // Encountered definition and field index for the + // first time + self.insert_initial_select_polymorph_data(ctx, id, struct_def_id.unwrap()); + }, + Ok(None) => { + // Type of subject is not yet known, so we + // cannot make any progress yet + return Ok(()) + }, + Err(()) => { + return Err(ParseError::new_error_at_span( + &ctx.module().source, field_name.span, format!( + "Can only apply field access to structs, got a subject of type '{}'", + subject_type.display_name(&ctx.heap) + ) + )); } } + } - if struct_def_id.is_none() { - let ast_struct_def = ctx.heap[type_def.ast_definition].as_struct(); - return Err(ParseError::new_error_at_span( - &ctx.module().source, select_expr.field_name.span, format!( - "this field does not exist on the struct '{}'", - ast_struct_def.identifier.value.as_str() - ) - )) - } + // If here then field index is known, and the referenced struct type + // information is inserted into `extra_data`. Check to see if we can + // do some mutual inference. + let poly_data = &mut self.extra_data[extra_idx as usize]; + let mut poly_progress = HashSet::new(); - // Encountered definition and field index for the - // first time - self.insert_initial_select_polymorph_data(ctx, id, struct_def_id.unwrap()); - }, - Ok(None) => { - // Type of subject is not yet known, so we - // cannot make any progress yet - return Ok(()) - }, - Err(()) => { - return Err(ParseError::new_error_at_span( - &ctx.module().source, select_expr.field_name.span, format!( - "Can only apply field access to structs, got a subject of type '{}'", - subject_type.display_name(&ctx.heap) - ) - )); + // Apply to struct's type + let signature_type: *mut _ = &mut poly_data.embedded[0]; + let subject_type: *mut _ = &mut self.expr_types[subject_expr_idx as usize].expr_type; + + let (_, progress_subject) = Self::apply_equal2_signature_constraint( + ctx, upcast_id, Some(subject_id), poly_data, &mut poly_progress, + signature_type, 0, subject_type, 0 + )?; + + if progress_subject { + self.expr_queued.push_back(subject_expr_idx); } - } - } - // If here then field index is known, and the referenced struct type - // information is inserted into `extra_data`. Check to see if we can - // do some mutual inference. - let poly_data = &mut self.extra_data[extra_idx as usize]; - let mut poly_progress = HashSet::new(); + // Apply to field's type + let signature_type: *mut _ = &mut poly_data.returned; + let expr_type: *mut _ = &mut self.expr_types[expr_idx as usize].expr_type; - // Apply to struct's type - let signature_type: *mut _ = &mut poly_data.embedded[0]; - let subject_type: *mut _ = &mut self.expr_types[subject_expr_idx as usize].expr_type; + let (_, progress_expr) = Self::apply_equal2_signature_constraint( + ctx, upcast_id, None, poly_data, &mut poly_progress, + signature_type, 0, expr_type, 0 + )?; - let (_, progress_subject) = Self::apply_equal2_signature_constraint( - ctx, upcast_id, Some(subject_id), poly_data, &mut poly_progress, - signature_type, 0, subject_type, 0 - )?; + if progress_expr { + if let Some(parent_id) = ctx.heap[upcast_id].parent_expr_id() { + let parent_idx = ctx.heap[parent_id].get_unique_id_in_definition(); + self.expr_queued.push_back(parent_idx); + } + } - if progress_subject { - self.expr_queued.push_back(subject_expr_idx); - } + // Reapply progress in polymorphic variables to struct's type + let signature_type: *mut _ = &mut poly_data.embedded[0]; + let subject_type: *mut _ = &mut self.expr_types[subject_expr_idx as usize].expr_type; - // Apply to field's type - let signature_type: *mut _ = &mut poly_data.returned; - let expr_type: *mut _ = &mut self.expr_types[expr_idx as usize].expr_type; + let progress_subject = Self::apply_equal2_polyvar_constraint( + poly_data, &poly_progress, signature_type, subject_type + ); - let (_, progress_expr) = Self::apply_equal2_signature_constraint( - ctx, upcast_id, None, poly_data, &mut poly_progress, - signature_type, 0, expr_type, 0 - )?; + let signature_type: *mut _ = &mut poly_data.returned; + let expr_type: *mut _ = &mut self.expr_types[expr_idx as usize].expr_type; - if progress_expr { - if let Some(parent_id) = ctx.heap[upcast_id].parent_expr_id() { - let parent_idx = ctx.heap[parent_id].get_unique_id_in_definition(); - self.expr_queued.push_back(parent_idx); - } - } + let progress_expr = Self::apply_equal2_polyvar_constraint( + poly_data, &poly_progress, signature_type, expr_type + ); - // Reapply progress in polymorphic variables to struct's type - let signature_type: *mut _ = &mut poly_data.embedded[0]; - let subject_type: *mut _ = &mut self.expr_types[subject_expr_idx as usize].expr_type; + (progress_subject, progress_expr) + }, + SelectKind::TupleMember(member_index) => { + let member_index = *member_index; + + if infer_expr.field_or_monomorph_idx < 0 { + // We don't know what kind of tuple we're accessing yet + let subject_type = &self.expr_types[subject_expr_idx as usize].expr_type; + let tuple_size = try_get_tuple_size_from_inference_type(subject_type); + + match tuple_size { + Ok(Some(enum_size)) => { + // Make sure we don't access an element outside of + // the tuple's bounds + if member_index >= enum_size as u64 { + return Err(ParseError::new_error_at_span( + &ctx.module().source, select_expr.full_span, format!( + "element index {} is out of bounds, tuple has {} elements", + member_index, enum_size + ) + )); + } + + // Within bounds, so set the index (such that we + // will not perform this lookup again) + let infer_expr = &mut self.expr_types[expr_idx as usize]; + infer_expr.field_or_monomorph_idx = member_index as i32; + }, + Ok(None) => { + // Nothing is known about the tuple yet + return Ok(()); + }, + Err(()) => { + return Err(ParseError::new_error_at_span( + &ctx.module().source, select_expr.full_span, format!( + "Can only apply tuple element selection to tuples, got a subject of type '{}'", + subject_type.display_name(&ctx.heap) + ) + )); + } + } + } - let progress_subject = Self::apply_equal2_polyvar_constraint( - poly_data, &poly_progress, signature_type, subject_type - ); + // If here then we know which member we're accessing. So seek + // that member in the subject type and apply inference. + let subject_type = &self.expr_types[subject_expr_idx as usize].expr_type; + let mut member_start_idx = 1; + for _ in 0..member_index { + member_start_idx = InferenceType::find_subtree_end_idx(&subject_type.parts, member_start_idx); + } - let signature_type: *mut _ = &mut poly_data.returned; - let expr_type: *mut _ = &mut self.expr_types[expr_idx as usize].expr_type; + let (progress_expr, progress_subject) = self.apply_equal2_constraint( + ctx, upcast_id, upcast_id, 0, subject_id, member_start_idx + )?; - let progress_expr = Self::apply_equal2_polyvar_constraint( - poly_data, &poly_progress, signature_type, expr_type - ); + (progress_subject, progress_expr) + }, + }; if progress_subject { self.queue_expr(ctx, subject_id); } if progress_expr { self.queue_expr_parent(ctx, upcast_id); } @@ -3817,10 +3897,14 @@ impl PassTyping { }, Expression::Select(expr) => { let (poly_var, struct_name) = get_poly_var_and_definition_name(ctx, poly_var_idx, poly_data.definition_id); + let field_name = match &expr.kind { + SelectKind::StructField(v) => v, + SelectKind::TupleMember(_) => unreachable!(), // because we're constructing a polymorph error, and tuple access does not deal with polymorphs + }; return ParseError::new_error_at_span( &ctx.module().source, expr.full_span, format!( "Conflicting type for polymorphic variable '{}' while accessing field '{}' of '{}'", - poly_var, expr.field_name.value.as_str(), struct_name + poly_var, field_name.value.as_str(), struct_name ) ) }