use std::iter::Peekable;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use cairo_lang_defs::ids::{
GenericKind, GenericParamId, GenericTypeId, ImplDefId, LanguageElementId, LookupItemId,
ModuleFileId, ModuleId, ModuleItemId, TraitId, TraitItemId,
};
use cairo_lang_diagnostics::Maybe;
use cairo_lang_filesystem::db::{CORELIB_CRATE_NAME, CrateSettings};
use cairo_lang_filesystem::ids::{CrateId, CrateLongId};
use cairo_lang_proc_macros::DebugWithDb;
use cairo_lang_syntax as syntax;
use cairo_lang_syntax::node::ast::TerminalIdentifier;
use cairo_lang_syntax::node::helpers::PathSegmentEx;
use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
use cairo_lang_syntax::node::{Terminal, TypedSyntaxNode, ast};
use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;
use cairo_lang_utils::{Intern, LookupIntern, extract_matches, require, try_extract_matches};
pub use item::{ResolvedConcreteItem, ResolvedGenericItem};
use itertools::Itertools;
use smol_str::SmolStr;
use syntax::node::TypedStablePtr;
use syntax::node::db::SyntaxGroup;
use syntax::node::helpers::QueryAttrs;
use crate::corelib::{core_submodule, get_submodule};
use crate::db::SemanticGroup;
use crate::diagnostic::SemanticDiagnosticKind::{self, *};
use crate::diagnostic::{NotFoundItemType, SemanticDiagnostics, SemanticDiagnosticsBuilder};
use crate::expr::compute::{
ComputationContext, ContextFunction, Environment, compute_expr_semantic,
get_statement_item_by_name,
};
use crate::expr::inference::canonic::ResultNoErrEx;
use crate::expr::inference::conform::InferenceConform;
use crate::expr::inference::infers::InferenceEmbeddings;
use crate::expr::inference::{Inference, InferenceData, InferenceId};
use crate::items::constant::{ConstValue, ImplConstantId, resolve_const_expr_and_evaluate};
use crate::items::enm::SemanticEnumEx;
use crate::items::feature_kind::{FeatureConfig, FeatureKind, extract_feature_config};
use crate::items::functions::{GenericFunctionId, ImplGenericFunctionId};
use crate::items::generics::generic_params_to_args;
use crate::items::imp::{
ConcreteImplId, ConcreteImplLongId, ImplImplId, ImplLongId, ImplLookupContext,
};
use crate::items::module::ModuleItemInfo;
use crate::items::trt::{
ConcreteTraitConstantLongId, ConcreteTraitGenericFunctionLongId, ConcreteTraitId,
ConcreteTraitImplLongId, ConcreteTraitLongId, ConcreteTraitTypeId,
};
use crate::items::{TraitOrImplContext, visibility};
use crate::substitution::{GenericSubstitution, SemanticRewriter, SubstitutionRewriter};
use crate::types::{ImplTypeId, are_coupons_enabled, resolve_type};
use crate::{
ConcreteFunction, ConcreteTypeId, ExprId, FunctionId, FunctionLongId, GenericArgumentId,
GenericParam, Member, Mutability, TypeId, TypeLongId,
};
#[cfg(test)]
mod test;
mod item;
pub const SELF_TYPE_KW: &str = "Self";
pub const SUPER_KW: &str = "super";
pub const CRATE_KW: &str = "crate";
#[derive(Clone, Default, Debug, PartialEq, Eq, DebugWithDb)]
#[debug_db(dyn SemanticGroup + 'static)]
pub struct ResolvedItems {
pub concrete: UnorderedHashMap<ast::TerminalIdentifierPtr, ResolvedConcreteItem>,
pub generic: UnorderedHashMap<ast::TerminalIdentifierPtr, ResolvedGenericItem>,
}
impl ResolvedItems {
pub fn mark_concrete(
&mut self,
db: &dyn SemanticGroup,
segment: &syntax::node::ast::PathSegment,
resolved_item: ResolvedConcreteItem,
) -> ResolvedConcreteItem {
let identifier = segment.identifier_ast(db.upcast());
if let Some(generic_item) = resolved_item.generic(db) {
self.generic.insert(identifier.stable_ptr(), generic_item);
}
self.concrete.insert(identifier.stable_ptr(), resolved_item.clone());
resolved_item
}
pub fn mark_generic(
&mut self,
db: &dyn SemanticGroup,
segment: &syntax::node::ast::PathSegment,
resolved_item: ResolvedGenericItem,
) -> ResolvedGenericItem {
let identifier = segment.identifier_ast(db.upcast());
self.generic.insert(identifier.stable_ptr(), resolved_item.clone());
resolved_item
}
}
#[derive(Debug, PartialEq, Eq, DebugWithDb, Clone)]
#[debug_db(dyn SemanticGroup + 'static)]
pub struct EnrichedMembers {
pub members: OrderedHashMap<SmolStr, (Member, usize)>,
pub deref_functions: Vec<(FunctionId, Mutability)>,
pub exploration_tail: Option<ExprId>,
}
impl EnrichedMembers {
pub fn get_member(&self, name: &str) -> Option<EnrichedTypeMemberAccess> {
let (member, n_derefs) = self.members.get(name)?;
Some(EnrichedTypeMemberAccess {
member: member.clone(),
deref_functions: self.deref_functions[..*n_derefs].to_vec(),
})
}
}
pub struct EnrichedTypeMemberAccess {
pub member: Member,
pub deref_functions: Vec<(FunctionId, Mutability)>,
}
#[derive(Debug, PartialEq, Eq, DebugWithDb)]
#[debug_db(dyn SemanticGroup + 'static)]
pub struct ResolverData {
pub module_file_id: ModuleFileId,
generic_param_by_name: OrderedHashMap<SmolStr, GenericParamId>,
pub generic_params: Vec<GenericParamId>,
pub type_enriched_members: OrderedHashMap<(TypeId, bool), EnrichedMembers>,
pub resolved_items: ResolvedItems,
pub inference_data: InferenceData,
pub trait_or_impl_ctx: TraitOrImplContext,
pub feature_config: FeatureConfig,
pub used_items: OrderedHashSet<LookupItemId>,
}
impl ResolverData {
pub fn new(module_file_id: ModuleFileId, inference_id: InferenceId) -> Self {
Self {
module_file_id,
generic_param_by_name: Default::default(),
generic_params: Default::default(),
type_enriched_members: Default::default(),
resolved_items: Default::default(),
inference_data: InferenceData::new(inference_id),
trait_or_impl_ctx: TraitOrImplContext::None,
feature_config: Default::default(),
used_items: Default::default(),
}
}
pub fn clone_with_inference_id(
&self,
db: &dyn SemanticGroup,
inference_id: InferenceId,
) -> Self {
Self {
module_file_id: self.module_file_id,
generic_param_by_name: self.generic_param_by_name.clone(),
generic_params: self.generic_params.clone(),
type_enriched_members: self.type_enriched_members.clone(),
resolved_items: self.resolved_items.clone(),
inference_data: self.inference_data.clone_with_inference_id(db, inference_id),
trait_or_impl_ctx: self.trait_or_impl_ctx,
feature_config: self.feature_config.clone(),
used_items: self.used_items.clone(),
}
}
}
pub struct Resolver<'db> {
db: &'db dyn SemanticGroup,
pub data: ResolverData,
pub owning_crate_id: CrateId,
pub settings: CrateSettings,
}
impl Deref for Resolver<'_> {
type Target = ResolverData;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl DerefMut for Resolver<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}
impl Resolver<'_> {
pub fn set_feature_config(
&mut self,
element_id: &impl LanguageElementId,
syntax: &impl QueryAttrs,
diagnostics: &mut SemanticDiagnostics,
) {
self.feature_config =
extract_feature_config(self.db.upcast(), element_id, syntax, diagnostics);
}
}
enum UseStarResult {
UniquePathFound(ModuleItemInfo),
AmbiguousPath(Vec<ModuleItemId>),
PathNotFound,
ItemNotVisible(ModuleItemId, Vec<ModuleId>),
}
pub trait AsSegments {
fn to_segments(self, db: &dyn SyntaxGroup) -> Vec<ast::PathSegment>;
}
impl AsSegments for &ast::ExprPath {
fn to_segments(self, db: &dyn SyntaxGroup) -> Vec<ast::PathSegment> {
self.elements(db)
}
}
impl AsSegments for Vec<ast::PathSegment> {
fn to_segments(self, _: &dyn SyntaxGroup) -> Vec<ast::PathSegment> {
self
}
}
impl<'db> Resolver<'db> {
pub fn new(
db: &'db dyn SemanticGroup,
module_file_id: ModuleFileId,
inference_id: InferenceId,
) -> Self {
Self::with_data(db, ResolverData::new(module_file_id, inference_id))
}
pub fn with_data(db: &'db dyn SemanticGroup, data: ResolverData) -> Self {
let owning_crate_id = data.module_file_id.0.owning_crate(db.upcast());
let settings = db.crate_config(owning_crate_id).map(|c| c.settings).unwrap_or_default();
Self { owning_crate_id, settings, db, data }
}
pub fn inference(&mut self) -> Inference<'_> {
self.data.inference_data.inference(self.db)
}
pub fn add_generic_param(&mut self, generic_param_id: GenericParamId) {
self.generic_params.push(generic_param_id);
let db = self.db.upcast();
if let Some(name) = generic_param_id.name(db) {
self.generic_param_by_name.insert(name, generic_param_id);
}
}
fn resolve_path_inner<ResolvedItem: Clone>(
&mut self,
diagnostics: &mut SemanticDiagnostics,
path: impl AsSegments,
item_type: NotFoundItemType,
statement_env: Option<&mut Environment>,
mut callbacks: ResolvePathInnerCallbacks<
ResolvedItem,
impl FnMut(
&mut Resolver<'_>,
&mut SemanticDiagnostics,
&mut Peekable<std::slice::Iter<'_, ast::PathSegment>>,
Option<&mut Environment>,
) -> Maybe<ResolvedItem>,
impl FnMut(
&mut Resolver<'_>,
&mut SemanticDiagnostics,
&ResolvedItem,
&ast::PathSegment,
NotFoundItemType,
) -> Maybe<ResolvedItem>,
impl FnMut(&mut SemanticDiagnostics, &ast::PathSegment) -> Maybe<()>,
impl FnMut(
&mut ResolvedItems,
&dyn SemanticGroup,
&syntax::node::ast::PathSegment,
ResolvedItem,
),
>,
) -> Maybe<ResolvedItem> {
let db = self.db;
let syntax_db = db.upcast();
let elements_vec = path.to_segments(syntax_db);
let mut segments = elements_vec.iter().peekable();
let mut item: ResolvedItem = (callbacks.resolve_path_first_segment)(
self,
diagnostics,
&mut segments,
statement_env,
)?;
while let Some(segment) = segments.next() {
(callbacks.validate_segment)(diagnostics, segment)?;
let cur_item_type =
if segments.peek().is_some() { NotFoundItemType::Identifier } else { item_type };
item = (callbacks.resolve_path_next_segment)(
self,
diagnostics,
&item,
segment,
cur_item_type,
)?;
(callbacks.mark)(&mut self.resolved_items, db, segment, item.clone());
}
Ok(item)
}
pub fn resolve_concrete_path(
&mut self,
diagnostics: &mut SemanticDiagnostics,
path: impl AsSegments,
item_type: NotFoundItemType,
) -> Maybe<ResolvedConcreteItem> {
self.resolve_concrete_path_ex(diagnostics, path, item_type, None)
}
pub fn resolve_concrete_path_ex(
&mut self,
diagnostics: &mut SemanticDiagnostics,
path: impl AsSegments,
item_type: NotFoundItemType,
statement_env: Option<&mut Environment>,
) -> Maybe<ResolvedConcreteItem> {
self.resolve_path_inner::<ResolvedConcreteItem>(
diagnostics,
path,
item_type,
statement_env,
ResolvePathInnerCallbacks {
resolved_item_type: PhantomData,
resolve_path_first_segment: |resolver, diagnostics, segments, statement_env| {
resolver.resolve_concrete_path_first_segment(
diagnostics,
segments,
statement_env,
)
},
resolve_path_next_segment: |resolver, diagnostics, item, segment, item_type| {
resolver.resolve_path_next_segment_concrete(
diagnostics,
item,
segment,
item_type,
)
},
validate_segment: |_, _| Ok(()),
mark: |resolved_items, db, segment, item| {
resolved_items.mark_concrete(db, segment, item.clone());
},
},
)
}
fn specialize_generic_inner_item(
&mut self,
diagnostics: &mut SemanticDiagnostics,
module_id: ModuleId,
identifier: &TerminalIdentifier,
inner_item_info: ModuleItemInfo,
segment: &ast::PathSegment,
) -> Maybe<ResolvedConcreteItem> {
let generic_args_syntax = segment.generic_args(self.db.upcast());
let segment_stable_ptr = segment.stable_ptr().untyped();
self.validate_item_usability(diagnostics, module_id, identifier, &inner_item_info);
self.data.used_items.insert(LookupItemId::ModuleItem(inner_item_info.item_id));
let inner_generic_item =
ResolvedGenericItem::from_module_item(self.db, inner_item_info.item_id)?;
let specialized_item = self.specialize_generic_module_item(
diagnostics,
identifier,
inner_generic_item,
generic_args_syntax.clone(),
)?;
self.warn_same_impl_trait(
diagnostics,
&specialized_item,
&generic_args_syntax.unwrap_or_default(),
segment_stable_ptr,
);
Ok(specialized_item)
}
fn resolve_concrete_path_first_segment(
&mut self,
diagnostics: &mut SemanticDiagnostics,
segments: &mut Peekable<std::slice::Iter<'_, ast::PathSegment>>,
statement_env: Option<&mut Environment>,
) -> Maybe<ResolvedConcreteItem> {
if let Some(base_module) = self.try_handle_super_segments(diagnostics, segments) {
return Ok(ResolvedConcreteItem::Module(base_module?));
}
let db = self.db;
let syntax_db = db.upcast();
Ok(match segments.peek().unwrap() {
syntax::node::ast::PathSegment::WithGenericArgs(generic_segment) => {
let identifier = generic_segment.ident(syntax_db);
match self.determine_base(&identifier, statement_env) {
ResolvedBase::Module(module_id) => ResolvedConcreteItem::Module(module_id),
ResolvedBase::Crate(_) => {
return Err(diagnostics.report(
&generic_segment.generic_args(syntax_db),
UnexpectedGenericArgs,
));
}
ResolvedBase::StatementEnvironment(generic_item) => {
let segment = segments.next().unwrap();
self.specialize_generic_statement_arg(
segment,
diagnostics,
&identifier,
generic_item,
segment.generic_args(syntax_db),
)
}
ResolvedBase::FoundThroughGlobalUse {
item_info: inner_module_item,
containing_module: module_id,
} => {
let segment = segments.next().unwrap();
self.specialize_generic_inner_item(
diagnostics,
module_id,
&identifier,
inner_module_item,
segment,
)?
}
ResolvedBase::Ambiguous(module_items) => {
return Err(diagnostics.report(&identifier, AmbiguousPath(module_items)));
}
ResolvedBase::ItemNotVisible(module_item_id, containing_modules) => {
return Err(diagnostics.report(
&identifier,
ItemNotVisible(module_item_id, containing_modules),
));
}
}
}
syntax::node::ast::PathSegment::Simple(simple_segment) => {
let identifier = simple_segment.ident(syntax_db);
if let Some(resolved_item) =
resolve_self_segment(db, diagnostics, &identifier, &self.data.trait_or_impl_ctx)
{
segments.next().unwrap();
return resolved_item;
}
if let Some(local_item) = self.determine_base_item_in_local_scope(&identifier) {
self.resolved_items.mark_concrete(db, segments.next().unwrap(), local_item)
} else {
match self.determine_base(&identifier, statement_env) {
ResolvedBase::Module(module_id) => ResolvedConcreteItem::Module(module_id),
ResolvedBase::Crate(crate_id) => self.resolved_items.mark_concrete(
db,
segments.next().unwrap(),
ResolvedConcreteItem::Module(ModuleId::CrateRoot(crate_id)),
),
ResolvedBase::StatementEnvironment(generic_item) => {
let segment = segments.next().unwrap();
self.specialize_generic_statement_arg(
segment,
diagnostics,
&identifier,
generic_item,
segment.generic_args(syntax_db),
)
}
ResolvedBase::FoundThroughGlobalUse {
item_info: inner_module_item,
containing_module: module_id,
} => {
let segment = segments.next().unwrap();
self.specialize_generic_inner_item(
diagnostics,
module_id,
&identifier,
inner_module_item,
segment,
)?
}
ResolvedBase::Ambiguous(module_items) => {
return Err(
diagnostics.report(&identifier, AmbiguousPath(module_items))
);
}
ResolvedBase::ItemNotVisible(module_item_id, containing_modules) => {
return Err(diagnostics.report(
&identifier,
ItemNotVisible(module_item_id, containing_modules),
));
}
}
}
}
})
}
pub fn resolve_generic_path(
&mut self,
diagnostics: &mut SemanticDiagnostics,
path: impl AsSegments,
item_type: NotFoundItemType,
statement_env: Option<&mut Environment>,
) -> Maybe<ResolvedGenericItem> {
self.resolve_generic_path_inner(diagnostics, path, item_type, false, statement_env)
}
pub fn resolve_generic_path_with_args(
&mut self,
diagnostics: &mut SemanticDiagnostics,
path: impl AsSegments,
item_type: NotFoundItemType,
statement_env: Option<&mut Environment>,
) -> Maybe<ResolvedGenericItem> {
self.resolve_generic_path_inner(diagnostics, path, item_type, true, statement_env)
}
fn resolve_generic_path_inner(
&mut self,
diagnostics: &mut SemanticDiagnostics,
path: impl AsSegments,
item_type: NotFoundItemType,
allow_generic_args: bool,
statement_env: Option<&mut Environment>,
) -> Maybe<ResolvedGenericItem> {
let validate_segment =
|diagnostics: &mut SemanticDiagnostics, segment: &ast::PathSegment| match segment {
ast::PathSegment::WithGenericArgs(generic_args) if !allow_generic_args => {
Err(diagnostics.report(generic_args, UnexpectedGenericArgs))
}
_ => Ok(()),
};
self.resolve_path_inner::<ResolvedGenericItem>(
diagnostics,
path,
item_type,
statement_env,
ResolvePathInnerCallbacks {
resolved_item_type: PhantomData,
resolve_path_first_segment: |resolver, diagnostics, segments, statement_env| {
resolver.resolve_generic_path_first_segment(
diagnostics,
segments,
allow_generic_args,
statement_env,
)
},
resolve_path_next_segment: |resolver, diagnostics, item, segment, item_type| {
let identifier = segment.identifier_ast(self.db.upcast());
resolver.resolve_path_next_segment_generic(
diagnostics,
item,
&identifier,
item_type,
)
},
validate_segment,
mark: |resolved_items, db, segment, item| {
resolved_items.mark_generic(db, segment, item.clone());
},
},
)
}
fn resolve_generic_path_first_segment(
&mut self,
diagnostics: &mut SemanticDiagnostics,
segments: &mut Peekable<std::slice::Iter<'_, ast::PathSegment>>,
allow_generic_args: bool,
statement_env: Option<&mut Environment>,
) -> Maybe<ResolvedGenericItem> {
if let Some(base_module) = self.try_handle_super_segments(diagnostics, segments) {
return Ok(ResolvedGenericItem::Module(base_module?));
}
let db = self.db;
let syntax_db = db.upcast();
Ok(match segments.peek().unwrap() {
syntax::node::ast::PathSegment::WithGenericArgs(generic_segment) => {
if !allow_generic_args {
return Err(diagnostics
.report(&generic_segment.generic_args(syntax_db), UnexpectedGenericArgs));
}
let identifier = generic_segment.ident(syntax_db);
match self.determine_base(&identifier, statement_env) {
ResolvedBase::Module(module_id) => ResolvedGenericItem::Module(module_id),
ResolvedBase::Crate(_) => {
return Err(diagnostics.report(
&generic_segment.generic_args(syntax_db),
UnexpectedGenericArgs,
));
}
ResolvedBase::StatementEnvironment(generic_item) => generic_item,
ResolvedBase::FoundThroughGlobalUse {
item_info: inner_module_item, ..
} => {
segments.next();
self.data
.used_items
.insert(LookupItemId::ModuleItem(inner_module_item.item_id));
ResolvedGenericItem::from_module_item(self.db, inner_module_item.item_id)?
}
ResolvedBase::Ambiguous(module_items) => {
return Err(diagnostics.report(&identifier, AmbiguousPath(module_items)));
}
ResolvedBase::ItemNotVisible(module_item_id, containing_modules) => {
return Err(diagnostics.report(
&identifier,
ItemNotVisible(module_item_id, containing_modules),
));
}
}
}
syntax::node::ast::PathSegment::Simple(simple_segment) => {
let identifier = simple_segment.ident(syntax_db);
match self.determine_base(&identifier, statement_env) {
ResolvedBase::Module(module_id) => ResolvedGenericItem::Module(module_id),
ResolvedBase::Crate(crate_id) => self.resolved_items.mark_generic(
db,
segments.next().unwrap(),
ResolvedGenericItem::Module(ModuleId::CrateRoot(crate_id)),
),
ResolvedBase::StatementEnvironment(generic_item) => {
segments.next();
generic_item
}
ResolvedBase::FoundThroughGlobalUse {
item_info: inner_module_item, ..
} => {
segments.next();
self.data
.used_items
.insert(LookupItemId::ModuleItem(inner_module_item.item_id));
ResolvedGenericItem::from_module_item(self.db, inner_module_item.item_id)?
}
ResolvedBase::Ambiguous(module_items) => {
return Err(diagnostics.report(&identifier, AmbiguousPath(module_items)));
}
ResolvedBase::ItemNotVisible(module_item_id, containing_modules) => {
return Err(diagnostics.report(
&identifier,
ItemNotVisible(module_item_id, containing_modules),
));
}
}
}
})
}
fn try_handle_super_segments(
&self,
diagnostics: &mut SemanticDiagnostics,
segments: &mut Peekable<std::slice::Iter<'_, ast::PathSegment>>,
) -> Option<Maybe<ModuleId>> {
let syntax_db = self.db.upcast();
let mut module_id = self.module_file_id.0;
for segment in segments.peeking_take_while(|segment| match segment {
ast::PathSegment::WithGenericArgs(_) => false,
ast::PathSegment::Simple(simple) => simple.ident(syntax_db).text(syntax_db) == SUPER_KW,
}) {
module_id = match module_id {
ModuleId::CrateRoot(_) => {
return Some(Err(diagnostics.report(segment, SuperUsedInRootModule)));
}
ModuleId::Submodule(submodule_id) => submodule_id.parent_module(self.db.upcast()),
};
}
(module_id != self.module_file_id.0).then_some(Ok(module_id))
}
fn resolve_module_inner_item(
&mut self,
module_id: &ModuleId,
ident: SmolStr,
diagnostics: &mut SemanticDiagnostics,
identifier: &TerminalIdentifier,
item_type: NotFoundItemType,
) -> Maybe<ModuleItemInfo> {
match self.db.module_item_info_by_name(*module_id, ident.clone())? {
Some(info) => Ok(info),
None => match self.resolve_path_using_use_star(*module_id, identifier) {
UseStarResult::UniquePathFound(item_info) => Ok(item_info),
UseStarResult::AmbiguousPath(module_items) => {
Err(diagnostics.report(identifier, AmbiguousPath(module_items)))
}
UseStarResult::PathNotFound => {
Err(diagnostics.report(identifier, PathNotFound(item_type)))
}
UseStarResult::ItemNotVisible(module_item_id, containing_modules) => {
Err(diagnostics
.report(identifier, ItemNotVisible(module_item_id, containing_modules)))
}
},
}
}
fn resolve_path_next_segment_concrete(
&mut self,
diagnostics: &mut SemanticDiagnostics,
containing_item: &ResolvedConcreteItem,
segment: &ast::PathSegment,
item_type: NotFoundItemType,
) -> Maybe<ResolvedConcreteItem> {
let syntax_db = self.db.upcast();
let identifier = &segment.identifier_ast(syntax_db);
let generic_args_syntax = segment.generic_args(syntax_db);
let ident = identifier.text(syntax_db);
if identifier.text(syntax_db) == SELF_TYPE_KW {
return Err(diagnostics.report(identifier, SelfMustBeFirst));
}
match containing_item {
ResolvedConcreteItem::Module(module_id) => {
if ident == SUPER_KW {
return Err(diagnostics.report(identifier, InvalidPath));
}
let inner_item_info = self.resolve_module_inner_item(
module_id,
ident,
diagnostics,
identifier,
item_type,
)?;
self.specialize_generic_inner_item(
diagnostics,
*module_id,
identifier,
inner_item_info,
segment,
)
}
ResolvedConcreteItem::Type(ty) => {
if let TypeLongId::Concrete(ConcreteTypeId::Enum(concrete_enum_id)) =
ty.lookup_intern(self.db)
{
let enum_id = concrete_enum_id.enum_id(self.db);
let variants = self
.db
.enum_variants(enum_id)
.map_err(|_| diagnostics.report(identifier, UnknownEnum))?;
let variant_id = variants.get(&ident).ok_or_else(|| {
diagnostics
.report(identifier, NoSuchVariant { enum_id, variant_name: ident })
})?;
let variant = self.db.variant_semantic(enum_id, *variant_id)?;
let concrete_variant =
self.db.concrete_enum_variant(concrete_enum_id, &variant)?;
Ok(ResolvedConcreteItem::Variant(concrete_variant))
} else {
Err(diagnostics.report(identifier, InvalidPath))
}
}
ResolvedConcreteItem::Trait(concrete_trait_id) => {
let long_trait_id = concrete_trait_id.lookup_intern(self.db);
let trait_id = long_trait_id.trait_id;
let Some(trait_item_id) = self.db.trait_item_by_name(trait_id, ident)? else {
return Err(diagnostics.report(identifier, InvalidPath));
};
self.data.used_items.insert(LookupItemId::TraitItem(trait_item_id));
match trait_item_id {
TraitItemId::Function(trait_function_id) => {
let concrete_trait_function = ConcreteTraitGenericFunctionLongId::new(
self.db,
*concrete_trait_id,
trait_function_id,
)
.intern(self.db);
let identifier_stable_ptr = identifier.stable_ptr().untyped();
if let TraitOrImplContext::Trait(ctx_trait_id) = &self.trait_or_impl_ctx {
if trait_id == *ctx_trait_id {
return Ok(ResolvedConcreteItem::Function(
self.specialize_function(
diagnostics,
identifier_stable_ptr,
GenericFunctionId::Trait(concrete_trait_function),
&generic_args_syntax.unwrap_or_default(),
)?,
));
}
}
let impl_lookup_context = self.impl_lookup_context();
let generic_function = self.inference().infer_trait_generic_function(
concrete_trait_function,
&impl_lookup_context,
Some(identifier_stable_ptr),
);
Ok(ResolvedConcreteItem::Function(self.specialize_function(
diagnostics,
identifier_stable_ptr,
generic_function,
&generic_args_syntax.unwrap_or_default(),
)?))
}
TraitItemId::Type(trait_type_id) => {
if let TraitOrImplContext::Trait(ctx_trait_id) = &self.trait_or_impl_ctx {
if trait_id == *ctx_trait_id {
return Ok(ResolvedConcreteItem::Type(
TypeLongId::TraitType(trait_type_id).intern(self.db),
));
}
}
let concrete_trait_type =
ConcreteTraitTypeId::new(self.db, *concrete_trait_id, trait_type_id);
let impl_lookup_context = self.impl_lookup_context();
let identifier_stable_ptr = identifier.stable_ptr().untyped();
let ty = self.inference().infer_trait_type(
concrete_trait_type,
&impl_lookup_context,
Some(identifier_stable_ptr),
);
Ok(ResolvedConcreteItem::Type(self.inference().rewrite(ty).no_err()))
}
TraitItemId::Constant(trait_constant_id) => {
let concrete_trait_constant = ConcreteTraitConstantLongId::new(
self.db,
*concrete_trait_id,
trait_constant_id,
)
.intern(self.db);
if let TraitOrImplContext::Trait(ctx_trait_id) = &self.trait_or_impl_ctx {
if trait_id == *ctx_trait_id {
return Ok(ResolvedConcreteItem::Constant(
ConstValue::TraitConstant(trait_constant_id).intern(self.db),
));
}
}
let impl_lookup_context = self.impl_lookup_context();
let identifier_stable_ptr = identifier.stable_ptr().untyped();
let imp_constant_id = self.inference().infer_trait_constant(
concrete_trait_constant,
&impl_lookup_context,
Some(identifier_stable_ptr),
);
self.inference().solve().ok();
Ok(ResolvedConcreteItem::Constant(
ConstValue::ImplConstant(imp_constant_id).intern(self.db),
))
}
TraitItemId::Impl(trait_impl_id) => {
let concrete_trait_impl = ConcreteTraitImplLongId::new(
self.db,
*concrete_trait_id,
trait_impl_id,
)
.intern(self.db);
if let TraitOrImplContext::Trait(ctx_trait_id) = &self.trait_or_impl_ctx {
if trait_id == *ctx_trait_id {
return Ok(ResolvedConcreteItem::Impl(
ImplLongId::TraitImpl(trait_impl_id).intern(self.db),
));
}
}
let impl_lookup_context = self.impl_lookup_context();
let identifier_stable_ptr = identifier.stable_ptr().untyped();
let impl_impl_id = self.inference().infer_trait_impl(
concrete_trait_impl,
&impl_lookup_context,
Some(identifier_stable_ptr),
);
self.inference().solve().ok();
Ok(ResolvedConcreteItem::Impl(
ImplLongId::ImplImpl(impl_impl_id).intern(self.db),
))
}
}
}
ResolvedConcreteItem::Impl(impl_id) => {
let concrete_trait_id = self.db.impl_concrete_trait(*impl_id)?;
let trait_id = concrete_trait_id.trait_id(self.db);
let Some(trait_item_id) = self.db.trait_item_by_name(trait_id, ident)? else {
return Err(diagnostics.report(identifier, InvalidPath));
};
self.data.used_items.insert(LookupItemId::TraitItem(trait_item_id));
match trait_item_id {
TraitItemId::Function(trait_function_id) => {
let generic_function_id = GenericFunctionId::Impl(ImplGenericFunctionId {
impl_id: *impl_id,
function: trait_function_id,
});
Ok(ResolvedConcreteItem::Function(self.specialize_function(
diagnostics,
identifier.stable_ptr().untyped(),
generic_function_id,
&generic_args_syntax.unwrap_or_default(),
)?))
}
TraitItemId::Type(trait_type_id) => {
let impl_type_id = ImplTypeId::new(*impl_id, trait_type_id, self.db);
let ty = self
.inference()
.reduce_impl_ty(impl_type_id)
.unwrap_or_else(|_| TypeLongId::ImplType(impl_type_id).intern(self.db));
Ok(ResolvedConcreteItem::Type(ty))
}
TraitItemId::Constant(trait_constant_id) => {
let impl_constant_id =
ImplConstantId::new(*impl_id, trait_constant_id, self.db);
let constant =
self.inference().reduce_impl_constant(impl_constant_id).unwrap_or_else(
|_| ConstValue::ImplConstant(impl_constant_id).intern(self.db),
);
Ok(ResolvedConcreteItem::Constant(constant))
}
TraitItemId::Impl(trait_impl_id) => {
let impl_impl_id = ImplImplId::new(*impl_id, trait_impl_id, self.db);
let imp = self
.inference()
.reduce_impl_impl(impl_impl_id)
.unwrap_or_else(|_| ImplLongId::ImplImpl(impl_impl_id).intern(self.db));
Ok(ResolvedConcreteItem::Impl(imp))
}
}
}
ResolvedConcreteItem::Function(function_id) if ident == "Coupon" => {
if !are_coupons_enabled(self.db, self.module_file_id) {
diagnostics.report(identifier, CouponsDisabled);
}
if matches!(
function_id.get_concrete(self.db).generic_function,
GenericFunctionId::Extern(_)
) {
return Err(diagnostics.report(identifier, CouponForExternFunctionNotAllowed));
}
Ok(ResolvedConcreteItem::Type(TypeLongId::Coupon(*function_id).intern(self.db)))
}
_ => Err(diagnostics.report(identifier, InvalidPath)),
}
}
fn specialize_generic_module_item(
&mut self,
diagnostics: &mut SemanticDiagnostics,
identifier: &syntax::node::ast::TerminalIdentifier,
generic_item: ResolvedGenericItem,
generic_args_syntax: Option<Vec<ast::GenericArg>>,
) -> Maybe<ResolvedConcreteItem> {
Ok(match generic_item {
ResolvedGenericItem::GenericConstant(id) => {
ResolvedConcreteItem::Constant(self.db.constant_const_value(id)?)
}
ResolvedGenericItem::Module(module_id) => {
if generic_args_syntax.is_some() {
return Err(diagnostics.report(identifier, UnexpectedGenericArgs));
}
ResolvedConcreteItem::Module(module_id)
}
ResolvedGenericItem::GenericFunction(generic_function) => {
ResolvedConcreteItem::Function(self.specialize_function(
diagnostics,
identifier.stable_ptr().untyped(),
generic_function,
&generic_args_syntax.unwrap_or_default(),
)?)
}
ResolvedGenericItem::GenericType(generic_type) => {
ResolvedConcreteItem::Type(self.specialize_type(
diagnostics,
identifier.stable_ptr().untyped(),
generic_type,
&generic_args_syntax.unwrap_or_default(),
)?)
}
ResolvedGenericItem::GenericTypeAlias(module_type_alias_id) => {
let ty = self.db.module_type_alias_resolved_type(module_type_alias_id)?;
let generic_params =
self.db.module_type_alias_generic_params(module_type_alias_id)?;
let generic_args = self.resolve_generic_args(
diagnostics,
&generic_params,
&generic_args_syntax.unwrap_or_default(),
identifier.stable_ptr().untyped(),
)?;
let substitution = GenericSubstitution::new(&generic_params, &generic_args);
let ty = SubstitutionRewriter { db: self.db, substitution: &substitution }
.rewrite(ty)?;
ResolvedConcreteItem::Type(ty)
}
ResolvedGenericItem::GenericImplAlias(impl_alias_id) => {
let impl_id = self.db.impl_alias_resolved_impl(impl_alias_id)?;
let generic_params = self.db.impl_alias_generic_params(impl_alias_id)?;
let generic_args = self.resolve_generic_args(
diagnostics,
&generic_params,
&generic_args_syntax.unwrap_or_default(),
identifier.stable_ptr().untyped(),
)?;
let substitution = GenericSubstitution::new(&generic_params, &generic_args);
let impl_id = SubstitutionRewriter { db: self.db, substitution: &substitution }
.rewrite(impl_id)?;
ResolvedConcreteItem::Impl(impl_id)
}
ResolvedGenericItem::Trait(trait_id) => {
ResolvedConcreteItem::Trait(self.specialize_trait(
diagnostics,
identifier.stable_ptr().untyped(),
trait_id,
&generic_args_syntax.unwrap_or_default(),
)?)
}
ResolvedGenericItem::Impl(impl_def_id) => ResolvedConcreteItem::Impl(
ImplLongId::Concrete(self.specialize_impl(
diagnostics,
identifier.stable_ptr().untyped(),
impl_def_id,
&generic_args_syntax.unwrap_or_default(),
)?)
.intern(self.db),
),
ResolvedGenericItem::Variant(_) => panic!("Variant is not a module item."),
ResolvedGenericItem::TraitFunction(_) => panic!("TraitFunction is not a module item."),
ResolvedGenericItem::Variable(_) => panic!("Variable is not a module item."),
})
}
fn resolve_path_using_use_star(
&mut self,
module_id: ModuleId,
identifier: &ast::TerminalIdentifier,
) -> UseStarResult {
let mut item_info = None;
let mut module_items_found: OrderedHashSet<ModuleItemId> = OrderedHashSet::default();
let imported_modules = self.db.priv_module_use_star_modules(module_id);
let mut containing_modules = vec![];
let mut is_accessible = false;
for (star_module_id, item_module_id) in &imported_modules.accessible {
if let Some(inner_item_info) =
self.resolve_item_in_imported_module(*item_module_id, identifier)
{
item_info = Some(inner_item_info.clone());
is_accessible |=
self.is_item_visible(*item_module_id, &inner_item_info, *star_module_id)
&& self.is_item_feature_usable(&inner_item_info);
module_items_found.insert(inner_item_info.item_id);
}
}
for star_module_id in &imported_modules.all {
if let Some(inner_item_info) =
self.resolve_item_in_imported_module(*star_module_id, identifier)
{
item_info = Some(inner_item_info.clone());
module_items_found.insert(inner_item_info.item_id);
containing_modules.push(*star_module_id);
}
}
if module_items_found.len() > 1 {
return UseStarResult::AmbiguousPath(module_items_found.iter().cloned().collect());
}
match item_info {
Some(item_info) => {
if is_accessible {
UseStarResult::UniquePathFound(item_info)
} else {
UseStarResult::ItemNotVisible(item_info.item_id, containing_modules)
}
}
None => UseStarResult::PathNotFound,
}
}
fn resolve_item_in_imported_module(
&mut self,
module_id: ModuleId,
identifier: &ast::TerminalIdentifier,
) -> Option<ModuleItemInfo> {
let inner_item_info =
self.db.module_item_info_by_name(module_id, identifier.text(self.db.upcast()));
if let Ok(Some(inner_item_info)) = inner_item_info {
self.data.used_items.insert(LookupItemId::ModuleItem(inner_item_info.item_id));
return Some(inner_item_info);
}
None
}
fn resolve_path_next_segment_generic(
&mut self,
diagnostics: &mut SemanticDiagnostics,
containing_item: &ResolvedGenericItem,
identifier: &ast::TerminalIdentifier,
item_type: NotFoundItemType,
) -> Maybe<ResolvedGenericItem> {
let syntax_db = self.db.upcast();
let ident = identifier.text(syntax_db);
match containing_item {
ResolvedGenericItem::Module(module_id) => {
let inner_item_info = self.resolve_module_inner_item(
module_id,
ident,
diagnostics,
identifier,
item_type,
)?;
self.validate_item_usability(diagnostics, *module_id, identifier, &inner_item_info);
self.data.used_items.insert(LookupItemId::ModuleItem(inner_item_info.item_id));
ResolvedGenericItem::from_module_item(self.db, inner_item_info.item_id)
}
ResolvedGenericItem::GenericType(GenericTypeId::Enum(enum_id)) => {
let variants = self.db.enum_variants(*enum_id)?;
let variant_id = variants.get(&ident).ok_or_else(|| {
diagnostics.report(identifier, NoSuchVariant {
enum_id: *enum_id,
variant_name: ident,
})
})?;
let variant = self.db.variant_semantic(*enum_id, *variant_id)?;
Ok(ResolvedGenericItem::Variant(variant))
}
_ => Err(diagnostics.report(identifier, InvalidPath)),
}
}
fn determine_base_item_in_local_scope(
&mut self,
identifier: &ast::TerminalIdentifier,
) -> Option<ResolvedConcreteItem> {
let syntax_db = self.db.upcast();
let ident = identifier.text(syntax_db);
if let Some(generic_param_id) = self.data.generic_param_by_name.get(&ident) {
let item = match generic_param_id.kind(self.db.upcast()) {
GenericKind::Type => ResolvedConcreteItem::Type(
TypeLongId::GenericParameter(*generic_param_id).intern(self.db),
),
GenericKind::Const => ResolvedConcreteItem::Constant(
ConstValue::Generic(*generic_param_id).intern(self.db),
),
GenericKind::Impl => ResolvedConcreteItem::Impl(
ImplLongId::GenericParameter(*generic_param_id).intern(self.db),
),
GenericKind::NegImpl => return None,
};
return Some(item);
}
None
}
fn determine_base(
&mut self,
identifier: &ast::TerminalIdentifier,
statement_env: Option<&mut Environment>,
) -> ResolvedBase {
let syntax_db = self.db.upcast();
let ident = identifier.text(syntax_db);
let module_id = self.module_file_id.0;
if let Some(env) = statement_env {
if let Some(inner_generic_arg) = get_statement_item_by_name(env, &ident) {
return ResolvedBase::StatementEnvironment(inner_generic_arg);
}
}
if let Ok(Some(_)) = self.db.module_item_by_name(module_id, ident.clone()) {
return ResolvedBase::Module(module_id);
}
if ident == CRATE_KW {
return ResolvedBase::Crate(self.owning_crate_id);
}
if let Some(dep) = self.settings.dependencies.get(ident.as_str()) {
return ResolvedBase::Crate(
CrateLongId::Real { name: ident, discriminator: dep.discriminator.clone() }
.intern(self.db),
);
}
match self.resolve_path_using_use_star(module_id, identifier) {
UseStarResult::UniquePathFound(inner_module_item) => {
return ResolvedBase::FoundThroughGlobalUse {
item_info: inner_module_item,
containing_module: module_id,
};
}
UseStarResult::AmbiguousPath(module_items) => {
return ResolvedBase::Ambiguous(module_items);
}
UseStarResult::PathNotFound => {}
UseStarResult::ItemNotVisible(module_item_id, containing_modules) => {
return ResolvedBase::ItemNotVisible(module_item_id, containing_modules);
}
}
if ident == CORELIB_CRATE_NAME {
return ResolvedBase::Crate(CrateId::core(self.db));
}
ResolvedBase::Module(self.prelude_submodule())
}
pub fn prelude_submodule(&self) -> ModuleId {
let prelude_submodule_name = self.settings.edition.prelude_submodule_name();
let core_prelude_submodule = core_submodule(self.db, "prelude");
get_submodule(self.db, core_prelude_submodule, prelude_submodule_name).unwrap_or_else(
|| {
panic!(
"expected prelude submodule `{prelude_submodule_name}` not found in \
`core::prelude`."
)
},
)
}
fn specialize_trait(
&mut self,
diagnostics: &mut SemanticDiagnostics,
stable_ptr: SyntaxStablePtrId,
trait_id: TraitId,
generic_args: &[ast::GenericArg],
) -> Maybe<ConcreteTraitId> {
let generic_params = self
.db
.trait_generic_params(trait_id)
.map_err(|_| diagnostics.report(stable_ptr, UnknownTrait))?;
let generic_args =
self.resolve_generic_args(diagnostics, &generic_params, generic_args, stable_ptr)?;
Ok(ConcreteTraitLongId { trait_id, generic_args }.intern(self.db))
}
fn specialize_impl(
&mut self,
diagnostics: &mut SemanticDiagnostics,
stable_ptr: SyntaxStablePtrId,
impl_def_id: ImplDefId,
generic_args: &[ast::GenericArg],
) -> Maybe<ConcreteImplId> {
let generic_params = self
.db
.impl_def_generic_params(impl_def_id)
.map_err(|_| diagnostics.report(stable_ptr, UnknownImpl))?;
let generic_args =
self.resolve_generic_args(diagnostics, &generic_params, generic_args, stable_ptr)?;
Ok(ConcreteImplLongId { impl_def_id, generic_args }.intern(self.db))
}
pub fn specialize_function(
&mut self,
diagnostics: &mut SemanticDiagnostics,
stable_ptr: SyntaxStablePtrId,
generic_function: GenericFunctionId,
generic_args: &[ast::GenericArg],
) -> Maybe<FunctionId> {
let generic_params: Vec<_> = generic_function.generic_params(self.db)?;
let generic_args =
self.resolve_generic_args(diagnostics, &generic_params, generic_args, stable_ptr)?;
Ok(FunctionLongId { function: ConcreteFunction { generic_function, generic_args } }
.intern(self.db))
}
pub fn specialize_type(
&mut self,
diagnostics: &mut SemanticDiagnostics,
stable_ptr: SyntaxStablePtrId,
generic_type: GenericTypeId,
generic_args: &[ast::GenericArg],
) -> Maybe<TypeId> {
let generic_params = self
.db
.generic_type_generic_params(generic_type)
.map_err(|_| diagnostics.report(stable_ptr, UnknownType))?;
let generic_args =
self.resolve_generic_args(diagnostics, &generic_params, generic_args, stable_ptr)?;
Ok(TypeLongId::Concrete(ConcreteTypeId::new(self.db, generic_type, generic_args))
.intern(self.db))
}
pub fn impl_lookup_context(&self) -> ImplLookupContext {
let mut lookup_context =
ImplLookupContext::new(self.module_file_id.0, self.generic_params.clone());
if let TraitOrImplContext::Impl(impl_def_id) = &self.trait_or_impl_ctx {
let Ok(generic_params) = self.db.impl_def_generic_params(*impl_def_id) else {
return lookup_context;
};
let generic_args = generic_params_to_args(generic_params.as_slice(), self.db);
let impl_id: ConcreteImplId =
ConcreteImplLongId { impl_def_id: *impl_def_id, generic_args }.intern(self.db);
lookup_context.insert_impl(ImplLongId::Concrete(impl_id).intern(self.db));
}
lookup_context
}
pub fn resolve_generic_args(
&mut self,
diagnostics: &mut SemanticDiagnostics,
generic_params: &[GenericParam],
generic_args_syntax: &[ast::GenericArg],
stable_ptr: SyntaxStablePtrId,
) -> Maybe<Vec<GenericArgumentId>> {
let mut substitution = GenericSubstitution::default();
let mut resolved_args = vec![];
let arg_syntax_per_param =
self.get_arg_syntax_per_param(diagnostics, generic_params, generic_args_syntax)?;
for generic_param in generic_params.iter() {
let generic_param = SubstitutionRewriter { db: self.db, substitution: &substitution }
.rewrite(generic_param.clone())?;
let generic_arg = self.resolve_generic_arg(
&generic_param,
arg_syntax_per_param
.get(&generic_param.id())
.and_then(|arg_syntax| {
if let ast::GenericArgValue::Expr(expr) = arg_syntax {
Some(expr.expr(self.db.upcast()))
} else {
None
}
})
.as_ref(),
stable_ptr,
diagnostics,
)?;
resolved_args.push(generic_arg);
substitution.insert(generic_param.id(), generic_arg);
}
Ok(resolved_args)
}
fn get_arg_syntax_per_param(
&self,
diagnostics: &mut SemanticDiagnostics,
generic_params: &[GenericParam],
generic_args_syntax: &[ast::GenericArg],
) -> Maybe<UnorderedHashMap<GenericParamId, ast::GenericArgValue>> {
let syntax_db = self.db.upcast();
let mut arg_syntax_per_param =
UnorderedHashMap::<GenericParamId, ast::GenericArgValue>::default();
let mut last_named_arg_index = None;
let generic_param_by_name = generic_params
.iter()
.enumerate()
.filter_map(|(i, param)| Some((param.id().name(self.db.upcast())?, (i, param.id()))))
.collect::<UnorderedHashMap<_, _>>();
for (idx, generic_arg_syntax) in generic_args_syntax.iter().enumerate() {
match generic_arg_syntax {
ast::GenericArg::Named(arg_syntax) => {
let name = arg_syntax.name(syntax_db).text(syntax_db);
let Some((index, generic_param_id)) = generic_param_by_name.get(&name) else {
return Err(diagnostics.report(arg_syntax, UnknownGenericParam(name)));
};
if let Some(prev_index) = last_named_arg_index {
if prev_index > index {
return Err(diagnostics.report(arg_syntax, GenericArgOutOfOrder(name)));
}
}
last_named_arg_index = Some(index);
if arg_syntax_per_param
.insert(*generic_param_id, arg_syntax.value(syntax_db))
.is_some()
{
return Err(diagnostics.report(arg_syntax, GenericArgDuplicate(name)));
}
}
ast::GenericArg::Unnamed(arg_syntax) => {
if last_named_arg_index.is_some() {
return Err(diagnostics.report(arg_syntax, PositionalGenericAfterNamed));
}
let generic_param = generic_params.get(idx).ok_or_else(|| {
diagnostics.report(arg_syntax, TooManyGenericArguments {
expected: generic_params.len(),
actual: generic_args_syntax.len(),
})
})?;
assert_eq!(
arg_syntax_per_param
.insert(generic_param.id(), arg_syntax.value(syntax_db)),
None,
"Unexpected duplication in ordered params."
);
}
}
}
Ok(arg_syntax_per_param)
}
fn resolve_generic_arg(
&mut self,
generic_param: &GenericParam,
generic_arg_syntax_opt: Option<&ast::Expr>,
stable_ptr: SyntaxStablePtrId,
diagnostics: &mut SemanticDiagnostics,
) -> Result<GenericArgumentId, cairo_lang_diagnostics::DiagnosticAdded> {
let Some(generic_arg_syntax) = generic_arg_syntax_opt else {
let lookup_context = self.impl_lookup_context();
let inference = &mut self.data.inference_data.inference(self.db);
return inference
.infer_generic_arg(generic_param, lookup_context, Some(stable_ptr))
.map_err(|err_set| {
inference.report_on_pending_error(err_set, diagnostics, stable_ptr)
});
};
Ok(match generic_param {
GenericParam::Type(_) => {
let ty = resolve_type(self.db, diagnostics, self, generic_arg_syntax);
GenericArgumentId::Type(ty)
}
GenericParam::Const(const_param) => {
let environment = Environment::empty();
let mut resolver_data =
ResolverData::new(self.module_file_id, self.inference_data.inference_id);
std::mem::swap(&mut self.data, &mut resolver_data);
let mut ctx = ComputationContext::new(
self.db,
diagnostics,
Resolver::with_data(self.db, resolver_data),
None,
environment,
ContextFunction::Global,
);
let value = compute_expr_semantic(&mut ctx, generic_arg_syntax);
let const_value = resolve_const_expr_and_evaluate(
self.db,
&mut ctx,
&value,
generic_arg_syntax.stable_ptr().untyped(),
const_param.ty,
);
std::mem::swap(&mut ctx.resolver.data, &mut self.data);
GenericArgumentId::Constant(const_value.intern(self.db))
}
GenericParam::Impl(param) => {
let expr_path = try_extract_matches!(generic_arg_syntax, ast::Expr::Path)
.ok_or_else(|| diagnostics.report(generic_arg_syntax, UnknownImpl))?;
let resolved_impl = try_extract_matches!(
self.resolve_concrete_path(diagnostics, expr_path, NotFoundItemType::Impl)?,
ResolvedConcreteItem::Impl
)
.ok_or_else(|| diagnostics.report(generic_arg_syntax, UnknownImpl))?;
let impl_def_concrete_trait = self.db.impl_concrete_trait(resolved_impl)?;
let expected_concrete_trait = param.concrete_trait?;
if let Err(err_set) = self
.inference()
.conform_traits(impl_def_concrete_trait, expected_concrete_trait)
{
let diag_added = diagnostics.report(generic_arg_syntax, TraitMismatch {
expected_trt: expected_concrete_trait,
actual_trt: impl_def_concrete_trait,
});
self.inference().consume_reported_error(err_set, diag_added);
} else {
for (trait_ty, ty1) in param.type_constraints.iter() {
let ty0 = TypeLongId::ImplType(ImplTypeId::new(
resolved_impl,
trait_ty.trait_type(self.db),
self.db,
))
.intern(self.db);
let _ = self.inference().conform_ty(ty0, *ty1).map_err(|err_set| {
self.inference().report_on_pending_error(
err_set,
diagnostics,
stable_ptr,
)
});
}
}
GenericArgumentId::Impl(resolved_impl)
}
GenericParam::NegImpl(_) => {
return Err(diagnostics.report(generic_arg_syntax, ArgPassedToNegativeImpl));
}
})
}
pub fn ignore_visibility_checks(&self, module_id: ModuleId) -> bool {
let module_crate = module_id.owning_crate(self.db.upcast());
let module_edition =
self.db.crate_config(module_crate).map(|c| c.settings.edition).unwrap_or_default();
module_edition.ignore_visibility()
|| self.settings.edition.ignore_visibility() && module_crate == self.db.core_crate()
}
fn validate_item_usability(
&self,
diagnostics: &mut SemanticDiagnostics,
containing_module_id: ModuleId,
identifier: &ast::TerminalIdentifier,
item_info: &ModuleItemInfo,
) {
if !self.is_item_visible(containing_module_id, item_info, self.module_file_id.0) {
diagnostics.report(identifier, ItemNotVisible(item_info.item_id, vec![]));
}
match &item_info.feature_kind {
FeatureKind::Unstable { feature, note }
if !self.data.feature_config.allowed_features.contains(feature) =>
{
diagnostics.report(identifier, UnstableFeature {
feature_name: feature.clone(),
note: note.clone(),
});
}
FeatureKind::Deprecated { feature, note }
if !self.data.feature_config.allow_deprecated
&& !self.data.feature_config.allowed_features.contains(feature) =>
{
diagnostics.report(identifier, DeprecatedFeature {
feature_name: feature.clone(),
note: note.clone(),
});
}
FeatureKind::Internal { feature, note }
if !self.data.feature_config.allowed_features.contains(feature) =>
{
diagnostics.report(identifier, InternalFeature {
feature_name: feature.clone(),
note: note.clone(),
});
}
_ => {}
}
}
fn is_item_visible(
&self,
containing_module_id: ModuleId,
item_info: &ModuleItemInfo,
user_module: ModuleId,
) -> bool {
let db = self.db.upcast();
self.ignore_visibility_checks(containing_module_id)
|| visibility::peek_visible_in(
db,
item_info.visibility,
containing_module_id,
user_module,
)
}
fn is_item_feature_usable(&self, item_info: &ModuleItemInfo) -> bool {
match &item_info.feature_kind {
FeatureKind::Unstable { feature, .. }
| FeatureKind::Deprecated { feature, .. }
| FeatureKind::Internal { feature, .. } => {
self.data.feature_config.allowed_features.contains(feature)
}
_ => true,
}
}
fn warn_same_impl_trait(
&mut self,
diagnostics: &mut SemanticDiagnostics,
specialized_item: &ResolvedConcreteItem,
generic_args_syntax_slice: &[ast::GenericArg],
segment_stable_ptr: SyntaxStablePtrId,
) {
match *specialized_item {
ResolvedConcreteItem::Trait(current_segment_concrete_trait) => {
match self.trait_or_impl_ctx {
TraitOrImplContext::None => {}
TraitOrImplContext::Trait(ctx_trait) => {
self.warn_trait_in_same_trait(
diagnostics,
current_segment_concrete_trait.trait_id(self.db),
generic_args_syntax_slice,
ctx_trait,
segment_stable_ptr,
)
.ok();
}
TraitOrImplContext::Impl(ctx_impl_def_id) => {
self.warn_trait_in_its_impl(
diagnostics,
current_segment_concrete_trait,
ctx_impl_def_id,
segment_stable_ptr,
)
.ok();
}
};
}
ResolvedConcreteItem::Impl(current_segment_impl_id) => {
if let TraitOrImplContext::Impl(ctx_impl) = self.trait_or_impl_ctx {
let current_segment_concrete_impl_id = extract_matches!(
current_segment_impl_id.lookup_intern(self.db),
ImplLongId::Concrete
);
self.warn_impl_in_same_impl(
diagnostics,
current_segment_concrete_impl_id.impl_def_id(self.db),
generic_args_syntax_slice,
ctx_impl,
segment_stable_ptr,
)
.ok();
}
}
_ => {}
};
}
fn warn_impl_in_same_impl(
&mut self,
diagnostics: &mut SemanticDiagnostics,
current_segment_impl_def_id: ImplDefId,
current_segment_generic_args: &[ast::GenericArg],
ctx_impl: ImplDefId,
segment_stable_ptr: SyntaxStablePtrId,
) -> Maybe<()> {
if current_segment_impl_def_id != ctx_impl {
return Ok(());
}
let generic_params = self.db.impl_def_generic_params(ctx_impl)?;
self.compare_segment_args_to_params(
diagnostics,
current_segment_generic_args,
generic_params,
segment_stable_ptr,
ImplInImplMustBeExplicit,
ImplItemForbiddenInTheImpl,
)
}
fn warn_trait_in_its_impl(
&mut self,
diagnostics: &mut SemanticDiagnostics,
current_segment_concrete_trait_id: ConcreteTraitId,
impl_ctx: ImplDefId,
segment_stable_ptr: SyntaxStablePtrId,
) -> Maybe<()> {
let ctx_impl_trait = self.db.impl_def_trait(impl_ctx)?;
if current_segment_concrete_trait_id.trait_id(self.db) != ctx_impl_trait {
return Ok(());
}
let ctx_impl_concrete_trait = self.db.impl_def_concrete_trait(impl_ctx)?;
if ctx_impl_concrete_trait.generic_args(self.db)
== current_segment_concrete_trait_id.generic_args(self.db)
{
return Err(diagnostics.report(segment_stable_ptr, TraitItemForbiddenInItsImpl));
}
Ok(())
}
fn warn_trait_in_same_trait(
&mut self,
diagnostics: &mut SemanticDiagnostics,
current_segment_trait_id: TraitId,
current_segment_generic_args: &[ast::GenericArg],
ctx_trait: TraitId,
segment_stable_ptr: SyntaxStablePtrId,
) -> Maybe<()> {
if current_segment_trait_id != ctx_trait {
return Ok(());
}
let generic_params = self.db.trait_generic_params(ctx_trait)?;
self.compare_segment_args_to_params(
diagnostics,
current_segment_generic_args,
generic_params,
segment_stable_ptr,
TraitInTraitMustBeExplicit,
TraitItemForbiddenInTheTrait,
)
}
fn compare_segment_args_to_params(
&mut self,
diagnostics: &mut SemanticDiagnostics,
current_segment_generic_args: &[ast::GenericArg],
generic_params: Vec<GenericParam>,
segment_stable_ptr: SyntaxStablePtrId,
must_be_explicit_error: SemanticDiagnosticKind,
item_forbidden_in_itself_explicit_error: SemanticDiagnosticKind,
) -> Maybe<()> {
if current_segment_generic_args.len() < generic_params.len() {
return Err(diagnostics.report(segment_stable_ptr, must_be_explicit_error));
}
let resolved_args = self.resolve_generic_args(
diagnostics,
&generic_params,
current_segment_generic_args,
segment_stable_ptr,
)?;
if generic_params
.iter()
.zip_eq(resolved_args.iter())
.all(|(gparam, garg)| gparam.as_arg(self.db) == *garg)
{
return Err(
diagnostics.report(segment_stable_ptr, item_forbidden_in_itself_explicit_error)
);
}
Ok(())
}
fn specialize_generic_statement_arg(
&mut self,
segment: &ast::PathSegment,
diagnostics: &mut SemanticDiagnostics,
identifier: &ast::TerminalIdentifier,
inner_generic_item: ResolvedGenericItem,
generic_args_syntax: Option<Vec<ast::GenericArg>>,
) -> ResolvedConcreteItem {
let segment_stable_ptr = segment.stable_ptr().untyped();
let specialized_item = self
.specialize_generic_module_item(
diagnostics,
identifier,
inner_generic_item.clone(),
generic_args_syntax.clone(),
)
.unwrap();
self.warn_same_impl_trait(
diagnostics,
&specialized_item,
&generic_args_syntax.unwrap_or_default(),
segment_stable_ptr,
);
specialized_item
}
}
fn resolve_self_segment(
db: &dyn SemanticGroup,
diagnostics: &mut SemanticDiagnostics,
identifier: &ast::TerminalIdentifier,
trait_or_impl_ctx: &TraitOrImplContext,
) -> Option<Maybe<ResolvedConcreteItem>> {
require(identifier.text(db.upcast()) == SELF_TYPE_KW)?;
Some(resolve_actual_self_segment(db, diagnostics, identifier, trait_or_impl_ctx))
}
fn resolve_actual_self_segment(
db: &dyn SemanticGroup,
diagnostics: &mut SemanticDiagnostics,
identifier: &ast::TerminalIdentifier,
trait_or_impl_ctx: &TraitOrImplContext,
) -> Maybe<ResolvedConcreteItem> {
match trait_or_impl_ctx {
TraitOrImplContext::None => Err(diagnostics.report(identifier, SelfNotSupportedInContext)),
TraitOrImplContext::Trait(trait_id) => {
let generic_parameters = db.trait_generic_params(*trait_id)?;
let concrete_trait_id = ConcreteTraitLongId {
trait_id: *trait_id,
generic_args: generic_params_to_args(&generic_parameters, db),
}
.intern(db);
Ok(ResolvedConcreteItem::Trait(concrete_trait_id))
}
TraitOrImplContext::Impl(impl_def_id) => {
let generic_parameters = db.impl_def_generic_params(*impl_def_id)?;
let impl_id = ImplLongId::Concrete(
ConcreteImplLongId {
impl_def_id: *impl_def_id,
generic_args: generic_params_to_args(&generic_parameters, db),
}
.intern(db),
);
Ok(ResolvedConcreteItem::Impl(impl_id.intern(db)))
}
}
}
enum ResolvedBase {
Module(ModuleId),
Crate(CrateId),
StatementEnvironment(ResolvedGenericItem),
FoundThroughGlobalUse { item_info: ModuleItemInfo, containing_module: ModuleId },
Ambiguous(Vec<ModuleItemId>),
ItemNotVisible(ModuleItemId, Vec<ModuleId>),
}
struct ResolvePathInnerCallbacks<ResolvedItem, ResolveFirst, ResolveNext, Validate, Mark>
where
ResolveFirst: FnMut(
&mut Resolver<'_>,
&mut SemanticDiagnostics,
&mut Peekable<std::slice::Iter<'_, ast::PathSegment>>,
Option<&mut Environment>,
) -> Maybe<ResolvedItem>,
ResolveNext: FnMut(
&mut Resolver<'_>,
&mut SemanticDiagnostics,
&ResolvedItem,
&ast::PathSegment,
NotFoundItemType,
) -> Maybe<ResolvedItem>,
Validate: FnMut(&mut SemanticDiagnostics, &ast::PathSegment) -> Maybe<()>,
Mark: FnMut(
&mut ResolvedItems,
&dyn SemanticGroup,
&syntax::node::ast::PathSegment,
ResolvedItem,
),
{
resolved_item_type: PhantomData<ResolvedItem>,
resolve_path_first_segment: ResolveFirst,
resolve_path_next_segment: ResolveNext,
validate_segment: Validate,
mark: Mark,
}