Skip to content

prepareAlts does not take into account equalities which are in scope

If we consider this program submitted for our consideration by Andres we see some surprising behaviour.

https://gist.github.com/kosmikus/237946a2335600690208a4a36efef988

{-# LANGUAGE TypeOperators, GADTs, FlexibleContexts, DataKinds, RankNTypes, PolyKinds, TypeFamilies, MultiParamTypeClasses, UndecidableInstances, UndecidableSuperClasses, FlexibleInstances, ConstraintKinds, TypeApplications, EmptyCase, ScopedTypeVariables, PartialTypeSignatures, TemplateHaskell #-}
module Partition where

import Data.Coerce
import Data.Kind
import Data.Proxy

data NP (f :: k -> Type) (xs :: [k]) where
  Nil :: NP f '[]
  (:*) :: f x -> NP f xs -> NP f (x : xs)

infixr 5 :*

strictToPair :: forall f a b . NP f '[a, b] -> (f a, f b)
strictToPair np =
  case np of
    (fx :* fxs) ->
      case (fxs {- :: NP f '[b] -}) of
        (fy :* fys) ->
          (fx, fy)

Both pattern matches are exhaustive so we don't need to generate any failure cases when pattern matching.

Notice in the generated core that we have a match on Partition.Nil even though the match will never be reached.

 Partition.strictToPair                                                          
  :: forall k (f :: k -> *) (a :: k) (b :: k).                                  
      Partition.NP f '[a, b] -> (f a, f b)                                       
 [GblId, Arity=1, Str=<S,1*U>m, Unf=OtherCon []]                                 
 Partition.strictToPair                                                          
   = \ (@ k_a1gV)                                                                
       (@ (f_a1gW :: k_a1gV -> *))                                               
       (@ (a_a1gX :: k_a1gV))                                                    
       (@ (b_a1gY :: k_a1gV))                                                    
       (np_s1yz [Occ=Once!] :: Partition.NP f_a1gW '[a_a1gX, b_a1gY]) ->         
       case np_s1yz of                                                           
       { Partition.:* @ x_a1h2 @ xs_a1h3 co_a1h4 fx_s1yB [Occ=Once]              
                      fxs_s1yC [Occ=Once!] ->                                    
       case fxs_s1yC of {                                                        
         Partition.Nil _ [Occ=Dead, Dmd=<B,A>] ->                                
           Partition.strictToPair1 @ k_a1gV @ a_a1gX @ f_a1gW @ b_a1gY;          
         Partition.:* @ x1_a1h7 @ xs1_a1h8 co1_a1h9 fy_s1yE [Occ=Once]           
                      _ [Occ=Dead] ->                                            
           (fx_s1yB                                                              
            `cast` (<f_a1gW>_R (Nth:1 (Sym co_a1h4))                             
                    :: (f_a1gW x_a1h2 :: *) ~R# (f_a1gW a_a1gX :: *)),           
            fy_s1yE                                                              
            `cast` (<f_a1gW>_R (Nth:1 (Sym co1_a1h9 ; Nth:2 (Sym co_a1h4)))      
                   :: (f_a1gW x1_a1h7 :: *) ~R# (f_a1gW b_a1gY :: *)))          
       }                                                                         
      } 

This is because in prepareAlts are try to inspect the type of fxs which looks like a type variable, however it has since been refined by the pattern match on np above. Adding the explicit type signature to fxs makes prepareAlts treat it correctly.

Trac metadata
Trac field Value
Version 8.6.2
Type Bug
TypeOfFailure OtherFailure
Priority normal
Resolution Unresolved
Component Compiler
Test case
Differential revisions
BlockedBy
Related
Blocking
CC
Operating system
Architecture
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information