use super::Pass;
use crate::backend::binder::{Definition, ParameterDefinition, Scope, Typing};
use crate::backend::types::{FunctionType, Type, TypeId};
use crate::cst::NodeId;
impl Pass<'_> {
fn get_function_definition_parameters(
&self,
definition_id: Option<NodeId>,
) -> Option<&[ParameterDefinition]> {
let Some(Definition::Function(function_definition)) =
self.binder.find_definition_by_id(definition_id?)
else {
return None;
};
let Scope::Parameters(parameters_scope) = self
.binder
.get_scope_by_id(function_definition.parameters_scope_id)
else {
unreachable!("incorrect scope kind, expected parameters");
};
Some(¶meters_scope.parameters)
}
pub(super) fn lookup_function_matching_positional_arguments<'a>(
&'a self,
type_ids: &[TypeId],
argument_typings: &[Typing],
receiver_type_id: Option<TypeId>,
) -> Option<&'a FunctionType> {
let mut function_types = type_ids.iter().filter_map(|type_id| {
if let Type::Function(function_type) = self.types.get_type_by_id(*type_id) {
Some(function_type)
} else {
None
}
});
function_types.find(|function_type| {
let Some(parameters) =
self.get_function_definition_parameters(function_type.definition_id)
else {
return false;
};
if let Some(receiver_type_id) = receiver_type_id {
if parameters.len() == argument_typings.len()
&& function_type.implicit_receiver_type.is_some_and(
|implicit_receiver_type_id| {
self.types.implicitly_convertible_to(
receiver_type_id,
implicit_receiver_type_id,
)
},
)
{
self.parameters_match_positional_arguments(
parameters,
argument_typings,
function_type.external,
)
} else if parameters.len() == argument_typings.len() + 1
&& function_type.implicit_receiver_type.is_none()
&& parameters.first().is_some_and(|parameter| {
parameter.type_id.is_some_and(|type_id| {
self.types
.implicitly_convertible_to(receiver_type_id, type_id)
})
})
{
self.parameters_match_positional_arguments(
¶meters[1..],
argument_typings,
function_type.external,
)
} else {
false
}
} else if parameters.len() == argument_typings.len() {
self.parameters_match_positional_arguments(
parameters,
argument_typings,
function_type.external,
)
} else {
false
}
})
}
fn parameters_match_positional_arguments(
&self,
parameters: &[ParameterDefinition],
argument_typings: &[Typing],
external_call: bool,
) -> bool {
parameters
.iter()
.zip(argument_typings)
.all(|(parameter, argument_typing)| {
parameter.type_id.is_some_and(|type_id| {
self.parameter_type_matches_argument_typing(
type_id,
argument_typing,
external_call,
)
})
})
}
fn parameter_type_matches_argument_typing(
&self,
parameter_type: TypeId,
argument_typing: &Typing,
external_call: bool,
) -> bool {
match argument_typing {
Typing::Resolved(type_id) => {
if external_call {
self.types
.implicitly_convertible_to_for_external_call(*type_id, parameter_type)
} else {
self.types
.implicitly_convertible_to(*type_id, parameter_type)
}
}
Typing::This => self
.types
.implicitly_convertible_to(self.types.address(), parameter_type),
_ => false,
}
}
pub(super) fn lookup_function_matching_named_arguments<'a>(
&'a self,
type_ids: &[TypeId],
argument_typings: &[(String, Typing)],
receiver_type_id: Option<TypeId>,
) -> Option<&'a FunctionType> {
let mut function_types = type_ids.iter().filter_map(|type_id| {
if let Type::Function(function_type) = self.types.get_type_by_id(*type_id) {
Some(function_type)
} else {
None
}
});
function_types.find(|function_type| {
let Some(parameters) =
self.get_function_definition_parameters(function_type.definition_id)
else {
return false;
};
if parameters.iter().any(|parameter| parameter.name.is_none()) {
return false;
}
if parameters.len() == argument_typings.len() {
self.parameters_match_named_arguments(
parameters,
argument_typings,
function_type.external,
)
} else if let Some(receiver_type_id) = receiver_type_id {
if parameters.len() == argument_typings.len() + 1
&& parameters.first().is_some_and(|parameter| {
parameter.type_id.is_some_and(|type_id| {
self.types
.implicitly_convertible_to(receiver_type_id, type_id)
})
})
{
self.parameters_match_named_arguments(
¶meters[1..],
argument_typings,
function_type.external,
)
} else {
false
}
} else {
false
}
})
}
fn parameters_match_named_arguments(
&self,
parameters: &[ParameterDefinition],
argument_typings: &[(String, Typing)],
external_call: bool,
) -> bool {
argument_typings
.iter()
.all(|(argument_name, argument_typing)| {
let Some(parameter) = parameters.iter().find(|parameter| {
parameter
.name
.as_ref()
.is_some_and(|name| name == argument_name)
}) else {
return false;
};
parameter.type_id.is_some_and(|type_id| {
self.parameter_type_matches_argument_typing(
type_id,
argument_typing,
external_call,
)
})
})
}
fn get_event_definition_parameters(
&self,
definition_id: NodeId,
) -> Option<&[ParameterDefinition]> {
let Some(Definition::Event(event_definition)) =
self.binder.find_definition_by_id(definition_id)
else {
return None;
};
let Scope::Parameters(parameters_scope) = self
.binder
.get_scope_by_id(event_definition.parameters_scope_id)
else {
unreachable!("incorrect scope kind, expected parameters");
};
Some(¶meters_scope.parameters)
}
pub(super) fn lookup_event_matching_positional_arguments(
&self,
definition_ids: &[NodeId],
argument_typings: &[Typing],
) -> Option<NodeId> {
for definition_id in definition_ids {
let Some(parameters) = self.get_event_definition_parameters(*definition_id) else {
continue;
};
if parameters.len() == argument_typings.len() {
if self.parameters_match_positional_arguments(parameters, argument_typings, false) {
return Some(*definition_id);
}
}
}
None
}
pub(super) fn lookup_event_matching_named_arguments(
&self,
definition_ids: &[NodeId],
argument_typings: &[(String, Typing)],
) -> Option<NodeId> {
for definition_id in definition_ids {
let Some(parameters) = self.get_event_definition_parameters(*definition_id) else {
continue;
};
if parameters.iter().any(|parameter| parameter.name.is_none()) {
continue;
}
if parameters.len() == argument_typings.len() {
if self.parameters_match_named_arguments(parameters, argument_typings, false) {
return Some(*definition_id);
}
}
}
None
}
}