Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 35 additions & 15 deletions crates/cairo-lang-lowering/src/lower/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ use cairo_lang_semantic::items::functions::{
FunctionsSemantic, GenericFunctionId, ImplGenericFunctionId,
};
use cairo_lang_semantic::items::imp::ImplLongId;
use cairo_lang_semantic::items::structure::StructSemantic;
use cairo_lang_semantic::items::structure::{StructSemantic, TypeMemberKind};
use cairo_lang_semantic::items::trt::TraitSemantic;
use cairo_lang_semantic::types::peel_snapshots;
use cairo_lang_semantic::usage::MemberPath;
use cairo_lang_semantic::{
ConcreteFunction, ConcreteTraitLongId, ExprVar, LocalVariable, VarId, corelib,
Expand Down Expand Up @@ -1808,23 +1809,42 @@ fn lower_expr_member_access<'db>(
));
}
let location = ctx.get_location(expr.stable_ptr.untyped());
let members = ctx
.db
.concrete_struct_members(expr.concrete_struct_id)
.map_err(LoweringFlowError::Failed)?;
let member_idx =
members.iter().position(|(_, member)| member.id == expr.member).ok_or_else(|| {
LoweringFlowError::Failed(
ctx.diagnostics.report(expr.stable_ptr.untyped(), UnexpectedError),
)
})?;
let container_ty = ctx.function_body.arenas.exprs[expr.expr].ty();
let (_, container_long_ty) = peel_snapshots(ctx.db, container_ty);
let (member_tys, member_idx) = match &expr.type_member.kind {
TypeMemberKind::StructMember(member_id) => {
let semantic::ConcreteTypeId::Struct(concrete_struct_id) =
extract_matches!(container_long_ty, TypeLongId::Concrete)
else {
unreachable!("Container must be a struct for StructMember access")
};
let members = ctx
.db
.concrete_struct_members(concrete_struct_id)
.map_err(LoweringFlowError::Failed)?;
let member_idx =
members.iter().position(|(_, m)| m.id == *member_id).ok_or_else(|| {
LoweringFlowError::Failed(
ctx.diagnostics.report(expr.stable_ptr.untyped(), UnexpectedError),
)
})?;
let member_tys = members
.iter()
.map(|(_, m)| wrap_in_snapshots(ctx.db, m.ty, expr.n_snapshots))
.collect();
(member_tys, member_idx)
}
TypeMemberKind::TupleElement(idx) => {
let tys = extract_matches!(container_long_ty, TypeLongId::Tuple);
let member_tys =
tys.iter().map(|&ty| wrap_in_snapshots(ctx.db, ty, expr.n_snapshots)).collect();
(member_tys, *idx)
}
};
Ok(LoweredExpr::AtVariable(
generators::StructMemberAccess {
input: lower_expr_to_var_usage(ctx, builder, expr.expr)?,
member_tys: members
.iter()
.map(|(_, member)| wrap_in_snapshots(ctx.db, member.ty, expr.n_snapshots))
.collect(),
member_tys,
member_idx,
location,
}
Expand Down
161 changes: 77 additions & 84 deletions crates/cairo-lang-semantic/src/expr/compute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3760,35 +3760,48 @@ fn try_extract_identifier_from_path<'a>(
}
}

/// Given an expression syntax, if it's an identifier, returns it. Otherwise, returns the proper
/// error.
fn expr_as_identifier<'db>(
ctx: &mut ComputationContext<'db, '_>,
path: &ast::ExprPath<'db>,
db: &'db dyn Database,
) -> Maybe<SmolStrId<'db>> {
let segments_var = path.segments(db);
let mut segments = segments_var.elements(db);
if segments.len() == 1 {
Ok(segments.next().unwrap().identifier(db))
} else {
Err(ctx.diagnostics.report(path.stable_ptr(db), InvalidMemberExpression))
}
}

