Skip to content

PatternMatchFailure on nested case of Requests continuations #6183

@lJoublanc

Description

@lJoublanc

Describe and demonstrate the bug

I'm seeing PatternMatchFailures when using nested handle ... with clauses (I want to replace certain calls to optimise performance). I can't pinpoint the issue. Here is a very simple example:

handler : { Stream () } r -> r
handler prog =
  go : Request { Stream () } r -> r
  go = cases
    { emit _ -> cont } ->
      handle cont () with cases
        { r } -> r
        { emit _ -> cont } ->
          handle cont () with go
    other -> todo other
  handle prog with go

If I replace the inner cases with a catch-all like so, it stops compiling (PatternMatchFailure):

handler : { Stream () } r -> r
handler prog =
  go : Request { Stream () } r -> r
  go = cases
    { emit _ -> cont } ->
      handle cont () with cases
        -- { r } -> r
        { emit _ -> cont } ->
          handle cont () with go
        other -> todo other
    other -> todo other
  handle prog with go

This (somewhat nonsensical handler) compiles fine

handleRand2 : {Random} r -> r
handleRand2 prog = 
  step1 : Request {Random } r -> r
  step1 = cases
    {nat! -> cont } -> 
      handle cont 0 with step2
    other -> todo "other0"

  step2 : Request {Random} r -> r
  step2 = cases
    { nat! -> cont } ->
      handle cont 1 with step2
    other -> todo "other1"
      
  handle prog with step1

But again, inlining step2 directly results in a PatternMatchFailure:

handleRand : {Random} r -> r
handleRand prog = 
  go : Request {Random } r -> r
  go = cases
    { r } -> r
    {nat! -> cont } -> 
      handle cont 0 with cases
        { r } -> r
        { nat! -> cont } ->
          handle cont 1 with go
        other -> todo "other1"
    other -> todo "other0"
      
  handle prog with go

I can't tell for sure, but it looks like if you have a 'catch-all' inside of a nested case that handles a Request, it will throw a PatternMatchFailure. The above examples can again be fixed by listing all the cases:

handleRand : {Random} r -> r
handleRand prog = 
  go : Request {Random } r -> r
  go = cases
    { r } -> r
    {nat! -> cont } -> 
      handle cont 0 with cases
        { r } -> r
        { nat! -> cont } ->
          handle cont 1 with go
        { bytes n -> cont } ->
          handle cont Bytes.empty with go
        { split! -> cont } ->
          handle cont (todo "wtf") with go
    other -> todo "other0"
      
  handle prog with go

Environment (please complete the following information):

  • ucm --version 1.1.0
  • OS/Architecture: ubuntu linux

Additional context
I'm trying to optimise certain call trees, and I would like to avoid exhaustively listing all the cases (which can be quite large in my example). This is also important for working with fixed points, as you need the catch-all to capture the remaining requests to pass on.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions