1(* ----------------------------------------------------------------------
2    Proves theorems about record types.  A record type is set up by first
3    calling standard datatype code to set up a new type with one
4    constructor and as many arguments to that constructor as there are
5    fields in the desired record type, and with the corresponding types.
6
7    The main function takes this new type's tyinfo data, as well as a
8    list of the field names.
9
10    Author: Michael Norrish
11   ---------------------------------------------------------------------- *)
12
13structure RecordType :> RecordType =
14struct
15
16open HolKernel Parse boolLib
17
18structure Parse = struct
19  open Parse
20  val (Type,Term) = parse_from_grammars boolTheory.bool_grammars
21end
22open Parse
23
24val K_tm = combinSyntax.K_tm
25
26
27val ERR = mk_HOL_ERR "RecordType";
28fun mk_recordtype_constructor s = "recordtype." ^ s
29
30(* ----------------------------------------------------------------------
31    General utilities
32   ---------------------------------------------------------------------- *)
33
34fun map3 f ([], _, _) = []
35  | map3 f (x::xs, ys, zs) = f (x, hd ys, hd zs) :: map3 f (xs, tl ys, tl zs)
36
37fun insert n e l =
38    if n < 1 then raise Fail "Bad position to insert at" else
39    if n = 1 then e::l else (hd l)::(insert (n-1) e (tl l));
40fun delete n l =
41    if n < 1 then raise Fail "Bad position to delete at" else
42    if n = 1 then tl l
43    else (hd l)::(delete (n-1) (tl l));
44fun update n e = (insert n e) o (delete n);
45fun findi e [] = raise Fail "Couldn't find element"
46  | findi e (x::xs) = if e = x then 1 else 1 + findi e xs;
47fun foldr f zero []      = zero
48  | foldr f zero (x::xs) = f x (foldr f zero xs)
49fun foldl f zero []      = zero
50  | foldl f zero (x::xs) = foldl f (f zero x) xs
51
52fun dest_all_type ty =
53    dest_type ty handle HOL_ERR _ => (dest_vartype ty, [])
54
55fun variant tml tm = let
56  fun name_of tm = if is_var tm then SOME (fst(dest_var tm)) else NONE
57  val avoidstrs = List.mapPartial name_of tml
58  val (Name, Ty) = Term.dest_var tm
59in
60  Term.mk_var(Lexis.gen_variant Lexis.tmvar_vary avoidstrs Name,Ty)
61end
62
63
64(* app_letter = "appropriate letter" *)
65fun app_letter ty =
66    if is_vartype ty then String.substring(dest_vartype ty, 1, 1)
67    else String.substring(#Tyop (dest_thy_type ty), 0, 1)
68
69(* given two lists of equal length n, produce a list of length *)
70(* n(n-1) where each element of the first list is paired with  *)
71(* each of the second, except the one that corresponds to it   *)
72local
73  fun nrecfilter fnc [] n       = []
74    | nrecfilter fnc (hd::tl) n =
75      if not (fnc (n,hd)) then
76        nrecfilter fnc tl (n+1)
77      else
78        hd::nrecfilter fnc tl (n+1)
79in
80fun nfilter fnc l = nrecfilter fnc l 1
81end
82
83fun crossprod l1 l2 =
84    let
85      fun pairer x y = x @ map (fn item => (y,item)) l2 in
86      foldl pairer [] l1
87    end
88
89fun crosslessdiag l1 l2 =
90    let
91      val cross = crossprod l1 l2 and
92                  diaglength = List.length l1 + 1 in
93      nfilter (fn (n,_) => not (n mod diaglength = 1)) cross
94    end
95
96fun update_tyinfo opt new_simpls tyinfo =
97 let open TypeBasePure
98     val existing_simpls = simpls_of tyinfo
99     fun add_rwts {convs,rewrs} newrwts = {convs=convs, rewrs=rewrs @ newrwts}
100     val base = put_simpls (add_rwts existing_simpls new_simpls) tyinfo
101 in
102  case opt of
103    NONE => base
104  | SOME (flds,accs,upds) =>
105      put_accessors accs
106       (put_updates upds
107         (put_fields flds base))
108end
109
110fun digit_suffix s =
111  let
112    val ss = Substring.full s
113    val (l,r) = Substring.splitr Char.isDigit ss
114  in
115    (Substring.string l, Int.fromString (Substring.string r))
116  end
117
118fun nexttyvar ty =
119  let
120    val qnm = dest_vartype ty
121    val nm = String.extract(qnm, 1, NONE)
122  in
123    if size nm = 1 andalso Char.isLower (String.sub(nm, 0)) then
124      if nm <> "z" then
125        mk_vartype("'" ^ String.str (Char.chr(Char.ord (String.sub(nm,0)) + 1)))
126      else
127        mk_vartype "'a0"
128    else
129      case digit_suffix qnm of
130          (pfx, NONE) => mk_vartype (pfx ^ "0")
131        | (pfx, SOME i) => mk_vartype (pfx ^ Int.toString (i + 1))
132  end
133
134fun tyvariant avoids ty =
135  if mem ty avoids then tyvariant avoids (nexttyvar ty)
136  else ty
137
138(* assumes that avoids is a superset of tyvs *)
139fun freshsubst avoids tyvs =
140  let
141    fun foldthis (tyv, (avoids, s)) =
142      let
143        val v' = tyvariant avoids tyv
144      in
145        (v'::avoids, (tyv |-> v') :: s)
146      end
147  in
148    #2 (List.foldl foldthis (avoids, []) tyvs)
149  end
150
151
152(* ----------------------------------------------------------------------
153    prove_recordtype_thms
154
155    Where all the work happens. The following function is huge and
156    revolting.
157   ---------------------------------------------------------------------- *)
158
159fun prove_recordtype_thms (tyinfo, fields) = let
160
161  val save_thm = Feedback.trace ("Theory.save_thm_reporting", 0) save_thm
162  fun store_thm (n, t, tac) = save_thm(n, prove(t,tac))
163
164  val app2 = C (curry op^)
165  val typthm = TypeBasePure.axiom_of tyinfo
166  val (thyname,typename) = TypeBasePure.ty_name_of tyinfo
167  val constructor =
168    case TypeBasePure.constructors_of tyinfo of
169      [x] => x
170    | _ => raise ERR "prove_recordtype_thms"
171                     "Type to be record has more than one constructor"
172  val (typ, types) = let
173    fun domtys acc ty = let
174      val (d1, rty) = dom_rng ty
175    in
176      domtys (d1::acc) rty
177    end handle HOL_ERR _ => (ty, List.rev acc)
178  in
179    domtys [] (type_of constructor)
180  end
181  val varying_tyvs = let
182    val tymap = Binarymap.mkDict Type.compare : (hol_type,int) Binarymap.dict
183    fun recurse m tys =
184      case tys of
185          [] => m
186        | ty::tyrest =>
187          let
188            val tyvs = HOLset.fromList Type.compare (Type.type_vars ty)
189            fun foldthis (tyv,m) =
190                case Binarymap.peek(m, tyv) of
191                    NONE => Binarymap.insert(m,tyv,1)
192                  | SOME i => Binarymap.insert(m,tyv,i+1)
193          in
194            recurse (HOLset.foldl foldthis m tyvs) tyrest
195          end
196  in
197    Binarymap.foldl (fn (tyv,c,acc) => if c = 1 then tyv::acc else acc)
198                    []
199                    (recurse tymap types)
200  end
201
202  val tysigma = let
203    val avoids = Type.type_varsl types
204  in
205    freshsubst avoids varying_tyvs
206  end
207  val sigsubst = type_subst tysigma
208  val var = Psyntax.mk_var(app_letter typ, typ)
209  val size = length fields
210
211  val _ = length types = length fields orelse
212    raise HOL_ERR {origin_function = "prove_recordtype_thms",
213                   origin_structure = "RecordType",
214                   message =
215                   "Number of fields doesn't match number of types"}
216
217  (* we now have the type actually defined in typthm *)
218  fun letgen x y = x @ [variant x (mk_var (app_letter y,y))]
219  val typeletters = foldl letgen [] types
220
221  (* now generate accessor functions *)
222  val accfn_types = map (fn x => (typ --> x)) types
223  local
224    fun constructor_args [] = map boolSyntax.mk_arb types
225      | constructor_args ((f,t)::xs) = let
226          val rest = constructor_args xs
227          val posn = findi f fields handle _ =>
228            raise Fail "Bad field name"
229        in
230          update posn t rest
231        end
232  in
233    fun create_term ftl =
234      list_mk_comb(constructor, constructor_args ftl)
235  end
236  val cons_comb = list_mk_comb(constructor,  typeletters)
237  val access_fn_names = map (fn n => typename^"_"^n) fields
238  val access_defn_terms =
239    map3 (fn (afn_name, typeletter, accfn_type) =>
240          mk_eq(mk_comb(mk_var(afn_name, accfn_type),
241                        cons_comb),
242                typeletter))
243    (access_fn_names, typeletters, accfn_types)
244  val access_defns =
245    ListPair.map
246    (fn (name, tm) => Prim_rec.new_recursive_definition
247     {def = tm, name = name, rec_axiom = typthm})
248    (access_fn_names, access_defn_terms)
249  val accessor_thm =
250    save_thm(typename^"_accessors", LIST_CONJ access_defns)
251  fun mk_const(n,ty) = mk_thy_const{Thy=current_theory(), Name = n, Ty = ty}
252  val accfn_terms = ListPair.map mk_const (access_fn_names, accfn_types)
253
254  (* generate functional update functions *)
255  (* these are of the form :
256       field_fupd f o = field_update (f (field o)) o
257   *)
258  val fupd_names = map (fn s => s ^ "_fupd") access_fn_names
259  fun getredex t = let
260    val tyvs = type_vars t
261  in
262    List.filter (fn {redex,residue} => mem redex tyvs)
263  end
264  val fupd_fun_types =
265      map (fn t => (getredex t tysigma, t --> sigsubst t)) types
266  val fupd_types =
267      map (fn (s,t) => (s, t --> typ --> type_subst s typ)) fupd_fun_types
268  val fupd_terms =
269      ListPair.map (fn (n,(s,ty)) => (s, mk_var(n,ty))) (fupd_names, fupd_types)
270  fun mk_fupd_defn ((s, fldfupd), result, (pos, acc)) = let
271    val (f_ty, _) = dom_rng (type_of fldfupd)
272    val f0 = mk_var("f", f_ty)
273    val f = variant typeletters f0
274  in
275    (pos + 1,
276     mk_eq(list_mk_comb(fldfupd, [f, cons_comb]),
277           list_mk_comb(Term.inst s constructor,
278                        update pos (mk_comb(f, result)) typeletters)) :: acc)
279  end
280  val (_, fupd_def_terms0) = ListPair.foldl mk_fupd_defn (1, [])
281                                            (fupd_terms, typeletters)
282  val fupd_def_terms = List.rev fupd_def_terms0
283  fun mk_defn_th (n, t) =
284      new_recursive_definition {def = t, name = n, rec_axiom = typthm}
285  val fupdfn_thms = ListPair.map mk_defn_th (fupd_names, fupd_def_terms)
286  val fupdfn_thm =
287    save_thm(typename^"_fn_updates", LIST_CONJ fupdfn_thms)
288  val fupdfn_terms =
289      map (fn (s, v) => (s, mk_const (dest_var v))) fupd_terms
290
291
292  (* do cases and induction theorem *)
293  val induction_thm = TypeBasePure.induction_of tyinfo
294  val cases_thm = TypeBasePure.nchotomy_of tyinfo
295
296  fun gen_var_domty(name, tm, avoids) = let
297    val v0 = mk_var(name, #1 (dom_rng (type_of tm)))
298  in
299    variant avoids v0
300  end
301
302  (* do acc of fupdates theorems *)
303  (* i.e. thms of the form
304        (r with fld1 updated by val).fld2 = r.fld2
305     ( ==
306         fld1 (fld2_fupd val r) = fld1 r
307      )
308  *)
309  fun create_goal (acc, (s, fupd)) = let
310    val f = gen_var_domty("f", fupd, [var])
311  in
312    Term`^(Term.inst s acc) (^fupd ^f ^var) = ^acc ^var`
313  end
314  val combinations = crosslessdiag accfn_terms fupdfn_terms
315  val goals = map create_goal combinations
316  fun create_goal (acc, (s, fupd)) = let
317    val f = gen_var_domty("f", fupd, [var])
318  in
319    Term`^(Term.inst s acc) (^fupd ^f ^var) = ^f (^acc ^var)`
320  end
321  val diag_goals = ListPair.map create_goal (accfn_terms, fupdfn_terms)
322  val tactic = STRUCT_CASES_TAC (SPEC var cases_thm) THEN
323    REWRITE_TAC [accessor_thm, fupdfn_thm, combinTheory.o_THM] THEN
324    BETA_TAC THEN REWRITE_TAC []
325  val thms = map (C (curry prove) tactic) (goals @ diag_goals)
326  val accfupd_thm =
327    save_thm(typename^"_accfupds", LIST_CONJ (map GEN_ALL thms))
328
329  fun to_composition t = let
330    val (f, gx) = dest_comb t
331  in
332    if is_comb gx then let
333        val (g, x) = dest_comb gx
334      in
335        SYM (ISPECL [f, g, x] combinTheory.o_THM)
336      end
337    else REFL t
338  end
339  fun o_assoc_munge th = let
340    (* th of form f o g = x; want to produce f o (g o h) = x o h *)
341    val (l, r) = dest_eq (concl th)
342    val l_ty = type_of l
343    val (dom, rng) = dom_rng l_ty
344    val tyvs = map dest_vartype (type_vars_in_term l)
345    val newtyv = mk_vartype(Lexis.gen_variant Lexis.tyvar_vary tyvs "'a")
346    val h = mk_var("h", newtyv --> dom)
347    val new_o = mk_thy_const{Name = "o", Thy = "combin",
348                             Ty = l_ty --> (type_of h --> (newtyv --> rng))}
349    val newth = AP_THM (AP_TERM new_o th) h
350  in
351    REWRITE_RULE [GSYM combinTheory.o_ASSOC] newth
352  end
353
354  fun munge_to_composition thm = let
355    val thm = SPEC_ALL thm
356    val (l, r) = dest_eq (concl thm)
357    val lthm = SYM (to_composition l)
358    val rthm = to_composition r
359    val new_eq = TRANS (TRANS lthm thm) rthm
360    val gnew_eq = EXT (GEN (rand (rand (concl new_eq))) new_eq)
361    val o_assoc_version = o_assoc_munge gnew_eq
362  in
363    CONJ (GEN_ALL gnew_eq) (GEN_ALL o_assoc_version)
364  end
365
366  (* do fupdates of (same) fupdates *)
367  (* i.e., fupd_fld1 f (fupd_fld1 g r) = fupd_fld1 (f o g) r *)
368  fun create_goal (s, fupd) = let
369    val fupdty = type_of fupd
370    val (gty,r) = dom_rng fupdty
371    val (gd, gr) = dom_rng gty
372    val ftys = freshsubst (type_vars fupdty) (map #residue s)
373    val fty = gr --> type_subst ftys gr
374    val f = variant [var] (mk_var("f", fty))
375    val g = variant [var, f] (mk_var("g", gty))
376  in
377    mk_eq(list_mk_comb(inst s (inst ftys fupd),
378                       [f, list_mk_comb(fupd, [g, var])]),
379          list_mk_comb(inst ftys fupd, [combinSyntax.mk_o(f, g), var]))
380  end
381  val goals = map create_goal fupdfn_terms
382  val thms = map (C (curry prove) tactic) goals
383  val fupdfupd_thm =
384      save_thm(typename^"_fupdfupds", LIST_CONJ (map GEN_ALL thms))
385  val fupdfupds_comp_thm =
386      save_thm(typename^"_fupdfupds_comp",
387               LIST_CONJ (map munge_to_composition thms))
388
389  (* do fupdates of (different) fupdates *)
390  val combinations = crossprod fupdfn_terms fupdfn_terms
391  val filterfn = (fn (n,_) => let val m = n - 1
392                                  val d = m div size
393                                  val m = m - (d * size) in
394                                    d > m end)
395  val lower_triangle = nfilter filterfn combinations
396  fun create_goal((s1, f1),(s2, f2)) = let
397    val (f_t, _) = dom_rng (type_of f1)
398    val (g_t, _) = dom_rng (type_of f2)
399    val f = variant [var] (mk_var("f", f_t))
400    val g = variant [var, f] (mk_var("g", g_t))
401  in
402    mk_eq(list_mk_comb(inst s2 f1, [f, list_mk_comb(f2, [g, var])]),
403          list_mk_comb(inst s1 f2, [g, list_mk_comb(f1, [f, var])]))
404  end
405  val goals = map create_goal lower_triangle
406  val fupdcanon_thms = map (C (curry prove) tactic) goals
407  val fupdcanon_thm =
408      if length fupdcanon_thms > 0 then
409        save_thm(typename^"_fupdcanon",
410                 LIST_CONJ (map GEN_ALL fupdcanon_thms))
411      else TRUTH
412  val fupdcanon_comp_thm =
413      if length fupdcanon_thms > 0 then
414        save_thm(typename^"_fupdcanon_comp",
415                 LIST_CONJ (map munge_to_composition fupdcanon_thms))
416      else TRUTH
417
418  val oneone_thm = valOf (TypeBasePure.one_one_of tyinfo)
419
420  (* prove that equality of values is equivalent to equality of fields:
421     i.e. for a record type with two fields:
422        !r1 r2. (r1 = r2) = (r1.fld1 = r2.fld1) /\ (r1.fld2 = r2.fld2)
423  *)
424  local
425    val var1 = mk_var(app_letter typ ^ "1", typ)
426    val var2 = mk_var(app_letter typ ^ "2", typ)
427    val lhs = mk_eq(var1, var2)
428    val rhs_tms =
429      map (fn tm => mk_eq(mk_comb(tm, var1), mk_comb(tm, var2)))
430      accfn_terms
431    val rhs = list_mk_conj rhs_tms
432    val thmname = typename ^ "_component_equality"
433    val goal = mk_eq(lhs, rhs)
434    val tactic =
435      REPEAT GEN_TAC THEN
436      MAP_EVERY (STRUCT_CASES_TAC o C SPEC cases_thm) [var1, var2] THEN
437      REWRITE_TAC [oneone_thm, accessor_thm]
438    val thm0 = prove(goal, tactic)
439  in
440    val component_wise_equality =
441      save_thm(thmname, GENL [var1, var2] thm0)
442  end
443
444  (* prove
445
446     1.  that a complete chain of updates over any value is
447         equivalent to a chain of updates over ARB.  This particular
448         formulation is chosen to help the pretty-printer, which
449         will print such updates over ARB as record literals.
450         e.g.
451             r1 with <| fld1 := v1 ; fld2 := v2 |> =
452             <| fld1 := v1; fld2 := v2 |>
453     2.  prove a version of the nchotomy theorem that uses literal
454         notation rather than the underlying notation involving the
455         constructor
456         e.g.,
457           !r. ?v1 v2 v3. r = <| fld1 := v1; fld2 := v2; fld3 := v3 |>
458
459     3.  a FORALL_RECD theorem of the form
460            (!r. P r) =
461            (!v1 v2 v3. P <| fld1 := v1; fld2 := v2; fld3 := v3 |>)
462
463     4.  likewise, an EXISTS_RECD
464
465     5.  one-one ness for literals
466            (<| fld1 := v11; fld2 := v21; fld3 := v31 |> =
467             <| fld1 := v12; fld2 := v22; fld3 := v32 |>) =
468            (v11 = v12) /\ (v21 = v22) /\ (v31 = v32)
469  *)
470  local
471    fun mk_var_avds (nm, ty, avoids) = let
472      val v0 = mk_var(nm, ty)
473    in
474      variant avoids v0
475    end
476
477    fun rng_of_dom ty = ty |> dom_rng |> #1 |> dom_rng |> #2
478    fun dom_of_dom ty = ty |> dom_rng |> #1 |> dom_rng |> #1
479    val value_vars =
480        List.foldr
481          (fn ((_,updt), sofar) =>
482              let val ty = rng_of_dom (type_of updt)
483              in
484                mk_var_avds(app_letter ty, ty, var::sofar)::sofar
485              end)
486          [var] fupdfn_terms |> (fn l => List.take(l, length fields))
487    fun augvar n v = let
488      val (nm, ty) = dest_var v
489    in
490      mk_var(nm ^ Int.toString n, ty)
491    end
492    val vvars1 = map (augvar 1) value_vars
493    val vvars2 = map (augvar 2) value_vars
494    val arb = mk_arb typ
495    fun foldthis ((s,upd),v,(s0,acc)) = let
496      val ty = type_of v
497      val K = Term.inst [alpha |-> ty, beta |-> dom_of_dom (type_of upd)] K_tm
498    in
499      (s @ s0,mk_comb(mk_comb(inst s0 upd, mk_comb(K, v)), acc))
500    end
501    val (_, lhs) = ListPair.foldr foldthis ([], var) (fupdfn_terms, value_vars)
502    val (_, rhs) = ListPair.foldr foldthis ([], arb) (fupdfn_terms, value_vars)
503
504    val (_, lit1) = ListPair.foldr foldthis ([], arb) (fupdfn_terms, vvars1)
505    val (_, lit2) = ListPair.foldr foldthis ([], arb) (fupdfn_terms, vvars2)
506
507    val literal_equality =
508        GENL (var::value_vars)
509             (prove(mk_eq(lhs, rhs),
510                    MAP_EVERY (STRUCT_CASES_TAC o
511                               C SPEC cases_thm) [arb, var] THEN
512                    REWRITE_TAC [fupdfn_thm, combinTheory.K_THM]))
513
514    val var' = inst tysigma var
515    val typ' = type_of var'
516    val literal_nchotomy =
517        GEN var'
518            (prove(list_mk_exists(value_vars, mk_eq(var', rhs)),
519                   MAP_EVERY (STRUCT_CASES_TAC o C ISPEC cases_thm)
520                             [arb, var'] THEN
521                             REWRITE_TAC [fupdfn_thm, accessor_thm, oneone_thm,
522                                          combinTheory.K_THM] THEN
523                             REPEAT Unwind.UNWIND_EXISTS_TAC))
524
525    val pred_r = mk_var_avds("P", typ' --> bool, var::value_vars)
526    val P_r = mk_comb(pred_r, var')
527    val P_literal = mk_comb(pred_r, rhs)
528    val forall_goal = mk_eq(mk_forall(var', P_r),
529                            list_mk_forall(value_vars, P_literal))
530    val exists_goal = mk_eq(mk_exists(var', P_r),
531                            list_mk_exists(value_vars, P_literal))
532    val forall_thm =
533        GEN_ALL
534          (prove(forall_goal,
535                 EQ_TAC THEN STRIP_TAC THEN ASM_REWRITE_TAC [] THEN
536                 X_GEN_TAC var' THEN
537                 STRUCT_CASES_TAC (ISPEC var' literal_nchotomy) THEN
538                 ASM_REWRITE_TAC []))
539    val exists_thm =
540        GEN_ALL
541        (prove(exists_goal,
542               EQ_TAC THENL [
543                 DISCH_THEN (X_CHOOSE_THEN var' ASSUME_TAC) THEN
544                 EVERY_TCL (map X_CHOOSE_THEN value_vars)
545                           SUBST_ALL_TAC (ISPEC var' literal_nchotomy) THEN
546                 MAP_EVERY EXISTS_TAC value_vars THEN ASM_REWRITE_TAC [],
547                 DISCH_THEN (EVERY_TCL (map X_CHOOSE_THEN value_vars)
548                                       ASSUME_TAC) THEN
549                 EXISTS_TAC rhs THEN ASM_REWRITE_TAC []
550               ]))
551
552    val literal_11 =
553        GENL (vvars1 @ vvars2)
554             (REWRITE_RULE [accfupd_thm, combinTheory.K_THM]
555                           (ISPECL [lit1, lit2] component_wise_equality))
556
557  in
558    val literal_equality =
559        save_thm(typename ^ "_updates_eq_literal", literal_equality)
560    val literal_nchotomy =
561        save_thm(typename ^ "_literal_nchotomy", literal_nchotomy)
562    val forall_thm = save_thm("FORALL_" ^ typename, forall_thm)
563    val exists_thm = save_thm("EXISTS_"^ typename, exists_thm)
564    val literal_11 = save_thm(typename ^ "_literal_11", literal_11)
565  end
566
567  (* add to the TypeBase's simpls entry for the record type *)
568  val new_simpls = let
569    val new_simpls0 =  [accessor_thm, accfupd_thm,
570                        literal_equality, literal_11, fupdfupd_thm,
571                        fupdfupds_comp_thm]
572  in
573    if not (null fupdcanon_thms) then
574      fupdcanon_thm :: fupdcanon_comp_thm :: new_simpls0
575    else new_simpls0
576  end
577  val new_tyinfo =
578       update_tyinfo (SOME (zip fields types,access_defns,fupdfn_thms))
579                     new_simpls tyinfo
580
581  (* set up parsing for the record type *)
582  val brss = GrammarSpecials.bigrec_subdivider_string
583  val _ =
584      if List.exists (is_substring brss) (typename :: fields) then
585        ()
586      else let
587          fun do_fupdfn (name0, (_, tm)) =
588            let val name = name0 ^ "_fupd"
589            in
590              Parse.overload_on(name, tm)
591            end
592        in
593          ListPair.app add_record_field (fields, accfn_terms);
594          (* overload strings of the form fld_fupd to refer to the
595             real fupdate functions, which have names of the form
596             type_fld_fupd.  Make sure that this overloading is
597             done before the add_record_fupdate function is called
598             because this will also overload this constant to the
599             "artificial" constant for special printing of record
600             syntax, and we want this to be preferred where
601             possible. *)
602          ListPair.app do_fupdfn (fields, fupdfn_terms);
603          ListPair.app (fn (n,(_,t)) => add_record_fupdate(n,t))
604                       (fields, fupdfn_terms);
605          Parse.overload_on(typename, constructor)
606        end
607in
608  new_tyinfo
609end
610
611end (* struct *)
612