summaryrefslogtreecommitdiff
path: root/src/type_error.ml
blob: 8b3237149d4f3d1e33e0b146ba77171b38a8b990 (plain)
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
(**************************************************************************)
(*     Sail                                                               *)
(*                                                                        *)
(*  Copyright (c) 2013-2017                                               *)
(*    Kathyrn Gray                                                        *)
(*    Shaked Flur                                                         *)
(*    Stephen Kell                                                        *)
(*    Gabriel Kerneis                                                     *)
(*    Robert Norton-Wright                                                *)
(*    Christopher Pulte                                                   *)
(*    Peter Sewell                                                        *)
(*    Alasdair Armstrong                                                  *)
(*    Brian Campbell                                                      *)
(*    Thomas Bauereiss                                                    *)
(*    Anthony Fox                                                         *)
(*    Jon French                                                          *)
(*    Dominic Mulligan                                                    *)
(*    Stephen Kell                                                        *)
(*    Mark Wassell                                                        *)
(*                                                                        *)
(*  All rights reserved.                                                  *)
(*                                                                        *)
(*  This software was developed by the University of Cambridge Computer   *)
(*  Laboratory as part of the Rigorous Engineering of Mainstream Systems  *)
(*  (REMS) project, funded by EPSRC grant EP/K008528/1.                   *)
(*                                                                        *)
(*  Redistribution and use in source and binary forms, with or without    *)
(*  modification, are permitted provided that the following conditions    *)
(*  are met:                                                              *)
(*  1. Redistributions of source code must retain the above copyright     *)
(*     notice, this list of conditions and the following disclaimer.      *)
(*  2. Redistributions in binary form must reproduce the above copyright  *)
(*     notice, this list of conditions and the following disclaimer in    *)
(*     the documentation and/or other materials provided with the         *)
(*     distribution.                                                      *)
(*                                                                        *)
(*  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''    *)
(*  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED     *)
(*  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A       *)
(*  PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR   *)
(*  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,          *)
(*  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      *)
(*  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF      *)
(*  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND   *)
(*  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,    *)
(*  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT    *)
(*  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF    *)
(*  SUCH DAMAGE.                                                          *)
(**************************************************************************)

open Util
open Ast
open Ast_defs
open Ast_util
open Type_check

type suggestion =
  | Suggest_add_constraint of n_constraint
  | Suggest_none

