Sitelet https://github.com/starkware-libs/cairo/commit/9cb2c32944e3ad9f30dcf91be9f869922f9ce58d
Skip to content

Commit 9cb2c32

Browse files
feat(semantic): support tuple element access via t.0 syntax
Adds positional tuple element access by index using dot notation (e.g. `t.0`, `t.1`), resolved in the semantic model. Member access is generalized via a new `MemberAccessKind` enum (`Struct{..}` | `Index{..}`) threaded through `ExprMemberAccess`, `ExprVarMemberPath` and `usage::MemberPath`, so a member is identified either by a struct member id or by a numeric tuple index. The index must be a plain non-negative decimal literal (no base prefix or type suffix); out-of-bounds and non-tuple accesses are reported. Lowering of tuple index access is not yet implemented and currently reports an "Unsupported" error; it will be added in a follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent bf991a1 commit 9cb2c32

17 files changed

Lines changed: 695 additions & 177 deletions

File tree

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

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use cairo_lang_syntax::node::TypedStablePtr;
1010
use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
1111
use cairo_lang_utils::{Intern, require};
1212
use itertools::{Itertools, chain, zip_eq};
13-
use semantic::{ConcreteTypeId, ExprVarMemberPath, TypeLongId};
13+
use semantic::{ConcreteTypeId, ExprVarMemberPath, MemberAccessKind, TypeLongId};
1414

1515
use super::context::{LoweredExpr, LoweringContext, LoweringFlowError, LoweringResult, VarRequest};
1616
use super::generators;
@@ -221,8 +221,11 @@ impl<'db> BlockBuilder<'db> {
221221
if let Some(var_id) = ctx.snapped_semantics.get::<MemberPath<'_>>(&member_path.into()) {
222222
return Some(VarUsage { var_id: *var_id, location });
223223
}
224-
let ExprVarMemberPath::Member { parent, member_id, concrete_struct_id, .. } = member_path
225-
else {
224+
let ExprVarMemberPath::Member { parent, kind, .. } = member_path else {
225+
return None;
226+
};
227+
// TODO(TomerStarkware): Support lowering of tuple index access (`t.0`).
228+
let MemberAccessKind::Struct { concrete_struct_id, member_id } = kind else {
226229
return None;
227230
};
228231
let parent_var = self.get_snap_ref(ctx, parent)?;
@@ -419,9 +422,17 @@ fn get_ty<'db>(
419422
) -> semantic::TypeId<'db> {
420423
match member_path {
421424
MemberPath::Var(var) => ctx.semantic_defs[var].ty(),
422-
MemberPath::Member { member_id, concrete_struct_id, .. } => {
423-
ctx.db.concrete_struct_members(*concrete_struct_id).unwrap()[&member_id.name(ctx.db)].ty
424-
}
425+
MemberPath::Member { kind, .. } => match kind {
426+
MemberAccessKind::Struct { concrete_struct_id, member_id } => {
427+
ctx.db.concrete_struct_members(*concrete_struct_id).unwrap()
428+
[&member_id.name(ctx.db)]
429+
.ty
430+
}
431+
// TODO(TomerStarkware): Support lowering of tuple index access (`t.0`).
432+
MemberAccessKind::Index { .. } => {
433+
unreachable!("Tuple index access is not supported in lowering.")
434+
}
435+
},
425436
}
426437
}
427438

