[commit: ghc] master: Do not bind coercion variables in SpecConstr rules (fb050a3)

git at git.haskell.org git at git.haskell.org
Thu Oct 12 11:53:55 UTC 2017


Repository : ssh://git@git.haskell.org/ghc

On branch  : master
Link       : http://ghc.haskell.org/trac/ghc/changeset/fb050a330ad202c1eb43038dc18cca2a5be26f4a/ghc

>---------------------------------------------------------------

commit fb050a330ad202c1eb43038dc18cca2a5be26f4a
Author: Simon Peyton Jones <simonpj at microsoft.com>
Date:   Thu Oct 12 11:00:19 2017 +0100

    Do not bind coercion variables in SpecConstr rules
    
    Trac #14270 showed that SpecConstr could cause nasty Lint failures
    if it generates a RULE that binds coercion varables.  See
    
     * Note [SpecConstr and casts], and
     * the test simplCore/should_compile/T14270.
    
    This doesn't feel like the final word to me, because somehow the
    specialisation "ought" to work.  So I left in a debug WARN to yell if
    the new check acutally fires.
    
    Meanwhile, it stops the erroneous specialisation.
    
    binding coercion


>---------------------------------------------------------------

fb050a330ad202c1eb43038dc18cca2a5be26f4a
 compiler/specialise/SpecConstr.hs                  | 44 +++++++++++++++++++++-
 .../tests/simplCore/should_compile/T14270a.hs      | 27 +++++++++++++
 testsuite/tests/simplCore/should_compile/all.T     |  1 +
 3 files changed, 70 insertions(+), 2 deletions(-)

diff --git a/compiler/specialise/SpecConstr.hs b/compiler/specialise/SpecConstr.hs
index 609e70c..86d7093 100644
--- a/compiler/specialise/SpecConstr.hs
+++ b/compiler/specialise/SpecConstr.hs
@@ -1883,6 +1883,41 @@ by trim_pats.
 
 * Otherwise we sort the patterns to choose the most general
   ones first; more general => more widely applicable.
+
+Note [SpecConstr and casts]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Consider (Trac #14270) a call like
+
+    let f = e
+    in ... f (K @(a |> co)) ...
+
+where 'co' is a coercion variable not in scope at f's definition site.
+If we aren't caereful we'll get
+
+    let $sf a co = e (K @(a |> co))
+        RULE "SC:f" forall a co.  f (K @(a |> co)) = $sf a co
+        f = e
+    in ...
+
+But alas, when we match the call we won't bind 'co', because type-matching
+(for good reasons) discards casts).
+
+I don't know how to solve this, so for now I'm just discarding any
+call patterns that
+  * Mentions a coercion variable
+  * That is not in scope at the binding of the function
+
+I think this is very rare.
+
+Note that this /also/ discards the call pattern if we have a cast in a
+/term/, although in fact Rules.match does make a very flaky and
+fragile attempt to match coercions.  e.g. a call like
+    f (Maybe Age) (Nothing |> co) blah
+    where co :: Maybe Int ~ Maybe Age
+will be discarded.  It's extremely fragile to match on the form of a
+coercion, so I think it's better just not to try.  A more complicated
+alternative would be to discard calls that mention coercion variables
+only in kind-casts, but I'm doing the simple thing for now.
 -}
 
 type CallPat = ([Var], [CoreExpr])      -- Quantified variables and arguments
@@ -1985,7 +2020,7 @@ callToPats :: ScEnv -> [ArgOcc] -> Call -> UniqSM (Maybe CallPat)
         --      Type variables come first, since they may scope
         --      over the following term variables
         -- The [CoreExpr] are the argument patterns for the rule
-callToPats env bndr_occs (Call _ args con_env)
+callToPats env bndr_occs call@(Call _ args con_env)
   | args `ltLength` bndr_occs      -- Check saturated
   = return Nothing
   | otherwise
@@ -2014,8 +2049,13 @@ callToPats env bndr_occs (Call _ args con_env)
               sanitise id   = id `setIdType` expandTypeSynonyms (idType id)
                 -- See Note [Free type variables of the qvar types]
 
+              bad_covars = filter isCoVar ids
+                -- See Note [SpecConstr and casts]
+
         ; -- pprTrace "callToPats"  (ppr args $$ ppr bndr_occs) $
-          if interesting
+          WARN( not (null bad_covars), text "SpecConstr: bad covars:" <+> ppr bad_covars
+                                       $$ ppr call )
+          if interesting && null bad_covars
           then return (Just (qvars', pats))
           else return Nothing }
 
diff --git a/testsuite/tests/simplCore/should_compile/T14270a.hs b/testsuite/tests/simplCore/should_compile/T14270a.hs
new file mode 100644
index 0000000..840b1e8
--- /dev/null
+++ b/testsuite/tests/simplCore/should_compile/T14270a.hs
@@ -0,0 +1,27 @@
+{-# LANGUAGE TypeApplications, ScopedTypeVariables, GADTs, RankNTypes, TypeInType, KindSignatures #-}
+{-# OPTIONS_GHC -O2 #-} -- We are provoking a bug in SpecConstr
+
+module T14270a where
+
+import Data.Kind
+import Data.Proxy
+
+data T a = T1 (T a) | T2
+
+data K (a :: k) where
+  K1 :: K (a :: Type)
+  K2 :: K a
+
+f :: T (a :: Type) -> Bool
+f (T1 x) = f x
+f T2     = True
+
+g :: forall (a :: k). K a -> T a -> Bool
+g kv x = case kv of
+            K1 -> f @a T2   -- f @a (T1 x) gives a different crash
+            k2 -> True
+
+-- The point here is that the call to f looks like
+--    f @(a |> co) (T2 @(a |> co))
+-- where 'co' is bound by the pattern match on K1
+-- See Note [SpecConstr and casts] in SpecConstr
diff --git a/testsuite/tests/simplCore/should_compile/all.T b/testsuite/tests/simplCore/should_compile/all.T
index 7f21331..28e26ed 100644
--- a/testsuite/tests/simplCore/should_compile/all.T
+++ b/testsuite/tests/simplCore/should_compile/all.T
@@ -281,3 +281,4 @@ test('T14140',
      ['$MAKE -s --no-print-directory T14140'])
 
 test('T14272', normal, compile, [''])
+test('T14270a', normal, compile, [''])



More information about the ghc-commits mailing list