Opened 2 years ago

Closed 2 years ago

#9783 closed bug (fixed)

Pattern synonym matcher is unnecessarily strict on unboxed continuations

Reported by: cactus Owned by: cactus
Priority: normal Milestone: 7.10.1
Component: Compiler (Type checker) Version: 7.8.3
Keywords: PatternSynonyms Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: Incorrect result at runtime Test Case:
Blocked By: Blocking:
Related Tickets: 9732 Differential Rev(s):
Wiki Page:


As discovered while investigating #9732, if you have something like

{-# LANGUAGE PatternSynonyms, MagicHash #-}
import GHC.Base

pattern P = True

f :: Bool -> Int#
f P = 42#

f is compiled into

Main.f :: GHC.Types.Bool -> GHC.Prim.Int#
[LclIdX, Str=DmdType]
Main.f =
  letrec {
    f_apU :: GHC.Types.Bool -> GHC.Prim.Int#
    [LclId, Str=DmdType]
    f_apU =
      \ (ds_dq1 :: GHC.Types.Bool) ->
        let {
          fail_dq2 :: GHC.Prim.Void# -> GHC.Prim.Int#
          [LclId, Str=DmdType]
          fail_dq2 =
            \ (ds_dq3 [OS=OneShot] :: GHC.Prim.Void#) ->
                @ GHC.Prim.Int# "unboxed.hs:7:1-9|function f"# } in
        case fail_dq2 GHC.Prim.void# of wild_00 { __DEFAULT ->
        (case break<1>() 42 of wild_00 { __DEFAULT ->
         Main.$mP @ GHC.Prim.Int# ds_dq1 wild_00
        }; } in

Note how fail_dq2 is applied on void# _before_ the pattern match, meaning the following expression:

I# (f True)

will fail with

*** Exception: unboxed.hs:7:1-9: Non-exhaustive patterns in function f

This is because the the type of P's matcher, instantiated for its use in f, is

$mP :: Bool -> Int# -> Int# -> Int#

so of course it is strict both on the success and the failure continuation.

Change History (9)

comment:1 Changed 2 years ago by Dr. ERDI Gergo <gergo@…>

In 474e535b6b121809a8d75df5a4c37dc574d3d302/ghc:

In pattern synonym matchers, support unboxed continuation results (fixes #9783).

This requires ensuring the continuations have arguments by adding a dummy
Void# argument when needed. This is so that matching on a pattern synonym
is lazy even when the result is unboxed, e.g.

    pattern P = ()
    f P = 0#

In this case, without dummy arguments, the generated matcher's type would be

   $mP :: forall (r :: ?). () -> r -> r -> r

which is called in `f` at type `() -> Int# -> Int# -> Int#`,
so it would be strict, in particular, in the failure continuation
of `patError`.

We work around this by making sure both continuations have arguments:

  $mP :: forall (r :: ?). () -> (Void# -> r) -> (Void# -> r) -> r

Of course, if `P` (and thus, the success continuation) has any arguments,
we are only adding the extra dummy argument to the failure continuation.

comment:2 Changed 2 years ago by cactus

Status: newmerge

comment:3 Changed 2 years ago by thomie

cactus: if you meant for thoughtpolice to merge this into 7.8.4, you should set the milestone to 7.8.4. If it can wait till 7.10.1, the status of the ticket can be changed to close (it's already merged).

comment:4 Changed 2 years ago by cactus


@thomie: Thanks, yes I think this one should be merged into 7.8.4.

comment:5 Changed 2 years ago by cactus

Keywords: PatternSynonyms added; pattern synonyms removed

comment:6 Changed 2 years ago by thoughtpolice

Resolution: fixed
Status: mergeclosed

This couldn't be merged cleanly; so I'm afraid I'm going to drop it.

comment:7 Changed 2 years ago by thoughtpolice


comment:8 Changed 2 years ago by cactus

Status: closedmerge

I have pushed a new version of the commit that applies cleanly to ghc-7.8 as a91a2af, please merge that.

Also, in the future, please try applying the patches in question at an earlier time, instead of discovering if they have problems so close to the freeze date.

comment:9 Changed 2 years ago by thoughtpolice

Status: mergeclosed

7.8.4 is already done; closing.

Note: See TracTickets for help on using tickets.