Skip to content

Commit e75b089

Browse files
Cache deref chain
1 parent 75af9f6 commit e75b089

File tree

5 files changed

+133
-75
lines changed

5 files changed

+133
-75
lines changed

Diff for: crates/cairo-lang-semantic/src/db.rs

+5
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,11 @@ pub trait SemanticGroup:
926926
impl_type_def_id: ImplTypeDefId,
927927
) -> Maybe<GenericParamsData>;
928928

929+
/// Returns the deref chain and diagnostics for a given type.
930+
#[salsa::invoke(items::imp::deref_chain)]
931+
#[salsa::cycle(items::imp::deref_chain_cycle)]
932+
fn deref_chain(&self, ty: TypeId, try_deref_mut: bool) -> Maybe<items::imp::DerefChain>;
933+
929934
// Impl type.
930935
// ================
931936
/// Returns the implized impl type if the impl is concrete. Returns a TypeId that's not an impl

Diff for: crates/cairo-lang-semantic/src/expr/compute.rs

+22-69
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ use cairo_lang_syntax::node::kind::SyntaxKind;
3131
use cairo_lang_syntax::node::{Terminal, TypedStablePtr, TypedSyntaxNode, ast};
3232
use cairo_lang_utils as utils;
3333
use cairo_lang_utils::ordered_hash_map::{Entry, OrderedHashMap};
34-
use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
3534
use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;
3635
use cairo_lang_utils::unordered_hash_set::UnorderedHashSet;
3736
use cairo_lang_utils::{Intern, LookupIntern, OptionHelper, extract_matches, try_extract_matches};
@@ -51,9 +50,9 @@ use super::pattern::{
5150
PatternOtherwise, PatternTuple, PatternVariable,
5251
};
5352
use crate::corelib::{
54-
CoreTraitContext, core_binary_operator, core_bool_ty, core_unary_operator, deref_mut_trait,
55-
deref_trait, false_literal_expr, get_core_trait, get_usize_ty, never_ty, numeric_literal_trait,
56-
true_literal_expr, try_get_core_ty_by_name, unit_expr, unit_ty, unwrap_error_propagation_type,
53+
CoreTraitContext, core_binary_operator, core_bool_ty, core_unary_operator, false_literal_expr,
54+
get_core_trait, get_usize_ty, never_ty, numeric_literal_trait, true_literal_expr,
55+
try_get_core_ty_by_name, unit_expr, unit_ty, unwrap_error_propagation_type,
5756
};
5857
use crate::db::SemanticGroup;
5958
use crate::diagnostic::SemanticDiagnosticKind::{self, *};
@@ -3040,12 +3039,13 @@ fn get_enriched_type_member_access(
30403039
accessed_member_name: &str,
30413040
) -> Maybe<Option<EnrichedTypeMemberAccess>> {
30423041
let (_, mut long_ty) = peel_snapshots(ctx.db, expr.ty());
3043-
if matches!(long_ty, TypeLongId::Var(_)) {
3044-
// Save some work. ignore the result. The error, if any, will be reported later.
3045-
ctx.resolver.inference().solve().ok();
3046-
long_ty = ctx.resolver.inference().rewrite(long_ty).no_err();
3047-
}
3042+
// Run solver to get as much info on the type as possible.
3043+
// Ignore the result of the `solve()` call - the error, if any, will be
3044+
// reported later.
3045+
ctx.resolver.inference().solve().ok();
3046+
ctx.resolver.inference().internal_rewrite(&mut long_ty).no_err();
30483047
let (_, long_ty) = peel_snapshots_ex(ctx.db, long_ty);
3048+
let ty = long_ty.clone().intern(ctx.db);
30493049
let base_var = match &expr.expr {
30503050
Expr::Var(expr_var) => Some(expr_var.var),
30513051
Expr::MemberAccess(ExprMemberAccess { member_path: Some(member_path), .. }) => {
@@ -3056,15 +3056,14 @@ fn get_enriched_type_member_access(
30563056
let is_mut_var = base_var
30573057
.filter(|var_id| matches!(ctx.semantic_defs.get(var_id), Some(var) if var.is_mut()))
30583058
.is_some();
3059-
let ty = long_ty.clone().intern(ctx.db);
30603059
let key = (ty, is_mut_var);
30613060
let mut enriched_members = match ctx.resolver.type_enriched_members.entry(key) {
30623061
Entry::Occupied(entry) => {
30633062
let e = entry.get();
30643063
match e.get_member(accessed_member_name) {
30653064
Some(value) => return Ok(Some(value)),
30663065
None => {
3067-
if e.exploration_tail.is_none() {
3066+
if e.exploration_done {
30683067
// There's no further exploration to be done, and member was not found.
30693068
return Ok(None);
30703069
}
@@ -3088,10 +3087,10 @@ fn get_enriched_type_member_access(
30883087
} else {
30893088
Default::default()
30903089
};
3091-
EnrichedMembers { members, deref_functions: vec![], exploration_tail: Some(expr.id) }
3090+
EnrichedMembers { members, deref_functions: vec![], exploration_done: false }
30923091
}
30933092
};
3094-
enrich_members(ctx, &mut enriched_members, is_mut_var, stable_ptr, accessed_member_name)?;
3093+
enrich_members(ctx, &mut enriched_members, ty, is_mut_var, stable_ptr, accessed_member_name)?;
30953094
let e = ctx.resolver.type_enriched_members.entry(key).or_insert(enriched_members);
30963095
Ok(e.get_member(accessed_member_name))
30973096
}
@@ -3103,66 +3102,25 @@ fn get_enriched_type_member_access(
31033102
fn enrich_members(
31043103
ctx: &mut ComputationContext<'_>,
31053104
enriched_members: &mut EnrichedMembers,
3105+
ty: TypeId,
31063106
is_mut_var: bool,
31073107
stable_ptr: ast::ExprPtr,
31083108
accessed_member_name: &str,
31093109
) -> Maybe<()> {
3110-
let EnrichedMembers { members: enriched, deref_functions, exploration_tail } = enriched_members;
3111-
let mut visited_types: OrderedHashSet<TypeId> = OrderedHashSet::default();
3112-
3113-
let expr_id =
3114-
exploration_tail.expect("`enrich_members` should be called with a `calc_tail` value.");
3115-
let mut expr = ExprAndId { expr: ctx.arenas.exprs[expr_id].clone(), id: expr_id };
3116-
3117-
let deref_mut_trait_id = deref_mut_trait(ctx.db);
3118-
let deref_trait_id = deref_trait(ctx.db);
3119-
3120-
let compute_deref_method_function_call_data =
3121-
|ctx: &mut ComputationContext<'_>, expr: ExprAndId, use_deref_mut: bool| {
3122-
let deref_trait = if use_deref_mut { deref_mut_trait_id } else { deref_trait_id };
3123-
compute_method_function_call_data(
3124-
ctx,
3125-
&[deref_trait],
3126-
if use_deref_mut { "deref_mut".into() } else { "deref".into() },
3127-
expr.clone(),
3128-
stable_ptr.0,
3129-
None,
3130-
|_, _, _| None,
3131-
|_, _, _| None,
3132-
)
3133-
};
3134-
3135-
// If the variable is mutable, and implements DerefMut, we use DerefMut in the first iteration.
3136-
let mut use_deref_mut = deref_functions.is_empty()
3137-
&& is_mut_var
3138-
&& compute_deref_method_function_call_data(ctx, expr.clone(), true).is_ok();
3110+
let EnrichedMembers { members: enriched, deref_functions, exploration_done } = enriched_members;
31393111

3140-
// This function either finds a member and sets `exploration_tail` or finishes the exploration
3141-
// and leaves that exploration tail as `None`.
3142-
*exploration_tail = None;
3112+
let deref_chain = ctx.db.deref_chain(ty, is_mut_var)?;
31433113

31443114
// Add members of derefed types.
3145-
while let Ok((function_id, _, cur_expr, mutability)) =
3146-
compute_deref_method_function_call_data(ctx, expr, use_deref_mut)
3147-
{
3148-
deref_functions.push((function_id, mutability));
3149-
use_deref_mut = false;
3115+
for deref_info in deref_chain.derefs.iter().skip(deref_functions.len()).cloned() {
3116+
deref_functions.push((deref_info.function_id, deref_info.self_mutability));
31503117
let n_deref = deref_functions.len();
3151-
expr = cur_expr;
3152-
let derefed_expr = expr_function_call(
3153-
ctx,
3154-
function_id,
3155-
vec![NamedArg(expr, None, mutability)],
3156-
stable_ptr,
3157-
stable_ptr,
3158-
)?;
3159-
let ty = ctx.reduce_ty(derefed_expr.ty());
3160-
let (_, long_ty) = finalized_snapshot_peeled_ty(ctx, ty, stable_ptr)?;
3118+
3119+
let (_, long_ty) = finalized_snapshot_peeled_ty(ctx, deref_info.target_ty, stable_ptr)?;
31613120
// If the type is still a variable we stop looking for derefed members.
31623121
if let TypeLongId::Var(_) = long_ty {
31633122
break;
31643123
}
3165-
expr = ExprAndId { expr: derefed_expr.clone(), id: ctx.arenas.exprs.alloc(derefed_expr) };
31663124
if let TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)) = long_ty {
31673125
let members = ctx.db.concrete_struct_members(concrete_struct_id)?;
31683126
for (member_name, member) in members.iter() {
@@ -3171,17 +3129,12 @@ fn enrich_members(
31713129
}
31723130
// If member is contained we can stop the calculation post the lookup.
31733131
if members.contains_key(accessed_member_name) {
3174-
// Found member, so exploration isn't done - setting up the tail.
3175-
*exploration_tail = Some(expr.id);
3176-
break;
3132+
// Found member, so exploration isn't done.
3133+
return Ok(());
31773134
}
31783135
}
3179-
if !visited_types.insert(long_ty.intern(ctx.db)) {
3180-
// Break if we have a cycle. A diagnostic will be reported from the impl and not from
3181-
// member access.
3182-
break;
3183-
}
31843136
}
3137+
*exploration_done = true;
31853138
Ok(())
31863139
}
31873140

Diff for: crates/cairo-lang-semantic/src/expr/inference/solver.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ pub fn enrich_lookup_context(
144144
}
145145

146146
/// Adds the defining module of the type to the lookup context.
147-
fn enrich_lookup_context_with_ty(
147+
pub fn enrich_lookup_context_with_ty(
148148
db: &dyn SemanticGroup,
149149
ty: TypeId,
150150
lookup_context: &mut ImplLookupContext,

Diff for: crates/cairo-lang-semantic/src/items/imp.rs

+103-3
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ use super::visibility::peek_visible_in;
6161
use super::{TraitOrImplContext, resolve_trait_path};
6262
use crate::corelib::{
6363
CoreTraitContext, concrete_destruct_trait, concrete_drop_trait, copy_trait, core_crate,
64-
deref_trait, destruct_trait, drop_trait, fn_once_trait, fn_trait, get_core_trait,
65-
panic_destruct_trait,
64+
deref_mut_trait, deref_trait, destruct_trait, drop_trait, fn_once_trait, fn_trait,
65+
get_core_trait, panic_destruct_trait,
6666
};
6767
use crate::db::{SemanticGroup, get_resolver_data_options};
6868
use crate::diagnostic::SemanticDiagnosticKind::{self, *};
@@ -71,7 +71,7 @@ use crate::expr::compute::{ComputationContext, ContextFunction, Environment, com
7171
use crate::expr::inference::canonic::ResultNoErrEx;
7272
use crate::expr::inference::conform::InferenceConform;
7373
use crate::expr::inference::infers::InferenceEmbeddings;
74-
use crate::expr::inference::solver::SolutionSet;
74+
use crate::expr::inference::solver::{SolutionSet, enrich_lookup_context_with_ty};
7575
use crate::expr::inference::{
7676
ImplVarId, ImplVarTraitItemMappings, Inference, InferenceError, InferenceId,
7777
};
@@ -768,6 +768,106 @@ pub fn impl_semantic_definition_diagnostics(
768768
diagnostics.build()
769769
}
770770

771+
#[derive(Clone, Debug, Eq, PartialEq)]
772+
pub struct DerefChain {
773+
pub derefs: Arc<[DerefInfo]>,
774+
}
775+
776+
#[derive(Clone, Debug, Eq, PartialEq)]
777+
pub struct DerefInfo {
778+
pub function_id: FunctionId,
779+
pub self_mutability: Mutability,
780+
pub target_ty: TypeId,
781+
}
782+
783+
pub fn deref_chain_cycle(
784+
_db: &dyn SemanticGroup,
785+
_cycle: &salsa::Cycle,
786+
_ty: &TypeId,
787+
_try_deref_mut: &bool,
788+
) -> Maybe<DerefChain> {
789+
// `SemanticDiagnosticKind::DerefCycle` will be reported by `deref_impl_diagnostics`.
790+
Maybe::Err(skip_diagnostic())
791+
}
792+
793+
pub fn deref_chain(db: &dyn SemanticGroup, ty: TypeId, try_deref_mut: bool) -> Maybe<DerefChain> {
794+
let mut opt_deref = None;
795+
if try_deref_mut {
796+
opt_deref = get_deref_func_and_target(db, ty, deref_mut_trait(db), "deref_mut".into())?;
797+
}
798+
let self_mutability = if opt_deref.is_some() {
799+
Mutability::Reference
800+
} else {
801+
opt_deref = get_deref_func_and_target(db, ty, deref_trait(db), "deref".into())?;
802+
Mutability::Immutable
803+
};
804+
805+
let Some((function_id, target_ty)) = opt_deref else {
806+
return Ok(DerefChain { derefs: Arc::new([]) });
807+
};
808+
809+
let inner_chain = db.deref_chain(target_ty, false)?;
810+
811+
Ok(DerefChain {
812+
derefs: chain!(
813+
[DerefInfo { function_id, target_ty, self_mutability }],
814+
inner_chain.derefs.iter().cloned()
815+
)
816+
.collect(),
817+
})
818+
}
819+
820+
pub fn get_deref_func_and_target(
821+
db: &dyn SemanticGroup,
822+
ty: TypeId,
823+
deref_trait_id: TraitId,
824+
deref_method: SmolStr,
825+
) -> Result<Option<(FunctionId, TypeId)>, DiagnosticAdded> {
826+
let defs_db = db.upcast();
827+
let mut lookup_context =
828+
ImplLookupContext::new(deref_trait_id.module_file_id(defs_db).0, vec![]);
829+
enrich_lookup_context_with_ty(db, ty, &mut lookup_context);
830+
let concrete_trait = ConcreteTraitLongId {
831+
trait_id: deref_trait_id,
832+
generic_args: vec![GenericArgumentId::Type(ty)],
833+
}
834+
.intern(db);
835+
let Ok(deref_impl) = get_impl_at_context(db, lookup_context, concrete_trait, None) else {
836+
return Ok(None);
837+
};
838+
let concrete_impl_id = match deref_impl.lookup_intern(db) {
839+
ImplLongId::Concrete(concrete_impl_id) => concrete_impl_id,
840+
_ => panic!("Expected concrete impl"),
841+
};
842+
843+
let deref_trait_func =
844+
db.trait_function_by_name(deref_trait_id, deref_method).unwrap().unwrap();
845+
let function_id = FunctionLongId {
846+
function: ConcreteFunction {
847+
generic_function: GenericFunctionId::Impl(ImplGenericFunctionId {
848+
impl_id: deref_impl,
849+
function: deref_trait_func,
850+
}),
851+
generic_args: vec![],
852+
},
853+
}
854+
.intern(db);
855+
856+
let data = db.priv_impl_definition_data(concrete_impl_id.impl_def_id(db)).unwrap();
857+
let mut types_iter = data.item_type_asts.iter();
858+
let (impl_item_type_id, _) = types_iter.next().unwrap();
859+
if types_iter.next().is_some() {
860+
panic!(
861+
"get_impl_based_on_single_impl_type called with an impl that has more than one type"
862+
);
863+
}
864+
let ty = db.impl_type_def_resolved_type(*impl_item_type_id).unwrap();
865+
let ty = SubstitutionRewriter { db, substitution: &concrete_impl_id.substitution(db)? }
866+
.rewrite(ty)?;
867+
868+
Ok(Some((function_id, ty)))
869+
}
870+
771871
/// Reports diagnostic for a deref impl.
772872
fn deref_impl_diagnostics(
773873
db: &dyn SemanticGroup,

Diff for: crates/cairo-lang-semantic/src/resolve/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ use crate::items::{TraitOrImplContext, visibility};
5555
use crate::substitution::{GenericSubstitution, SemanticRewriter};
5656
use crate::types::{ConcreteEnumLongId, ImplTypeId, are_coupons_enabled, resolve_type};
5757
use crate::{
58-
ConcreteFunction, ConcreteTypeId, ConcreteVariant, ExprId, FunctionId, FunctionLongId,
58+
ConcreteFunction, ConcreteTypeId, ConcreteVariant, FunctionId, FunctionLongId,
5959
GenericArgumentId, GenericParam, Member, Mutability, TypeId, TypeLongId,
6060
};
6161

@@ -124,7 +124,7 @@ pub struct EnrichedMembers {
124124
/// from this point.
125125
/// Useful for partial computation of enriching members where a member was already previously
126126
/// found.
127-
pub exploration_tail: Option<ExprId>,
127+
pub exploration_done: bool,
128128
}
129129
impl EnrichedMembers {
130130
/// Returns `EnrichedTypeMemberAccess` for a single member if exists.

0 commit comments

Comments
 (0)