#[cfg(test)]
#[path = "resolve_path_test.rs"]
mod test;
use std::iter::Peekable;
use cairo_lang_defs::ids::{
ConstantId, GenericParamId, GenericTypeId, ImplId, LanguageElementId, ModuleFileId, ModuleId,
ModuleItemId, TraitFunctionId, TraitId, TypeAliasId,
};
use cairo_lang_diagnostics::Maybe;
use cairo_lang_filesystem::ids::CrateLongId;
use cairo_lang_proc_macros::DebugWithDb;
use cairo_lang_syntax as syntax;
use cairo_lang_syntax::node::helpers::PathSegmentEx;
use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
use cairo_lang_syntax::node::{ast, Terminal, TypedSyntaxNode};
use cairo_lang_utils::extract_matches;
use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;
use itertools::Itertools;
use smol_str::SmolStr;
use crate::corelib::core_module;
use crate::db::SemanticGroup;
use crate::diagnostic::SemanticDiagnosticKind::*;
use crate::diagnostic::{NotFoundItemType, SemanticDiagnostics};
use crate::expr::inference::Inference;
use crate::items::enm::{ConcreteVariant, SemanticEnumEx};
use crate::items::functions::GenericFunctionId;
use crate::items::imp::{
find_impls_at_context, ConcreteImplId, ConcreteImplLongId, ImplLookupContext,
};
use crate::items::trt::{
ConcreteTraitGenericFunctionId, ConcreteTraitGenericFunctionLongId, ConcreteTraitId,
ConcreteTraitLongId,
};
use crate::literals::LiteralLongId;
use crate::types::{resolve_type_with_inference, substitute_generics, GenericSubstitution};
use crate::{
ConcreteFunction, ConcreteTypeId, FunctionId, FunctionLongId, GenericArgumentId, TypeId,
TypeLongId, Variant,
};
#[derive(Clone, PartialEq, Eq, Debug, DebugWithDb)]
#[debug_db(dyn SemanticGroup + 'static)]
pub enum ResolvedConcreteItem {
Constant(ConstantId),
Module(ModuleId),
Function(FunctionId),
TraitFunction(ConcreteTraitGenericFunctionId),
Type(TypeId),
Variant(ConcreteVariant),
Trait(ConcreteTraitId),
Impl(ConcreteImplId),
}
#[derive(Clone, PartialEq, Eq, Debug, DebugWithDb)]
#[debug_db(dyn SemanticGroup + 'static)]
pub enum ResolvedGenericItem {
Constant(ConstantId),
Module(ModuleId),
GenericFunction(GenericFunctionId),
TraitFunction(TraitFunctionId),
GenericType(GenericTypeId),
GenericTypeAlias(TypeAliasId),
Variant(Variant),
Trait(TraitId),
Impl(ImplId),
}
impl ResolvedConcreteItem {
pub fn generic(&self, db: &dyn SemanticGroup) -> Option<ResolvedGenericItem> {
Some(match self {
ResolvedConcreteItem::Constant(id) => ResolvedGenericItem::Constant(*id),
ResolvedConcreteItem::Module(item) => ResolvedGenericItem::Module(*item),
ResolvedConcreteItem::Function(function) => ResolvedGenericItem::GenericFunction(
db.lookup_intern_function(*function).function.generic_function,
),
ResolvedConcreteItem::TraitFunction(trait_function) => {
ResolvedGenericItem::TraitFunction(trait_function.function_id(db))
}
ResolvedConcreteItem::Type(ty) => {
if let TypeLongId::Concrete(concrete) = db.lookup_intern_type(*ty) {
ResolvedGenericItem::GenericType(concrete.generic_type(db))
} else {
return None;
}
}
ResolvedConcreteItem::Variant(ConcreteVariant { concrete_enum_id, id, ty, idx }) => {
ResolvedGenericItem::Variant(Variant {
enum_id: concrete_enum_id.enum_id(db),
id: *id,
ty: *ty,
idx: *idx,
})
}
ResolvedConcreteItem::Trait(concrete_trait) => ResolvedGenericItem::Trait(
db.lookup_intern_concrete_trait(*concrete_trait).trait_id,
),
ResolvedConcreteItem::Impl(concrete_impl) => {
ResolvedGenericItem::Impl(db.lookup_intern_concrete_impl(*concrete_impl).impl_id)
}
})
}
}
#[derive(Clone, Default, Debug, PartialEq, Eq, DebugWithDb)]
#[debug_db(dyn SemanticGroup + 'static)]
pub struct ResolvedLookback {
pub concrete: UnorderedHashMap<ast::TerminalIdentifierPtr, ResolvedConcreteItem>,
pub generic: UnorderedHashMap<ast::TerminalIdentifierPtr, ResolvedGenericItem>,
}
impl ResolvedLookback {
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
}
}
pub struct Resolver<'db> {
db: &'db dyn SemanticGroup,
pub module_file_id: ModuleFileId,
generic_params: OrderedHashMap<SmolStr, GenericParamId>,
pub lookback: ResolvedLookback,
}
impl<'db> Resolver<'db> {
pub fn new(
db: &'db dyn SemanticGroup,
module_file_id: ModuleFileId,
generic_params: &[GenericParamId],
) -> Self {
Self {
db,
module_file_id,
generic_params: generic_params
.iter()
.map(|generic_param| (generic_param.name(db.upcast()), *generic_param))
.collect(),
lookback: ResolvedLookback::default(),
}
}
pub fn resolve_concrete_path(
&mut self,
diagnostics: &mut SemanticDiagnostics,
inference: &mut Inference<'_>,
path: &ast::ExprPath,
item_type: NotFoundItemType,
) -> Maybe<ResolvedConcreteItem> {
let syntax_db = self.db.upcast();
let elements_vec = path.elements(syntax_db);
let mut segments = elements_vec.iter().peekable();
let mut item = self.resolve_concrete_path_first_segment(diagnostics, &mut segments)?;
while segments.peek().is_some() {
let segment = segments.next().unwrap();
let (identifier, generic_args) =
self.resolve_segment(diagnostics, inference, segment)?;
let cur_item_type =
if segments.peek().is_some() { NotFoundItemType::Identifier } else { item_type };
item = self.resolve_next_concrete(
diagnostics,
inference,
&item,
&identifier,
generic_args,
cur_item_type,
)?;
self.lookback.mark_concrete(self.db, segment, item.clone());
}
Ok(item)
}
pub fn resolve_segment(
&mut self,
diagnostics: &mut SemanticDiagnostics,
inference: &mut Inference<'_>,
segment: &ast::PathSegment,
) -> Maybe<(ast::TerminalIdentifier, Option<Vec<GenericArgumentId>>)> {
let syntax_db = self.db.upcast();
let (identifier, generic_args) = match segment {
syntax::node::ast::PathSegment::WithGenericArgs(segment) => {
let mut generic_args = vec![];
for generic_arg_syntax in
segment.generic_args(syntax_db).generic_args(syntax_db).elements(syntax_db)
{
match generic_arg_syntax {
ast::Expr::Literal(literal_syntax) => {
let literal = LiteralLongId::try_from(literal_syntax.text(syntax_db))
.map_err(|_| {
diagnostics.report(&literal_syntax, UnknownLiteral)
})?;
generic_args
.push(GenericArgumentId::Literal(self.db.intern_literal(literal)));
}
ast::Expr::Unary(unary)
if matches!(unary.expr(syntax_db), ast::Expr::Literal(_))
&& matches!(unary.op(syntax_db), ast::UnaryOperator::Minus(_)) =>
{
let mut literal = LiteralLongId::try_from(
extract_matches!(unary.expr(syntax_db), ast::Expr::Literal)
.text(syntax_db),
)
.map_err(|_| diagnostics.report(&unary, UnknownLiteral))?;
literal.value *= -1;
generic_args
.push(GenericArgumentId::Literal(self.db.intern_literal(literal)));
}
_ => {
let ty = resolve_type_with_inference(
self.db,
diagnostics,
inference,
self,
&generic_arg_syntax,
);
generic_args.push(GenericArgumentId::Type(ty));
}
}
}
(segment.ident(syntax_db), Some(generic_args))
}
syntax::node::ast::PathSegment::Simple(segment) => (segment.ident(syntax_db), None),
};
Ok((identifier, generic_args))
}
fn resolve_concrete_path_first_segment(
&mut self,
diagnostics: &mut SemanticDiagnostics,
segments: &mut Peekable<std::slice::Iter<'_, ast::PathSegment>>,
) -> Maybe<ResolvedConcreteItem> {
if let Some(base_module) = self.try_handle_super_segments(diagnostics, segments) {
return Ok(ResolvedConcreteItem::Module(base_module?));
}
let syntax_db = self.db.upcast();
Ok(match segments.peek().unwrap() {
syntax::node::ast::PathSegment::WithGenericArgs(generic_segment) => {
let identifier = generic_segment.ident(syntax_db);
if let Some(module_id) = self.determine_base_module(&identifier) {
ResolvedConcreteItem::Module(module_id)
} else {
return Err(diagnostics
.report(&generic_segment.generic_args(syntax_db), UnexpectedGenericArgs));
}
}
syntax::node::ast::PathSegment::Simple(simple_segment) => {
let identifier = simple_segment.ident(syntax_db);
if let Some(local_item) = self.determine_base_item_in_local_scope(&identifier) {
self.lookback.mark_concrete(self.db, segments.next().unwrap(), local_item)
} else if let Some(module_id) = self.determine_base_module(&identifier) {
ResolvedConcreteItem::Module(module_id)
} else {
self.lookback.mark_concrete(
self.db,
segments.next().unwrap(),
ResolvedConcreteItem::Module(ModuleId::CrateRoot(
self.db.intern_crate(CrateLongId(identifier.text(syntax_db))),
)),
)
}
}
})
}
pub fn resolve_generic_path(
&mut self,
diagnostics: &mut SemanticDiagnostics,
path: &ast::ExprPath,
item_type: NotFoundItemType,
) -> Maybe<ResolvedGenericItem> {
let syntax_db = self.db.upcast();
let elements_vec = path.elements(syntax_db);
let mut segments = elements_vec.iter().peekable();
let mut item = self.resolve_generic_path_first_segment(diagnostics, &mut segments)?;
while segments.peek().is_some() {
let segment = segments.next().unwrap();
let identifier = match segment {
syntax::node::ast::PathSegment::WithGenericArgs(segment) => {
return Err(
diagnostics.report(&segment.generic_args(syntax_db), UnexpectedGenericArgs)
);
}
syntax::node::ast::PathSegment::Simple(segment) => segment.ident(syntax_db),
};
let cur_item_type =
if segments.peek().is_some() { NotFoundItemType::Identifier } else { item_type };
item = self.resolve_next_generic(diagnostics, &item, &identifier, cur_item_type)?;
self.lookback.mark_generic(self.db, segment, item.clone());
}
Ok(item)
}
fn resolve_generic_path_first_segment(
&mut self,
diagnostics: &mut SemanticDiagnostics,
segments: &mut Peekable<std::slice::Iter<'_, ast::PathSegment>>,
) -> Maybe<ResolvedGenericItem> {
if let Some(base_module) = self.try_handle_super_segments(diagnostics, segments) {
return Ok(ResolvedGenericItem::Module(base_module?));
}
let syntax_db = self.db.upcast();
Ok(match segments.peek().unwrap() {
syntax::node::ast::PathSegment::WithGenericArgs(generic_segment) => {
return Err(diagnostics
.report(&generic_segment.generic_args(syntax_db), UnexpectedGenericArgs));
}
syntax::node::ast::PathSegment::Simple(simple_segment) => {
let identifier = simple_segment.ident(syntax_db);
if let Some(module_id) = self.determine_base_module(&identifier) {
ResolvedGenericItem::Module(module_id)
} else {
self.lookback.mark_generic(
self.db,
segments.next().unwrap(),
ResolvedGenericItem::Module(ModuleId::CrateRoot(
self.db.intern_crate(CrateLongId(identifier.text(syntax_db))),
)),
)
}
}
})
}
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",
}) {
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()),
};
}
if module_id == self.module_file_id.0 { None } else { Some(Ok(module_id)) }
}
fn resolve_next_concrete(
&mut self,
diagnostics: &mut SemanticDiagnostics,
inference: &mut Inference<'_>,
item: &ResolvedConcreteItem,
identifier: &ast::TerminalIdentifier,
generic_args: Option<Vec<GenericArgumentId>>,
item_type: NotFoundItemType,
) -> Maybe<ResolvedConcreteItem> {
let syntax_db = self.db.upcast();
let ident = identifier.text(syntax_db);
match item {
ResolvedConcreteItem::Module(module_id) => {
if ident == "super" {
return Err(diagnostics.report(identifier, InvalidPath));
}
let module_item = self
.db
.module_item_by_name(*module_id, ident)?
.ok_or_else(|| diagnostics.report(identifier, PathNotFound(item_type)))?;
let generic_item = self.module_item_to_generic_item(diagnostics, module_item)?;
Ok(self.specialize_generic_module_item(
diagnostics,
inference,
identifier,
generic_item,
generic_args,
)?)
}
ResolvedConcreteItem::Type(ty) => {
if let TypeLongId::Concrete(ConcreteTypeId::Enum(concrete_enum_id)) =
self.db.lookup_intern_type(*ty)
{
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 = self.db.lookup_intern_concrete_trait(*concrete_trait_id);
let trait_id = long_trait_id.trait_id;
let Some(trait_function_id) = self.db.trait_function_by_name(trait_id, ident)? else {
return Err(diagnostics.report(identifier, InvalidPath));
};
let trait_function = self.db.intern_concrete_trait_function(
ConcreteTraitGenericFunctionLongId::new(
self.db,
*concrete_trait_id,
trait_function_id,
),
);
Ok(ResolvedConcreteItem::Function(specialize_function(
self.db,
diagnostics,
inference,
identifier.stable_ptr().untyped(),
GenericFunctionId::Trait(trait_function),
generic_args.unwrap_or_default(),
)?))
}
_ => Err(diagnostics.report(identifier, InvalidPath)),
}
}
pub fn resolve_trait(
&mut self,
diagnostics: &mut SemanticDiagnostics,
inference: &mut Inference<'_>,
concrete_trait_function_id: ConcreteTraitGenericFunctionId,
stable_ptr: SyntaxStablePtrId,
) -> Maybe<ConcreteImplId> {
let defs_db = self.db.upcast();
let concrete_trait_id = concrete_trait_function_id.concrete_trait_id(self.db);
let function_id = concrete_trait_function_id.function_id(self.db);
let trait_id = function_id.trait_id(defs_db);
let lookup_context = self.impl_lookup_context(trait_id);
let impl_id = match &find_impls_at_context(
self.db,
inference,
&lookup_context,
concrete_trait_id,
stable_ptr,
)?
.into_iter()
.collect_vec()[..]
{
&[] => {
return Err(diagnostics.report_by_ptr(
stable_ptr,
NoImplementationOfTraitFunction {
concrete_trait_id,
function_name: function_id.name(defs_db),
},
));
}
&[impl_id] => impl_id,
impls => {
return Err(diagnostics.report_by_ptr(
stable_ptr,
MultipleImplementationOfTraitFunction {
trait_id,
all_impl_ids: impls.to_vec(),
function_name: function_id.name(defs_db),
},
));
}
};
let long_concrete_trait = self.db.lookup_intern_concrete_trait(concrete_trait_id);
let impl_generic_params = self.db.impl_generic_params(impl_id)?;
let impl_concrete_trait = self.db.impl_trait(impl_id)?;
let long_imp_concrete_trait = self.db.lookup_intern_concrete_trait(impl_concrete_trait);
let generic_args = inference
.infer_generics(
&impl_generic_params,
&long_imp_concrete_trait.generic_args,
&long_concrete_trait.generic_args,
stable_ptr,
)
.expect("Impl returned from find_impls_at_context() must be conformable.");
Ok(self.db.intern_concrete_impl(ConcreteImplLongId { impl_id, generic_args }))
}
pub fn impl_lookup_context(&mut self, trait_id: TraitId) -> ImplLookupContext {
let lookup_context = ImplLookupContext {
module_id: self.module_file_id.0,
extra_modules: vec![trait_id.module_file_id(self.db.upcast()).0],
generic_params: self.generic_params.values().copied().collect(),
};
lookup_context
}
fn specialize_generic_module_item(
&mut self,
diagnostics: &mut SemanticDiagnostics,
inference: &mut Inference<'_>,
identifier: &syntax::node::ast::TerminalIdentifier,
generic_item: ResolvedGenericItem,
generic_args: Option<Vec<GenericArgumentId>>,
) -> Maybe<ResolvedConcreteItem> {
Ok(match generic_item {
ResolvedGenericItem::Constant(id) => ResolvedConcreteItem::Constant(id),
ResolvedGenericItem::Module(module_id) => {
if generic_args.is_some() {
return Err(diagnostics.report(identifier, UnexpectedGenericArgs));
}
ResolvedConcreteItem::Module(module_id)
}
ResolvedGenericItem::GenericFunction(generic_function) => {
ResolvedConcreteItem::Function(specialize_function(
self.db,
diagnostics,
inference,
identifier.stable_ptr().untyped(),
generic_function,
generic_args.unwrap_or_default(),
)?)
}
ResolvedGenericItem::GenericType(generic_type) => {
ResolvedConcreteItem::Type(specialize_type(
self.db,
diagnostics,
inference,
identifier.stable_ptr().untyped(),
generic_type,
generic_args.unwrap_or_default(),
)?)
}
ResolvedGenericItem::GenericTypeAlias(type_alias_id) => {
self.db.priv_type_alias_semantic_data(type_alias_id)?.check_no_cycle()?;
let ty = self.db.type_alias_resolved_type(type_alias_id)?;
let mut generic_args = generic_args.unwrap_or_default();
let generic_params = self.db.type_alias_generic_params(type_alias_id)?;
conform_generic_args(
self.db,
diagnostics,
inference,
&generic_params,
&mut generic_args,
identifier.stable_ptr().untyped(),
);
let substitution = GenericSubstitution(
generic_params.into_iter().zip(generic_args.into_iter()).collect(),
);
ResolvedConcreteItem::Type(substitute_generics(self.db, &substitution, ty))
}
ResolvedGenericItem::Trait(trait_id) => ResolvedConcreteItem::Trait(specialize_trait(
self.db,
diagnostics,
inference,
identifier.stable_ptr().untyped(),
trait_id,
generic_args.unwrap_or_default(),
)?),
ResolvedGenericItem::Impl(impl_id) => ResolvedConcreteItem::Impl(specialize_impl(
self.db,
diagnostics,
inference,
identifier.stable_ptr().untyped(),
impl_id,
generic_args.unwrap_or_default(),
)?),
ResolvedGenericItem::Variant(_) => panic!("Variant is not a module item."),
ResolvedGenericItem::TraitFunction(_) => panic!("TraitFunction is not a module item."),
})
}
fn resolve_next_generic(
&mut self,
diagnostics: &mut SemanticDiagnostics,
item: &ResolvedGenericItem,
identifier: &ast::TerminalIdentifier,
item_type: NotFoundItemType,
) -> Maybe<ResolvedGenericItem> {
let syntax_db = self.db.upcast();
let ident = identifier.text(syntax_db);
match item {
ResolvedGenericItem::Module(module_id) => {
let module_item = self
.db
.module_item_by_name(*module_id, ident)?
.ok_or_else(|| diagnostics.report(identifier, PathNotFound(item_type)))?;
self.module_item_to_generic_item(diagnostics, module_item)
}
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 module_item_to_generic_item(
&mut self,
diagnostics: &mut SemanticDiagnostics,
module_item: ModuleItemId,
) -> Maybe<ResolvedGenericItem> {
Ok(match module_item {
ModuleItemId::Constant(id) => ResolvedGenericItem::Constant(id),
ModuleItemId::Submodule(id) => ResolvedGenericItem::Module(ModuleId::Submodule(id)),
ModuleItemId::Use(id) => {
diagnostics.diagnostics.extend(self.db.use_semantic_diagnostics(id));
self.db.use_resolved_item(id)?
}
ModuleItemId::FreeFunction(id) => {
ResolvedGenericItem::GenericFunction(GenericFunctionId::Free(id))
}
ModuleItemId::ExternFunction(id) => {
ResolvedGenericItem::GenericFunction(GenericFunctionId::Extern(id))
}
ModuleItemId::Struct(id) => ResolvedGenericItem::GenericType(GenericTypeId::Struct(id)),
ModuleItemId::Enum(id) => ResolvedGenericItem::GenericType(GenericTypeId::Enum(id)),
ModuleItemId::TypeAlias(id) => ResolvedGenericItem::GenericTypeAlias(id),
ModuleItemId::ExternType(id) => {
ResolvedGenericItem::GenericType(GenericTypeId::Extern(id))
}
ModuleItemId::Trait(id) => ResolvedGenericItem::Trait(id),
ModuleItemId::Impl(id) => ResolvedGenericItem::Impl(id),
})
}
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.generic_params.get(&ident) {
return Some(ResolvedConcreteItem::Type(
self.db.intern_type(TypeLongId::GenericParameter(*generic_param_id)),
));
}
None
}
fn determine_base_module(&mut self, identifier: &ast::TerminalIdentifier) -> Option<ModuleId> {
let syntax_db = self.db.upcast();
let ident = identifier.text(syntax_db);
if let Ok(Some(_)) = self.db.module_item_by_name(self.module_file_id.0, ident.clone()) {
return Some(self.module_file_id.0);
}
let crate_id = self.db.intern_crate(CrateLongId(ident));
if self.db.crate_root_dir(crate_id).is_some() {
return None;
}
Some(core_module(self.db))
}
}
fn specialize_trait(
db: &dyn SemanticGroup,
diagnostics: &mut SemanticDiagnostics,
inference: &mut Inference<'_>,
stable_ptr: SyntaxStablePtrId,
trait_id: TraitId,
mut generic_args: Vec<GenericArgumentId>,
) -> Maybe<ConcreteTraitId> {
let generic_params = db
.trait_generic_params(trait_id)
.map_err(|_| diagnostics.report_by_ptr(stable_ptr, UnknownTrait))?;
conform_generic_args(
db,
diagnostics,
inference,
&generic_params,
&mut generic_args,
stable_ptr,
);
Ok(db.intern_concrete_trait(ConcreteTraitLongId { trait_id, generic_args }))
}
fn specialize_impl(
db: &dyn SemanticGroup,
diagnostics: &mut SemanticDiagnostics,
inference: &mut Inference<'_>,
stable_ptr: SyntaxStablePtrId,
impl_id: ImplId,
mut generic_args: Vec<GenericArgumentId>,
) -> Maybe<ConcreteImplId> {
db.priv_impl_declaration_data(impl_id)?.check_no_cycle()?;
let generic_params = db
.impl_generic_params(impl_id)
.map_err(|_| diagnostics.report_by_ptr(stable_ptr, UnknownImpl))?;
conform_generic_args(
db,
diagnostics,
inference,
&generic_params,
&mut generic_args,
stable_ptr,
);
Ok(db.intern_concrete_impl(ConcreteImplLongId { impl_id, generic_args }))
}
pub fn specialize_function(
db: &dyn SemanticGroup,
diagnostics: &mut SemanticDiagnostics,
inference: &mut Inference<'_>,
stable_ptr: SyntaxStablePtrId,
generic_function: GenericFunctionId,
mut generic_args: Vec<GenericArgumentId>,
) -> Maybe<FunctionId> {
let generic_params: Vec<_> = db
.function_signature_generic_params(generic_function.signature(db))
.map_err(|_| diagnostics.report_by_ptr(stable_ptr, UnknownFunction))?;
conform_generic_args(
db,
diagnostics,
inference,
&generic_params,
&mut generic_args,
stable_ptr,
);
Ok(db.intern_function(FunctionLongId {
function: ConcreteFunction { generic_function, generic_args },
}))
}
pub fn specialize_type(
db: &dyn SemanticGroup,
diagnostics: &mut SemanticDiagnostics,
inference: &mut Inference<'_>,
stable_ptr: SyntaxStablePtrId,
generic_type: GenericTypeId,
mut generic_args: Vec<GenericArgumentId>,
) -> Maybe<TypeId> {
let generic_params = db
.generic_type_generic_params(generic_type)
.map_err(|_| diagnostics.report_by_ptr(stable_ptr, UnknownType))?;
conform_generic_args(
db,
diagnostics,
inference,
&generic_params,
&mut generic_args,
stable_ptr,
);
Ok(db.intern_type(TypeLongId::Concrete(ConcreteTypeId::new(db, generic_type, generic_args))))
}
pub fn conform_generic_args(
db: &dyn SemanticGroup,
diagnostics: &mut SemanticDiagnostics,
inference: &mut Inference<'_>,
generic_params: &[GenericParamId],
generic_args: &mut Vec<GenericArgumentId>,
stable_ptr: SyntaxStablePtrId,
) {
let mut diag_added = None;
let err = WrongNumberOfGenericArguments {
expected: generic_params.len(),
actual: generic_args.len(),
};
let mut report =
|| *diag_added.get_or_insert_with(|| diagnostics.report_by_ptr(stable_ptr, err.clone()));
if generic_args.len() > generic_params.len() {
generic_args
.resize(generic_params.len(), GenericArgumentId::Type(TypeId::missing(db, report())));
}
for (i, _) in generic_params.iter().enumerate() {
if i >= generic_args.len() {
if inference.enabled {
generic_args.push(GenericArgumentId::Type(inference.new_var(stable_ptr)))
} else {
generic_args.push(GenericArgumentId::Type(TypeId::missing(db, report())))
}
}
}
}