use core::cmp::Ordering;
use super::name::InputPosition;
use crate::ids::FileId;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
#[repr(u8)]
pub enum DefinitionKind {
#[default]
Undefined = 0,
Lazy = 1,
Shared = 2,
Weak = 3,
Common = 4,
Regular = 5,
}
impl DefinitionKind {
#[inline]
pub(crate) const fn from_u8(raw: u8) -> Self {
match raw {
1 => Self::Lazy,
2 => Self::Shared,
3 => Self::Weak,
4 => Self::Common,
5 => Self::Regular,
_ => Self::Undefined,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Definition {
pub kind: DefinitionKind,
pub file: FileId,
pub index: u32,
pub position: InputPosition,
pub aux: u64,
}
impl Default for Definition {
fn default() -> Self {
Self::undefined()
}
}
impl Definition {
#[inline]
#[must_use]
pub fn undefined() -> Self {
Self {
kind: DefinitionKind::Undefined,
file: FileId::from_u32(u32::MAX),
index: u32::MAX,
position: InputPosition::from_raw(u64::MAX),
aux: 0,
}
}
#[inline]
#[must_use]
pub fn is_defined(&self) -> bool {
self.kind != DefinitionKind::Undefined
}
#[inline]
#[must_use]
pub fn tie_key(&self) -> (InputPosition, FileId, u32) {
(self.position, self.file, self.index)
}
#[inline]
#[must_use]
pub fn same_origin(&self, other: &Self) -> bool {
self.file == other.file && self.index == other.index
}
}
pub trait Resolver: Sync {
fn compare(&self, a: &Definition, b: &Definition) -> Ordering;
fn is_duplicate(&self, winner: &Definition, other: &Definition) -> bool;
fn extracts(&self, current: &Definition) -> bool {
current.kind == DefinitionKind::Lazy
}
}
#[inline]
pub fn takes_precedence<R: Resolver + ?Sized>(
resolver: &R,
candidate: &Definition,
current: &Definition,
) -> bool {
if !candidate.is_defined() {
return false;
}
if !current.is_defined() {
return true;
}
match resolver.compare(candidate, current) {
Ordering::Greater => true,
Ordering::Less => false,
Ordering::Equal => candidate.tie_key() < current.tie_key(),
}
}
#[cfg(test)]
mod tests {
use super::*;
struct Positional;
impl Resolver for Positional {
fn compare(&self, _: &Definition, _: &Definition) -> Ordering {
Ordering::Equal
}
fn is_duplicate(&self, _: &Definition, _: &Definition) -> bool {
false
}
}
fn def(kind: DefinitionKind, file: u32, position: u64) -> Definition {
Definition {
kind,
file: FileId::from_u32(file),
index: 0,
position: InputPosition::from_raw(position),
aux: 0,
}
}
#[test]
fn undefined_never_wins_and_always_loses() {
let d = def(DefinitionKind::Lazy, 0, 5);
assert!(takes_precedence(&Positional, &d, &Definition::undefined()));
assert!(!takes_precedence(&Positional, &Definition::undefined(), &d));
assert!(!takes_precedence(
&Positional,
&Definition::undefined(),
&Definition::undefined()
));
}
#[test]
fn ties_go_to_the_earlier_position() {
let early = def(DefinitionKind::Regular, 7, 1);
let late = def(DefinitionKind::Regular, 3, 2);
assert!(takes_precedence(&Positional, &early, &late));
assert!(!takes_precedence(&Positional, &late, &early));
assert!(!takes_precedence(&Positional, &early, &early));
}
#[test]
fn kind_round_trips_through_u8() {
for kind in [
DefinitionKind::Undefined,
DefinitionKind::Lazy,
DefinitionKind::Shared,
DefinitionKind::Weak,
DefinitionKind::Common,
DefinitionKind::Regular,
] {
assert_eq!(DefinitionKind::from_u8(kind as u8), kind);
}
assert_eq!(DefinitionKind::from_u8(200), DefinitionKind::Undefined);
}
}