so I have a bunch of work on auto-derived Inspect, but I think I should really focus on documentation and FAQs and such this final week before advent of code - is anyone else able to pick that up?
I really think it'd help people out a lot to have a reliable dbg
for Advent of Code, and this is the only nontrivial blocker to that!
What's the current state?
it's partially implemented on a branch, but getting an error in tests
if you're interested in it, I'd be happy to chat about it and get you up to speed! :smiley:
I have a lot of random stuff I want to get to, but this seems more important
What is the best way to start understanding a bug with an error like:
thread 'gen_abilities::inspect_bool' panicked at 'assertion failed: `(left == right)`
left: `0`,
right: `1`: (LambdaSet([], ^<2128>), Func([<260>EmptyRecord,], <2130=263>LambdaSet([], ^<2128>), <264>Opaque(`Inspect.DbgFormatter`, [], <3300>{ 'data' : Apply(`Str.Str`, []), }<3301>)))', crates/compiler/mono/src/ir.rs:6129:9
or
thread 'gen_abilities::inspect_num' panicked at 'No lambda set at region 2 found', crates/compiler/types/src/subs.rs:6087:5
Usually this means the specialization of an ability member for a specific type is missing or under defined. Do you what a lambda region is?
I do not know what a lambda region is. Have not interacted with them before.
Also, at least for a type like Bool.Bool
where the inspector just generates a direct call to the immediate Inspect.bool
, I don't see a location where were are doing anything complex within the derive code for inspect itself. Totally see how dict, list, record, etc are complex and could hit bigger issues, but unsure why something like just Bool.Bool
would hit some sort of issue.
what’s the definition of inspect for Bool?
On mobile so I’ll take a deeper look later and explain lambda regions but I can explain broad details
in ability type definition: bool : Bool -> Inspector f where f implements InspectFormatter
With Inspector
as Inspector f := f -> f where f implements InspectFormatter
Specific bool
impl it should be using:
dbgBool : Bool -> Inspector DbgFormatter
dbgBool = \b ->
if b then
f0 <- custom
dbgWrite f0 "true"
else
f0 <- custom
dbgWrite f0 "false"
Oh, and I guess custom
just expose the opaque wrapper:
custom : (f -> f) -> Inspector f where f implements InspectFormatter
custom = @Inspector
what’s the definition of dbgWrite?
Just string concat:
dbgWrite : DbgFormatter, Str -> DbgFormatter
dbgWrite = \@DbgFormatter { data }, added ->
@DbgFormatter { data: Str.concat data added }
Hm that’s weird
Okay i’ll take a deeper look when off mobile. do you have a branch?
in the meantime, here is the description of a lambda set region: https://github.com/roc-lang/roc/blob/main/crates/compiler/solve/docs/ambient_lambda_set_specialization.md#some-definitions
It’s usually missing when a specialization function corresponding to the same arrow as defined in an ability member is missing. But the specialization here seems to have three arrows, like the ability member
Been adding to inspect-derive
.
The test is run with cargo test -p test_gen -- inspect_bool
Was trying to fix bool or num tests before turning to the derive implementations that actually generate code rather than just call an existing function.
you might also want to run with ROC_TRACE_COMPACTION=1. It will show some useful specialization traces
Looking at the compaction, the main thing I derive is that the failing match looks to be for Inspect.init
. That is the function that generates the initial formatter.
Given the uitest
app "test" provides [main] to "./platform"
main =
Inspect.toInspector Bool.true |> Inspect.apply (Inspect.init {}) |> Inspect.toDbgStr
# ^^^^^^^^^^^^^^^^^^^ Inspect#Inspect.toInspector(32): Bool -[[]]-> Inspector DbgFormatter
we have the trace
===lambda set compaction===
concrete type: Opaque(`Inspect.DbgFormatter`, [], <2679>{ 'data' : Apply(`Str.Str`, []), }<115>)
step 1:
uls_a = { LambdaSet([] + (<2710>Opaque(`Inspect.DbgFormatter`, [], <2679>{ 'data' : Apply(`Str.Str`, []), }<115>):`Inspect.init`:1), ^<2709>),LambdaSet([] + (<2699>Op
aque(`Inspect.DbgFormatter`, [], <2679>{ 'data' : Apply(`Str.Str`, []), }<115>):`Inspect.bool`:2), ^<2694>),LambdaSet([] + (<2706>Opaque(`Inspect.DbgFormatter`, [], <2679
>{ 'data' : Apply(`Str.Str`, []), }<115>):`Inspect.bool`:1), ^<2688>) }
step 2:
uls_a' = { LambdaSet([] + (<2710>Opaque(`Inspect.DbgFormatter`, [], <2679>{ 'data' : Apply(`Str.Str`, []), }<115>):`Inspect.init`:1), ^<2709>),LambdaSet([] + (<2699>O
paque(`Inspect.DbgFormatter`, [], <2679>{ 'data' : Apply(`Str.Str`, []), }<115>):`Inspect.bool`:2), ^<2694>),LambdaSet([] + (<2706>Opaque(`Inspect.DbgFormatter`, [], <267
9>{ 'data' : Apply(`Str.Str`, []), }<115>):`Inspect.bool`:1), ^<2688>) }
step 3:
SKIP
= Func([<74>EmptyRecord,], <2711=77>LambdaSet([], ^<2709>), <78>Opaque(`Inspect.DbgFormatter`, [], <2679>{ 'data' : Apply(`Str.Str`, []), }<115>))
SKIP
= Func([<2682>Opaque(`Inspect.DbgFormatter`, [], <2679>{ 'data' : Apply(`Str.Str`, []), }<115>),], <2693=2685>LambdaSet([], ^<2694>), <2682>Opaque(`Inspect.DbgForm
atter`, [], <2679>{ 'data' : Apply(`Str.Str`, []), }<115>))
SKIP
= Func([<2704>Opaque(`Bool.Bool`, [], <2697>['False' , 'True' , ]<Any(92)>),], <2690=2703>LambdaSet([], ^<2688>), <2702>Opaque(`Inspect.Inspector`, [2692, 2693], <
2694>Func([<2682>Opaque(`Inspect.DbgFormatter`, [], <2679>{ 'data' : Apply(`Str.Str`, []), }<115>),], <2693=2685>LambdaSet([], ^<2694>), <2682>Opaque(`Inspect.DbgFormatte
r`, [], <2679>{ 'data' : Apply(`Str.Str`, []), }<115>))))
The skips are suspicious
The specialization is getting marked as "Drop" because it hits this branch: https://github.com/roc-lang/roc/blob/d478ec56da88268dd39752c73d42c2f60a20ef38/crates/compiler/solve/src/specialize.rs#L669
But why? We are specializing DbgFormatter, which is an opaque type.
It's because this condition isn't met: https://github.com/roc-lang/roc/blob/d478ec56da88268dd39752c73d42c2f60a20ef38/crates/compiler/solve/src/specialize.rs#L669
I'm not sure why https://github.com/roc-lang/roc/blob/d478ec56da88268dd39752c73d42c2f60a20ef38/crates/compiler/solve/src/ability.rs#L1509
lists Inspect in there. I don't think Inspect declares any opaque types that must be derived but cannot list the abilities they need derived.
If we remove that, we get a uitest that looks good
app "test" provides [main] to "./platform"
main =
Inspect.toInspector Bool.true |> Inspect.apply (Inspect.init {}) |> Inspect.toDbgStr
# ^^^^^^^^^^^^^^^^^^^ Inspect#Inspect.toInspector(32): Bool -[[Inspect.dbgBool(43)]]-> Inspector DbgFormatter
And the gen test fails, but it runs correctly and shows the expected output.
thread 'gen_abilities::inspect_bool' panicked at 'assertion failed: `(left == right)`
left: `"true, false"`,
right: `"Bool.true, Bool.false"`: LLVM test failed', crates/compiler/test_gen/src/helpers/llvm.rs:631:13
As a side note, I would put all of the inspect gen tests into a mod inspect
like is done for hash, encode, decode, etc. It helps a lot with navigating the file.
Or split them out into separate module files.
thanks!
Also, I highly recommend adding uitests of the form I described under https://github.com/roc-lang/roc/tree/d478ec56da88268dd39752c73d42c2f60a20ef38/crates/compiler/uitest/tests/ability/specialize. It helps a lot in catching the specialization issues at the source.
And, adding mono tests as well as gen tests. The mono tests will also catch things early on. If mono succeeds, codegen very likely will
Gotta love when this fails:
custom = @Inspector
but this works:
custom = \fn -> @Inspector fn
what's the error
that's a known problem, right?
like how you can't do mainForHost = main
if main
is a function, you have to do mainForHost = \arg -> main arg
custom = @Inspector
is sugar though
For the latter
It won't generate specializations correct as only custom = @Inspector
Anyway, I think I have autoderived inspect working now. Not wired to dbg
, but otherwise working.
Just for context, for specialization, errors look like:
── PROC SPECIALIZATION NOT DEFINED ─────────────────────────────────────────────
in toInspector_[A 2] : ({Str, Str}) -> {Str, Str} ((niche {}))
0│ procedure `#Derived.toInspector_[A 2]` (`#Derived.tag`):
1│> let `#Derived_gen.0` : {Str, Str} = CallByName `Inspect.custom` `#Derived.tag`;
2│ ret `#Derived_gen.0`;
No specialization
custom : ({Str, Str}) -> {Str, Str} ((niche {}))
was found
The following specializations of Inspect.custom were built:custom : () ->
{} ((niche {}))
custom : () -> {} ((niche {}))
custom : () -> {} ((niche {}))', crates/compiler/test_mono/src/tests.rs:177:5
yooooooo, amazing @Brendan Hansknecht!!! :heart_eyes: :heart_eyes: :heart_eyes:
thank you so much!
NGL that PROC SPECIALIZATION NOT DEFINED
error looks like magic to me.
Also, I think the plan is to submit #5775, make another PR for dbg changes, and then add some post fixes as needed.
I'll review this tonight. Do you guys mind holding off merging until I have a chance to look at this?
totally fine. Thanks
Also, looks like I still have a few minor issues to figure out.
Wouldn't mind some help with them if you have any ideas. Just updated tests to show the errors:
The two issues seem to be:
Frac a
is not turning into a concrete type. Instead, inspect is finding a
as the inner type and failing. So something is missing here that should end up selecting Dec
. Can repro by running cargo test -p test_gen -- gen_abilities::inspect::num
.List *
doesn't work, but we would want it to still be able to output []
. Currently we get unspecialized lambda sets left over during resolution
probably from the elem to inspector function iiuc. Can repro by running cargo test -p test_gen -- gen_abilities::inspect::list
.(1) Is probably because Frac is missing in the switch here https://github.com/roc-lang/roc/blob/3eb6f9179752a12622813d985f90b297d7baec81/crates/compiler/derive_key/src/inspect.rs#L186-L208
Regarding (2), this is a bit of a problem. In the future I'd like to get rid of that panic and instead have the "unspecialized..." thing return an error function, because if the type variable is unbound, we know that the specialization function will never be called. But as you've noticed, in the meantime it finds actual errors. So I am inclined to say that debugging a list with non concrete type should not be supported for now, or we should generate the synthetic error function only in non-debug builds.
(1) I don't think it is Frac missing in the switch. Num and Integer are also not in the switch, but they work just fine. Frac is a wrapping type, so we want to unwrap it and get to the underlying Dec/F32/F64. Unspecified number types work because when we unwrap, it results in an I64. For Frac, it should unwrap to Dec by default if the type is unspecfied. So I think this is an issue with default number handling logic elsewhere.
For (2), sounds reasonable to ignore for now. Long term can either pipeline through a nicer error or a special exception.
Regarding 1, Num and Integer work fine because if has a type variable, then either (1) the type variable is bound to a type variable that implements the ability, (2) the type variable is concrete, or (3) the number is a ranged number, the logic for which is implemented https://github.com/roc-lang/roc/blob/6c72bf9bf77c016428aaaad187e817dfb99d9eda/crates/compiler/derive_key/src/inspect.rs#L171-L173.
So what you want is to check if it's a Frac with an unbound type variable (FlexVar - Rigid means it's a bug, because it's trying to specialize a generalized type, which is nonsense). In that case, you know it will compile to the default Dec, so you can use the Dec specialization.
That makes sense. will test
Ok. Fixed.
all tests passed locally for me. So hopefully ci will be all good too.
Interesting wasm failure. Is this a case where we are passing a wrapping type to NumToStr, but it has the same underlying layout as a Numeric Type. As such, it theoretically can just be treated as the underlying numeric type? Not exactly sure the correct fix on the wasm side, but I assume it is something like instead of checking if the type is valid, check if the underlying layout is valid.
thread 'gen_abilities::inspect::list' panicked at 'NumToStr is not defined for LambdaSet(LambdaSet { set: [( 15.295, [InLayout(DEC)])], args: [InLayout(25)], ret: InLayout(25), representation: InLayout(DEC), full_layout: InLayout(81) })', crates/compiler/gen_wasm/src/low_level.rs:2272:18
So yeah, using the runtime representation is a fix in the wasm backend. That said, I wonder if that means tons of low levels for wasm should be using the runtime representation to extract the inner layout from lambdasets. For now, just changing the single location to fix a test, but feels like probably a symptom of a bigger issue.
For using Inspect
with dbg
. That will need to be handled at the mono or higher level, correct? Cause dbg
no longer has enough information to just be dealt with by the backends. Instead, dbg val
essentially has to desugar into Inspect.inspect val |> Inspect.toDbgStr |> roc_dbg
, right? Then that new set of commands will get lowered by the backends.
yeah exactly :100:
At what level do we normally desugar these things? Any specific code location? I assume either can or mono somewhere.
Maybe even when converting between the two
I'd do it in can
and then add a LowLevel
op for RocDbg
as for where to do it, we already desugar dbg
here - so I'd modify that!
Oh, should debug still automatically add on [examples/helloWorld.roc 7:9]
, I guess that is not covered by inspect. Though in a number of platforms, I assume it is not wanted. Maybe dbg should pass two strings, location and message?
I like that idea! :100:
What is the difference between the Expr
and ValueDef
forms of Dbg
?
hm, that I'm not sure
The Expr
version sounds like elm’s Debug.log
which returns the value it logs. I thought dbg
couldn’t do that but I’d love it if it did :grinning:
It’s very nice when you have some code already written and want to “sprinkle” some logging for debug purposes
So it looks like the expression version is actually our:
dbg "some value"
It just also includes a continuation that is really what is returned:
dbg "some value"
continuation
I don't think the valuedef is ever actually used.
Aside, I guess since desugaring still generates an Expr
, I have to actually make an expr that is Expr::LowLevelDbg
.
That version would just be used for actually calling roc_dbg
in the host.
Agus Zubiaga said:
The
Expr
version sounds like elm’sDebug.log
which returns the value it logs. I thoughtdbg
couldn’t do that but I’d love it if it did :grinning:
yeah the idea is to add support for that!
I just wanted to start with the "statement" version because I know from Elm that if that doesn't exist, people will do _ = dbg ...
and I wanted to see if there was still demand for the other style if we had the statement version
turns out yes :big_smile:
Yeah, I also like the statement version. Both are nice in different situations.
For dbg, do we want to attempt to wire through a file name or should we just give a module name (or other I guess)?
I think filename is nicest if possible
I mean we currently use filename, just need to figure out wiring it through differently. Cause now we need it at compile time instead of runtime.
Brendan Hansknecht said:
I wonder if that means tons of low levels for wasm should be using the runtime representation to extract the inner layout from lambdasets.
No, there are only 5 existing low-level ops that can actually take lambdasets as operands in the first place! It's just List.map
and the other "higher order low levels". In the wasm backend, those lambda sets are converted to their runtime representations here.
Apart from low-levels, there are about 4 or 5 other places where it's needed. Converting IR symbol layout to WasmLayout, inserting data into structs, etc.
Richard Feldman said:
turns out yes :big_smile:
If I understand correctly that means behaving like dbg! In rust where I get the value back out so I can just add the debug macro into a bigger expression. Yeah I'm a big fan of that. I'd extra love it if I didn't need to add parentheses cause that is cumbersome in rust because I'm potentially wrapping a huge chunk of code
yeah I think |> dbg
should let you avoid parens!
Brian Carroll said:
No, there are only 5 existing low-level ops that can actually take lambdasets as operands in the first place! It's just
List.map
and the other "higher order low levels". In the wasm backend, those lambda sets are converted to their runtime representations here.
I wonder if that means requiring this change was caused by a bug elsewhere.
https://github.com/roc-lang/roc/pull/5775/commits/248976d632b0827032171ba00b8372dbd59a39c0
Somehow the inspect ability lead to a lambdaset getting past to Num.toStr in the wasm backend. The Lambdaset was just a Dec under the hood.
I would assume it happened during the codegen of this ability impl function:
https://github.com/roc-lang/roc/blob/ead90313d89e8939549732136d536f301e522a16/crates/compiler/builtins/roc/Inspect.roc#L339-L342
That seems like a bug. A lambda set should never compile to a Dec
And a lambda set should never be an argument to Num.toStr
Lambda sets should compile to an int, struct, or union. And they should only appear where function types are expected
if you revert that change what tests fail?
gen_abilities::inspect::list
and gen_abilities::inspect::num
, but only for test-gen-wasm
Not sure why, but the incorrect parameters only happen with the dev wasm backend.
The llvm backend uses match layout_interner.get_repr(num_layout)
without handling lambasets and just works. So maybe the wasm backend has a bug with mapping layouts to args?
Do you know what particular case in inspect::num
?
I'll try this actually
I am not sure, but I recall it resolving to a Dec. So probably Dec or Frac case of some sort.
For symbols like roc_alloc
in the llvm backend, where do the external function headers for them get defined. Searched around a bit, but not finding it except for testing.
Trying to do let function = self.module.get_function("roc_dbg").unwrap();
, but unwrapping will result in a None.
I don't this is a backend bug. This is a bug in the code generation
With this diff
diff --git a/crates/compiler/mono/src/layout/intern.rs b/crates/compiler/mono/src/layout/intern.rs
index 9891c59f2..138d602cb 100644
--- a/crates/compiler/mono/src/layout/intern.rs
+++ b/crates/compiler/mono/src/layout/intern.rs
@@ -341,9 +341,10 @@ pub trait LayoutInterner<'a>: Sized {
}
doc
}
- LambdaSet(lambda_set) => {
- self.to_doc(lambda_set.runtime_representation(), alloc, seen_rec, parens)
- }
+ LambdaSet(lambda_set) => alloc
+ .text("LambdaSet(")
+ .append(self.to_doc(lambda_set.runtime_representation(), alloc, seen_rec, parens))
+ .append(")"),
RecursivePointer(rec_layout) => {
if seen_rec.contains(&rec_layout) {
alloc.text("*self")
diff --git a/crates/compiler/test_gen/src/gen_abilities.rs b/crates/compiler/test_gen/src/gen_abilities.rs
index 2bc4791b7..4d099c386 100644
--- a/crates/compiler/test_gen/src/gen_abilities.rs
+++ b/crates/compiler/test_gen/src/gen_abilities.rs
@@ -2222,21 +2222,7 @@ mod inspect {
app "test" provides [main] to "./platform"
main = [
- Inspect.inspect 0, # Num a
Inspect.inspect 1u8, # U8
- Inspect.inspect 2i8, # I8
- Inspect.inspect 3u16, # U16
- Inspect.inspect 4i16, # I16
- Inspect.inspect 5u32, # U32
- Inspect.inspect 6i32, # I32
- Inspect.inspect 7u64, # U64
- Inspect.inspect 8i64, # I64
- Inspect.inspect 9u128, # U128
- Inspect.inspect 10i128, # I128
- Inspect.inspect 0.5, # Frac a
- Inspect.inspect 1.5f32, # F32
- Inspect.inspect 2.2f64, # F64
- Inspect.inspect (1.1dec + 2.2), # Dec
] |> List.map Inspect.toDbgStr |> Str.joinWith ", "
"#
),
And this invocation
❯ ROC_PRINT_IR_AFTER_RESET_REUSE=1 ROC_CHECK_MONO_IR=1 cargo test-gen-wasm inspect::num
There's stuff like this
procedure : `Inspect.256` Str
procedure = `Inspect.256` (`Inspect.f0`: Str, `Inspect.num`: LambdaSet(U8)):
let `Inspect.318` : Str = CallByName `Num.toStr` `Inspect.num`;
let `Inspect.317` : Str = CallByName `Inspect.dbgWrite` `Inspect.f0` `Inspect.318`;
ret `Inspect.317`;
The initial generator of the inspector looks right
procedure : `Inspect.inspect` Str
procedure = `Inspect.inspect` (`Inspect.val`: U8):
let `Inspect.312` : LambdaSet(U8) = CallByName `Inspect.dbgU8` `Inspect.val`;
let `Inspect.309` : {} = Struct {};
let `Inspect.308` : Str = CallByName `Inspect.dbgInit` `Inspect.309`;
let `Inspect.307` : Str = CallByName `Inspect.256` `Inspect.308` `Inspect.312`;
ret `Inspect.307`;
But that Inspect.256
is definitely wrong, which I think you're right is that dbg*
function
It's almost certainly a problem in the chosen specialization, since after the specialization is applied it's not type-checked again
First signs of life:
$ cargo run examples/platform-switching/main.roc
Finished dev [unoptimized + debuginfo] target(s) in 0.50s
Running `target/debug/roc examples/platform-switching/main.roc`
🔨 Rebuilding platform...
[TODO: add filepath here] {num: 32, str: "test"}
Which platform am I running on now?
Also, in all of our platform switching examples, roc_panic
is technically wrong. It only works with small strings cause it assume a char*
. instead of a RocStr*
.
#6116 is a first pass at creating a PR for dbg using inspect.
It is still missing a lot, but is nearly at a place where we could submit it just to enable a better version of dbg
for advent of code. At a minimum, I need to update all the platforms to include roc_dbg
, but I listed a number of other goals in the PR. Not sure how many we will get to, but I think even without the extra goals, it should be submittable soon.
Also, a first pass on review comments would be nice.
Ok. So I think this is at a state where I want to submit it and do anything else in a follow up PR (sans fixing tests). That way, it hopefully can be merged tomorrow and ready for advent of code on friday.
There are still a number of changes listed in the PR and a suggestion by Ayaz that I want to make, but I want to push that all to follow ups in order to get this in.
related:
@Anton, if you get the chance, can you merge and cut a release for those 2 PRs? Basic CLI being the more important one for advent of code. Should be fine to cut a release before the new roc version is out.
Massive effort Brendan!! This will be such a nice improvement for everyone, even if most people never see this work. :octopus:
Can I get a review on #6116 to submit it? All test are green now (had to disable 2 that have unrelated bugs to be fixed later related to wrong exit codes)
As a note, though the PR is large, it is mostly fluff from platform changes to roc_dbg
and roc_panic
. The crates/compiler
folder being the main changes that matter.
Ok, now we just need to cut new releases of platforms that will be used with AOC such that they have a roc_dbg
impl.
amazing work @Brendan Hansknecht!!!
related:
- basic cli update: https://github.com/roc-lang/basic-cli/pull/138
- basic webserver update: https://github.com/roc-lang/basic-webserver/pull/13
This two PRs are both ultra simple if someone wants to review and merge them. For basic-cli and basic-webserver.
also in order to get the new basic-cli into the tutorial, we need to double check that it still works after the breaking Stdin
change
If we need anything updated I have time today to do that. Should I test basic-cli with a branch?
yeah sounds good :thumbs_up:
that would be great, thank you @Luke Boswell!
With what was just merged and the new compiler, basic-cli should just work with dbg.
So I think we just need to cut a new release (and update the examples in the repo to use the new release).
Oh, and update any examples for AOC to the new release as well.
I think we need Anton to be able to cut a new release?
So is that a release of basic-cli or just a nightly?
both
Cause the platform now has a roc_dbg
that needs to be available.
We have a bug with dbg
for opaque types. That should probably be resolved before cutting a new nightly. I don't think this blocks platform release, but the platform releases should stay in testing until we have a new nightly.
So for the opaque bug, it is an issue with not creating a correct specialization when dealing with inspect an opaque value with an automatically derived impl.
Essentially, Inspect.opaque
is a * -> Inspector f
, but we need to specialize it to the opaque type. So for Utc := ...
we want the final version of the function to be Utc -> Inspector f
. I think that specialized version is not being generating and we are hitting issues. That is at least my guess.
I tried to write a manual impl:
toInspector = \u ->
fmt <- Inspect.custom
Inspect.opaque u
|> Inspect.apply fmt
This maybe gives a clearer error:
This value is a declared specialization of type:
* -> Inspector f where f implements InspectFormatter
But the type annotation on toInspector says it must match:
val ->
Inspector f where val implements Inspect, f implements InspectFormatter
The function has to be explicitly typed to avoid the error:
toInspector : Utc -> Inspect.Inspector f where f implements Inspect.InspectFormatter
So maybe I can change the auo derivation to add a type and that will fix the issue.
@Ayaz Hafiz any chance you can take a look at this? Repro is just to try and run dbg
on an opaque
type that doesn't implement Inspect
at all. So something like:
Op := {}
dbg (@Op {})
I get the feeling it is probably a simple fix but I am not following it fully.
What branch is this on?
main
Where is the initial bug report?
I havent made an Issue for it
I can if that helps
Can you share it with me?
Maybe just a repro here
Does it fail with "expected to know a specialization for #UserApp.Op
#Inspect.toInspector
, but it wasn't found"?
$ roc dev examples/platform-switching/rocLovesZig.roc
An internal compiler expectation was broken.
This is definitely a compiler bug.
Please file an issue here: https://github.com/roc-lang/roc/issues/new/choose
thread '<unnamed>' panicked at 'expected to know a specialization for `17.IdentId(1)`#`15.IdentId(32)`, but it wasn't found', /Users/luke/Documents/GitHub/roc/crates/compiler/solve/src/specialize.rs:744:33
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
# examples/platform-switching/rocLovesZig.roc
app "rocLovesZig"
packages { pf: "zig-platform/main.roc" }
imports []
provides [main] to pf
main =
dbg (@Op {})
"Roc <3 Zig!\n"
Op := {}
Created an issue for this https://github.com/roc-lang/roc/issues/6127
https://github.com/roc-lang/roc/pull/6128 should take care of it
I left a detailed explanation of the issue.
Super easy review to add inspect to most of the opaque types in basic-cli: https://github.com/roc-lang/basic-cli/pull/139
It's failing CI: Only builtin abilities can be derived.
That suggests it's using the previous version of the compiler?
We need to cut a nightly release before updating the platforms after Ayaz's change.
Ah, ok. Didn't think about that.
Guess it can wait
I'll get started on releasing all the things :p
Thanks for getting this out :thank_you:
Thanks for getting it all ready :)
yeah it's really great that people today have been able to use it to get unblocked from dbg
bugs - huge thanks to both of you for shipping it in time! :smiley:
I currently don't get any output from dbg when I am running a test. Is there some flag I need to enable for that to happen? The results appear just fine in an actual run
Hmm, I wonder which roc_dbg
that hooks into. I guess whichever one the compiler itself exposes. I guess we need to update that.
When running a test, do you mean roc test myfile.roc
?
I think roc build myfile.roc
will still drop dbg like it used to before the new dbg
Anton said:
When running a test, do you mean
roc test myfile.roc
?
Yeah exactly
Can you make an issue and share your code there @Pearce Keesling?
Sure thing :thumbs_up: . I do need to verify that I actually understand the issue though. I noticed just now when I was running the test that it marked it as a "warning" which makes me think it is interpreting stderr output as warning on a test?
that it marked it as a "warning"
I'm not sure what you mean here, can you be more specific?
I'm seeing
Undefined symbols for architecture arm64:
"_roc_dbg", referenced from:
_#UserApp_determineCalibration_3bbacd33228bca14fe5573efe7278cde33c78fe9028ba98810cff368dece in roc_appGIhqSC.o
ld: symbol(s) not found for architecture arm64
with basic-cli 0.6.2. Is there a blocker here?
I think roc_dbg
is in 0.7
#announcements > basic-cli 0.7.0
So 1 thing I think we missed with dbg
that might be quite nice. Do we also want to send the source to the platform. So we send location in the form path:line
, the src, and the dbg str. As a result, this:
#App.roc
x = 27
dbg x
as:
[path/App.roc:2] x = 27
Thoughts? The one pain about this is it requires adding one more parameter to all roc_debug
impls and it is another breaking change that requires some update shuffling.
Currently, we already have location, so we can get [path/App.roc:2] 27
without the context of the src.
With the source add, our debug would be able to print exactly like rust.
I love that idea! :smiley:
I definitely think it's worth the breaking change assuming we like the idea, although we don't need to rush it out this time :sweat_smile:
So I may have just implemented it and that is why I asked... :sweat_smile:
haha awesome!
yeah let's try it!
[../roc-basic-cli/examples/time.roc:12] (x * 2) = 44
and it works :tada:
This is pretty cool. Question, will this print the entire expression always? What if it’s multiline?
The current setup I have is to just grab the entire condition region, so yes.
Would be up to the platform currently to elide or do something fancier
Not exactly the prettiest:
[../roc-basic-cli/examples/time.roc:13] (x
*
2) = 44
Which to be fair is exactly what rust does;
[crates/compiler/can/src/operator.rs:661] arena.alloc(Loc {
value: LowLevelDbg(arena.alloc(format!("{}:{}", module_path,
line_col.line)), arena.alloc(dbg_src), dbg_str,
desugared_continuation),
region: loc_expr.region,
}) = // Elide for brevty
We just need to clean up our paths eventually.
I definitely think it's worth the breaking change assuming we like the idea, although we don't need to rush it out this time :sweat_smile:
We can bundle this breaking change with the one for the simple-c-abi branch
So one thing I realized though haven't done yet....I can technically make this a non breaking change by putting the args in a weird order.
The args are currently (loc, msg)
If I make the new args be (loc, msg, src)
, all of the old functions will just access the first two args and ignore the third. They would have the exact same behaviour as current.
could do that for now and then switch the order to a more logical one when doing the breaking abi change
That said, currently on the branch, the args are in the order (loc, src, msg)
cause I like that order better. This could be merged without totally breaking dbg, but with non-updated platforms, the dbg statements would be useless. For:
x = 3
dbg x
It would print
[some/file:1] x
I think #6143 is ready for review. Should be passing tests now, we'll see if all of CI is green in a bit. @Richard Feldman
Would you prefer for me to reorder all the args to (loc, msg, src)
such that current platforms with the old version of roc_dbg
just keep working instead of printing out as I mentioned in the comment directly above this? Or should I just leave it as is accepting the mildly breaking change until other platforms are updated?
I think reordering for backwards compatibility is worth it for now, to avoid disrupting advent of code :big_smile:
Last updated: Jul 06 2025 at 12:14 UTC