[Git][ghc/ghc][wip/ak/spec-loop] Specialise: Stop looping on recursive dictionaries in interestingDict
Andreas Klebinger pushed to branch wip/ak/spec-loop at Glasgow Haskell Compiler / GHC Commits: b4f2c4b2 by Andreas Klebinger at 2026-08-25T21:05:01+02:00 Specialise: Stop looping on recursive dictionaries in interestingDict interestingDict now doesn't look through loopbreaker unfoldings. Doing so would cause infinite loops on certain dictionaries. Fixes #27705. - - - - - 6 changed files: - + changelog.d/T27705 - compiler/GHC/Core/Opt/Specialise.hs - + testsuite/tests/simplCore/should_run/T27705.hs - + testsuite/tests/simplCore/should_run/T27705.stdout - + testsuite/tests/simplCore/should_run/T27705_Inst.hs - testsuite/tests/simplCore/should_run/all.T Changes: ===================================== changelog.d/T27705 ===================================== @@ -0,0 +1,5 @@ +section: ghc +synopsis: Prevent the specializer from looping on recursive dictionary superclasses. +issues: #27705 +mrs: !16559 + ===================================== compiler/GHC/Core/Opt/Specialise.hs ===================================== @@ -3120,8 +3120,8 @@ interestingDict :: SpecEnv -> CoreExpr -> Bool -- This is a subtle and important function -- See Note [Interesting dictionary arguments] interestingDict env (Var v) -- See (ID3) and (ID5) + -- (ID6.a) Might fail for loop breaker dicts but that seems fine. | Just rhs <- maybeUnfoldingTemplate (idUnfolding v) - -- Might fail for loop breaker dicts but that seems fine. = interestingDict env rhs interestingDict env arg -- Main Plan: use exprIsConApp_maybe @@ -3152,7 +3152,8 @@ interestingDict env arg -- Main Plan: use exprIsConApp_maybe where arg_ty = exprType arg definitely_not_ip_like = not (couldBeIPLike arg_ty) - in_scope_env = ISE (substInScopeSet $ se_subst env) realIdUnfolding + -- idUnfolding rather than realIdUnfolding: See (ID6.a) + in_scope_env = ISE (substInScopeSet $ se_subst env) idUnfolding {- Note [Ticks on applications] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -3268,6 +3269,24 @@ case we can clearly specialise. But there are wrinkles: (Remember: a constraint tuple is just a class with N superclasses and no methods.) See discussion on #26831. +(ID6.a) If we deal with a recursive dictionary as in #27705 we want to avoid + infinite recursion while recursing into superclasses. + + For example we might have: + + class D1 a => D2 a + class D2 a => D1 a + + The primary concern is that we want to avoid looping on recursive instances. + We can achieve this by simply not looking through loop breakers by using idUnfolding + rather than readIdUnfolding. + + It's possible that this prevents specialization of edge cases that have loop breakers + in their recursive loop. But even if we can find a dictionary like this the simplifier + won't look through loopbreaker dictionaries either killing any potential benefit. + So while we could handle this case via a already-seen set or fuel we simply don't bother + for now. + (ID7) A unary (single-method) class is currently represented by (meth |> co). We will unwrap the cast (see (ID5)) and then want to reply "yes" if the method has any struture. We rather arbitrarily use `exprIsHNF` for this. (We plan a ===================================== testsuite/tests/simplCore/should_run/T27705.hs ===================================== @@ -0,0 +1,9 @@ +module Main where + +import T27705_Inst + +-- The dictionaries (D1/D2) are mutually recursive. We have to watch +-- out for the specializer looping on them. This was first detected in #22802 +-- but no test was added, which caused it to break again #27705 :( +main :: IO () +main = print (b (3 :: Int)) ===================================== testsuite/tests/simplCore/should_run/T27705.stdout ===================================== @@ -0,0 +1 @@ +42 ===================================== testsuite/tests/simplCore/should_run/T27705_Inst.hs ===================================== @@ -0,0 +1,13 @@ +{-# LANGUAGE UndecidableInstances, UndecidableSuperClasses, FlexibleInstances #-} +module T27705_Inst where + +-- The two dictionaries are mutually recursive, and we have to ensure the specialiser +-- doesn't loop when it's peaking through their unfoldings. +class D2 a => D1 a +class D1 a => D2 a +instance D2 Int => D1 Int +instance D1 Int => D2 Int + +{-# NOINLINE b #-} +b :: D1 a => a -> Int +b _ = 42 ===================================== testsuite/tests/simplCore/should_run/all.T ===================================== @@ -123,3 +123,5 @@ test('T24359b', normal, compile_and_run, ['-O']) test('T23429', normal, compile_and_run, ['-O']) test('T27071', normal, compile_and_run, ['-O -fworker-wrapper-cbv']) test('T27005', [], multimod_compile_and_run, ['T27005', '-O']) +test('T27705', [extra_hc_opts('+RTS -M500M -RTS')], multimod_compile_and_run, + ['T27705', '-O2 -fexpose-all-unfoldings']) View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/b4f2c4b23cdbb919e8efeb349d197804... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/b4f2c4b23cdbb919e8efeb349d197804... You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
participants (1)
-
Andreas Klebinger (@AndreasK)