Skip to content

Commit e0ec9a0

Browse files
committed
refactor(semantic): introduce TypeMember abstraction for unified struct/tuple member access
1 parent 244cd0a commit e0ec9a0

4 files changed

Lines changed: 120 additions & 25 deletions

File tree

crates/cairo-lang-semantic/src/expr/compute.rs

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ use crate::items::macro_declaration::{
8181
};
8282
use crate::items::modifiers::compute_mutability;
8383
use crate::items::module::ModuleSemantic;
84-
use crate::items::structure::StructSemantic;
84+
use crate::items::structure::{StructSemantic, TypeMember, TypeMemberKind};
8585
use crate::items::trt::TraitSemantic;
8686
use crate::items::visibility;
8787
use crate::keyword::MACRO_CALL_SITE;
@@ -3949,14 +3949,23 @@ fn member_access_expr<'db>(
39493949

39503950
match &long_ty {
39513951
TypeLongId::Concrete(_) | TypeLongId::Tuple(_) | TypeLongId::FixedSizeArray { .. } => {
3952-
let Some(EnrichedTypeMemberAccess { member, deref_functions }) =
3952+
let Some(EnrichedTypeMemberAccess { type_member, deref_functions }) =
39533953
get_enriched_type_member_access(ctx, lexpr.clone(), stable_ptr, member_name)?
39543954
else {
39553955
return Err(ctx.diagnostics.report(
39563956
rhs_syntax.stable_ptr(db),
39573957
NoSuchTypeMember { ty: long_ty.intern(ctx.db), member_name },
39583958
));
39593959
};
3960+
// TODO(#7608): handle TypeMemberKind::TupleElement here.
3961+
let TypeMemberKind::StructMember(member_id) = type_member.kind else {
3962+
return Err(ctx.diagnostics.report(rhs_syntax.stable_ptr(db), Unsupported));
3963+
};
3964+
let member = semantic::Member {
3965+
id: member_id,
3966+
ty: type_member.ty,
3967+
visibility: type_member.visibility,
3968+
};
39603969
check_struct_member_is_visible(
39613970
ctx,
39623971
&member,
@@ -4081,20 +4090,19 @@ fn get_enriched_type_member_access<'db>(
40814090
}
40824091
Entry::Vacant(_) => {
40834092
let (_, long_ty) = finalized_snapshot_peeled_ty(ctx, ty, stable_ptr)?;
4084-
let members =
4085-
if let TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)) = long_ty {
4086-
let members = ctx.db.concrete_struct_members(concrete_struct_id)?;
4087-
if let Some(member) = members.get(&accessed_member_name) {
4088-
// Found direct member access - so directly returning it.
4089-
return Ok(Some(EnrichedTypeMemberAccess {
4090-
member: member.clone(),
4091-
deref_functions: vec![],
4092-
}));
4093-
}
4094-
members.iter().map(|(k, v)| (*k, (v.clone(), 0))).collect()
4095-
} else {
4096-
Default::default()
4097-
};
4093+
let peeled_ty = long_ty.intern(ctx.db);
4094+
let members = if let Some(type_members_map) = ctx.db.type_members(peeled_ty)? {
4095+
if let Some(type_member) = type_members_map.get(&accessed_member_name) {
4096+
// Found direct member access - so directly returning it.
4097+
return Ok(Some(EnrichedTypeMemberAccess {
4098+
type_member: type_member.clone(),
4099+
deref_functions: vec![],
4100+
}));
4101+
}
4102+
type_members_map.iter().map(|(k, v)| (*k, (v.clone(), 0))).collect()
4103+
} else {
4104+
Default::default()
4105+
};
40984106

40994107
EnrichedMembers {
41004108
members,
@@ -4132,7 +4140,16 @@ fn enrich_members<'db>(
41324140
let members = ctx.db.concrete_struct_members(concrete_struct_id)?;
41334141
for (member_name, member) in members.iter() {
41344142
// Insert member if there is not already a member with the same name.
4135-
enriched.entry(*member_name).or_insert_with(|| (member.clone(), *explored_derefs));
4143+
enriched.entry(*member_name).or_insert_with(|| {
4144+
(
4145+
TypeMember {
4146+
ty: member.ty,
4147+
visibility: member.visibility,
4148+
kind: TypeMemberKind::StructMember(member.id),
4149+
},
4150+
*explored_derefs,
4151+
)
4152+
});
41364153
}
41374154
// If member is contained we can stop the calculation post the lookup.
41384155
if members.contains_key(&accessed_member_name) {

crates/cairo-lang-semantic/src/items/structure.rs

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ use crate::expr::inference::InferenceId;
2222
use crate::expr::inference::canonic::ResultNoErrEx;
2323
use crate::resolve::{Resolver, ResolverData};
2424
use crate::substitution::{GenericSubstitution, SemanticRewriter};
25-
use crate::types::{ConcreteStructId, add_type_based_diagnostics, resolve_type};
26-
use crate::{GenericParam, SemanticDiagnostic, semantic};
25+
use crate::types::{ConcreteStructId, ConcreteTypeId, add_type_based_diagnostics, resolve_type};
26+
use crate::{GenericParam, SemanticDiagnostic, TypeId, TypeLongId, semantic};
2727

2828
#[cfg(test)]
2929
#[path = "structure_test.rs"]
@@ -121,6 +121,76 @@ pub struct Member<'db> {
121121
pub visibility: Visibility,
122122
}
123123

124+
/// A member or element of a type, abstracting over named struct members and positional tuple
125+
/// elements, enabling unified member access across both.
126+
#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, SemanticObject, salsa::Update)]
127+
#[debug_db(dyn Database)]
128+
pub struct TypeMember<'db> {
129+
pub ty: TypeId<'db>,
130+
#[dont_rewrite]
131+
pub visibility: Visibility,
132+
pub kind: TypeMemberKind<'db>,
133+
}
134+
135+
/// Distinguishes between named struct members and positional tuple elements.
136+
#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, SemanticObject, salsa::Update)]
137+
#[debug_db(dyn Database)]
138+
pub enum TypeMemberKind<'db> {
139+
/// A named struct member, identified by its `MemberId`.
140+
StructMember(MemberId<'db>),
141+
/// A positional tuple element at the given index.
142+
TupleElement(#[dont_rewrite] usize),
143+
}
144+
145+
/// Returns the members of a type keyed by their access name, or `None` for types without
146+
/// members.
147+
///
148+
/// For struct types, members are keyed by their declared name. For tuple types, members are keyed
149+
/// by their stringified index ("0", "1", ...).
150+
#[salsa::tracked(returns(ref))]
151+
fn type_members_query<'db>(
152+
db: &'db dyn Database,
153+
ty: TypeId<'db>,
154+
) -> Maybe<Option<OrderedHashMap<SmolStrId<'db>, TypeMember<'db>>>> {
155+
match ty.long(db) {
156+
TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)) => {
157+
let members = db.concrete_struct_members(*concrete_struct_id)?;
158+
Ok(Some(
159+
members
160+
.iter()
161+
.map(|(name, member)| {
162+
(
163+
*name,
164+
TypeMember {
165+
ty: member.ty,
166+
visibility: member.visibility,
167+
kind: TypeMemberKind::StructMember(member.id),
168+
},
169+
)
170+
})
171+
.collect(),
172+
))
173+
}
174+
TypeLongId::Tuple(tys) => Ok(Some(
175+
tys.iter()
176+
.enumerate()
177+
.map(|(i, &ty)| {
178+
let name = SmolStrId::from(db, i.to_string());
179+
(
180+
name,
181+
TypeMember {
182+
ty,
183+
visibility: Visibility::Public,
184+
kind: TypeMemberKind::TupleElement(i),
185+
},
186+
)
187+
})
188+
.collect(),
189+
)),
190+
_ => Ok(None),
191+
}
192+
}
193+
124194
/// Returns the definition data of a struct.
125195
#[salsa::tracked(returns(ref))]
126196
fn struct_definition_data<'db>(
@@ -296,6 +366,14 @@ pub trait StructSemantic<'db>: Database {
296366
) -> Maybe<&'db OrderedHashMap<SmolStrId<'db>, semantic::Member<'db>>> {
297367
concrete_struct_members(self.as_dyn_database(), concrete_struct_id).maybe_as_ref()
298368
}
369+
/// Returns the members of a type keyed by their access name, or `None` for types without
370+
/// members. Works for both struct types and tuples.
371+
fn type_members(
372+
&'db self,
373+
ty: TypeId<'db>,
374+
) -> Maybe<Option<&'db OrderedHashMap<SmolStrId<'db>, TypeMember<'db>>>> {
375+
type_members_query(self.as_dyn_database(), ty).maybe_as_ref().map(|opt| opt.as_ref())
376+
}
299377
}
300378