// TODO(spapini): Consider moving some checks here to the responsibility of the parser.
/// Computes the semantic expression for a dot expression.
fn dot_expr<'db>(
ctx: &mut ComputationContext<'db, '_>,
lexpr: ExprAndId<'db>,
rhs_syntax: ast::Expr<'db>,
stable_ptr: ast::ExprPtr<'db>,
) -> Maybe<Expr<'db>> {
// Find MemberId.
match rhs_syntax {
ast::Expr::Path(expr) => member_access_expr(ctx, lexpr, expr, stable_ptr),
ast::Expr::FunctionCall(expr) => method_call_expr(ctx, lexpr, expr, stable_ptr),
_ => Err(ctx.diagnostics.report(rhs_syntax.stable_ptr(ctx.db), InvalidMemberExpression)),
if let Some(member_name) = match rhs_syntax {
ast::Expr::FunctionCall(expr) => return method_call_expr(ctx, lexpr, expr, stable_ptr),
ast::Expr::Path(ref expr) => path_as_identifier(ctx.db, &expr),
ast::Expr::Literal(ref literal) => literal_as_identifier(ctx.db, &literal),
_ => None,
} {
member_access_expr_by_name(
ctx,
lexpr,
member_name,
rhs_syntax.stable_ptr(ctx.db).untyped(),
stable_ptr,
)
} else {
Err(ctx.diagnostics.report(rhs_syntax.stable_ptr(ctx.db), InvalidMemberExpression))
}
}

/// Given a path syntax, return it as an identifier if possible, otherwise, return None.
fn path_as_identifier<'db>(
db: &'db dyn Database,
path: &ast::ExprPath<'db>,
) -> Option<SmolStrId<'db>> {
Some(path.segments(db).elements(db).exactly_one().ok()?.identifier(db))
}

/// Given a literal syntax, return it as an identifier if possible, otherwise, return None.
fn literal_as_identifier<'db>(
db: &'db dyn Database,
literal: &ast::TerminalLiteralNumber<'db>,
) -> Option<SmolStrId<'db>> {
if let (value, None) = literal.numeric_value_and_suffix(db)? {
Some(SmolStrId::from(db, value.to_usize()?.to_string()))
} else {
None
}
}

Expand Down Expand Up @@ -3933,55 +3946,46 @@ fn method_call_expr<'db>(
expr_function_call(ctx, function_id, named_args, expr.stable_ptr(db), stable_ptr)
}

/// Computes the semantic model of a member access expression (e.g. "expr.member").
fn member_access_expr<'db>(
/// Computes the semantic model of a member access expression for a given member name.
fn member_access_expr_by_name<'db>(
ctx: &mut ComputationContext<'db, '_>,
lexpr: ExprAndId<'db>,
rhs_syntax: ast::ExprPath<'db>,
member_name: SmolStrId<'db>,
name_stable_ptr: SyntaxStablePtrId<'db>,
stable_ptr: ast::ExprPtr<'db>,
) -> Maybe<Expr<'db>> {
let db = ctx.db;

// Find MemberId.
let member_name = expr_as_identifier(ctx, &rhs_syntax, db)?;
let (n_snapshots, long_ty) =
finalized_snapshot_peeled_ty(ctx, lexpr.ty(), rhs_syntax.stable_ptr(db))?;
let (n_snapshots, long_ty) = finalized_snapshot_peeled_ty(ctx, lexpr.ty(), name_stable_ptr)?;

