Merge pull request #2977 from rtfeldman/debug_flags

Factor bespoke debug variables into debug_flags crate
This commit is contained in:
Ayaz 2022-04-30 18:16:06 -04:00 committed by GitHub
commit 3197cd97ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 167 additions and 70 deletions

8
Cargo.lock generated
View file

@ -3540,6 +3540,10 @@ dependencies = [
"roc_types",
]
[[package]]
name = "roc_debug_flags"
version = "0.1.0"
[[package]]
name = "roc_docs"
version = "0.1.0"
@ -3765,6 +3769,7 @@ dependencies = [
"roc_can",
"roc_collections",
"roc_constrain",
"roc_debug_flags",
"roc_error_macros",
"roc_module",
"roc_mono",
@ -3806,6 +3811,7 @@ dependencies = [
"roc_builtins",
"roc_can",
"roc_collections",
"roc_debug_flags",
"roc_error_macros",
"roc_exhaustive",
"roc_module",
@ -4004,6 +4010,7 @@ version = "0.1.0"
dependencies = [
"bumpalo",
"roc_collections",
"roc_debug_flags",
"roc_error_macros",
"roc_module",
"roc_region",
@ -4017,6 +4024,7 @@ version = "0.1.0"
dependencies = [
"bitflags",
"roc_collections",
"roc_debug_flags",
"roc_error_macros",
"roc_module",
"roc_types",

View file

@ -26,6 +26,7 @@ members = [
"compiler/arena_pool",
"compiler/test_gen",
"compiler/roc_target",
"compiler/debug_flags",
"vendor/ena",
"vendor/inkwell",
"vendor/pathfinding",

View file

@ -165,23 +165,19 @@ The compiler is invoked from the CLI via `build_file` in cli/src/build.rs
For a more detailed understanding of the compilation phases, see the `Phase`, `BuildTask`, and `Msg` enums in `load/src/file.rs`.
## Debugging intermediate representations
## Debugging the compiler
### Debugging the typechecker
Please see the [debug flags](./debug_flags/src/lib.rs) for information on how to
ask the compiler to emit debug information during various stages of compilation.
Setting the following environment variables:
There are some goals for more sophisticated debugging tools:
- `ROC_PRINT_UNIFICATIONS` prints all type unifications that are done,
before and after the unification.
- `ROC_PRINT_MISMATCHES` prints all type mismatches hit during unification.
- `ROC_PRETTY_PRINT_ALIAS_CONTENTS` expands the contents of aliases during
pretty-printing of types.
- A nicer unification debugger, see https://github.com/rtfeldman/roc/issues/2486.
Any interest in helping out here is greatly appreciated.
Note that this is only relevant during debug builds. Eventually we should have
some better debugging tools here, see https://github.com/rtfeldman/roc/issues/2486
for one.
### General Tips
### The mono IR
#### Miscompilations
If you observe a miscomplication, you may first want to check the generated mono
IR for your code - maybe there was a problem during specialization or layout
@ -189,13 +185,16 @@ generation. One way to do this is to add a test to `test_mono/src/tests.rs`
and run the tests with `cargo test -p test_mono`; this will write the mono
IR to a file.
You may also want to set some or all of the following environment variables:
#### Typechecking errors
- `PRINT_IR_AFTER_SPECIALIZATION=1` prints the mono IR after function
specialization to stdout
- `PRINT_IR_AFTER_RESET_REUSE=1` prints the mono IR after insertion of
reset/reuse isntructions to stdout
- `PRINT_IR_AFTER_REFCOUNT=1` prints the mono IR after insertion of reference
counting instructions to stdout
- `PRETTY_PRINT_IR_SYMBOLS=1` instructs the pretty printer to dump all the
information it knows about the mono IR whenever it is printed
First, try to minimize your reproduction into a test that fits in
[`solve_expr`](./solve/tests/solve_expr.rs).
Once you've done this, check out the `ROC_PRINT_UNIFICATIONS` debug flag. It
will show you where type unification went right and wrong. This is usually
enough to figure out a fix for the bug.
If that doesn't work and you know your error has something to do with ranks,
you may want to instrument `deep_copy_var_help` in [solve](./solve/src/solve.rs).
If that doesn't work, chatting on Zulip is always a good strategy.

View file

@ -0,0 +1,6 @@
[package]
name = "roc_debug_flags"
version = "0.1.0"
edition = "2021"
[dependencies]

View file

@ -0,0 +1,79 @@
//! Flags for debugging the Roc compiler.
//!
//! Lists environment variable flags that can be enabled for verbose debugging features in debug
//! builds of the compiler.
//!
//! For example, I might define the following alias to run cargo with all unifications and
//! expanded type aliases printed:
//!
//! ```bash
//! alias cargo="\
//! ROC_PRINT_UNIFICATIONS=1 \
//! ROC_PRETTY_PRINT_ALIAS_CONTENTS=1 \
//! cargo"
//! ```
//!
//! More generally, I have the following:
//!
//! ```bash
//! alias cargo="\
//! ROC_PRETTY_PRINT_ALIAS_CONTENTS=0 \
//! ROC_PRINT_UNIFICATIONS=0 \
//! ROC_PRINT_MISMATCHES=0 \
//! ROC_PRINT_IR_AFTER_SPECIALIZATION=0 \
//! ROC_PRINT_IR_AFTER_RESET_REUSE=0 \
//! ROC_PRINT_IR_AFTER_REFCOUNT=0 \
//! ROC_PRETTY_PRINT_IR_SYMBOLS=0 \
//! cargo"
//! ```
//!
//! Now you can turn debug flags on and off as you like.
#[macro_export]
macro_rules! dbg_do {
($flag:path, $expr:expr) => {
#[cfg(debug_assertions)]
{
if std::env::var($flag).unwrap_or("0".to_string()) != "0" {
$expr
}
}
};
}
macro_rules! flags {
($($(#[doc = $doc:expr])+ $flag:ident)*) => {$(
$(#[doc = $doc])+
pub static $flag: &str = stringify!($flag);
)*};
}
flags! {
// ===Types===
/// Expands the contents of aliases during pretty-printing of types.
ROC_PRETTY_PRINT_ALIAS_CONTENTS
// ===Solve===
/// Prints type unifications, before and after they happen.
ROC_PRINT_UNIFICATIONS
/// Prints all type mismatches hit during type unification.
ROC_PRINT_MISMATCHES
// ===Mono===
/// Writes the mono IR to stderr after function specialization
ROC_PRINT_IR_AFTER_SPECIALIZATION
/// Writes the mono IR to stderr after insertion of reset/reuse instructions
ROC_PRINT_IR_AFTER_RESET_REUSE
/// Writes the mono IR to stderr after insertion of refcount instructions
ROC_PRINT_IR_AFTER_REFCOUNT
/// Instructs the mono IR pretty printer to dump pretty symbols and verbose
/// layout information
ROC_PRETTY_PRINT_IR_SYMBOLS
}

View file

@ -21,6 +21,7 @@ roc_solve = { path = "../solve" }
roc_mono = { path = "../mono" }
roc_target = { path = "../roc_target" }
roc_reporting = { path = "../../reporting" }
roc_debug_flags = { path = "../debug_flags" }
morphic_lib = { path = "../../vendor/morphic_lib" }
ven_pretty = { path = "../../vendor/pretty" }
bumpalo = { version = "3.8.0", features = ["collections"] }
@ -33,4 +34,4 @@ tempfile = "3.2.0"
pretty_assertions = "1.0.0"
maplit = "1.0.2"
indoc = "1.0.3"
roc_test_utils = { path = "../../test_utils" }
roc_test_utils = { path = "../../test_utils" }

View file

@ -15,6 +15,10 @@ use roc_constrain::module::{
constrain_builtin_imports, constrain_module, ExposedByModule, ExposedForModule,
ExposedModuleTypes,
};
use roc_debug_flags::{
dbg_do, ROC_PRINT_IR_AFTER_REFCOUNT, ROC_PRINT_IR_AFTER_RESET_REUSE,
ROC_PRINT_IR_AFTER_SPECIALIZATION,
};
use roc_error_macros::internal_error;
use roc_module::ident::{Ident, ModuleName, QualifiedModuleName};
use roc_module::symbol::{
@ -1627,21 +1631,20 @@ fn start_tasks<'a>(
Ok(())
}
#[cfg(debug_assertions)]
fn debug_print_ir(state: &State, flag: &str) {
if env::var(flag) != Ok("1".into()) {
return;
}
macro_rules! debug_print_ir {
($state:expr, $flag:path) => {
dbg_do!($flag, {
let procs_string = $state
.procedures
.values()
.map(|proc| proc.to_pretty(200))
.collect::<Vec<_>>();
let procs_string = state
.procedures
.values()
.map(|proc| proc.to_pretty(200))
.collect::<Vec<_>>();
let result = procs_string.join("\n");
let result = procs_string.join("\n");
println!("{}", result);
eprintln!("{}", result);
})
};
}
/// Report modules that are imported, but from which nothing is used
@ -2181,8 +2184,7 @@ fn update<'a>(
&& state.dependencies.solved_all()
&& state.goal_phase == Phase::MakeSpecializations
{
#[cfg(debug_assertions)]
debug_print_ir(&state, "PRINT_IR_AFTER_SPECIALIZATION");
debug_print_ir!(state, ROC_PRINT_IR_AFTER_SPECIALIZATION);
Proc::insert_reset_reuse_operations(
arena,
@ -2192,13 +2194,11 @@ fn update<'a>(
&mut state.procedures,
);
#[cfg(debug_assertions)]
debug_print_ir(&state, "PRINT_IR_AFTER_RESET_REUSE");
debug_print_ir!(state, ROC_PRINT_IR_AFTER_RESET_REUSE);
Proc::insert_refcount_operations(arena, &mut state.procedures);
#[cfg(debug_assertions)]
debug_print_ir(&state, "PRINT_IR_AFTER_REFCOUNT");
debug_print_ir!(state, ROC_PRINT_IR_AFTER_REFCOUNT);
// This is not safe with the new non-recursive RC updates that we do for tag unions
//

View file

@ -19,6 +19,7 @@ roc_problem = { path = "../problem" }
roc_builtins = { path = "../builtins" }
roc_target = { path = "../roc_target" }
roc_error_macros = {path="../../error_macros"}
roc_debug_flags = {path="../debug_flags"}
ven_pretty = { path = "../../vendor/pretty" }
morphic_lib = { path = "../../vendor/morphic_lib" }
bumpalo = { version = "3.8.0", features = ["collections"] }

View file

@ -10,6 +10,7 @@ use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_can::abilities::AbilitiesStore;
use roc_can::expr::{AnnotatedMark, ClosureData, IntValue};
use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap};
use roc_debug_flags::{dbg_do, ROC_PRETTY_PRINT_IR_SYMBOLS};
use roc_exhaustive::{Ctor, CtorName, Guard, RenderAs, TagId};
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
use roc_module::low_level::LowLevel;
@ -25,11 +26,11 @@ use roc_types::subs::{
use std::collections::HashMap;
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder};
#[inline(always)]
pub fn pretty_print_ir_symbols() -> bool {
#[cfg(debug_assertions)]
if std::env::var("PRETTY_PRINT_IR_SYMBOLS") == Ok("1".into()) {
dbg_do!(ROC_PRETTY_PRINT_IR_SYMBOLS, {
return true;
}
});
false
}

View file

@ -10,6 +10,7 @@ roc_collections = { path = "../collections" }
roc_region = { path = "../region" }
roc_module = { path = "../module" }
roc_error_macros = {path="../../error_macros"}
roc_debug_flags = {path="../debug_flags"}
ven_ena = { path = "../../vendor/ena" }
bumpalo = { version = "3.8.0", features = ["collections"] }
static_assertions = "1.1.0"

View file

@ -449,15 +449,12 @@ fn write_content<'a>(
);
}
// useful for debugging
if cfg!(debug_assertions)
&& std::env::var("ROC_PRETTY_PRINT_ALIAS_CONTENTS").is_ok()
{
roc_debug_flags::dbg_do!(roc_debug_flags::ROC_PRETTY_PRINT_ALIAS_CONTENTS, {
buf.push_str("[[ but really ");
let content = subs.get_content_without_compacting(*_actual);
write_content(env, ctx, content, subs, buf, parens);
buf.push_str("]]");
}
});
}),
}
}

View file

@ -19,3 +19,6 @@ path = "../module"
[dependencies.roc_types]
path = "../types"
[dependencies.roc_debug_flags]
path = "../debug_flags"

View file

@ -1,4 +1,5 @@
use bitflags::bitflags;
use roc_debug_flags::{dbg_do, ROC_PRINT_MISMATCHES, ROC_PRINT_UNIFICATIONS};
use roc_error_macros::internal_error;
use roc_module::ident::{Lowercase, TagName};
use roc_module::symbol::Symbol;
@ -11,14 +12,14 @@ use roc_types::types::{AliasKind, DoesNotImplementAbility, ErrorType, Mismatch,
macro_rules! mismatch {
() => {{
if cfg!(debug_assertions) && std::env::var("ROC_PRINT_MISMATCHES").is_ok() {
println!(
dbg_do!(ROC_PRINT_MISMATCHES, {
eprintln!(
"Mismatch in {} Line {} Column {}",
file!(),
line!(),
column!()
);
}
})
Outcome {
mismatches: vec![Mismatch::TypeMismatch],
@ -26,17 +27,16 @@ macro_rules! mismatch {
}
}};
($msg:expr) => {{
if cfg!(debug_assertions) && std::env::var("ROC_PRINT_MISMATCHES").is_ok() {
println!(
dbg_do!(ROC_PRINT_MISMATCHES, {
eprintln!(
"Mismatch in {} Line {} Column {}",
file!(),
line!(),
column!()
);
println!($msg);
println!("");
}
eprintln!($msg);
eprintln!("");
});
Outcome {
mismatches: vec![Mismatch::TypeMismatch],
@ -47,16 +47,16 @@ macro_rules! mismatch {
mismatch!($msg)
}};
($msg:expr, $($arg:tt)*) => {{
if cfg!(debug_assertions) && std::env::var("ROC_PRINT_MISMATCHES").is_ok() {
println!(
dbg_do!(ROC_PRINT_MISMATCHES, {
eprintln!(
"Mismatch in {} Line {} Column {}",
file!(),
line!(),
column!()
);
println!($msg, $($arg)*);
println!("");
}
eprintln!($msg, $($arg)*);
eprintln!("");
});
Outcome {
mismatches: vec![Mismatch::TypeMismatch],
@ -64,16 +64,16 @@ macro_rules! mismatch {
}
}};
(%not_able, $var:expr, $ability:expr, $msg:expr, $($arg:tt)*) => {{
if cfg!(debug_assertions) && std::env::var("ROC_PRINT_MISMATCHES").is_ok() {
println!(
dbg_do!(ROC_PRINT_MISMATCHES, {
eprintln!(
"Mismatch in {} Line {} Column {}",
file!(),
line!(),
column!()
);
println!($msg, $($arg)*);
println!("");
}
eprintln!($msg, $($arg)*);
eprintln!("");
});
Outcome {
mismatches: vec![Mismatch::TypeMismatch, Mismatch::DoesNotImplementAbiity($var, $ability)],
@ -298,7 +298,7 @@ fn debug_print_unified_types(subs: &mut Subs, ctx: &Context, opt_outcome: Option
static mut UNIFICATION_DEPTH: usize = 0;
if std::env::var("ROC_PRINT_UNIFICATIONS").is_ok() {
dbg_do!(ROC_PRINT_UNIFICATIONS, {
let prefix = match opt_outcome {
None => "",
Some(outcome) if outcome.mismatches.is_empty() => "",
@ -340,7 +340,7 @@ fn debug_print_unified_types(subs: &mut Subs, ctx: &Context, opt_outcome: Option
);
unsafe { UNIFICATION_DEPTH = new_depth };
}
})
}
fn unify_context(subs: &mut Subs, pool: &mut Pool, ctx: Context) -> Outcome {