@@ -480,7 +491,7 @@ pub type SealedBlockBuilder<'db> = Option<SealedGotoCallsite<'db>>;
480491
pub struct BlockStructRecomposer<'a, 'b, 'db> {
481492
statements: &'a mut StatementsBuilder<'db>,
482493
pub ctx: &'a mut LoweringContext<'db, 'b>,
483-
location: LocationId<'db>,
494+
pub(crate) location: LocationId<'db>,
484495
}
485496
impl<'db> BlockStructRecomposer<'_, '_, 'db> {
486497
pub fn deconstruct(

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

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ use semantic::items::constant::ConstValue;
4444
use semantic::types::wrap_in_snapshots;
4545
use semantic::{
4646
ExprFunctionCallArg, ExprId, ExprPropagateError, ExprVarMemberPath, GenericArgumentId,
47-
MatchArmSelector, SemanticDiagnostic, TypeLongId,
47+
MatchArmSelector, MemberAccessKind, SemanticDiagnostic, TypeLongId,
4848
};
4949

5050
use self::block_builder::{BlockBuilder, SealedBlockBuilder, SealedGotoCallsite};
@@ -1536,12 +1536,11 @@ fn lower_expr_loop<'db>(
15361536
ExprVarMemberPath::Var(var) => {
15371537
ExprVarMemberPath::Var(ExprVar { ty: wrap_in_snapshots(db, var.ty, 1), ..*var })
15381538
}
1539-
ExprVarMemberPath::Member { parent, member_id, stable_ptr, concrete_struct_id, ty } => {
1539+
ExprVarMemberPath::Member { parent, kind, stable_ptr, ty } => {
15401540
ExprVarMemberPath::Member {
15411541
parent: parent.clone(),
1542-
member_id: *member_id,
1542+
kind: kind.clone(),
15431543
stable_ptr: *stable_ptr,
1544-
concrete_struct_id: *concrete_struct_id,
15451544
ty: wrap_in_snapshots(db, *ty, 1),
15461545
}
15471546
}
@@ -1803,19 +1802,23 @@ fn lower_expr_member_access<'db>(
18031802
builder: &mut BlockBuilder<'db>,
18041803
) -> LoweringResult<'db, LoweredExpr<'db>> {
18051804
log::trace!("Lowering a member-access expression: {:?}", expr.debug(&ctx.expr_formatter));
1805+
// TODO(TomerStarkware): Support lowering of tuple index access (`t.0`).
1806+
let MemberAccessKind::Struct { concrete_struct_id, member_id } = expr.kind else {
1807+
return Err(LoweringFlowError::Failed(
1808+
ctx.diagnostics.report(expr.stable_ptr.untyped(), Unsupported),
1809+
));
1810+
};
18061811
if let Some(member_path) = &expr.member_path {
18071812
return Ok(LoweredExpr::MemberPath(
18081813
member_path.clone(),
18091814
ctx.get_location(expr.stable_ptr.untyped()),
18101815
));
18111816
}
18121817
let location = ctx.get_location(expr.stable_ptr.untyped());
1813-
let members = ctx
1814-
.db
1815-
.concrete_struct_members(expr.concrete_struct_id)
1816-
.map_err(LoweringFlowError::Failed)?;
1818+
let members =
1819+
ctx.db.concrete_struct_members(concrete_struct_id).map_err(LoweringFlowError::Failed)?;
18171820
let member_idx =
1818-
members.iter().position(|(_, member)| member.id == expr.member).ok_or_else(|| {
1821+
members.iter().position(|(_, member)| member.id == member_id).ok_or_else(|| {
18191822
LoweringFlowError::Failed(
18201823
ctx.diagnostics.report(expr.stable_ptr.untyped(), UnexpectedError),
18211824
)
@@ -1861,9 +1864,11 @@ fn lower_expr_struct_ctor<'db>(
18611864
};
18621865
let member_path = ExprVarMemberPath::Member {
18631866
parent: Box::new(path.clone()),
1864-
member_id: member.id,
1867+
kind: MemberAccessKind::Struct {
1868+
concrete_struct_id: expr.concrete_struct_id,
1869+
member_id: member.id,
1870+
},
18651871
stable_ptr: path.stable_ptr(),
1866-
concrete_struct_id: expr.concrete_struct_id,
18671872
ty: member.ty,
18681873
};
18691874
entry

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ use cairo_lang_semantic::expr::fmt::ExprFormatter;
44
use cairo_lang_semantic::expr::inference::InferenceError;
55
use cairo_lang_semantic::items::structure::StructSemantic;
66
use cairo_lang_semantic::usage::MemberPath;
7-
use cairo_lang_semantic::{self as semantic};
7+
use cairo_lang_semantic::{self as semantic, MemberAccessKind};
88
use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
99
use cairo_lang_utils::{extract_matches, try_extract_matches};
1010
use itertools::{Itertools, chain};
1111

1212
use super::block_builder::BlockStructRecomposer;
1313
use super::context::VarRequest;
1414
use crate::VariableId;
15+
use crate::diagnostic::{LoweringDiagnosticKind, LoweringDiagnosticsBuilder};
1516
use crate::ids::LocationId;
1617

1718
/// Information about members captured by the closure and their types.
@@ -163,7 +164,15 @@ impl<'db> SemanticLoweringMapping<'db> {
163164
return self.scattered.get_mut(path);
164165
}
165166

166-
let &MemberPath::Member { ref parent, member_id, concrete_struct_id, .. } = path else {
167+
let MemberPath::Member { parent, kind } = path else {
168+
return None;
169+
};
170+
// TODO(TomerStarkware): Support lowering of tuple index access (`t.0`).
171+
let &MemberAccessKind::Struct { member_id, concrete_struct_id } = kind else {
172+
ctx.ctx.diagnostics.report_by_location(
173+
ctx.location.long(ctx.ctx.db).clone(),
174+
LoweringDiagnosticKind::Unsupported,
175+
);
167176
return None;
168177
};
169178

@@ -332,8 +341,7 @@ fn compute_remapped_variables<'db>(
332341
.map(|member_id| {
333342
let member_path = MemberPath::Member {
334343
parent: parent_path.clone().into(),
335-
member_id: *member_id,
336-
concrete_struct_id,
344+
kind: MemberAccessKind::Struct { concrete_struct_id, member_id: *member_id },
337345
};
338346
// Call `compute_remapped_variables` recursively on the scattered values.
339347
// If there is a [Value::Var], `require_remapping` will be set to `true` to account

‎crates/cairo-lang-semantic/src/cache/mod.rs‎

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ use crate::{
5858
ConcreteEnumId, ConcreteExternTypeId, ConcreteFunction, ConcreteFunctionWithBodyId,
5959
ConcreteImplId, ConcreteImplLongId, ConcreteStructId, ConcreteTraitId, ConcreteTraitLongId,
6060
ConcreteTypeId, ConcreteVariant, ExprVar, ExprVarMemberPath, FunctionId, FunctionLongId,
61-
GenericArgumentId, GenericParam, MatchArmSelector, TypeId, TypeLongId, ValueSelectorArm,
61+
GenericArgumentId, GenericParam, MatchArmSelector, MemberAccessKind, TypeId, TypeLongId,
62+
ValueSelectorArm,
6263
};
6364

6465
type SemanticCache<'db> = (CrateSemanticCache, SemanticCacheLookups);
@@ -476,13 +477,56 @@ impl FeatureKindCached {
476477
}
477478
}
478479

480+
#[derive(Serialize, Deserialize)]
481+
pub enum MemberAccessKindCached {
482+
Struct { concrete_struct_id: ConcreteStructCached, member_id: LanguageElementCached },
483+
Index { tuple_ty: TypeIdCached, index: usize },
484+
}
485+
impl MemberAccessKindCached {
486+
pub fn new<'db>(
487+
kind: MemberAccessKind<'db>,
488+
ctx: &mut SemanticCacheSavingContext<'db>,
489+
) -> Self {
490+
match kind {
491+
MemberAccessKind::Struct { concrete_struct_id, member_id } => {
492+
MemberAccessKindCached::Struct {
493+
concrete_struct_id: ConcreteStructCached::new(concrete_struct_id, ctx),
494+
member_id: LanguageElementCached::new(member_id, &mut ctx.defs_ctx),
495+
}
496+
}
497+
MemberAccessKind::Index { tuple_ty, index } => {
498+
MemberAccessKindCached::Index { tuple_ty: TypeIdCached::new(tuple_ty, ctx), index }
499+
}
500+
}
501+
}
502+
pub fn get_embedded<'db>(
503+
self,
504+
data: &Arc<SemanticCacheLoadingData<'db>>,
505+
db: &'db dyn Database,
506+
) -> MemberAccessKind<'db> {
507+
match self {
508+
MemberAccessKindCached::Struct { concrete_struct_id, member_id } => {
509+
let (module_id, member_stable_ptr) =
510+
member_id.get_embedded(&data.defs_loading_data);
511+
let member_id = MemberLongId(module_id, MemberPtr(member_stable_ptr)).intern(db);
512+
MemberAccessKind::Struct {
513+
concrete_struct_id: concrete_struct_id.get_embedded(data, db),
514+
member_id,
515+
}
516+
}
517+
MemberAccessKindCached::Index { tuple_ty, index } => {
518+
MemberAccessKind::Index { tuple_ty: tuple_ty.get_embedded(data), index }
519+
}
520+
}
521+
}
522+
}
523+
479524
#[derive(Serialize, Deserialize)]
480525
pub enum ExprVarMemberPathCached {
481526
Var(ExprVarCached),
482527
Member {
483528
parent: Box<ExprVarMemberPathCached>,
484-
member_id: LanguageElementCached,
485-
concrete_struct_id: ConcreteStructCached,
529+
kind: MemberAccessKindCached,
486530
stable_ptr: SyntaxStablePtrIdCached,
487531
ty: TypeIdCached,
488532
},
@@ -496,11 +540,10 @@ impl ExprVarMemberPathCached {
496540
ExprVarMemberPath::Var(var) => {
497541
ExprVarMemberPathCached::Var(ExprVarCached::new(var, ctx))
498542
}
499-
ExprVarMemberPath::Member { parent, member_id, concrete_struct_id, stable_ptr, ty } => {
543+
ExprVarMemberPath::Member { parent, kind, stable_ptr, ty } => {
500544
ExprVarMemberPathCached::Member {
501545
parent: Box::new(ExprVarMemberPathCached::new(*parent, ctx)),
502-
member_id: LanguageElementCached::new(member_id, &mut ctx.defs_ctx),
503-
concrete_struct_id: ConcreteStructCached::new(concrete_struct_id, ctx),
546+
kind: MemberAccessKindCached::new(kind, ctx),
504547
stable_ptr: SyntaxStablePtrIdCached::new(
505548
stable_ptr.untyped(),
506549
&mut ctx.defs_ctx,
@@ -517,21 +560,11 @@ impl ExprVarMemberPathCached {
517560
) -> ExprVarMemberPath<'db> {
518561
match self {
519562
ExprVarMemberPathCached::Var(var) => ExprVarMemberPath::Var(var.get_embedded(data, db)),
520-
ExprVarMemberPathCached::Member {
521-
parent,
522-
member_id,
523-
concrete_struct_id,
524-
stable_ptr,
525-
ty,
526-
} => {
563+
ExprVarMemberPathCached::Member { parent, kind, stable_ptr, ty } => {
527564
let parent = Box::new(parent.get_embedded(data, db));
528-
let (module_id, member_stable_ptr) =
529-
member_id.get_embedded(&data.defs_loading_data);
530-
let member_id = MemberLongId(module_id, MemberPtr(member_stable_ptr)).intern(db);
531565
ExprVarMemberPath::Member {
532566
parent,
533-
member_id,
534-
concrete_struct_id: concrete_struct_id.get_embedded(data, db),
567+
kind: kind.get_embedded(data, db),
535568
stable_ptr: ExprPtr(stable_ptr.get_embedded(&data.defs_loading_data)),
536569
ty: ty.get_embedded(data),
537570
}

0 commit comments

Comments
 (0)