Factor bespoke debug variables into debug_flags crate

This commit is contained in:
Ayaz Hafiz 2022-04-29 17:45:55 -04:00
parent 5e47e4767e
commit 9964f86a3d
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
13 changed files with 167 additions and 70 deletions

8
Cargo.lock generated
View file

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

View file

@ -26,6 +26,7 @@ members = [
"compiler/arena_pool", "compiler/arena_pool",
"compiler/test_gen", "compiler/test_gen",
"compiler/roc_target", "compiler/roc_target",
"compiler/debug_flags",
"vendor/ena", "vendor/ena",
"vendor/inkwell", "vendor/inkwell",
"vendor/pathfinding", "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`. 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, - A nicer unification debugger, see https://github.com/rtfeldman/roc/issues/2486.
before and after the unification. Any interest in helping out here is greatly appreciated.
- `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.
Note that this is only relevant during debug builds. Eventually we should have ### General Tips
some better debugging tools here, see https://github.com/rtfeldman/roc/issues/2486
for one.
### The mono IR #### Miscompilations
If you observe a miscomplication, you may first want to check the generated mono 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 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 and run the tests with `cargo test -p test_mono`; this will write the mono
IR to a file. 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 First, try to minimize your reproduction into a test that fits in
specialization to stdout [`solve_expr`](./solve/tests/solve_expr.rs).
- `PRINT_IR_AFTER_RESET_REUSE=1` prints the mono IR after insertion of
reset/reuse isntructions to stdout Once you've done this, check out the `ROC_PRINT_UNIFICATIONS` debug flag. It
- `PRINT_IR_AFTER_REFCOUNT=1` prints the mono IR after insertion of reference will show you where type unification went right and wrong. This is usually
counting instructions to stdout enough to figure out a fix for the bug.
- `PRETTY_PRINT_IR_SYMBOLS=1` instructs the pretty printer to dump all the
information it knows about the mono IR whenever it is printed 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_mono = { path = "../mono" }
roc_target = { path = "../roc_target" } roc_target = { path = "../roc_target" }
roc_reporting = { path = "../../reporting" } roc_reporting = { path = "../../reporting" }
roc_debug_flags = { path = "../debug_flags" }
morphic_lib = { path = "../../vendor/morphic_lib" } morphic_lib = { path = "../../vendor/morphic_lib" }
ven_pretty = { path = "../../vendor/pretty" } ven_pretty = { path = "../../vendor/pretty" }
bumpalo = { version = "3.8.0", features = ["collections"] } bumpalo = { version = "3.8.0", features = ["collections"] }
@ -33,4 +34,4 @@ tempfile = "3.2.0"
pretty_assertions = "1.0.0" pretty_assertions = "1.0.0"
maplit = "1.0.2" maplit = "1.0.2"
indoc = "1.0.3" 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, constrain_builtin_imports, constrain_module, ExposedByModule, ExposedForModule,
ExposedModuleTypes, 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_error_macros::internal_error;
use roc_module::ident::{Ident, ModuleName, QualifiedModuleName, TagName}; use roc_module::ident::{Ident, ModuleName, QualifiedModuleName, TagName};
use roc_module::symbol::{ use roc_module::symbol::{
@ -1627,21 +1631,20 @@ fn start_tasks<'a>(
Ok(()) Ok(())
} }
#[cfg(debug_assertions)] macro_rules! debug_print_ir {
fn debug_print_ir(state: &State, flag: &str) { ($state:expr, $flag:path) => {
if env::var(flag) != Ok("1".into()) { dbg_do!($flag, {
return; let procs_string = $state
} .procedures
.values()
.map(|proc| proc.to_pretty(200))
.collect::<Vec<_>>();
let procs_string = state let result = procs_string.join("\n");
.procedures
.values()
.map(|proc| proc.to_pretty(200))
.collect::<Vec<_>>();
let result = procs_string.join("\n"); eprintln!("{}", result);
})
println!("{}", result); };
} }
/// Report modules that are imported, but from which nothing is used /// Report modules that are imported, but from which nothing is used
@ -2181,8 +2184,7 @@ fn update<'a>(
&& state.dependencies.solved_all() && state.dependencies.solved_all()
&& state.goal_phase == Phase::MakeSpecializations && state.goal_phase == Phase::MakeSpecializations
{ {
#[cfg(debug_assertions)] debug_print_ir!(state, ROC_PRINT_IR_AFTER_SPECIALIZATION);
debug_print_ir(&state, "PRINT_IR_AFTER_SPECIALIZATION");
Proc::insert_reset_reuse_operations( Proc::insert_reset_reuse_operations(
arena, arena,
@ -2192,13 +2194,11 @@ fn update<'a>(
&mut state.procedures, &mut state.procedures,
); );
#[cfg(debug_assertions)] debug_print_ir!(state, ROC_PRINT_IR_AFTER_RESET_REUSE);
debug_print_ir(&state, "PRINT_IR_AFTER_RESET_REUSE");
Proc::insert_refcount_operations(arena, &mut state.procedures); Proc::insert_refcount_operations(arena, &mut state.procedures);
#[cfg(debug_assertions)] debug_print_ir!(state, ROC_PRINT_IR_AFTER_REFCOUNT);
debug_print_ir(&state, "PRINT_IR_AFTER_REFCOUNT");
// This is not safe with the new non-recursive RC updates that we do for tag unions // 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_builtins = { path = "../builtins" }
roc_target = { path = "../roc_target" } roc_target = { path = "../roc_target" }
roc_error_macros = {path="../../error_macros"} roc_error_macros = {path="../../error_macros"}
roc_debug_flags = {path="../debug_flags"}
ven_pretty = { path = "../../vendor/pretty" } ven_pretty = { path = "../../vendor/pretty" }
morphic_lib = { path = "../../vendor/morphic_lib" } morphic_lib = { path = "../../vendor/morphic_lib" }
bumpalo = { version = "3.8.0", features = ["collections"] } 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::abilities::AbilitiesStore;
use roc_can::expr::{AnnotatedMark, ClosureData, IntValue}; use roc_can::expr::{AnnotatedMark, ClosureData, IntValue};
use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap}; 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_exhaustive::{Ctor, CtorName, Guard, RenderAs, TagId};
use roc_module::ident::{ForeignSymbol, Lowercase, TagName}; use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
use roc_module::low_level::LowLevel; use roc_module::low_level::LowLevel;
@ -25,11 +26,11 @@ use roc_types::subs::{
use std::collections::HashMap; use std::collections::HashMap;
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder}; use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder};
#[inline(always)]
pub fn pretty_print_ir_symbols() -> bool { pub fn pretty_print_ir_symbols() -> bool {
#[cfg(debug_assertions)] dbg_do!(ROC_PRETTY_PRINT_IR_SYMBOLS, {
if std::env::var("PRETTY_PRINT_IR_SYMBOLS") == Ok("1".into()) {
return true; return true;
} });
false false
} }

View file

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

View file

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

View file

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

View file

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