Fix BuiltinCasing crash on GHC.Prim.Addr# (#7716)#7719
Fix BuiltinCasing crash on GHC.Prim.Addr# (#7716)#7719
Conversation
|
I'll need to understand what happened a bit more. There may be simpler solutions. The issue description says "But the plugin's own simplifier pass (mkSimplPass in Plugin/Common.hs) can produce join points" - that's false. The plugin cannot produce join points. In general I'd recommend against copy-pasting large amount of AI text. I generally find it verbose, with low signal-to-noise ratio, and unpleasant to read. It's better to use AI to understand the issue, then write the description on your own. In this particular case, what would be a useful issue description is: "for this Haskell function, without builtin casing, it generates this GHC Core, which the plugin can compile. But with builtin casing, the GHC Core becomes this, which is problematic". It's very useful to include GHC Core, and anything else is unnecessary. |
Execution Budget Golden Diffoutputplutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.12/geq1.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.12/geq2.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.12/geq3.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.12/geq4.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.12/geq5.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.12/gt1.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.12/gt2.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.12/gt3.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.12/gt4.golden.eval
plutus-tx-plugin/test-ledger-api/Spec/Data/Budget/9.12/gt5.golden.eval
plutus-tx-plugin/test/Budget/9.12/map2.golden.eval
plutus-tx-plugin/test/Budget/9.12/map3.golden.eval
This comment will get updated when changes are made. |
|
Thanks for the feedback — you're right, I was wrong about Here's the minimal Core showing the problem. Test function: useTwiceData :: BuiltinData -> BuiltinUnit
useTwiceData bd =
case toBuiltinData (firstOf items) of
_ -> case toBuiltinData (firstOf items) of
_ -> unitval
where
items = unsafeFromBuiltinData bd
firstOf = caseList' Nothing (\(h :: BuiltinData) _t -> Just h)GHC Core produced (without BuiltinCasing, stored in the The plugin (with BuiltinCasing) reads this Core via the interface file and tries to compile the type Without the second constructor, GHC's simplifier unwraps |
…type GHC optimizer can produce join points with naked PlutusCore.Data.Data in the type signature. The plugin with BuiltinCasing tries to compile Data as a regular ADT, walks B ByteString -> BS Addr# and crashes. Minimal trigger: caseList' applied twice to the same value in a module without BuiltinCasing, then compiled from a BuiltinCasing module.
GHC's simplifier unconditionally unwraps single-constructor types. For `data BuiltinData = BuiltinData ~Data`, this exposes `Data` in join point type signatures. The plugin with BuiltinCasing then tries to compile Data as a regular ADT, walks B ByteString -> BS Addr#, and crashes. Fix: add a second (unreachable) constructor to BuiltinData so GHC cannot apply case-of-single-constructor. A COMPLETE pragma ensures existing pattern matches remain exhaustive without warnings.
Rename failsToCompile -> caseListTwice (it compiles now). Update golden files affected by the BuiltinData second constructor.
When a local variable (e.g. from a where-clause) has no unfolding, show the stage violation help message instead of a generic FreeVariableError.
Same pattern as caseListTwice but with BuiltinByteString. Currently does not crash (ByteString is handled differently by the simplifier), but kept as a regression test for future GHC changes.
BuiltinByteString and BuiltinString don't have UnsafeFromData instances, so tests use BuiltinList and Builtins.Internal.caseList' directly instead of Data.List.caseList'.
useTwiceData (was caseListTwice), useTwiceByteString, useTwiceString. ByteString and String tests pass opaque types directly as arguments instead of wrapping in BuiltinList.
The BuiltinData second constructor affects PIR output and budget values slightly (CPU +0.7-1.5%, memory +1.5%, but AST/Flat size decreases). Also updates stage violation error messages and renames from fourmolu.
0fd4f71 to
6d710a2
Compare
Summary
BuiltinDatato prevent GHC's case-of-single-constructor optimization from exposingPlutusCore.Data.Datain join point typesCOMPLETEpragma so existing pattern matches remain exhaustiveBuiltinCasing/Lib.hs) that reproduces the crashCloses #7716
The problem
data BuiltinData = BuiltinData ~Datahas one constructor. GHC's simplifier always unwraps single-constructor types, producingcase bd of { BuiltinData d -> ... }in Core. This leaksd :: Datainto join point type signatures. When the plugin compiles with BuiltinCasing, it tries to compileDataas a regular ADT, followsB ByteStringtoBS Addr# Int, and crashes —Addr#has no Plutus Core equivalent.The fix
Add a second constructor
BuiltinDataUnreachablethat is never constructed. GHC won't case-simplify multi-constructor types, soDatastays behind theBuiltinDatawrapper. ACOMPLETEpragma marks the original constructor as exhaustive.Details in Note [Opaque builtin types] in
PlutusTx.Builtins.Internal.Alternatives I tried
.hifiles regardless of what the module exportsnewtype BuiltinData = UnsafeBuiltinData Any— newtypes are transparent in Core (coercions), so the plugin just crashes onKind: forall k. kinsteadDatato the builtin type in the plugin — fixes the Addr# crash but exposes a second error (Cannot construct BuiltinData); would need deeper changes to the expression compiler