Sitelet https://github.com/starkware-libs/cairo/commit/18033fe55c16e43f2b8583bdba91b51d0df44d51
Skip to content

Commit 18033fe

Browse files
feat(lowering): support tuple element access via t.0 syntax
Implements the lowering half of `t.0` tuple-index member access, building on the semantic support (#10111). Generalizes the member-access machinery from struct-only to any aggregate (struct or tuple): - Replace `concrete_struct_id`-keyed scattering with a `TypeId`-keyed `Scattered`, keyed by `MemberAccessKind` instead of `MemberId`. - Add `aggregate_ty`, `aggregate_members`, and `member_access_components` helpers in `refs.rs` that handle both structs and tuples uniformly. - Handle `MemberAccessKind::Index` in member-access lowering, ref-binding, and struct deconstruct/reconstruct. Adds lowering test_data and corelib tests covering tuple index read, nested access, assignment, `ref` arguments, and access through snapshots.
1 parent 9d86e35 commit 18033fe

6 files changed

Lines changed: 393 additions & 110 deletions

File tree

‎corelib/src/test.cairo‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,6 @@ mod language_features {
4949
mod panics_test;
5050
mod path_attr_test;
5151
mod trait_test;
52+
mod tuple_test;
5253
mod while_test;
5354
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#[test]
2+
fn test_tuple_index_read() {
3+
let t = (1_felt252, 2_u8, 3_u16);
4+
assert_eq!(t.0, 1);
5+
assert_eq!(t.1, 2);
6+
assert_eq!(t.2, 3);
7+
}
8+
9+
#[test]
10+
fn test_tuple_index_nested_read() {
11+
let t = (1_felt252, (2_u8, 3_u16));
12+
assert_eq!(t.0, 1);
13+
assert_eq!(t.1.0, 2);
14+
assert_eq!(t.1.1, 3);
15+
}
16+
17+
#[test]
18+
fn test_tuple_index_assignment() {
19+
let mut t = (1_felt252, 2_u8);
20+
t.0 = 5;
21+
t.1 = 7;
22+
assert_eq!(t.0, 5);
23+
assert_eq!(t.1, 7);
24+
}
25+
26+
#[test]
27+
fn test_tuple_index_nested_assignment() {
28+
let mut t = (1_felt252, (2_u8, 3_u16));
29+
t.1.0 = 20;
30+
t.1.1 = 30;
31+
assert_eq!(t.0, 1);
32+
assert_eq!(t.1.0, 20);
33+
assert_eq!(t.1.1, 30);
34+
}
35+
36+
fn set_to_seven(ref x: felt252) {
37+
x = 7;
38+
}
39+
40+
#[test]
41+
fn test_tuple_index_ref_argument() {
42+
let mut t = (1_felt252, 2_u8);
43+
set_to_seven(ref t.0);
44+
assert_eq!(t.0, 7);
45+
assert_eq!(t.1, 2);
46+
}
47+
48+
#[test]
49+
fn test_tuple_index_through_snapshot() {
50+
let t = (1_felt252, 2_u8);
51+
let s = @t;
52+
// In the corelib edition, member access through a snapshot auto-desnaps the element.
53+
assert_eq!(s.0, 1);
54+
assert_eq!(s.1, 2);
55+
}

‎crates/cairo-lang-lowering/src/lower/block_builder.rs‎

Lines changed: 23 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
use cairo_lang_debug::DebugWithDb;
2-
use cairo_lang_defs::ids::{MemberId, NamedLanguageElementId};
2+
use cairo_lang_defs::ids::NamedLanguageElementId;
33
use cairo_lang_diagnostics::{DiagnosticNote, Maybe};
44
use cairo_lang_semantic as semantic;
55
use cairo_lang_semantic::expr::fmt::ExprFormatter;
66
use cairo_lang_semantic::items::structure::StructSemantic;
7-
use cairo_lang_semantic::types::{peel_snapshots, wrap_in_snapshots};
87
use cairo_lang_semantic::usage::{MemberPath, Usage};
98
use cairo_lang_syntax::node::TypedStablePtr;
109
use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
11-
use cairo_lang_utils::{Intern, require};
10+
use cairo_lang_utils::require;
1211
use itertools::{Itertools, chain, zip_eq};
13-
use semantic::{ConcreteTypeId, ExprVarMemberPath, MemberAccessKind, TypeLongId};
12+
use semantic::{ExprVarMemberPath, MemberAccessKind, TypeLongId};
1413

1514
use super::context::{LoweredExpr, LoweringContext, LoweringFlowError, LoweringResult, VarRequest};
1615
use super::generators;
1716
use super::generators::StatementsBuilder;
18-
use super::refs::{AssembleValueError, MovedVar, SemanticLoweringMapping, merge_semantics};
17+
use super::refs::{
18+
AssembleValueError, MovedVar, SemanticLoweringMapping, aggregate_members,
19+
member_access_components, merge_semantics,
20+
};
1921
use crate::diagnostic::{LoweringDiagnosticKind, LoweringDiagnosticsBuilder};
2022
use crate::ids::LocationId;
2123
use crate::lower::refs::ClosureInfo;
@@ -225,28 +227,12 @@ impl<'db> BlockBuilder<'db> {
225227
let ExprVarMemberPath::Member { parent, kind, .. } = member_path else {
226228
return None;
227229
};
228-
// TODO(TomerStarkware): Support lowering of tuple index access (`t.0`).
229-
let MemberAccessKind::Struct { concrete_struct_id, member_id } = kind else {
230-
return None;
231-
};
232230
let parent_var = self.get_snap_ref(ctx, parent)?;
233-
let members = ctx.db.concrete_struct_members(*concrete_struct_id).ok()?;
234-
let (parent_number_of_snapshots, _) =
235-
peel_snapshots(ctx.db, ctx.variables[parent_var.var_id].ty);
236-
let member_idx = members.iter().position(|(_, member)| member.id == *member_id)?;
231+
let (member_tys, member_idx) =
232+
member_access_components(ctx, ctx.variables[parent_var.var_id].ty, kind)?;
237233
Some(
238-
generators::StructMemberAccess {
239-
input: parent_var,
240-
member_tys: members
241-
.iter()
242-
.map(|(_, member)| {
243-
wrap_in_snapshots(ctx.db, member.ty, parent_number_of_snapshots)
244-
})
245-
.collect(),
246-
member_idx,
247-
location,
248-
}
249-
.add(ctx, &mut self.statements),
234+
generators::StructMemberAccess { input: parent_var, member_tys, member_idx, location }
235+
.add(ctx, &mut self.statements),
250236
)
251237
}
252238

@@ -429,9 +415,11 @@ fn get_ty<'db>(
429415
[&member_id.name(ctx.db)]
430416
.ty
431417
}
432-
// TODO(TomerStarkware): Support lowering of tuple index access (`t.0`).
433-
MemberAccessKind::Index { .. } => {
434-
unreachable!("Tuple index access is not supported in lowering.")
418+
MemberAccessKind::Index { tuple_ty, index } => {
419+
let TypeLongId::Tuple(tys) = tuple_ty.long(ctx.db) else {
420+
unreachable!("Tuple index access on a non-tuple type.");
421+
};
422+
tys[*index]
435423
}
436424
},
437425
}
@@ -492,21 +480,18 @@ pub type SealedBlockBuilder<'db> = Option<SealedGotoCallsite<'db>>;
492480
pub struct BlockStructRecomposer<'a, 'b, 'db> {
493481
statements: &'a mut StatementsBuilder<'db>,
494482
pub ctx: &'a mut LoweringContext<'db, 'b>,
495-
pub(crate) location: LocationId<'db>,
483+
location: LocationId<'db>,
496484
}
497485
impl<'db> BlockStructRecomposer<'_, '_, 'db> {
498486
pub fn deconstruct(
499487
&mut self,
500-
concrete_struct_id: semantic::ConcreteStructId<'db>,
488+
ty: semantic::TypeId<'db>,
501489
value: VariableId,
502-
) -> OrderedHashMap<MemberId<'db>, VariableId> {
503-
let members = self.ctx.db.concrete_struct_members(concrete_struct_id).unwrap();
504-
let members = members.values().collect_vec();
505-
let member_ids = members.iter().map(|m| m.id);
506-
490+
) -> OrderedHashMap<MemberAccessKind<'db>, VariableId> {
491+
let members = aggregate_members(self.ctx.db, ty);
507492
let member_values =
508-
self.deconstruct_by_types(value, members.iter().map(|member| member.ty));
509-
OrderedHashMap::from_iter(zip_eq(member_ids, member_values))
493+
self.deconstruct_by_types(value, members.iter().map(|(_, member_ty)| *member_ty));
494+
OrderedHashMap::from_iter(zip_eq(members.into_iter().map(|(kind, _)| kind), member_values))
510495
}
511496

