Open langston-barrett opened 1 year ago
Not sure if this is related or separate, but this slight variation gives an assertion failure:
(type bool (primitive u8))
(type i8 (primitive i8))
(type Map extern (enum))
(decl simplify (Map) Map)
(decl pure ne (i8 i8) bool)
(extern constructor ne ne)
(decl Empty () Map)
(extern extractor Empty extr_empty)
(extern constructor Empty constr_empty)
(decl Insert (i8 i8 Map) Map)
(extern extractor Insert extr_insert)
(extern constructor Insert constr_insert)
(decl Remove (i8 Map) Map)
(extern extractor Remove extr_remove)
(rule 0
(simplify (Insert k v (Empty)))
(Insert k v (Empty)))
(rule 1
(simplify (Insert k v (Remove k m)))
(simplify (Insert k v m)))
(rule 2
(simplify (Insert k v1 (Insert k v2 m)))
(simplify (Insert k v1 m)))
(rule 3
(simplify (Insert k1 v1 (Insert k2 v2 m)))
(if (ne k1 k2))
(Insert k1 v1 (Insert k2 v2 (simplify m))))
(rule
(Remove k (Empty))
(Empty))
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `1`,
right: `0`', cranelift/isle/isle/src/codegen.rs:433:17
cc @jameysharp for the codegen assert, and we can take a look at the typechecking hole as well (probably on Monday), but one quick note as I see something:
(decl pure ne (i8 i8) bool)
(if (ne k1 k2))
We actually ran into this footgun elsewhere recently, and it is subtle: the (if (ne ...))
form is testing whether the ne
constructor matches, not whether it returns true. We'll probably rename if
(to if-matches
or similar?) to make this clearer -- see this thread on #5676.
So you'll probably want something like (if-let $true (ne k1 k2))
. We could potentially add some sugar for that form too if it becomes annoying enough.
Chris' suggestion to change the (if (ne ...))
to (if-let $true ...)
fixes the codegen assertion failure. The proximate cause of the assertion failure was that of the two rules matching (Insert ... (Insert ...))
, the higher-priority one matched first in all the cases where the lower-priority one could possibly match. If the assert were removed, ISLE would have emitted Rust with a return
statement for the higher-priority rule, immediately followed by an unreachable if
statement to check the equality constraint on the lower-priority rule. Then the Rust compiler would have refused to compile the generated code.
Ideally, overlap/subset checking would have noticed that the rule with priority 2 could never match because the rule with priority 3 would always match first. However, there's an equality constraint in the former rule, and currently that check gives up and decides everything's fine if a rule has any equality constraints, because they're harder to reason about.
I thought this assert was impossible if overlap checking passed, so I didn't think hard about good error messages. But I didn't think about the impact of this hole in overlap checking's completeness.
By the way, a better way to write this pair of rules is to swap their priority and remove the (if ...)
entirely. Because the rule which requires equality will match first, we'll only check the other rule when the map keys are not equal.
(rule 3
(simplify (Insert k v1 (Insert k v2 m)))
(simplify (Insert k v1 m)))
(rule 2
(simplify (Insert k1 v1 (Insert k2 v2 m)))
(Insert k1 v1 (Insert k2 v2 (simplify m))))
And I'm not sure why the error that "should have been caught by typechecking", er, wasn't. But I can tell you the problem is that in the first version, the ne
term was declared without a constructor, but used in expression context (in an if
). You fixed that in the second version by changing it from an extractor to a constructor.
The proximate cause of the assertion failure was that of the two rules matching (Insert ... (Insert ...)), the higher-priority one matched first in all the cases where the lower-priority one could possibly match.
I see, thanks! Changing the rules to not cover one another did indeed fix this.
Thanks to both of you for the ISLE programming tips! I'm excited to use this tool - it's very neat!
Glad we could help! I'd love to hear what you're using ISLE for.
Let's leave this issue open even though your immediate issues are resolved, because we really should make sure typechecking actually catches this case instead of hitting a later panic.
I found a program that crashes islec. Details: