package acgtk

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file type_system.ml

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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
(**************************************************************************)
(*                                                                        *)
(*                 ACG development toolkit                                *)
(*                                                                        *)
(*                  Copyright 2008-2023 INRIA                             *)
(*                                                                        *)
(*  More information on "https://acg.loria.fr/"                     *)
(*  License: CeCILL, see the LICENSE file or "http://www.cecill.info"     *)
(*  Authors: see the AUTHORS file                                         *)
(*                                                                        *)
(*                                                                        *)
(*                                                                        *)
(*                                                                        *)
(*                                                                        *)
(**************************************************************************)

open UtilsLib
open Logic.Abstract_syntax
open Logic.Lambda

let get_location = function
  | Abstract_syntax.Var (_, l) -> l
  | Abstract_syntax.Const (_, l) -> l
  | Abstract_syntax.Abs (_, _, _, l) -> l
  | Abstract_syntax.LAbs (_, _, _, l) -> l
  | Abstract_syntax.App (_, _, l) -> l

module Log = Xlog.Make (struct
  let name = "Type_system"
end)

module type SIG_ACCESS = sig
  exception Not_found

  type t

  val unfold_type_definition : int -> t -> Lambda.stype
  (** [unfold_type_definition id t] returns the actual type for the
      type defined by [id] as the identifier of a type definition in
      the signature [t]. Fails with "Bug" if [id] does not correspond
      to a type definition *)

  val expand_type : Lambda.stype -> t -> Lambda.stype
  val find_term : string -> t -> Lambda.term * Lambda.stype
  val pp_type : t -> Format.formatter -> Lambda.stype -> unit
  val pp_term : t -> Format.formatter -> Lambda.term -> unit
end

