Skip to content

Apparent nondeterministic compilation of certain pattern matching examples. #14088

Description

@cristianoc

There are cases where the same function compiled twice produces different code for pattern matching. (And compiled more times, produces some unpredictable-looking alternation of the two versions of the code).
This behaviour is not bad per se, as the two versions of the code are equivalent, but surprising and likely not intended.

Noticed here: rescript-lang/rescript#7557

Pattern matching compilation uses a global counter for handler ids. In principle, the value of the counter is irrelevant, however a function reintroduce_fail part of pattern matching compilation iterates on a hash table over handler ids, so the value of the global counter might affect the order in which ids are visited, and does produce different code for the same function below. (The first function changes the global counter, causing the different compilation for the second function).

(* repro.ml *)

type action =
  | WithoutPayload1
  | WithoutPayload2
  | WithPayload2 of { y : int }
  | WithPayload3 of { y : int }
  | WithPayload5 of { y : int }
  | WithPayload6 of { x : int }
  | WithPayload7 of { y : int }
  | WithPayload8 of { x : int }

let f1 (action : action) =
  (match action with
  | WithPayload5 _
  | WithPayload6 _
  | WithPayload7 _
  | WithPayload8 _ -> print_endline "hello"
  | _              -> ());
  42

let f2 (action : action) =
  (match action with
  | WithPayload5 _
  | WithPayload6 _
  | WithPayload7 _
  | WithPayload8 _ -> print_endline "hello"
  | _              -> ());
  42

Compilation produces different code for the same function. Here's the lambda output.
This is on 5.2 but the behaviour is a very old one.

% ocamlc -dlambda repro.ml
(setglobal Repro!
  (let
    (f1/285 =
       (function action/287 : int
         (seq
           (catch
             (catch
               (switch action/287
                case tag 2: (exit 1)
                case tag 3: (exit 1)
                case tag 4: (exit 1)
                case tag 5: (exit 1)
                default: (exit 2))
              with (2) 0)
            with (1) (apply (field_imm 45 (global Stdlib!)) "hello"))
           42))
     f2/288 =
       (function action/290 : int
         (seq
           (catch
             (switch action/290
              case int 0: (exit 4)
              case int 1: (exit 4)
              case tag 0: (exit 4)
              case tag 1: (exit 4)
              default: (apply (field_imm 45 (global Stdlib!)) "hello"))
            with (4) 0)
           42)))
    (makeblock 0 f1/285 f2/288)))

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions