Skip to content

Commit

Permalink
allow for implicit impls for impl impl items (#5915)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomerStarkware authored Jul 1, 2024
1 parent ae5751b commit 50d465c
Show file tree
Hide file tree
Showing 8 changed files with 330 additions and 31 deletions.
2 changes: 0 additions & 2 deletions corelib/src/array.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,6 @@ impl SpanIterator<T> of Iterator<SpanIter<T>> {
#[feature("collections-into-iter")]
impl SpanIntoIterator<T> of core::iter::IntoIterator<Span<T>> {
type IntoIter = SpanIter<T>;
impl Iterator = SpanIterator<T>;
fn into_iter(self: Span<T>) -> SpanIter<T> {
SpanIter { span: self }
}
Expand Down Expand Up @@ -415,7 +414,6 @@ impl ArrayIterator<T> of Iterator<ArrayIter<T>> {
#[feature("collections-into-iter")]
impl ArrayIntoIterator<T> of core::iter::IntoIterator<Array<T>> {
type IntoIter = ArrayIter<T>;
impl Iterator = ArrayIterator<T>;
fn into_iter(self: Array<T>) -> ArrayIter<T> {
ArrayIter { array: self }
}
Expand Down
46 changes: 44 additions & 2 deletions crates/cairo-lang-semantic/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ use crate::items::constant::{ConstValue, ConstValueId, Constant, ImplConstantId}
use crate::items::function_with_body::FunctionBody;
use crate::items::functions::{ImplicitPrecedence, InlineConfiguration};
use crate::items::generics::{GenericParam, GenericParamData, GenericParamsData};
use crate::items::imp::{ImplId, ImplImplId, ImplLookupContext, UninferredImpl};
use crate::items::imp::{
ImplId, ImplImplId, ImplLookupContext, ImplicitImplImplData, UninferredImpl,
};
use crate::items::module::{ModuleItemInfo, ModuleSemanticData};
use crate::items::trt::{
ConcreteTraitGenericFunctionId, ConcreteTraitId, TraitItemConstantData, TraitItemImplData,
Expand Down Expand Up @@ -702,6 +704,13 @@ pub trait SemanticGroup:
#[salsa::invoke(items::imp::impl_item_by_name)]
fn impl_item_by_name(&self, impl_def_id: ImplDefId, name: SmolStr)
-> Maybe<Option<ImplItemId>>;
/// Returns the trait impl of an implicit impl if `name` exists in trait and not in the impl.
#[salsa::invoke(items::imp::impl_implicit_impl_by_name)]
fn impl_implicit_impl_by_name(
&self,
impl_def_id: ImplDefId,
name: SmolStr,
) -> Maybe<Option<TraitImplId>>;
/// Returns the type items in the impl.
#[salsa::invoke(items::imp::impl_types)]
fn impl_types(
Expand Down Expand Up @@ -748,6 +757,14 @@ pub trait SemanticGroup:
impl_def_id: ImplDefId,
trait_impl_id: TraitImplId,
) -> Maybe<ImplImplDefId>;
/// Returns whether `trait_impl_id` is an implicit impl in `impl_def_id`.
#[salsa::invoke(items::imp::is_implicit_impl_impl)]
fn is_implicit_impl_impl(
&self,
impl_def_id: ImplDefId,
trait_impl_id: TraitImplId,
) -> Maybe<bool>;

/// Returns the impl constant item that matches the given trait constant item, if exists.
#[salsa::invoke(items::imp::impl_constant_by_trait_constant)]
fn impl_constant_by_trait_constant(
Expand Down Expand Up @@ -919,7 +936,7 @@ pub trait SemanticGroup:
#[salsa::invoke(items::imp::impl_impl_def_trait_impl)]
fn impl_impl_def_trait_impl(&self, impl_impl_def_id: ImplImplDefId) -> Maybe<TraitImplId>;

/// Returns the resolved impl an impl item impl.
/// Returns the resolved impl of an impl item impl.
#[salsa::invoke(items::imp::impl_impl_def_impl)]
#[salsa::cycle(items::imp::impl_impl_def_impl_cycle)]
fn impl_impl_def_impl(&self, impl_impl_def_id: ImplImplDefId) -> Maybe<ImplId>;
Expand All @@ -939,6 +956,31 @@ pub trait SemanticGroup:
impl_impl_def_id: ImplImplDefId,
) -> Maybe<GenericParamsData>;

/// Returns the semantic diagnostics of an implicit impl.
#[salsa::invoke(items::imp::implicit_impl_impl_semantic_diagnostics)]
fn implicit_impl_impl_semantic_diagnostics(
&self,
impl_def_id: ImplDefId,
trait_impl_id: TraitImplId,
) -> Diagnostics<SemanticDiagnostic>;

/// Returns the resolved impl of an implicit impl.
#[salsa::invoke(items::imp::implicit_impl_impl_impl)]
#[salsa::cycle(items::imp::implicit_impl_impl_impl_cycle)]
fn implicit_impl_impl_impl(
&self,
impl_def_id: ImplDefId,
trait_impl_id: TraitImplId,
) -> Maybe<ImplId>;
// Private query to compute data about an implicit impl.
#[salsa::invoke(items::imp::priv_implicit_impl_impl_semantic_data)]
#[salsa::cycle(items::imp::priv_implicit_impl_impl_semantic_data_cycle)]
fn priv_implicit_impl_impl_semantic_data(
&self,
impl_def_id: ImplDefId,
trait_impl_id: TraitImplId,
) -> Maybe<ImplicitImplImplData>;

// Impl impl.
// ================
/// Returns the implized impl impl if the impl is concrete.
Expand Down
20 changes: 18 additions & 2 deletions crates/cairo-lang-semantic/src/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use cairo_lang_debug::DebugWithDb;
use cairo_lang_defs::diagnostic_utils::StableLocation;
use cairo_lang_defs::ids::{
EnumId, FunctionTitleId, ImplDefId, ImplFunctionId, ModuleItemId, NamedLanguageElementId,
StructId, TopLevelLanguageElementId, TraitFunctionId, TraitId,
StructId, TopLevelLanguageElementId, TraitFunctionId, TraitId, TraitImplId,
};
use cairo_lang_defs::plugin::PluginDiagnostic;
use cairo_lang_diagnostics::{
Expand All @@ -21,8 +21,8 @@ use crate::db::SemanticGroup;
use crate::expr::inference::InferenceError;
use crate::items::feature_kind::FeatureMarkerDiagnostic;
use crate::resolve::ResolvedConcreteItem;
use crate::semantic;
use crate::types::peel_snapshots;
use crate::{semantic, ConcreteTraitId};

#[cfg(test)]
#[path = "diagnostic_test.rs"]
Expand Down Expand Up @@ -119,6 +119,18 @@ impl DiagnosticEntry for SemanticDiagnostic {
trait_id.name(defs_db)
)
}
SemanticDiagnosticKind::ImplicitImplNotInferred {
trait_impl_id,
concrete_trait_id,
} => {
let defs_db = db.upcast();
format!(
"Cannot infer implicit impl `{}.`\nCould not find implementation of trait \
`{:?}`",
trait_impl_id.name(defs_db),
concrete_trait_id.debug(db)
)
}
SemanticDiagnosticKind::GenericsNotSupportedInItem { scope, item_kind } => {
format!("Generic parameters are not supported in {scope} item {item_kind}.")
}
Expand Down Expand Up @@ -876,6 +888,10 @@ pub enum SemanticDiagnosticKind {
trait_id: TraitId,
item_kind: String,
},
ImplicitImplNotInferred {
trait_impl_id: TraitImplId,
concrete_trait_id: ConcreteTraitId,
},
GenericsNotSupportedInItem {
scope: String,
item_kind: String,
Expand Down
147 changes: 146 additions & 1 deletion crates/cairo-lang-semantic/src/items/imp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,9 @@ pub struct ImplDefinitionData {

/// Mapping of item names to their IDs. All the IDs should appear in one of the AST maps above.
item_id_by_name: Arc<OrderedHashMap<SmolStr, ImplItemId>>,

/// Mapping of missing impl names item names to the trait id.
implicit_impls_id_by_name: Arc<OrderedHashMap<SmolStr, TraitImplId>>,
}

// --- Selectors ---
Expand Down Expand Up @@ -632,6 +635,10 @@ pub fn impl_semantic_definition_diagnostics(
for impl_item_impl_id in data.item_impl_asts.keys() {
diagnostics.extend(db.impl_impl_def_semantic_diagnostics(*impl_item_impl_id));
}
for implicit_impl_id in data.implicit_impls_id_by_name.values() {
diagnostics
.extend(db.implicit_impl_impl_semantic_diagnostics(impl_def_id, *implicit_impl_id));
}
// Diagnostics for special traits.
if diagnostics.error_count == 0 {
let trait_id = db
Expand Down Expand Up @@ -758,6 +765,15 @@ pub fn impl_item_by_name(
Ok(db.priv_impl_definition_data(impl_def_id)?.item_id_by_name.get(&name).cloned())
}

/// Query implementation of [crate::db::SemanticGroup::impl_implicit_impl_by_name].
pub fn impl_implicit_impl_by_name(
db: &dyn SemanticGroup,
impl_def_id: ImplDefId,
name: SmolStr,
) -> Maybe<Option<TraitImplId>> {
Ok(db.priv_impl_definition_data(impl_def_id)?.implicit_impls_id_by_name.get(&name).cloned())
}

/// Query implementation of [crate::db::SemanticGroup::impl_types].
pub fn impl_types(
db: &dyn SemanticGroup,
Expand Down Expand Up @@ -882,6 +898,25 @@ pub fn impl_impl_by_trait_impl(
})
}

/// Query implementation of [crate::db::SemanticGroup::is_implicit_impl_impl].
pub fn is_implicit_impl_impl(
db: &dyn SemanticGroup,
impl_def_id: ImplDefId,
trait_impl_id: TraitImplId,
) -> Maybe<bool> {
if trait_impl_id.trait_id(db.upcast()) != db.impl_def_trait(impl_def_id)? {
unreachable!(
"impl_impl_by_trait_impl called with a trait impl that does not belong to the impl's \
trait"
)
}

let defs_db = db.upcast();
let name = trait_impl_id.name(defs_db);
// If the trait impl's name is not found, then a missing item diagnostic is reported.
Ok(db.impl_implicit_impl_by_name(impl_def_id, name)?.is_some())
}

// --- Computation ---

/// Query implementation of [crate::db::SemanticGroup::priv_impl_definition_data].
Expand Down Expand Up @@ -1017,11 +1052,21 @@ pub fn priv_impl_definition_data(
}
}

let mut implicit_impls_id_by_name = OrderedHashMap::default();

let trait_id = concrete_trait.lookup_intern(db).trait_id;
for trait_impl_id in db.trait_impls(trait_id)? {
if item_id_by_name.contains_key(&trait_impl_id.0) {
continue;
}
implicit_impls_id_by_name.insert(trait_impl_id.0, trait_impl_id.1);
}

// It is later verified that all items in this impl match items from `concrete_trait`.
// To ensure exact match (up to trait functions with default implementation), it is sufficient
// to verify here that all items in `concrete_trait` appear in this impl.
let impl_item_names: OrderedHashSet<SmolStr> = item_id_by_name.keys().cloned().collect();
let trait_id = concrete_trait.lookup_intern(db).trait_id;

let trait_required_item_names = db.trait_required_item_names(trait_id)?;
let missing_items_in_impl =
trait_required_item_names.difference(&impl_item_names).cloned().collect::<Vec<_>>();
Expand All @@ -1043,6 +1088,7 @@ pub fn priv_impl_definition_data(
item_id_by_name: item_id_by_name.into(),
item_constant_asts: item_constant_asts.into(),
item_impl_asts: item_impl_asts.into(),
implicit_impls_id_by_name: implicit_impls_id_by_name.into(),
})
}

Expand Down Expand Up @@ -2342,6 +2388,101 @@ fn validate_impl_item_impl(
Ok(trait_impl_id)
}

#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb)]
#[debug_db(dyn SemanticGroup + 'static)]
pub struct ImplicitImplImplData {
resolved_impl: Maybe<ImplId>,
trait_impl_id: TraitImplId,
diagnostics: Diagnostics<SemanticDiagnostic>,
}

/// Query implementation of [crate::db::SemanticGroup::implicit_impl_impl_semantic_diagnostics].
pub fn implicit_impl_impl_semantic_diagnostics(
db: &dyn SemanticGroup,
impl_def_id: ImplDefId,
trait_impl_id: TraitImplId,
) -> Diagnostics<SemanticDiagnostic> {
db.priv_implicit_impl_impl_semantic_data(impl_def_id, trait_impl_id)
.map(|data| data.diagnostics)
.unwrap_or_default()
}
/// Query implementation of [crate::db::SemanticGroup::implicit_impl_impl_impl].
pub fn implicit_impl_impl_impl(
db: &dyn SemanticGroup,
impl_def_id: ImplDefId,
trait_impl_id: TraitImplId,
) -> Maybe<ImplId> {
db.priv_implicit_impl_impl_semantic_data(impl_def_id, trait_impl_id)?.resolved_impl
}
/// Query implementation of [crate::db::SemanticGroup::implicit_impl_impl_impl].
pub fn implicit_impl_impl_impl_cycle(
db: &dyn SemanticGroup,
_cycle: &[String],
impl_def_id: &ImplDefId,
trait_impl_id: &TraitImplId,
) -> Maybe<ImplId> {
db.priv_implicit_impl_impl_semantic_data(*impl_def_id, *trait_impl_id)?.resolved_impl
}

/// Query implementation of [crate::db::SemanticGroup::priv_implicit_impl_impl_semantic_data].
pub fn priv_implicit_impl_impl_semantic_data(
db: &dyn SemanticGroup,
impl_def_id: ImplDefId,
trait_impl_id: TraitImplId,
) -> Maybe<ImplicitImplImplData> {
let mut diagnostics = SemanticDiagnostics::default();
let lookup_item_id = LookupItemId::ModuleItem(ModuleItemId::Impl(impl_def_id));

let inference_id = InferenceId::LookupItemGenerics(lookup_item_id);
let resolver_data = db.impl_def_resolver_data(impl_def_id)?;

let mut resolver =
Resolver::with_data(db, resolver_data.clone_with_inference_id(db, inference_id));
// We cannot use `Self` as it will always find the implicit impl.
resolver.trait_or_impl_ctx = TraitOrImplContext::None;
let trait_impl_concrete_trait =
db.trait_impl_concrete_trait(trait_impl_id).and_then(|concrete_trait_id| {
let impl_def_substitution = db.impl_def_substitution(impl_def_id)?;
SubstitutionRewriter { db, substitution: impl_def_substitution.as_ref() }
.rewrite(concrete_trait_id)
});
let impl_lookup_context = resolver.impl_lookup_context();
let resolved_impl = trait_impl_concrete_trait.map(|concrete_trait_id| {
let imp = resolver.inference().new_impl_var(concrete_trait_id, None, impl_lookup_context);
if let Err((err_set, _)) = resolver.inference().finalize_without_reporting() {
diagnostics.report(
impl_def_id.stable_ptr(db.upcast()).untyped(),
ImplicitImplNotInferred { trait_impl_id, concrete_trait_id },
);
resolver.inference().report_on_pending_error(
err_set,
&mut diagnostics,
impl_def_id.stable_ptr(db.upcast()).untyped(),
);
};
imp
});

Ok(ImplicitImplImplData { resolved_impl, trait_impl_id, diagnostics: diagnostics.build() })
}
/// Cycle handling for [crate::db::SemanticGroup::priv_implicit_impl_impl_semantic_data].
pub fn priv_implicit_impl_impl_semantic_data_cycle(
db: &dyn SemanticGroup,
_cycle: &[String],
impl_def_id: &ImplDefId,
trait_impl_id: &TraitImplId,
) -> Maybe<ImplicitImplImplData> {
// TODO(TomerStarkware): Add test case for implicit impl cycle if possible.
let mut diagnostics = SemanticDiagnostics::default();
let err =
Err(diagnostics.report(impl_def_id.stable_ptr(db.upcast()).untyped(), ImplAliasCycle));
Ok(ImplicitImplImplData {
resolved_impl: err,
trait_impl_id: *trait_impl_id,
diagnostics: diagnostics.build(),
})
}

// === Impl Impl ===

/// Query implementation of [crate::db::SemanticGroup::impl_impl_implized_by_context].
Expand All @@ -2350,6 +2491,10 @@ pub fn impl_impl_implized_by_context(
impl_impl_id: ImplImplId,
impl_def_id: ImplDefId,
) -> Maybe<ImplId> {
if db.is_implicit_impl_impl(impl_def_id, impl_impl_id.trait_impl_id())? {
return db.implicit_impl_impl_impl(impl_def_id, impl_impl_id.trait_impl_id());
}

let impl_impl_def_id = db.impl_impl_by_trait_impl(impl_def_id, impl_impl_id.trait_impl_id())?;

db.impl_impl_def_impl(impl_impl_def_id)
Expand Down
Loading

0 comments on commit 50d465c

Please sign in to comment.