(* TEST
 ocamlopt_flags = "-dcmm-invariants";
 native;
*)

module Constant = struct
  type ('a, 'b) result =
    | Ok of 'a
    | Error of 'b


  module S = struct
    type t =
      [
        `B0
      (* renaming the following variants makes the problem go away *)
      | `CA
      | `CH
      | `CI
      | `CL
      | `Other of string
      ]
  end

  type a1 = [ `CA ]
  type a2 = [ `CH ]
  type a3 = [ `CL ]

  let ok = Ok ()
  let error s =
    Error s

  let foo =
    let current =
      let module T = struct
        type a1 =
          [ `Common
          | `Uuuu
          | `Ssss
          | `Rrrr
          ]

        type a2 = [ `Common ]

        type a3 =
          [ `Common  ]
      end
      in
      fun ~s typ ->

        match s with
        | `None           -> error s
        | #a1         ->
          (match typ with
           | #T.a1 -> ok
           | _ -> error s)
        | #a2    ->
          (match typ with
           | #T.a2 -> ok
           | _ -> error s)
        | #a3 ->
          (match typ with
           | #T.a3 -> ok
           | _ -> error s)
        | #S.t -> error s
    in
    current
end

module Constr = struct
  type ('a, 'b) result =
    | Ok of 'a
    | Error of 'b


  module S = struct
    type t =
      [
        `B0 of int
      (* renaming the following variants makes the problem go away *)
      | `CA of int
      | `CH of int
      | `CI of int
      | `CL of int
      | `Other
      ]
  end

  type a1 = [ `CA of int ]
  type a2 = [ `CH of int ]
  type a3 = [ `CL of int ]

  let ok = Ok ()
  let error s =
    Error s

  let foo =
    let current =
      let module T = struct
        type a1 =
          [ `Common
          | `Uuuu
          | `Ssss
          | `Rrrr
          ]

        type a2 = [ `Common ]

        type a3 =
          [ `Common  ]
      end
      in
      fun ~s typ ->

        match s with
        | `None           -> error s
        | #a1         ->
          (match typ with
           | #T.a1 -> ok
           | _ -> error s)
        | #a2    ->
          (match typ with
           | #T.a2 -> ok
           | _ -> error s)
        | #a3 ->
          (match typ with
           | #T.a3 -> ok
           | _ -> error s)
        | #S.t -> error s
    in
    current
end

module Gadts = struct
  type keep = private Keep
  type drop = private Drop

  type _ t =
  | C00 : keep t
  | C01 : drop t
  | C02 : drop t
  | C03 : drop t
  | C04 : drop t
  | C05 : drop t
  | C06 : drop t
  | C07 : drop t
  | C08 : drop t
  | C09 : drop t
  | C10 : drop t
  | C11 : drop t
  | C12 : drop t
  | C13 : drop t
  | C14 : drop t
  | C15 : drop t
  | C16 : drop t
  | C17 : drop t
  | C18 : drop t
  | C19 : drop t
  | C20 : keep t
  | C21 : drop t
  | C22 : drop t
  | C23 : drop t
  | C24 : keep t
  | C25 : keep t
  | C26 : drop t
  | C27 : drop t
  | C28 : keep t

  let not_at_toplevel b =
    let f (x: keep t) =
      match x with
      | C00 -> 0
      | C20 ->
        begin
          (* The implementation of the local function optimisation will not
             handle well multiple bindings of the same function, so this
             code will reliably fail to compile with Closure if it ends up
             duplicated. *)
          let[@local] handler x = x + 2 in
          if b then handler 0 else handler 1
        end
      | C24 -> 2
      | C25 -> 3
      | C28 -> 4
    in
    f
end