match &long_ty {
TypeLongId::Concrete(_) | TypeLongId::Tuple(_) | TypeLongId::FixedSizeArray { .. } => {
let Some(EnrichedTypeMemberAccess { type_member, deref_functions }) =
get_enriched_type_member_access(ctx, lexpr.clone(), stable_ptr, member_name)?
else {
return Err(ctx.diagnostics.report(
rhs_syntax.stable_ptr(db),
name_stable_ptr,
NoSuchTypeMember { ty: long_ty.intern(ctx.db), member_name },
));
};
// TODO(#7608): handle TypeMemberKind::TupleElement here.
let TypeMemberKind::StructMember(member_id) = type_member.kind else {
return Err(ctx.diagnostics.report(rhs_syntax.stable_ptr(db), Unsupported));
};
let member = semantic::Member {
id: member_id,
ty: type_member.ty,
visibility: type_member.visibility,
};
check_struct_member_is_visible(
ctx,
&member,
rhs_syntax.stable_ptr(db).untyped(),
member_name,
);
let member_path = match &long_ty {
TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id))
if n_snapshots == 0 && deref_functions.is_empty() =>
{
if let TypeMemberKind::StructMember(member_id) = type_member.kind {
let member = semantic::Member {
id: member_id,
ty: type_member.ty,
visibility: type_member.visibility,
};
check_struct_member_is_visible(ctx, &member, name_stable_ptr, member_name);
}
let member_path = match (&type_member.kind, &long_ty) {
(
TypeMemberKind::StructMember(member_id),
TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)),
) if n_snapshots == 0 && deref_functions.is_empty() => {
lexpr.as_member_path().map(|parent| ExprVarMemberPath::Member {
parent: Box::new(parent),
member_id: member.id,
member_id: *member_id,
stable_ptr,
concrete_struct_id: *concrete_struct_id,
ty: member.ty,
ty: type_member.ty,
})
}
_ => None,
Expand All @@ -4000,19 +4004,12 @@ fn member_access_expr<'db>(
derefed_expr =
ExprAndId { expr: cur_expr.clone(), id: ctx.arenas.exprs.alloc(cur_expr) };
}
let (n_snapshots, long_ty) =
finalized_snapshot_peeled_ty(ctx, derefed_expr.ty(), rhs_syntax.stable_ptr(db))?;
let derefed_expr_concrete_struct_id = match long_ty {
TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)) => {
concrete_struct_id
}
_ => unreachable!(),
};
let ty = wrap_in_snapshots(ctx.db, member.ty, n_snapshots);
let (n_snapshots, _) =
finalized_snapshot_peeled_ty(ctx, derefed_expr.ty(), name_stable_ptr)?;
let ty = wrap_in_snapshots(ctx.db, type_member.ty, n_snapshots);
let mut final_expr = Expr::MemberAccess(ExprMemberAccess {
expr: derefed_expr.id,
concrete_struct_id: derefed_expr_concrete_struct_id,
member: member.id,
type_member,
ty,
member_path,
n_snapshots,
Expand All @@ -4031,20 +4028,15 @@ fn member_access_expr<'db>(
Ok(final_expr)
}

TypeLongId::Snapshot(_) => {
// TODO(spapini): Handle snapshot members.
Err(ctx.diagnostics.report(rhs_syntax.stable_ptr(db), Unsupported))
}
TypeLongId::Closure(_) => {
Err(ctx.diagnostics.report(rhs_syntax.stable_ptr(db), Unsupported))
}
TypeLongId::Snapshot(_) => Err(ctx.diagnostics.report(name_stable_ptr, Unsupported)),
TypeLongId::Closure(_) => Err(ctx.diagnostics.report(name_stable_ptr, Unsupported)),
TypeLongId::Var(_) => Err(ctx.diagnostics.report(
rhs_syntax.stable_ptr(db),
name_stable_ptr,
InternalInferenceError(InferenceError::TypeNotInferred(long_ty.intern(ctx.db))),
)),
TypeLongId::ImplType(_) | TypeLongId::GenericParameter(_) | TypeLongId::Coupon(_) => {
Err(ctx.diagnostics.report(
rhs_syntax.stable_ptr(db),
name_stable_ptr,
TypeHasNoMembers { ty: long_ty.intern(ctx.db), member_name },
))
}
Expand Down Expand Up @@ -4090,18 +4082,19 @@ fn get_enriched_type_member_access<'db>(
}
Entry::Vacant(_) => {
let (_, long_ty) = finalized_snapshot_peeled_ty(ctx, ty, stable_ptr)?;
let members = if let Some(type_members_map) = ctx.db.type_members(long_ty.intern(ctx.db))? {
if let Some(type_member) = type_members_map.get(&accessed_member_name) {
// Found direct member access - so directly returning it.
return Ok(Some(EnrichedTypeMemberAccess {
type_member: type_member.clone(),
deref_functions: vec![],
}));
}
type_members_map.iter().map(|(k, v)| (*k, (v.clone(), 0))).collect()
} else {
Default::default()
};
let members =
if let Some(type_members_map) = ctx.db.type_members(long_ty.intern(ctx.db))? {
if let Some(type_member) = type_members_map.get(&accessed_member_name) {
// Found direct member access - so directly returning it.
return Ok(Some(EnrichedTypeMemberAccess {
type_member: type_member.clone(),
deref_functions: vec![],
}));
}
type_members_map.iter().map(|(k, v)| (*k, (v.clone(), 0))).collect()
} else {
Default::default()
};

EnrichedMembers {
members,
Expand Down
4 changes: 2 additions & 2 deletions crates/cairo-lang-semantic/src/expr/inference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ use crate::{
ConcreteEnumId, ConcreteExternTypeId, ConcreteFunction, ConcreteImplId, ConcreteImplLongId,
ConcreteStructId, ConcreteTraitId, ConcreteTraitLongId, ConcreteTypeId, ConcreteVariant,
FunctionId, FunctionLongId, GenericArgumentId, GenericParam, LocalVariable, MatchArmSelector,
Member, Parameter, SemanticObject, Signature, TypeId, TypeLongId, ValueSelectorArm,
add_basic_rewrites, add_expr_rewrites, add_rewrite, semantic_object_for_id,
Member, Parameter, SemanticObject, Signature, TypeId, TypeLongId, TypeMember, TypeMemberKind,
ValueSelectorArm, add_basic_rewrites, add_expr_rewrites, add_rewrite, semantic_object_for_id,
};

pub mod canonic;
Expand Down
4 changes: 2 additions & 2 deletions crates/cairo-lang-semantic/src/expr/objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use salsa::Database;

use super::fmt::ExprFormatter;
use crate::items::constant::ConstValueId;
use crate::items::structure::TypeMember;
use crate::{ConcreteStructId, FunctionId, TypeId, semantic};

/// Defines an arena id type and its behavior for usage in an arena.
Expand Down Expand Up @@ -526,8 +527,7 @@ pub struct ExprStringLiteral<'db> {
#[debug_db(ExprFormatter<'db>)]
pub struct ExprMemberAccess<'db> {
pub expr: semantic::ExprId,
pub concrete_struct_id: ConcreteStructId<'db>,
pub member: MemberId<'db>,
pub type_member: TypeMember<'db>,
pub ty: semantic::TypeId<'db>,
#[hide_field_debug_with_db]
pub member_path: Option<ExprVarMemberPath<'db>>,
Expand Down
18 changes: 14 additions & 4 deletions crates/cairo-lang-semantic/src/expr/semantic_test_data/repr_ptr
Original file line number Diff line number Diff line change
Expand Up @@ -537,13 +537,23 @@ FunctionCall(
expr: Var(
LocalVarId(test::p),
),
concrete_struct_id: test::PPoint,
member: MemberId(test::PPoint::point),
type_member: TypeMember {
ty: test::Point,
visibility: Private,
kind: StructMember(
MemberId(test::PPoint::point),
),
},
ty: test::Point,
},
),
concrete_struct_id: test::Point,
member: MemberId(test::Point::x),
type_member: TypeMember {
ty: core::integer::u32,
visibility: Private,
kind: StructMember(
MemberId(test::Point::x),
),
},
ty: core::integer::u32,
},
),
Expand Down
Loading
Loading