module Type_System = struct
  module Make (Signature : SIG_ACCESS) = struct
    exception Not_functional_type
    exception Functional_type of Abstract_syntax.abstraction
    exception Not_normal_term

    exception
      Vacuous_abstraction of
        (string * Abstract_syntax.location * Abstract_syntax.location)

    exception Non_empty_linear_context of (string * Abstract_syntax.location)

    exception
      Not_linear of (Abstract_syntax.location * Abstract_syntax.location)

    exception
      Type_mismatch of (Abstract_syntax.location * Lambda.stype * Lambda.stype)

    let decompose_functional_type ty sg =
      match Signature.expand_type ty sg with
      | Lambda.LFun (ty1, ty2) -> (ty1, ty2, Abstract_syntax.Linear)
      | Lambda.Fun (ty1, ty2) -> (ty1, ty2, Abstract_syntax.Non_linear)
      (*      | Lambda.DAtom i -> decompose_functional_type (Signature.unfold_type_definition i sg) sg *)
      | _ -> raise Not_functional_type

    (* [get_typing x loc typing_env] returns [l,t,e] where [l] is the
       level of [x] and [t] its type in the typing environment
       [typing_env] when [x] is a variable located at [loc]. [e] is the
       new environment where [x] as been marked as used at location
       [l]. If [x] has been used alread once (this is marked by the
       [Some l], 3rd projection in the association list), the function
       raises [Not_linear (l,loc)] where [l] is the location of the former
       usage of [x]. If [x] is not in the typing environment
       [typing_env], it raises Not_found *)

    let get_typing x loc lst =
      let rec get_typing_aux lst k =
        match lst with
        | [] -> raise Not_found
        | (s, (level, ty, None)) :: tl when s = x ->
            k (level, ty, (s, (level, ty, Some loc)) :: tl)
        | (s, (_, _, Some l)) :: _ when s = x -> raise (Not_linear (l, loc))
        | hd :: tl ->
            get_typing_aux tl (fun (level, ty, r) -> k (level, ty, hd :: r))
      in
      get_typing_aux lst (fun (level, ty, env) -> (level, ty, env))
      [@@warning "-32"]

    type typing_env_content =
      | Delay of (int -> int -> Abstract_syntax.location -> Lambda.term)
      | Eval of
          (int ->
          int ->
          Abstract_syntax.location ->
          Lambda.term * typing_env_content)

    type typing_environment = {
      linear_level : int;
      (* The depth of the term with respect to the
         number of linear abstraction. Starting at 0 *)
      level : int;
      (* The depth of the term with respect to the number
         of non linear abstraction. Starting at 0 *)
      env :
        (typing_env_content * Lambda.stype * Abstract_syntax.abstraction * int)
        Utils.StringMap.t;
      (* the last int parameter is the nomber of occurences of the variable with name the key of the map *)
      wrapper :
        (Abstract_syntax.location * Lambda.stype * Abstract_syntax.location)
        option;
      almost_linearity : bool;
    }

    let remove_lin_context env =
      Utils.StringMap.fold
        (fun k ((_, _, abs, _) as v) (l_acc, nl_acc) ->
          match abs with
          | Abstract_syntax.Linear -> (Utils.StringMap.add k v l_acc, nl_acc)
          | Abstract_syntax.Non_linear -> (l_acc, Utils.StringMap.add k v nl_acc))
        env
        (Utils.StringMap.empty, Utils.StringMap.empty)

    let insert_lin_var (ty : Lambda.stype) (env : typing_environment) =
      ( Eval
          (fun l_level _ loc ->
            (* let i = (l_level - 1 - env.linear_level) in
               let () = Printf.printf "Inserting variable %d - 1 - %d = %d\n%!" l_level env.linear_level i in *)
            ( Lambda.LVar (l_level - 1 - env.linear_level),
              Delay (fun _ _ l -> raise (Not_linear (l, loc))) )),
        ty,
        Abstract_syntax.Linear,
        0 )

    let insert_non_lin_var (ty : Lambda.stype) (env : typing_environment) =
      ( Delay (fun _ level _ -> Lambda.Var (level - 1 - env.level)),
        ty,
        Abstract_syntax.Non_linear,
        0 )

    let compute (k : typing_env_content) (env : typing_environment)
        (l : Abstract_syntax.location) =
      match k with
      | Delay f -> (f env.linear_level env.level l, Delay f)
      | Eval f -> f env.linear_level env.level l

    let get_binding x map =
      try Some (Utils.StringMap.find x map) with Not_found -> None

    let replace_binding x v map =
      match v with
      | None -> Utils.StringMap.remove x map
      | Some b -> Utils.StringMap.add x b map

    let typecheck (t : Abstract_syntax.term) (ty : Lambda.stype)
        (sg : Signature.t) : Lambda.term * bool =
      let local_expand ty = Signature.expand_type ty sg in
      let rec typecheck_aux t ty (tenv : typing_environment) =
        match t with
        | Abstract_syntax.Var (x, l) -> (
            try
              let f_var, var_type, lin, nb_occ =
                Utils.StringMap.find x tenv.env
              in
              let var, new_f = compute f_var tenv l in
              match ty with
              | None ->
                  ( var,
                    var_type,
                    {
                      tenv with
                      env =
                        Utils.StringMap.add x
                          (new_f, var_type, lin, nb_occ + 1)
                          tenv.env;
                    } )
              | Some l_ty when l_ty = local_expand var_type ->
                  ( var,
                    var_type,
                    {
                      tenv with
                      env =
                        Utils.StringMap.add x
                          (new_f, var_type, lin, nb_occ + 1)
                          tenv.env;
                    } )
              | Some l_ty -> raise (Type_mismatch (l, l_ty, var_type))
            with Not_found -> raise (Non_empty_linear_context (x, l)))
        | Abstract_syntax.Const (x, l) -> (
            try
              let l_term, l_type = Signature.find_term x sg in
              match ty with
              | None -> (l_term, l_type, tenv)
              | Some l_ty when local_expand l_type = l_ty ->
                  (l_term, l_type, tenv)
              | Some l_ty -> raise (Type_mismatch (l, l_ty, l_type))
            with Signature.Not_found -> failwith "Bug")
        | Abstract_syntax.LAbs (x, l_x, u, l_u) -> (
            match ty with
            | None -> raise Not_normal_term
            | Some l_ty -> (
                let b = get_binding x tenv.env in
                try
                  let ty1, ty2, lin = decompose_functional_type l_ty sg in
                  match lin with
                  | Abstract_syntax.Linear -> (
                      let u_term, _, new_typing_env =
                        typecheck_aux u
                          (Some (local_expand ty2))
                          {
                            tenv with
                            linear_level = tenv.linear_level + 1;
                            env =
                              Utils.StringMap.add x (insert_lin_var ty1 tenv)
                                tenv.env;
                          }
                      in
                      let f_var, _, _, nb_occ =
                        Utils.StringMap.find x new_typing_env.env
                      in
                      try
                        let _ = compute f_var new_typing_env l_x in
                        (* if the Not_linear exception is not raised, it means the variable
                           was not
                           used in
                           u_term *)
                        let () = assert (nb_occ = 0) in
                        raise (Vacuous_abstraction (x, l_x, get_location u))
                      with Not_linear _ ->
                        let () = assert (nb_occ = 1) in
                        ( Lambda.LAbs (x, u_term),
                          l_ty,
                          {
                            new_typing_env with
                            env = replace_binding x b new_typing_env.env;
                            linear_level = tenv.linear_level;
                          } ))
                  | Abstract_syntax.Non_linear as l -> raise (Functional_type l)
                with
                | Not_functional_type
                | Functional_type Abstract_syntax.Non_linear ->
                  Errors.(TypeErrors.emit (Type_l.IsUsed
                   (Format.asprintf "%a" (Signature.pp_type sg) l_ty, "\"'a → 'b\" (linear abstraction)")) ~loc:l_u)))
        | Abstract_syntax.Abs (x, _, u, l_u) -> (
            match ty with
            | None -> raise Not_normal_term
            | Some l_ty -> (
                let b = get_binding x tenv.env in
                try
                  let ty1, ty2, lin = decompose_functional_type l_ty sg in
                  match lin with
                  | Abstract_syntax.Non_linear ->
                      let u_term, _, new_typing_env =
                        typecheck_aux u
                          (Some (local_expand ty2))
                          {
                            tenv with
                            level = tenv.level + 1;
                            env =
                              Utils.StringMap.add x
                                (insert_non_lin_var ty1 tenv)
                                tenv.env;
                          }
                      in
                      let () =
                        Log.debug (fun m ->
                            m "Binding and occurrences:@,@[<v>  @[%a@]@]"
                              (fun fmt env ->
                                 Utils.StringMap.iter
                                   (fun var_name (_, ty, _, nb) ->
                                      Format.fprintf fmt "%s: \"%a\", %d" var_name
                                        (Signature.pp_type sg)
                                        ty
                                        nb)
                                   env)
                              new_typing_env.env)
                      in
                      let is_almost_linear =
                        match get_binding x new_typing_env.env with
                        | Some (_, var_type, _, nb_occ)
                          when nb_occ = 0
                               || nb_occ > 1
                                  && not (Lambda.is_atomic var_type (fun i -> Signature.unfold_type_definition i sg)) ->
                            let () =
                              Log.info (fun m ->
                                  m "var \"%s\", nb occ: %d, atomic type: %b" x
                                    nb_occ
                                    (Lambda.is_atomic var_type (fun i -> Signature.unfold_type_definition i sg)))
                            in
                            false
                        | None ->
                            failwith "Bug: the variable should be given a type"
                        | _ -> true
                      in
                      ( Lambda.Abs (x, u_term),
                        l_ty,
                        {
                          new_typing_env with
                          env = replace_binding x b new_typing_env.env;
                          level = tenv.level;
                          almost_linearity =
                            new_typing_env.almost_linearity && is_almost_linear;
                        } )
                  | Abstract_syntax.Linear as l -> raise (Functional_type l)
                with Not_functional_type | Functional_type _ ->
                  Errors.(
                    TypeErrors.emit (Type_l.IsUsed
                   (Format.asprintf "%a" (Signature.pp_type sg) l_ty, "\"'a ⇒ 'b\" (non-linear abstraction)")) ~loc:l_u)))
        | Abstract_syntax.App (u, v, l) -> (
            let u_term, u_type, new_typing_env =
              try typecheck_aux u None tenv
              with Not_normal_term ->
                Errors.(TypeErrors.emit Type_l.NotNormal ~loc:l)
            in
            let ty1, ty2, lin =
              try decompose_functional_type u_type sg
              with Not_functional_type ->
                let u_loc = get_location u in
                Errors.(
                  TypeErrors.emit
                    (Type_l.IsUsed
                      (Format.asprintf "%a" (Signature.pp_type sg) u_type, "\"'a -> 'b\" or \"'a => 'b\" in order to enable application"))
                    ~loc:u_loc)
            in
            let v_term, _, new_new_typing_env =
              match lin with
              | Abstract_syntax.Linear ->
                  typecheck_aux v (Some (local_expand ty1)) new_typing_env
              | Abstract_syntax.Non_linear -> (
                  let lin_env, non_lin_env =
                    remove_lin_context new_typing_env.env
                  in
                  let wrapper = (get_location u, u_type, get_location v) in
                  try
                    let v_t, v_ty, new_env (*{ wrapper = w; env = l_env; _ }*) =
                      typecheck_aux v
                        (Some (local_expand ty1))
                        {
                          new_typing_env with
                          env = non_lin_env;
                          wrapper = Some wrapper;
                        }
                    in
                    ( v_t,
                      v_ty,
                      {
                        new_env with
                        env =
                          Utils.StringMap.union
                            (fun _ _ _ -> failwith "Bug")
                            lin_env new_env.env;
                      } )
                  with Non_empty_linear_context (x, l) ->
                    let func_loc, func_st, v_loc =
                      match tenv.wrapper with None -> wrapper | Some a -> a
                    in
                    (*        let v_loc = get_location v in*)
                    Errors.(
                      TypeErrors.emit
                        (Type_l.NonEmptyContext (x, l, func_loc,
                                                 (Format.asprintf "%a" (Signature.pp_type sg) func_st)))
                      ~loc:v_loc))
            in
            match ty with
            | None -> (Lambda.App (u_term, v_term), ty2, new_new_typing_env)
            | Some l_ty when l_ty = local_expand ty2 ->
                (Lambda.App (u_term, v_term), l_ty, new_new_typing_env)
            | Some l_ty -> raise (Type_mismatch (l, l_ty, ty2)))
      in
      try
        let t_term, t_type, ({ almost_linearity = b; _ } : typing_environment) =
          typecheck_aux t
            (Some (local_expand ty))
            {
              linear_level = 0;
              level = 0;
              env = Utils.StringMap.empty;
              wrapper = None;
              almost_linearity = true;
            }
        in
        Log.debug (fun m ->
            m "@[Type-checked: @[%a:@[<2>@ %a@]@]@]" (Signature.pp_term sg)
              t_term (Signature.pp_type sg) t_type);
        (t_term, b)
      with
      | Type_mismatch (loc, t1, t2) ->
        Errors.(
          TypeErrors.emit
            (Type_l.IsUsed (Format.asprintf "%a" (Signature.pp_type sg) t1,
              Format.asprintf "\"%a\"" (Signature.pp_type sg) t2))
            ~loc)
      | Not_linear (loc1, loc2) ->
          Errors.(TypeErrors.emit (Type_l.TwoOccurrencesOfLinearVariable loc1) ~loc:loc2)
      | Vacuous_abstraction (x, l1, l2) ->
          Errors.(TypeErrors.emit (Type_l.VacuousAbstraction (x, l2)) ~loc:l1)
  end
end
OCaml

Innovation. Community. Security.