let rec analyze_unresolved_quant locals ncs = function
  | QI_aux (QI_constraint nc, _) ->
     let gen_kids = List.filter is_kid_generated (KidSet.elements (tyvars_of_constraint nc)) in
     if gen_kids = [] then
       Suggest_add_constraint nc
     else
       (* If there are generated kind-identifiers in the constraint,
          we don't want to make a suggestion based on them, so try to
          look for generated kid free nexps in the set of constraints
          that are equal to the generated identifier. This often
          occurs due to how the type-checker introduces new type
          variables. *)
       let is_subst v = function
         | NC_aux (NC_equal (Nexp_aux (Nexp_var v', _), nexp), _)
              when Kid.compare v v' = 0 && not (KidSet.exists is_kid_generated (tyvars_of_nexp nexp)) ->
            [(v, nexp)]
         | NC_aux (NC_equal (nexp, Nexp_aux (Nexp_var v', _)), _)
              when Kid.compare v v' = 0 && not (KidSet.exists is_kid_generated (tyvars_of_nexp nexp)) ->
            [(v, nexp)]
         | _ -> []
       in
       let substs = List.concat (List.map (fun v -> List.concat (List.map (fun nc -> is_subst v nc) ncs)) gen_kids) in
       let nc = List.fold_left (fun nc (v, nexp) -> constraint_subst v (arg_nexp nexp) nc) nc substs in
       if not (KidSet.exists is_kid_generated (tyvars_of_constraint nc)) then
         Suggest_add_constraint nc
       else
         (* If we have a really anonymous type-variable, try to find a
            regular variable that corresponds to it. *)
         let is_linked v = function
           | (id, (Immutable, (Typ_aux (Typ_app (ty_id, [A_aux (A_nexp (Nexp_aux (Nexp_var v', _)), _)]), _) as typ)))
                when Id.compare ty_id (mk_id "atom") = 0 && Kid.compare v v' = 0 ->
              [(v, nid id, typ)]
           | (id, (mut, typ)) ->
              []
         in
         let substs = List.concat (List.map (fun v -> List.concat (List.map (fun nc -> is_linked v nc) (Bindings.bindings locals))) gen_kids) in
         let nc = List.fold_left (fun nc (v, nexp, _) -> constraint_subst v (arg_nexp nexp) nc) nc substs in
         if not (KidSet.exists is_kid_generated (tyvars_of_constraint nc)) then
           Suggest_none
         else
           Suggest_none

  | QI_aux (QI_id _, _) | QI_aux (QI_constant _, _) ->
     Suggest_none

let message_of_type_error =
  let open Error_format in
  let rec msg = function
    | Err_because (err, l', err') ->
       Seq [msg err;
            Line "This error was caused by:";
            Location (l', msg err')]

    | Err_other str -> Line str

    | Err_no_overloading (id, errs) ->
       Seq [Line ("No overloading for " ^ string_of_id id ^ ", tried:");
            List (List.map (fun (id, err) -> string_of_id id, msg err) errs)]

    | Err_unresolved_quants (id, quants, locals, ncs) ->
       Seq [Line ("Could not resolve quantifiers for " ^ string_of_id id);
            Line (bullet ^ " " ^ Util.string_of_list ("\n" ^ bullet ^ " ") string_of_quant_item quants)]

    | Err_lexp_bounds (check, locals, ncs) ->
       Line ("Bounds check failed for l-expression: " ^ string_of_n_constraint check)

    | Err_subtype (typ1, typ2, _, vars) ->
       let vars = KBindings.bindings vars in
       let vars = List.filter (fun (v, _) -> KidSet.mem v (KidSet.union (tyvars_of_typ typ1) (tyvars_of_typ typ2))) vars in
       With ((fun ppf -> { ppf with loc_color = Util.yellow }),
             Seq (Line (string_of_typ typ1 ^ " is not a subtype of " ^ string_of_typ typ2)
                  :: List.map (fun (kid, l) -> Location (l, Line (string_of_kid kid ^ " bound here"))) vars))

  | Err_no_num_ident id ->
     Line ("No num identifier " ^ string_of_id id)

  | Err_no_casts (exp, typ_from, typ_to, trigger, reasons) ->
     let coercion =
       Line ("Tried performing type coercion from " ^ string_of_typ typ_from
             ^ " to " ^ string_of_typ typ_to
             ^ " on " ^ string_of_exp exp)
     in
     Seq ([coercion; Line "Coercion failed because:"; msg trigger]
          @ if not (reasons = []) then
              Line "Possible reasons:" :: List.map msg reasons
            else
              [])
  in
  msg

let rec string_of_type_error err =
  let open Error_format in
  let b = Buffer.create 20 in
  format_message (message_of_type_error err) (buffer_formatter b);
  Buffer.contents b

let rec collapse_errors = function
  | (Err_no_overloading (_, errs) as no_collapse) ->
     let errs = List.map (fun (_, err) -> collapse_errors err) errs in
     let interesting = function
       | Err_other _ -> false
       | Err_no_casts _ -> false
       | _ -> true
     in
     begin match List.filter interesting errs with
     | err :: errs ->
        let fold_equal msg err =
          match msg, err with
          | Some msg, Err_no_overloading _ -> Some msg
          | Some msg, Err_no_casts _ -> Some msg
          | Some msg, err when msg = string_of_type_error err -> Some msg
          | _, _ -> None
        in
        begin match List.fold_left fold_equal (Some (string_of_type_error err)) errs with
        | Some _ -> err
        | None -> no_collapse
        end
     | [] -> no_collapse
     end
  | Err_because (err1, l, err2) as no_collapse ->
     let err1 = collapse_errors err1 in
     let err2 = collapse_errors err2 in
     if string_of_type_error err1 = string_of_type_error err2 then
       err1
     else
       Err_because (err1, l, err2)
  | err -> err

let check_defs : 'a. Env.t -> 'a def list -> tannot def list * Env.t =
  fun env defs ->
  try Type_check.check_defs env defs with
  | Type_error (env, l, err) ->
     raise (Reporting.err_typ l (string_of_type_error err))
         
let check : 'a. Env.t -> 'a ast -> tannot ast * Env.t =
  fun env defs ->
  try Type_check.check env defs with
  | Type_error (env, l, err) ->
     Interactive.env := env;
     raise (Reporting.err_typ l (string_of_type_error err))