1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
open Type

(*
  Maintain a map from con to info = {Nonproductive, Productive, Param n}`.
  After the analysis, this map says, for all type constructors, whether they are  really unproductive, productive, or if their unfolding is equal to one of their parameters.
* Maintain a set of type definitions under scrutiny
* For each type definition:
  * If it is already in the memo map, use that result. Else analyze it as follows, updating the memo map afterwards.
  * If it is already in the set, we have found a loop. Mark it as `unproductive`. Else add to the setduring the next step.
  * Consider its RHS:
    * If it is a concrete type, return `Productive`.
    * If it is the nth type parameter, return `Param n`
    * If it is a type application `T<t1,…,tn>`, recurse.
      * If recursion returns `Productive` or `Nonproductive`, return that.
      * If recursion returns `Param n`, loop to “Consider its RHS”, as if `tn`
        is the RHS.
*)

type info =
  | Nonproductive
  | Productive
  | Param of int

let non_productive cs =
  let map = ref ConEnv.empty in
  let rec rhs cs = function
    | Pre
    | Mut _ | Typ _ ->
      assert false (* body of a Def shouldn't be 2nd class *)
    | Var (s, j) ->
      Param j
    | Con (d, ts) ->
      begin
        visit_con cs d;
        match ConEnv.find d (!map) with
        | Param n ->
          begin
          match Cons.kind d with
          | Def (tbs, t) ->
            assert (n < List.length tbs); (* assume types are arity-correct *)
            rhs cs (List.nth ts n)
          | Abs (tbs, t) ->
            (* we could assert here since Defs should be closed *)
            Productive
          end
        | info -> info
      end
    | _ ->  (* anything else is productive *)
      Productive

  and visit_con cs c =
    match ConEnv.find_opt c (!map) with
    | Some info -> ()
    | None ->
      let info =
        if ConSet.mem c cs then
          Nonproductive
        else
          let t = match Cons.kind c with
            | Def (_, t) -> t
            | _ -> assert false
          in
          rhs (ConSet.add c cs) t
      in
      map := ConEnv.add c info !map
  in
  ConSet.iter (visit_con ConSet.empty) cs;
  ConSet.filter (fun c -> ConEnv.find c !map = Nonproductive) cs