512497
pub fn deconstruct_by_types(
@@ -528,11 +513,9 @@ impl<'db> BlockStructRecomposer<'_, '_, 'db> {
528513

529514
pub fn reconstruct(
530515
&mut self,
531-
concrete_struct_id: semantic::ConcreteStructId<'db>,
516+
ty: semantic::TypeId<'db>,
532517
members: Vec<VariableId>,
533518
) -> VariableId {
534-
let ty =
535-
TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)).intern(self.ctx.db);
536519
// TODO(ilya): Is using the `self.location` correct here?
537520
generators::StructConstruct {
538521
inputs: members

‎crates/cairo-lang-lowering/src/lower/mod.rs‎

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use flow_control::lower_graph::lower_graph;
3535
use itertools::{Itertools, chain, izip, zip_eq};
3636
use num_bigint::{BigInt, Sign};
3737
use num_traits::ToPrimitive;
38-
use refs::ClosureInfo;
38+
use refs::{ClosureInfo, member_access_components};
3939
use salsa::Database;
4040
use semantic::corelib::{
4141
core_submodule, get_core_function_id, get_core_ty_by_name, get_function_id, never_ty, unit_ty,
@@ -1803,38 +1803,25 @@ fn lower_expr_member_access<'db>(
18031803
builder: &mut BlockBuilder<'db>,
18041804
) -> LoweringResult<'db, LoweredExpr<'db>> {
18051805
log::trace!("Lowering a member-access expression: {:?}", expr.debug(&ctx.expr_formatter));
1806-
// TODO(TomerStarkware): Support lowering of tuple index access (`t.0`).
1807-
let MemberAccessKind::Struct { concrete_struct_id, member_id } = expr.kind else {
1808-
return Err(LoweringFlowError::Failed(
1809-
ctx.diagnostics.report(expr.stable_ptr.untyped(), Unsupported),
1810-
));
1811-
};
18121806
if let Some(member_path) = &expr.member_path {
18131807
return Ok(LoweredExpr::MemberPath(
18141808
member_path.clone(),
18151809
ctx.get_location(expr.stable_ptr.untyped()),
18161810
));
18171811
}
18181812
let location = ctx.get_location(expr.stable_ptr.untyped());
1819-
let members =
1820-
ctx.db.concrete_struct_members(concrete_struct_id).map_err(LoweringFlowError::Failed)?;
1821-
let member_idx =
1822-
members.iter().position(|(_, member)| member.id == member_id).ok_or_else(|| {
1823-
LoweringFlowError::Failed(
1824-
ctx.diagnostics.report(expr.stable_ptr.untyped(), UnexpectedError),
1825-
)
1826-
})?;
1813+
let input = lower_expr_to_var_usage(ctx, builder, expr.expr)?;
1814+
let (member_tys, member_idx) =
1815+
member_access_components(ctx, ctx.variables[input.var_id].ty, &expr.kind).ok_or_else(
1816+
|| {
1817+
LoweringFlowError::Failed(
1818+
ctx.diagnostics.report(expr.stable_ptr.untyped(), UnexpectedError),
1819+
)
1820+
},
1821+
)?;
18271822
Ok(LoweredExpr::AtVariable(
1828-
generators::StructMemberAccess {
1829-
input: lower_expr_to_var_usage(ctx, builder, expr.expr)?,
1830-
member_tys: members
1831-
.iter()
1832-
.map(|(_, member)| wrap_in_snapshots(ctx.db, member.ty, expr.n_snapshots))
1833-
.collect(),
1834-
member_idx,
1835-
location,
1836-
}
1837-
.add(ctx, &mut builder.statements),
1823+
generators::StructMemberAccess { input, member_tys, member_idx, location }
1824+
.add(ctx, &mut builder.statements),
18381825
))
18391826
}
18401827

0 commit comments

Comments
 (0)