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