use core::cmp::Ordering;
use super::definition::{Definition, DefinitionKind, Resolver};
#[derive(Clone, Copy, Debug, Default)]
pub struct ElfReferenceRules;
impl ElfReferenceRules {
#[must_use]
pub const fn rank(kind: DefinitionKind) -> u8 {
match kind {
DefinitionKind::Undefined => 0,
DefinitionKind::Lazy => 1,
DefinitionKind::Shared => 2,
DefinitionKind::Weak => 3,
DefinitionKind::Common => 4,
DefinitionKind::Regular => 5,
}
}
}
impl Resolver for ElfReferenceRules {
fn compare(&self, a: &Definition, b: &Definition) -> Ordering {
let by_rank = Self::rank(a.kind).cmp(&Self::rank(b.kind));
if by_rank == Ordering::Equal && a.kind == DefinitionKind::Common {
return a.aux.cmp(&b.aux);
}
by_rank
}
fn is_duplicate(&self, winner: &Definition, other: &Definition) -> bool {
winner.kind == DefinitionKind::Regular && other.kind == DefinitionKind::Regular
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ids::FileId;
use crate::symbols::definition::takes_precedence;
use crate::symbols::name::InputPosition;
fn def(kind: DefinitionKind, position: u32, aux: u64) -> Definition {
Definition {
kind,
file: FileId::new(position as usize),
index: 0,
position: InputPosition::new(position, 0),
aux,
}
}
const KINDS_WEAKEST_FIRST: [DefinitionKind; 5] = [
DefinitionKind::Lazy,
DefinitionKind::Shared,
DefinitionKind::Weak,
DefinitionKind::Common,
DefinitionKind::Regular,
];
#[test]
fn stronger_kind_wins_regardless_of_position() {
let rules = ElfReferenceRules;
for (i, &weaker) in KINDS_WEAKEST_FIRST.iter().enumerate() {
for &stronger in &KINDS_WEAKEST_FIRST[i + 1..] {
let w = def(weaker, 0, 8);
let s = def(stronger, 1, 8);
assert!(
takes_precedence(&rules, &s, &w),
"{stronger:?} > {weaker:?}"
);
assert!(
!takes_precedence(&rules, &w, &s),
"{weaker:?} < {stronger:?}"
);
}
}
}
#[test]
fn same_kind_goes_to_earlier_input() {
let rules = ElfReferenceRules;
for kind in KINDS_WEAKEST_FIRST {
let early = def(kind, 3, 8);
let late = def(kind, 9, 8);
assert!(takes_precedence(&rules, &early, &late), "{kind:?}");
assert!(!takes_precedence(&rules, &late, &early), "{kind:?}");
}
}
#[test]
fn larger_common_wins() {
let rules = ElfReferenceRules;
let small_early = def(DefinitionKind::Common, 0, 4);
let large_late = def(DefinitionKind::Common, 5, 16);
assert!(takes_precedence(&rules, &large_late, &small_early));
assert!(!takes_precedence(&rules, &small_early, &large_late));
}
#[test]
fn size_only_matters_between_commons() {
let rules = ElfReferenceRules;
let weak_huge = def(DefinitionKind::Weak, 0, 1 << 40);
let common_small = def(DefinitionKind::Common, 1, 1);
let strong_small = def(DefinitionKind::Regular, 2, 0);
assert!(takes_precedence(&rules, &common_small, &weak_huge));
assert!(takes_precedence(&rules, &strong_small, &common_small));
}
#[test]
fn only_two_strong_definitions_conflict() {
let rules = ElfReferenceRules;
for a in KINDS_WEAKEST_FIRST {
for b in KINDS_WEAKEST_FIRST {
let expected = a == DefinitionKind::Regular && b == DefinitionKind::Regular;
assert_eq!(
rules.is_duplicate(&def(a, 0, 0), &def(b, 1, 0)),
expected,
"{a:?} vs {b:?}"
);
}
}
}
#[test]
fn only_lazy_definitions_extract() {
let rules = ElfReferenceRules;
for kind in KINDS_WEAKEST_FIRST {
assert_eq!(
rules.extracts(&def(kind, 0, 0)),
kind == DefinitionKind::Lazy
);
}
}
#[test]
fn precedence_is_a_total_order() {
let rules = ElfReferenceRules;
let mut all = Vec::new();
for kind in KINDS_WEAKEST_FIRST {
for position in 0..3 {
for aux in [1, 2] {
let mut d = def(kind, position, aux);
d.index = aux as u32;
all.push(d);
}
}
}
for a in &all {
for b in &all {
if a == b {
assert!(!takes_precedence(&rules, a, b));
continue;
}
assert_ne!(
takes_precedence(&rules, a, b),
takes_precedence(&rules, b, a),
"{a:?} / {b:?}"
);
for c in &all {
if takes_precedence(&rules, a, b) && takes_precedence(&rules, b, c) {
assert!(takes_precedence(&rules, a, c));
}
}
}
}
}
}