301379
impl<'db, T: Database + ?Sized> StructSemantic<'db> for T {}

crates/cairo-lang-semantic/src/resolve/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ use crate::types::{
7777
};
7878
use crate::{
7979
ConcreteFunction, ConcreteTypeId, ConcreteVariant, FunctionId, FunctionLongId,
80-
GenericArgumentId, GenericParam, Member, Mutability, TypeId, TypeLongId,
80+
GenericArgumentId, GenericParam, Mutability, TypeId, TypeLongId, TypeMember,
8181
};
8282

8383
#[cfg(test)]
@@ -134,7 +134,7 @@ impl<'db> ResolvedItems<'db> {
134134
pub struct EnrichedMembers<'db> {
135135
/// A map from member names to their semantic representation and the number of deref operations
136136
/// needed to access them.
137-
pub members: OrderedHashMap<SmolStrId<'db>, (Member<'db>, usize)>,
137+
pub members: OrderedHashMap<SmolStrId<'db>, (TypeMember<'db>, usize)>,
138138
/// The sequence of deref needed to access the members.
139139
pub deref_chain: Arc<Vec<DerefInfo<'db>>>,
140140
// The number of derefs that were explored.
@@ -143,9 +143,9 @@ pub struct EnrichedMembers<'db> {
143143
impl<'db> EnrichedMembers<'db> {
144144
/// Returns `EnrichedTypeMemberAccess` for a single member if exists.
145145
pub fn get_member(&self, name: SmolStrId<'db>) -> Option<EnrichedTypeMemberAccess<'db>> {
146-
let (member, n_derefs) = self.members.get(&name)?;
146+
let (type_member, n_derefs) = self.members.get(&name)?;
147147
Some(EnrichedTypeMemberAccess {
148-
member: member.clone(),
148+
type_member: type_member.clone(),
149149
deref_functions: self
150150
.deref_chain
151151
.iter()
@@ -160,7 +160,7 @@ impl<'db> EnrichedMembers<'db> {
160160
/// access it.
161161
pub struct EnrichedTypeMemberAccess<'db> {
162162
/// The member itself.
163-
pub member: Member<'db>,
163+
pub type_member: TypeMember<'db>,
164164
/// The sequence of deref functions needed to access the member.
165165
pub deref_functions: Vec<(FunctionId<'db>, Mutability)>,
166166
}

crates/cairo-lang-semantic/src/semantic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub use crate::items::functions::{
2020
};
2121
pub use crate::items::generics::{GenericArgumentId, GenericParam};
2222
pub use crate::items::imp::{ConcreteImplId, ConcreteImplLongId};
23-
pub use crate::items::structure::Member;
23+
pub use crate::items::structure::{Member, TypeMember, TypeMemberKind};
2424
pub use crate::items::trt::{ConcreteTraitId, ConcreteTraitLongId};
2525
pub use crate::types::{
2626
ConcreteEnumId, ConcreteExternTypeId, ConcreteStructId, ConcreteTypeId, TypeId, TypeLongId,

0 commit comments

Comments
 (0)