idea (rejected): only generate work up to constraint gen, then look at what modules are actually used and generate Solve.. work based on that

This commit is contained in:
Folkert 2022-03-19 16:26:38 +01:00
parent 3db44af36f
commit 2cbe5f5231
No known key found for this signature in database
GPG Key ID: 1F17F6FFD112B97C
5 changed files with 156 additions and 23 deletions

View File

@ -157,6 +157,8 @@ pub fn build_file<'a>(
buf.push('\n'); buf.push('\n');
dbg!(module_id);
report_timing(buf, "Read .roc file from disk", module_timing.read_roc_file); report_timing(buf, "Read .roc file from disk", module_timing.read_roc_file);
report_timing(buf, "Parse header", module_timing.parse_header); report_timing(buf, "Parse header", module_timing.parse_header);
report_timing(buf, "Parse body", module_timing.parse_body); report_timing(buf, "Parse body", module_timing.parse_body);

View File

@ -32,13 +32,21 @@ impl ExposedByModule {
/// ///
/// Useful when we know what modules a particular module imports, and want just /// Useful when we know what modules a particular module imports, and want just
/// the exposed types for those exposed modules. /// the exposed types for those exposed modules.
pub fn retain_modules<'a>(&self, it: impl Iterator<Item = &'a ModuleId>) -> Self { pub fn retain_modules<'a>(
&self,
home: ModuleId,
it: impl Iterator<Item = &'a ModuleId>,
) -> Self {
let mut output = Self::default(); let mut output = Self::default();
for module_id in it { for module_id in it {
match self.exposed.get(module_id) { match self.exposed.get(module_id) {
None => { None => {
internal_error!("Module {:?} did not register its exposed values", module_id) internal_error!(
"Module {:?} did not register its exposed values for {:?}",
module_id,
home,
)
} }
Some(exposed_types) => { Some(exposed_types) => {
output.exposed.insert(*module_id, exposed_types.clone()); output.exposed.insert(*module_id, exposed_types.clone());

View File

@ -1099,7 +1099,7 @@ fn load<'a>(
) -> Result<LoadResult<'a>, LoadingProblem<'a>> { ) -> Result<LoadResult<'a>, LoadingProblem<'a>> {
// When compiling to wasm, we cannot spawn extra threads // When compiling to wasm, we cannot spawn extra threads
// so we have a single-threaded implementation // so we have a single-threaded implementation
if true || cfg!(target_family = "wasm") { if cfg!(target_family = "wasm") {
load_single_threaded( load_single_threaded(
arena, arena,
load_start, load_start,
@ -1614,14 +1614,18 @@ fn report_unused_imported_modules<'a>(
state: &mut State<'a>, state: &mut State<'a>,
module_id: ModuleId, module_id: ModuleId,
constrained_module: &ConstrainedModule, constrained_module: &ConstrainedModule,
) { ) -> (Vec<ModuleId>, Vec<ModuleId>) {
let mut kept = vec![];
let mut removed = vec![];
let mut unused_imported_modules = constrained_module.imported_modules.clone(); let mut unused_imported_modules = constrained_module.imported_modules.clone();
for symbol in constrained_module.module.referenced_values.iter() { for symbol in constrained_module.module.referenced_values.iter() {
kept.push(symbol.module_id());
unused_imported_modules.remove(&symbol.module_id()); unused_imported_modules.remove(&symbol.module_id());
} }
for symbol in constrained_module.module.referenced_types.iter() { for symbol in constrained_module.module.referenced_types.iter() {
kept.push(symbol.module_id());
unused_imported_modules.remove(&symbol.module_id()); unused_imported_modules.remove(&symbol.module_id());
} }
@ -1633,8 +1637,23 @@ fn report_unused_imported_modules<'a>(
for (unused, region) in unused_imported_modules.drain() { for (unused, region) in unused_imported_modules.drain() {
if !unused.is_builtin() { if !unused.is_builtin() {
existing.push(roc_problem::can::Problem::UnusedImport(unused, region)); existing.push(roc_problem::can::Problem::UnusedImport(unused, region));
// we will still typecheck this module to report errors
kept.push(unused)
} else {
// for builtin modules, we will just skip the rest of the process
// in the future, we can also do this for modules from packages
removed.push(unused);
} }
} }
kept.sort();
kept.dedup();
removed.sort();
removed.dedup();
(kept, removed)
} }
fn update<'a>( fn update<'a>(
@ -1870,7 +1889,7 @@ fn update<'a>(
work.extend(state.dependencies.add_module( work.extend(state.dependencies.add_module(
header.module_id, header.module_id,
&header.package_qualified_imported_modules, &header.package_qualified_imported_modules,
state.goal_phase, Phase::CanonicalizeAndConstrain,
)); ));
state.module_cache.headers.insert(header.module_id, header); state.module_cache.headers.insert(header.module_id, header);
@ -1919,7 +1938,7 @@ fn update<'a>(
} }
CanonicalizedAndConstrained { CanonicalizedAndConstrained {
constrained_module, mut constrained_module,
canonicalization_problems, canonicalization_problems,
module_docs, module_docs,
} => { } => {
@ -1934,7 +1953,51 @@ fn update<'a>(
state.module_cache.documentation.insert(module_id, docs); state.module_cache.documentation.insert(module_id, docs);
} }
report_unused_imported_modules(&mut state, module_id, &constrained_module); let (kept, removed) =
report_unused_imported_modules(&mut state, module_id, &constrained_module);
for rem in removed {
constrained_module.imported_modules.remove(&rem);
}
use std::fmt::Write;
let mut buf = String::new();
writeln!(buf, "{:?} depends on:", module_id);
for dep in kept {
if !constrained_module.imported_modules.contains_key(&dep) {
continue;
}
writeln!(buf, " {:?} ", dep);
if module_id != dep {
state
.dependencies
.add_dependency(module_id, dep, Phase::SolveTypes);
}
}
println!("{}", buf);
state.dependencies.add_dependency_help(
module_id,
module_id,
Phase::SolveTypes,
Phase::CanonicalizeAndConstrain,
);
state
.dependencies
.add_to_status(module_id, Phase::SolveTypes);
/*
for dependency in remove_dependencies {
println!("remove dep {:?} <- {:?}", module_id, dependency);
state.dependencies.remove_dependencies(
module_id,
dependency,
Phase::SolveTypes,
state.goal_phase,
);
}
*/
state state
.module_cache .module_cache
@ -1996,7 +2059,7 @@ fn update<'a>(
if is_host_exposed && state.goal_phase == Phase::SolveTypes { if is_host_exposed && state.goal_phase == Phase::SolveTypes {
debug_assert!(work.is_empty()); debug_assert!(work.is_empty());
debug_assert!(state.dependencies.solved_all()); // debug_assert!(state.dependencies.solved_all());
state.timings.insert(module_id, module_timing); state.timings.insert(module_id, module_timing);
@ -2787,10 +2850,7 @@ fn load_module<'a>(
roc_parse::header::ModuleName::new("Bool"), roc_parse::header::ModuleName::new("Bool"),
Collection::with_items(&[Loc::at_zero(Spaced::Item(ExposedName::new("Bool")))]), Collection::with_items(&[Loc::at_zero(Spaced::Item(ExposedName::new("Bool")))]),
)), )),
Loc::at_zero(ImportsEntry::Module( // Note: List is only used for the type, which we ensure is always in scope
roc_parse::header::ModuleName::new("List"),
Collection::with_items(&[Loc::at_zero(Spaced::Item(ExposedName::new("List")))]),
)),
Loc::at_zero(ImportsEntry::Module( Loc::at_zero(ImportsEntry::Module(
roc_parse::header::ModuleName::new("Num"), roc_parse::header::ModuleName::new("Num"),
Collection::with_items(&[ Collection::with_items(&[
@ -2856,9 +2916,6 @@ fn load_module<'a>(
toUtf8 : Str -> List U8 toUtf8 : Str -> List U8
# fromUtf8 : List U8 -> Result Str [ BadUtf8 Utf8Problem ]*
# fromUtf8Range : List U8 -> Result Str [ BadUtf8 Utf8Problem Nat, OutOfBounds ]*
fromUtf8 : List U8 -> Result Str [ BadUtf8 Utf8ByteProblem Nat ]* fromUtf8 : List U8 -> Result Str [ BadUtf8 Utf8ByteProblem Nat ]*
fromUtf8Range : List U8, { start : Nat, count : Nat } -> Result Str [ BadUtf8 Utf8ByteProblem Nat, OutOfBounds ]* fromUtf8Range : List U8, { start : Nat, count : Nat } -> Result Str [ BadUtf8 Utf8ByteProblem Nat, OutOfBounds ]*
@ -4344,7 +4401,8 @@ impl<'a> BuildTask<'a> {
dep_idents: MutMap<ModuleId, IdentIds>, dep_idents: MutMap<ModuleId, IdentIds>,
declarations: Vec<Declaration>, declarations: Vec<Declaration>,
) -> Self { ) -> Self {
let exposed_by_module = exposed_types.retain_modules(imported_modules.keys()); let exposed_by_module =
exposed_types.retain_modules(module.module_id, imported_modules.keys());
let exposed_for_module = let exposed_for_module =
ExposedForModule::new(module.referenced_values.iter(), exposed_by_module); ExposedForModule::new(module.referenced_values.iter(), exposed_by_module);
@ -4635,6 +4693,10 @@ fn canonicalize_and_constrain<'a>(
let constraint = let constraint =
constrain_module(&mut constraints, &module_output.declarations, module_id); constrain_module(&mut constraints, &module_output.declarations, module_id);
if module_id == ModuleId::STR {
dbg!(&constraints);
}
let after = roc_types::types::get_type_clone_count(); let after = roc_types::types::get_type_clone_count();
log!( log!(

View File

@ -32,7 +32,7 @@ enum Status {
Done, Done,
} }
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
enum Job<'a> { enum Job<'a> {
Step(ModuleId, Phase), Step(ModuleId, Phase),
ResolveShorthand(&'a str), ResolveShorthand(&'a str),
@ -74,8 +74,10 @@ impl<'a> Dependencies<'a> {
// to canonicalize a module, all its dependencies must be canonicalized // to canonicalize a module, all its dependencies must be canonicalized
self.add_dependency(module_id, dep, Phase::CanonicalizeAndConstrain); self.add_dependency(module_id, dep, Phase::CanonicalizeAndConstrain);
// to typecheck a module, all its dependencies must be type checked already if goal_phase >= Phase::SolveTypes {
self.add_dependency(module_id, dep, Phase::SolveTypes); // to typecheck a module, all its dependencies must be type checked already
self.add_dependency(module_id, dep, Phase::SolveTypes);
}
if goal_phase >= FindSpecializations { if goal_phase >= FindSpecializations {
self.add_dependency(module_id, dep, Phase::FindSpecializations); self.add_dependency(module_id, dep, Phase::FindSpecializations);
@ -101,7 +103,7 @@ impl<'a> Dependencies<'a> {
output output
} }
fn add_to_status(&mut self, module_id: ModuleId, goal_phase: Phase) { pub fn add_to_status(&mut self, module_id: ModuleId, goal_phase: Phase) {
for phase in PHASES.iter() { for phase in PHASES.iter() {
if *phase > goal_phase { if *phase > goal_phase {
break; break;
@ -193,13 +195,72 @@ impl<'a> Dependencies<'a> {
} }
} }
pub fn remove_dependencies(
&mut self,
a: ModuleId,
b: ModuleId,
start_phase: Phase,
goal_phase: Phase,
) {
let mut i = 0;
while PHASES[i] < goal_phase {
let phase = PHASES[i];
if phase >= start_phase {
self.remove_dependency(a, b, phase);
}
i += 1;
}
}
/// A waits for B, and B will notify A when it completes the phase /// A waits for B, and B will notify A when it completes the phase
fn add_dependency(&mut self, a: ModuleId, b: ModuleId, phase: Phase) { /// we will remove both of these connections
fn remove_dependency(&mut self, a: ModuleId, b: ModuleId, phase: Phase) {
let key = Job::Step(a, phase);
let mut notifications = vec![];
match self.waiting_for.get_mut(&key) {
None => unreachable!(),
Some(x) => {
x.retain(|job| match job {
Job::Step(module, _) => {
if *module == b {
notifications.push(*job);
false
} else {
true
}
}
Job::ResolveShorthand(_) => true,
});
}
}
for notification in notifications {
match self.notifies.get_mut(&notification) {
None => unreachable!(),
Some(x) => {
x.retain(|notify_job| notify_job != &key);
}
}
}
}
/// A waits for B, and B will notify A when it completes the phase
pub fn add_dependency(&mut self, a: ModuleId, b: ModuleId, phase: Phase) {
self.add_dependency_help(a, b, phase, phase); self.add_dependency_help(a, b, phase, phase);
} }
/// phase_a of module a is waiting for phase_b of module_b /// phase_a of module a is waiting for phase_b of module_b
fn add_dependency_help(&mut self, a: ModuleId, b: ModuleId, phase_a: Phase, phase_b: Phase) { pub fn add_dependency_help(
&mut self,
a: ModuleId,
b: ModuleId,
phase_a: Phase,
phase_b: Phase,
) {
// no need to wait if the dependency is already done! // no need to wait if the dependency is already done!
if let Some(Status::Done) = self.status.get(&Job::Step(b, phase_b)) { if let Some(Status::Done) = self.status.get(&Job::Step(b, phase_b)) {
return; return;

View File

@ -293,7 +293,7 @@ lazy_static! {
} }
/// A globally unique ID that gets assigned to each module as it is loaded. /// A globally unique ID that gets assigned to each module as it is loaded.
#[derive(Copy, Clone, PartialEq, Eq, Hash)] #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ModuleId(u32); pub struct ModuleId(u32);
impl ModuleId { impl ModuleId {