let rec check_unused ~lib tm =
  let rec check ?(toplevel=false) v tm = match tm.term with
    | Var s -> Vars.remove s v
    | Unit | Bool _ | Int _ | String _ | Float _ | Encoder _ -> v
    | Ref r -> check v r
    | Get r -> check v r
    | Product (a,b) | Set (a,b) -> check (check v a) b
    | Seq (a,b) -> check ~toplevel (check v a) b
    | List l -> List.fold_left check v l
    | App (hd,l) ->
        let v = check v hd in
          List.fold_left (fun v (lbl,t) -> check v t) v l
    | Fun (fv,p,body) ->
        let v =
          List.fold_left
            (fun v -> function
               | (_,_,_,Some default) -> check v default
               | _ -> v)
            v p
        in
        let masked,v =
          let v0 = v in
            List.fold_left
              (fun (masked,v) (_,var,_,_) ->
                 if Vars.mem var v0 then
                   Vars.add var masked, v
                 else
                   masked, Vars.add var v)
              (Vars.empty, v) p
        in
        let v = check v body in
          (* Restore masked variables
           * The masking variables have been used but it does not count
           * for the ones they masked. *)

          Vars.union masked v
    | Let { var = s ; def = def ; body = body } ->
        let v = check v def in
        let mask = Vars.mem s v in
        let v = Vars.add s v in
        let v = check ~toplevel v body in
          begin
            (* Do not check for anything at toplevel in libraries *)
            if not (toplevel && lib) then
            (* Do we have an unused definition? *)
            if Vars.mem s v then
            (* There are exceptions: unit, active_source
             * and functions when at toplevel (sort of a lib situation...) *)

            if not (can_ignore def.t || (toplevel && is_fun def.t)) then
              let start_pos = fst (Utils.get_some tm.t.T.pos) in
                if !errors_as_warnings then
                  Printf.printf
                    "Warning: unused variable %s at %s.\n%!"
                    s (T.print_single_pos start_pos)
                else
                  raise (Unused_variable (s,start_pos))
          end ;
          if mask then Vars.add s v else v
  in
    (* Unused free variables may remain *)
    ignore (check ~toplevel:true Vars.empty tm)