roc test stops if there are type errors

This commit is contained in:
Ayaz Hafiz 2022-12-02 10:52:31 -06:00
parent b5a195eaf0
commit 0a4ec1958b
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
2 changed files with 70 additions and 45 deletions

View file

@ -360,7 +360,7 @@ pub fn test(_matches: &ArgMatches, _triple: Triple) -> io::Result<i32> {
#[cfg(not(windows))]
pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
use roc_gen_llvm::llvm::build::LlvmBackendMode;
use roc_load::{ExecutionMode, LoadConfig};
use roc_load::{ExecutionMode, LoadConfig, LoadMonomorphizedError};
use roc_packaging::cache;
use roc_target::TargetInfo;
@ -425,16 +425,24 @@ pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
threading,
exec_mode: ExecutionMode::Test,
};
let loaded = roc_load::load_and_monomorphize(
let load_result = roc_load::load_and_monomorphize(
arena,
path.to_path_buf(),
subs_by_module,
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
load_config,
)
.unwrap();
);
let mut loaded = match load_result {
Ok(loaded) => loaded,
Err(LoadMonomorphizedError::LoadingProblem(problem)) => {
return handle_loading_problem(problem);
}
Err(LoadMonomorphizedError::ErrorModule(module)) => {
return handle_error_module(module, start_time.elapsed(), filename, false);
}
};
let mut loaded = loaded;
let mut expectations = std::mem::take(&mut loaded.expectations);
let loaded = loaded;
@ -733,35 +741,51 @@ pub fn build(
}
}
}
Err(BuildFileError::ErrorModule {
mut module,
total_time,
}) => {
debug_assert!(module.total_problems() > 0);
let problems = roc_build::program::report_problems_typechecked(&mut module);
print_problems(problems, total_time);
print!(".\n\nYou can run the program anyway with \x1B[32mroc run");
// If you're running "main.roc" then you can just do `roc run`
// to re-run the program.
if filename != DEFAULT_ROC_FILENAME {
print!(" {}", &filename.to_string_lossy());
}
println!("\x1B[39m");
Ok(problems.exit_code())
Err(BuildFileError::ErrorModule { module, total_time }) => {
handle_error_module(module, total_time, filename, true)
}
Err(BuildFileError::LoadingProblem(LoadingProblem::FormattedReport(report))) => {
print!("{}", report);
Err(BuildFileError::LoadingProblem(problem)) => handle_loading_problem(problem),
}
}
fn handle_error_module(
mut module: roc_load::LoadedModule,
total_time: std::time::Duration,
filename: &OsStr,
print_run_anyway_hint: bool,
) -> io::Result<i32> {
debug_assert!(module.total_problems() > 0);
let problems = roc_build::program::report_problems_typechecked(&mut module);
print_problems(problems, total_time);
if print_run_anyway_hint {
// If you're running "main.roc" then you can just do `roc run`
// to re-run the program.
print!(".\n\nYou can run the program anyway with \x1B[32mroc run");
if filename != DEFAULT_ROC_FILENAME {
print!(" {}", &filename.to_string_lossy());
}
println!("\x1B[39m");
}
Ok(problems.exit_code())
}
fn handle_loading_problem(problem: LoadingProblem) -> io::Result<i32> {
match problem {
LoadingProblem::FormattedReport(report) => {
print!("{}", report);
Ok(1)
}
Err(other) => {
panic!("build_file failed with error:\n{:?}", other);
_ => {
// TODO: tighten up the types here, we should always end up with a
// formatted report from load.
print!("Failed with error: {:?}", problem);
Ok(1)
}
}
}

View file

@ -97,20 +97,28 @@ pub struct LoadConfig {
#[derive(Debug, Clone, Copy)]
pub enum ExecutionMode {
Test,
Check,
Executable,
/// Like [`ExecutionMode::Executable`], but stops in the presence of type errors.
ExecutableIfCheck,
/// Test is like [`ExecutionMode::ExecutableIfCheck`], but rather than producing a proper
/// executable, run tests.
Test,
}
impl ExecutionMode {
fn goal_phase(&self) -> Phase {
match self {
ExecutionMode::Test | ExecutionMode::Executable => Phase::MakeSpecializations,
ExecutionMode::Check | ExecutionMode::ExecutableIfCheck => Phase::SolveTypes,
ExecutionMode::Executable => Phase::MakeSpecializations,
ExecutionMode::Check | ExecutionMode::ExecutableIfCheck | ExecutionMode::Test => {
Phase::SolveTypes
}
}
}
fn build_if_checks(&self) -> bool {
matches!(self, Self::ExecutableIfCheck | Self::Test)
}
}
/// Struct storing various intermediate stages by their ModuleId
@ -2615,7 +2623,7 @@ fn update<'a>(
let finish_type_checking = is_host_exposed &&
(state.goal_phase() == Phase::SolveTypes)
// If we're running in check-and-then-build mode, only exit now there are errors.
&& (!matches!(state.exec_mode, ExecutionMode::ExecutableIfCheck) || state.module_cache.total_problems() > 0);
&& (!state.exec_mode.build_if_checks() || state.module_cache.total_problems() > 0);
if finish_type_checking {
debug_assert!(work.is_empty());
@ -2623,7 +2631,7 @@ fn update<'a>(
state.timings.insert(module_id, module_timing);
if matches!(state.exec_mode, ExecutionMode::ExecutableIfCheck) {
if state.exec_mode.build_if_checks() {
// We there may outstanding modules in the typecheked cache whose ident IDs
// aren't registered; transfer all of their idents over to the state, since
// we're now done and ready to report errors.
@ -2677,9 +2685,7 @@ fn update<'a>(
},
);
if state.goal_phase() > Phase::SolveTypes
|| matches!(state.exec_mode, ExecutionMode::ExecutableIfCheck)
{
if state.goal_phase() > Phase::SolveTypes || state.exec_mode.build_if_checks() {
let layout_cache = state.layout_caches.pop().unwrap_or_else(|| {
LayoutCache::new(state.layout_interner.fork(), state.target_info)
});
@ -2703,17 +2709,12 @@ fn update<'a>(
state.timings.insert(module_id, module_timing);
}
let work = if is_host_exposed
&& matches!(state.exec_mode, ExecutionMode::ExecutableIfCheck)
{
let work = if is_host_exposed && state.exec_mode.build_if_checks() {
debug_assert!(
work.is_empty(),
"work left over after host exposed is checked"
);
// Update the goal phase to target full codegen.
state.exec_mode = ExecutionMode::Executable;
// Load the find + make specializations portion of the dependency graph.
state
.dependencies
@ -2785,7 +2786,7 @@ fn update<'a>(
layout_cache,
..
} => {
debug_assert!(state.goal_phase() == Phase::MakeSpecializations);
debug_assert!(state.goal_phase() == Phase::MakeSpecializations || state.exec_mode.build_if_checks());
log!("made specializations for {:?}", module_id);