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');
dbg!(module_id);
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 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
/// 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();
for module_id in it {
match self.exposed.get(module_id) {
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) => {
output.exposed.insert(*module_id, exposed_types.clone());

View File

@ -1099,7 +1099,7 @@ fn load<'a>(
) -> Result<LoadResult<'a>, LoadingProblem<'a>> {
// When compiling to wasm, we cannot spawn extra threads
// so we have a single-threaded implementation
if true || cfg!(target_family = "wasm") {
if cfg!(target_family = "wasm") {
load_single_threaded(
arena,
load_start,
@ -1614,14 +1614,18 @@ fn report_unused_imported_modules<'a>(
state: &mut State<'a>,
module_id: ModuleId,
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();
for symbol in constrained_module.module.referenced_values.iter() {
kept.push(symbol.module_id());
unused_imported_modules.remove(&symbol.module_id());
}
for symbol in constrained_module.module.referenced_types.iter() {
kept.push(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() {
if !unused.is_builtin() {
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>(
@ -1870,7 +1889,7 @@ fn update<'a>(
work.extend(state.dependencies.add_module(
header.module_id,
&header.package_qualified_imported_modules,
state.goal_phase,
Phase::CanonicalizeAndConstrain,
));
state.module_cache.headers.insert(header.module_id, header);
@ -1919,7 +1938,7 @@ fn update<'a>(
}
CanonicalizedAndConstrained {
constrained_module,
mut constrained_module,
canonicalization_problems,
module_docs,
} => {
@ -1934,7 +1953,51 @@ fn update<'a>(
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
.module_cache
@ -1996,7 +2059,7 @@ fn update<'a>(
if is_host_exposed && state.goal_phase == Phase::SolveTypes {
debug_assert!(work.is_empty());
debug_assert!(state.dependencies.solved_all());
// debug_assert!(state.dependencies.solved_all());
state.timings.insert(module_id, module_timing);
@ -2787,10 +2850,7 @@ fn load_module<'a>(
roc_parse::header::ModuleName::new("Bool"),
Collection::with_items(&[Loc::at_zero(Spaced::Item(ExposedName::new("Bool")))]),
)),
Loc::at_zero(ImportsEntry::Module(
roc_parse::header::ModuleName::new("List"),
Collection::with_items(&[Loc::at_zero(Spaced::Item(ExposedName::new("List")))]),
)),
// Note: List is only used for the type, which we ensure is always in scope
Loc::at_zero(ImportsEntry::Module(
roc_parse::header::ModuleName::new("Num"),
Collection::with_items(&[
@ -2856,9 +2916,6 @@ fn load_module<'a>(
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 ]*
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>,
declarations: Vec<Declaration>,
) -> 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 =
ExposedForModule::new(module.referenced_values.iter(), exposed_by_module);
@ -4635,6 +4693,10 @@ fn canonicalize_and_constrain<'a>(
let constraint =
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();
log!(

View File

@ -32,7 +32,7 @@ enum Status {
Done,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
enum Job<'a> {
Step(ModuleId, Phase),
ResolveShorthand(&'a str),
@ -74,8 +74,10 @@ impl<'a> Dependencies<'a> {
// to canonicalize a module, all its dependencies must be canonicalized
self.add_dependency(module_id, dep, Phase::CanonicalizeAndConstrain);
// to typecheck a module, all its dependencies must be type checked already
self.add_dependency(module_id, dep, Phase::SolveTypes);
if goal_phase >= 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 {
self.add_dependency(module_id, dep, Phase::FindSpecializations);
@ -101,7 +103,7 @@ impl<'a> Dependencies<'a> {
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() {
if *phase > goal_phase {
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
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);
}
/// 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!
if let Some(Status::Done) = self.status.get(&Job::Step(b, phase_b)) {
return;

View File

@ -293,7 +293,7 @@ lazy_static! {
}
/// 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);
impl ModuleId {