diff --git a/src/dylib_flag.rs b/src/dylib_flag.rs index e3a38a2..dcaba3e 100644 --- a/src/dylib_flag.rs +++ b/src/dylib_flag.rs @@ -66,7 +66,7 @@ impl RustFunction { let source_path = file.path().join("source.rs"); - std::fs::write(&source_path, &full_file).context("writing source")?; + std::fs::write(&source_path, full_file).context("writing source")?; let mut rustc = Command::new("rustc"); rustc.arg(source_path); diff --git a/src/processor/checker.rs b/src/processor/checker.rs new file mode 100644 index 0000000..cf10600 --- /dev/null +++ b/src/processor/checker.rs @@ -0,0 +1,194 @@ +use std::{collections::BTreeSet, mem}; + +use self::worklist::Worklist; + +use super::AstPath; + +#[derive(Debug)] +pub(crate) struct PassController { + state: PassControllerState, +} + +#[derive(Debug)] +enum PassControllerState { + InitialCollection { + candidates: Vec, + }, + Bisecting { + committed: BTreeSet, + failed: BTreeSet, + current: BTreeSet, + worklist: Worklist, + }, + Success, +} + +mod worklist { + use super::AstPath; + + /// A worklist that ensures that the inner list is never empty. + #[derive(Debug)] + pub(super) struct Worklist(Vec>); + + impl Worklist { + pub(super) fn new() -> Self { + Self(Vec::new()) + } + + pub(super) fn push(&mut self, next: Vec) { + if !next.is_empty() { + self.0.push(next); + } + } + + pub(super) fn pop(&mut self) -> Option> { + self.0.pop() + } + } +} + +// copied from `core` because who needs stable features anyways +pub const fn div_ceil(lhs: usize, rhs: usize) -> usize { + let d = lhs / rhs; + let r = lhs % rhs; + if r > 0 && rhs > 0 { + d + 1 + } else { + d + } +} + +fn split_owned, A: FromIterator, B: FromIterator>( + vec: From, +) -> (A, B) { + let candidates = vec.into_iter().collect::>(); + let half = div_ceil(candidates.len(), 2); + + let mut candidates = candidates.into_iter(); + + let first_half = candidates.by_ref().take(half).collect(); + let second_half = candidates.collect(); + + (first_half, second_half) +} + +impl PassController { + pub fn new() -> Self { + Self { + state: PassControllerState::InitialCollection { + candidates: Vec::new(), + }, + } + } + + pub fn reproduces(&mut self) { + match &mut self.state { + PassControllerState::InitialCollection { .. } => { + self.state = PassControllerState::Success; + } + PassControllerState::Bisecting { + committed, + failed: _, + current, + worklist: _, + } => { + committed.extend(mem::take(current)); + + self.next_in_worklist(); + } + PassControllerState::Success => unreachable!("Processed after success"), + } + } + + pub fn does_not_reproduce(&mut self) { + match &mut self.state { + PassControllerState::InitialCollection { candidates } => { + let (current, first_worklist_item) = split_owned(mem::take(candidates)); + + let mut worklist = Worklist::new(); + worklist.push(first_worklist_item); + + self.state = PassControllerState::Bisecting { + committed: BTreeSet::new(), + failed: BTreeSet::new(), + current, + worklist, + }; + } + PassControllerState::Bisecting { + committed: _, + failed, + current, + worklist, + } => { + if current.len() == 1 { + // We are at a leaf. This is a failure. + // FIXME: We should retry the failed ones until a fixpoint is reached. + failed.extend(mem::take(current)); + } else { + // Split it further and add it to the worklist. + let (first_half, second_half) = split_owned(mem::take(current)); + + worklist.push(first_half); + worklist.push(second_half); + } + + self.next_in_worklist() + } + PassControllerState::Success => unreachable!("Processed after success"), + } + } + + pub fn no_change(&mut self) { + match &self.state { + PassControllerState::InitialCollection { candidates } => { + assert!( + candidates.is_empty(), + "No change but received candidates: {candidates:?}" + ); + self.state = PassControllerState::Success; + } + PassControllerState::Bisecting { current, .. } => { + unreachable!("No change while bisecting, current was empty somehow: {current:?}"); + } + PassControllerState::Success => {} + } + } + + pub fn is_finished(&mut self) -> bool { + match &mut self.state { + PassControllerState::InitialCollection { .. } => false, + PassControllerState::Bisecting { .. } => false, + PassControllerState::Success => true, + } + } + + pub fn can_process(&mut self, path: &[String]) -> bool { + match &mut self.state { + PassControllerState::InitialCollection { candidates } => { + candidates.push(AstPath(path.to_owned())); + true + } + PassControllerState::Bisecting { current, .. } => current.contains(path), + PassControllerState::Success => { + unreachable!("Processed further after success"); + } + } + } + + fn next_in_worklist(&mut self) { + match &mut self.state { + PassControllerState::Bisecting { + current, worklist, .. + } => match worklist.pop() { + Some(next) => { + *current = next.into_iter().collect(); + } + None => { + self.state = PassControllerState::Success; + } + }, + _ => unreachable!("next_in_worklist called on non-bisecting state"), + } + } +} diff --git a/src/processor/mod.rs b/src/processor/mod.rs index c3efcd3..2cb6ce0 100644 --- a/src/processor/mod.rs +++ b/src/processor/mod.rs @@ -1,17 +1,14 @@ +mod checker; mod files; mod reaper; + pub(crate) use self::files::SourceFile; -use self::worklist::Worklist; use crate::{build::Build, processor::files::Changes, Options}; use anyhow::{Context, Result}; use owo_colors::OwoColorize; -use std::{ - borrow::Borrow, - collections::{BTreeSet, HashSet}, - ffi::OsStr, - fmt::Debug, - mem, -}; +use std::{borrow::Borrow, collections::HashSet, ffi::OsStr, fmt::Debug}; + +pub(crate) use self::checker::PassController; pub(crate) trait Processor { fn refresh_state(&mut self) -> Result<()> { @@ -198,195 +195,6 @@ impl Debug for AstPath { } } -#[derive(Debug)] -pub(crate) struct PassController { - state: PassControllerState, -} - -#[derive(Debug)] -enum PassControllerState { - InitialCollection { - candidates: Vec, - }, - Bisecting { - committed: BTreeSet, - failed: BTreeSet, - current: BTreeSet, - worklist: Worklist, - }, - Success, -} - -mod worklist { - use super::AstPath; - - /// A worklist that ensures that the inner list is never empty. - #[derive(Debug)] - pub(super) struct Worklist(Vec>); - - impl Worklist { - pub(super) fn new() -> Self { - Self(Vec::new()) - } - - pub(super) fn push(&mut self, next: Vec) { - if !next.is_empty() { - self.0.push(next); - } - } - - pub(super) fn pop(&mut self) -> Option> { - self.0.pop() - } - } -} - -// copied from `core` because who needs stable features anyways -pub const fn div_ceil(lhs: usize, rhs: usize) -> usize { - let d = lhs / rhs; - let r = lhs % rhs; - if r > 0 && rhs > 0 { - d + 1 - } else { - d - } -} - -fn split_owned, A: FromIterator, B: FromIterator>( - vec: From, -) -> (A, B) { - let candidates = vec.into_iter().collect::>(); - let half = div_ceil(candidates.len(), 2); - - let mut candidates = candidates.into_iter(); - - let first_half = candidates.by_ref().take(half).collect(); - let second_half = candidates.collect(); - - (first_half, second_half) -} - -impl PassController { - fn new() -> Self { - Self { - state: PassControllerState::InitialCollection { - candidates: Vec::new(), - }, - } - } - - fn next_in_worklist(&mut self) { - match &mut self.state { - PassControllerState::Bisecting { - current, worklist, .. - } => match worklist.pop() { - Some(next) => { - *current = next.into_iter().collect(); - } - None => { - self.state = PassControllerState::Success; - } - }, - _ => unreachable!("next_in_worklist called on non-bisecting state"), - } - } - - fn reproduces(&mut self) { - match &mut self.state { - PassControllerState::InitialCollection { .. } => { - self.state = PassControllerState::Success; - } - PassControllerState::Bisecting { - committed, - failed: _, - current, - worklist: _, - } => { - committed.extend(mem::take(current)); - - self.next_in_worklist(); - } - PassControllerState::Success => unreachable!("Processed after success"), - } - } - - fn does_not_reproduce(&mut self) { - match &mut self.state { - PassControllerState::InitialCollection { candidates } => { - let (current, first_worklist_item) = split_owned(mem::take(candidates)); - - let mut worklist = Worklist::new(); - worklist.push(first_worklist_item); - - self.state = PassControllerState::Bisecting { - committed: BTreeSet::new(), - failed: BTreeSet::new(), - current, - worklist, - }; - } - PassControllerState::Bisecting { - committed: _, - failed, - current, - worklist, - } => { - if current.len() == 1 { - // We are at a leaf. This is a failure. - // FIXME: We should retry the failed ones until a fixpoint is reached. - failed.extend(mem::take(current)); - } else { - // Split it further and add it to the worklist. - let (first_half, second_half) = split_owned(mem::take(current)); - - worklist.push(first_half); - worklist.push(second_half); - } - - self.next_in_worklist() - } - PassControllerState::Success => unreachable!("Processed after success"), - } - } - - fn no_change(&mut self) { - match &self.state { - PassControllerState::InitialCollection { candidates } => { - assert!( - candidates.is_empty(), - "No change but received candidates: {candidates:?}" - ); - self.state = PassControllerState::Success; - } - PassControllerState::Bisecting { current, .. } => { - unreachable!("No change while bisecting, current was empty somehow: {current:?}"); - } - PassControllerState::Success => {} - } - } - - fn is_finished(&mut self) -> bool { - match &mut self.state { - PassControllerState::InitialCollection { .. } => false, - PassControllerState::Bisecting { .. } => false, - PassControllerState::Success => true, - } - } - - pub(crate) fn can_process(&mut self, path: &[String]) -> bool { - match &mut self.state { - PassControllerState::InitialCollection { candidates } => { - candidates.push(AstPath(path.to_owned())); - true - } - PassControllerState::Bisecting { current, .. } => current.contains(path), - PassControllerState::Success => { - unreachable!("Processed further after success"); - } - } - } -} - macro_rules! tracking { () => { tracking!(visit_item_fn_mut);