1
(**************************************************************************)
3
(* Copyright (C) 2001-2003, *)
4
(* George C. Necula <necula@cs.berkeley.edu> *)
5
(* Scott McPeak <smcpeak@cs.berkeley.edu> *)
6
(* Wes Weimer <weimer@cs.berkeley.edu> *)
7
(* Ben Liblit <liblit@cs.berkeley.edu> *)
8
(* All rights reserved. *)
10
(* Redistribution and use in source and binary forms, with or without *)
11
(* modification, are permitted provided that the following conditions *)
14
(* 1. Redistributions of source code must retain the above copyright *)
15
(* notice, this list of conditions and the following disclaimer. *)
17
(* 2. Redistributions in binary form must reproduce the above copyright *)
18
(* notice, this list of conditions and the following disclaimer in the *)
19
(* documentation and/or other materials provided with the distribution. *)
21
(* 3. The names of the contributors may not be used to endorse or *)
22
(* promote products derived from this software without specific prior *)
23
(* written permission. *)
25
(* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *)
26
(* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *)
27
(* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *)
28
(* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *)
29
(* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, *)
30
(* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, *)
31
(* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; *)
32
(* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER *)
33
(* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT *)
34
(* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN *)
35
(* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *)
36
(* POSSIBILITY OF SUCH DAMAGE. *)
38
(* File modified by CEA (Commissariat ļæ½ l'ļæ½nergie Atomique). *)
39
(**************************************************************************)
43
(* CABS file patching *)
52
(* binding of a unification variable to a syntactic construct *)
54
| BSpecifier of string * spec_elem list
55
| BName of string * string
56
| BExpr of string * expression
58
(* thrown when unification fails *)
61
(* thrown when an attempt to find the associated binding fails *)
62
exception BadBind of string
64
(* trying to isolate performance problems; will hide all the *)
65
(* potentially expensive debugging output behind "if verbose .." *)
66
let verbose : bool = true
69
(* raise NoMatch if x and y are not equal *)
70
let mustEq (x : 'a) (y : 'a) : unit =
74
(trace "patchDebug" (dprintf "mismatch by structural disequality\n"));
79
(* why isn't this in the core Ocaml library? *)
83
let isPatternVar (s : string) : bool =
85
((String.length s) >= 1) && ((String.get s 0) = '@')
88
(* 's' is actually "@name(blah)"; extract the 'blah' *)
89
let extractPatternVar (s : string) : string =
90
(*(trace "patch" (dprintf "extractPatternVar %s\n" s));*)
91
(String.sub s 6 ((String.length s) - 7))
94
(* a few debugging printers.. *)
95
let printExpr (e : expression) =
97
if (verbose && traceActive "patchDebug") then (
98
Cprint.print_expression e; Cprint.force_new_line ();
103
let printSpec (spec: spec_elem list) =
105
if (verbose && traceActive "patchDebug") then (
106
Cprint.print_specifiers spec; Cprint.force_new_line ();
111
let printSpecs (pat : spec_elem list) (tgt : spec_elem list) =
117
let printDecl (pat : name) (tgt : name) =
119
if (verbose && traceActive "patchDebug") then (
120
Cprint.print_name pat; Cprint.force_new_line ();
121
Cprint.print_name tgt; Cprint.force_new_line ();
126
let printDeclType (pat : decl_type) (tgt : decl_type) =
128
if (verbose && traceActive "patchDebug") then (
129
Cprint.print_decl "__missing_field_name" pat; Cprint.force_new_line ();
130
Cprint.print_decl "__missing_field_name" tgt; Cprint.force_new_line ();
135
let printDefn (d : definition) =
137
if (verbose && traceActive "patchDebug") then (
144
(* class to describe how to modify the tree for subtitution *)
145
class substitutor (bindings : binding list) = object(self)
146
inherit nopCabsVisitor as super
148
(* look in the binding list for a given name *)
149
method findBinding (name : string) : binding =
155
| BSpecifier(n, _) -> n=name
156
| BName(n, _) -> n=name
157
| BExpr(n, _) -> n=name)
160
Not_found -> raise (BadBind ("name not found: " ^ name))
163
method vexpr (e:expression) : expression visitAction =
166
| EXPR_PATTERN(name) -> (
167
match (self#findBinding name) with
168
| BExpr(_, expr) -> ChangeTo(expr) (* substitute bound expression *)
169
| _ -> raise (BadBind ("wrong type: " ^ name))
175
method vvar (s:string) : string =
177
if (isPatternVar s) then (
178
let nameString = (extractPatternVar s) in
179
match (self#findBinding nameString) with
180
| BName(_, str) -> str (* substitute *)
181
| _ -> raise (BadBind ("wrong type: " ^ nameString))
187
(* binding introduction of a name *)
188
method vname (_k: nameKind) (_spec: specifier) (n: name) : name visitAction =
190
match n with (s (*variable name*), dtype, attrs, loc) -> (
191
let replacement = (self#vvar s) in (* use replacer from above *)
192
if (s <> replacement) then
193
ChangeTo(replacement, dtype, attrs, loc)
195
DoChildren (* no replacement *)
199
method vspec (specList: specifier) : specifier visitAction =
201
if verbose then (trace "patchDebug" (dprintf "substitutor: vspec\n"));
202
(printSpec specList);
204
(* are any of the specifiers SpecPatterns? we have to check the entire *)
205
(* list, not just the head, because e.g. "typedef @specifier(foo)" has *)
206
(* "typedef" as the head of the specifier list *)
207
if (List.exists (fun elt -> match elt with
208
| SpecPattern(_) -> true
211
(* yes, replace the existing list with one got by *)
212
(* replacing all occurrences of SpecPatterns *)
213
(trace "patchDebug" (dprintf "at least one spec pattern\n"));
217
(* for each specifier element, yield the specifier list *)
218
(* to which it maps; then we'll flatten the final result *)
221
| SpecPattern(name) -> (
222
match (self#findBinding name) with
223
| BSpecifier(_, replacement) -> (
224
(trace "patchDebug" (dprintf "replacing pattern %s\n" name));
227
| _ -> raise (BadBind ("wrong type: " ^ name))
229
| _ -> [elt] (* leave this one alone *)
236
(* none of the specifiers in specList are patterns *)
240
method vtypespec (tspec: typeSpecifier) : typeSpecifier visitAction =
243
| Tnamed(str) when (isPatternVar str) ->
244
ChangeTo(Tnamed(self#vvar str))
245
| Tstruct(str, fields, extraAttrs) when (isPatternVar str) -> (
246
(trace "patchDebug" (dprintf "substituting %s\n" str));
247
ChangeDoChildrenPost(Tstruct((self#vvar str), fields, extraAttrs), identity)
249
| Tunion(str, fields, extraAttrs) when (isPatternVar str) ->
250
(trace "patchDebug" (dprintf "substituting %s\n" str));
251
ChangeDoChildrenPost(Tunion((self#vvar str), fields, extraAttrs), identity)
258
(* why can't I have forward declarations in the language?!! *)
259
let unifyExprFwd : (expression -> expression -> binding list) ref
260
= ref (fun _e _e -> [])
263
(* substitution for expressions *)
264
let substExpr (bindings : binding list) (expr : expression) : expression =
267
(trace "patchDebug" (dprintf "substExpr with %d bindings\n" (List.length bindings)));
270
(* apply the transformation *)
271
let result = (visitCabsExpression (new substitutor bindings :> cabsVisitor) expr) in
277
let d_loc (_:unit) (loc: cabsloc) : doc =
278
text (fst loc).Lexing.pos_fname ++ chr ':' ++ num (fst loc).Lexing.pos_lnum
281
(* class to describe how to modify the tree when looking for places *)
282
(* to apply expression transformers *)
283
class exprTransformer (srcpattern : expression) (destpattern : expression)
284
(patchline : int) (srcloc : cabsloc) = object
285
inherit nopCabsVisitor as super
287
method vexpr (e:expression) : expression visitAction =
289
(* see if the source pattern matches this subexpression *)
291
let bindings = (!unifyExprFwd srcpattern e) in
294
(trace "patch" (dprintf "expr match: patch line %d, src %a\n"
295
patchline d_loc srcloc));
296
ChangeTo(substExpr bindings destpattern)
305
(* other constructs left unchanged *)
309
let unifyList (pat : 'a list) (tgt : 'a list)
310
(unifyElement : 'a -> 'a -> binding list) : binding list =
313
(trace "patchDebug" (dprintf "unifyList (pat len %d, tgt len %d)\n"
314
(List.length pat) (List.length tgt)));
316
(* walk down the lists *)
317
let rec loop pat tgt : binding list =
320
| (pelt :: prest), (telt :: trest) ->
321
(unifyElement pelt telt) @
326
(trace "patchDebug" (dprintf "mismatching list length\n"));
335
let gettime () : float =
336
(Unix.times ()).Unix.tms_utime
338
let rec applyPatch (patchFile : file) (srcFile : file) : file =
340
let patch : definition list = List.map snd (snd patchFile) in
341
let srcFname : string = (fst srcFile) in
342
let src : definition list = List.map snd (snd srcFile) in
344
(trace "patchTime" (dprintf "applyPatch start: %f\n" (gettime ())));
345
if (traceActive "patchDebug") then
346
Cprint.out := stdout (* hack *)
350
unifyExprFwd := unifyExpr;
352
(* patch a single source definition, yield transformed *)
353
let rec patchDefn (patch : definition list) (d : definition) : definition list =
356
| TRANSFORMER(srcpattern, destpattern, loc) :: rest -> (
359
(dprintf "considering applying defn pattern at line %d to src at %a\n"
360
(fst loc).Lexing.pos_lnum d_loc (get_definitionloc d)));
362
(* see if the source pattern matches the definition 'd' we have *)
364
let bindings = (unifyDefn srcpattern d) in
366
(* we have a match! apply the substitutions *)
367
(trace "patch" (dprintf "defn match: patch line %d, src %a\n"
368
(fst loc).Lexing.pos_lnum d_loc (get_definitionloc d)));
370
(List.map (fun destElt -> (substDefn bindings destElt)) destpattern)
374
(* no match, continue down list *)
375
(*(trace "patch" (dprintf "no match\n"));*)
380
| EXPRTRANSFORMER(srcpattern, destpattern, loc) :: rest -> (
383
(dprintf "considering applying expr pattern at line %d to src at %a\n"
384
(fst loc).Lexing.pos_lnum d_loc (get_definitionloc d)));
386
(* walk around in 'd' looking for expressions to modify *)
387
let dList = (visitCabsDefinition
388
((new exprTransformer srcpattern destpattern
389
(fst loc).Lexing.pos_lnum (get_definitionloc d))
394
(* recursively invoke myself to try additional patches *)
395
(* since visitCabsDefinition might return a list, I'll try my *)
396
(* addtional patches on every yielded definition, then collapse *)
397
(* all of them into a single list *)
398
(List.flatten (List.map (fun d -> (patchDefn rest d)) dList))
402
(* not a transformer; just keep going *)
406
(* reached the end of the patch file with no match *)
407
[d] (* have to wrap it in a list ... *)
411
(* transform all the definitions *)
412
let result : definition list =
413
(List.flatten (List.map (fun d -> (patchDefn patch d)) src)) in
415
(*Cprint.print_defs result;*)
417
if (traceActive "patchDebug") then (
418
(* avoid flush bug? yes *)
419
Cprint.force_new_line ();
423
(trace "patchTime" (dprintf "applyPatch finish: %f\n" (gettime ())));
424
(srcFname, List.map (fun x -> false,x) result)
428
(* given a definition pattern 'pat', and a target concrete defintion 'tgt', *)
429
(* determine if they can be unified; if so, return the list of bindings of *)
430
(* unification variables in pat; otherwise raise NoMatch *)
431
and unifyDefn (pat : definition) (tgt : definition) : binding list =
434
| DECDEF(_,(pspecifiers, pdeclarators), _),
435
DECDEF(_,(tspecifiers, tdeclarators), _) -> (
437
(trace "patchDebug" (dprintf "unifyDefn of DECDEFs\n"));
438
(unifySpecifiers pspecifiers tspecifiers) @
439
(unifyInitDeclarators pdeclarators tdeclarators)
442
| TYPEDEF((pspec, pdecl), _),
443
TYPEDEF((tspec, tdecl), _) -> (
445
(trace "patchDebug" (dprintf "unifyDefn of TYPEDEFs\n"));
446
(unifySpecifiers pspec tspec) @
447
(unifyDeclarators pdecl tdecl)
450
| ONLYTYPEDEF(pspec, _),
451
ONLYTYPEDEF(tspec, _) -> (
453
(trace "patchDebug" (dprintf "unifyDefn of ONLYTYPEDEFs\n"));
454
(unifySpecifiers pspec tspec)
459
(trace "patchDebug" (dprintf "mismatching definitions\n"));
464
and unifySpecifier (pat : spec_elem) (tgt : spec_elem) : binding list =
467
(trace "patchDebug" (dprintf "unifySpecifier\n"));
468
(printSpecs [pat] [tgt]);
470
if (pat = tgt) then [] else
473
| SpecType(tspec1), SpecType(tspec2) ->
474
(unifyTypeSpecifier tspec1 tspec2)
475
| SpecPattern(name), _ ->
476
(* record that future occurrances of @specifier(name) will yield this specifier *)
478
(trace "patchDebug" (dprintf "found specifier match for %s\n" name));
479
[BSpecifier(name, [tgt])]
483
(trace "patchDebug" (dprintf "mismatching specifiers\n"));
489
and unifySpecifiers (pat : spec_elem list) (tgt : spec_elem list) : binding list =
492
(trace "patchDebug" (dprintf "unifySpecifiers\n"));
493
(printSpecs pat tgt);
495
(* canonicalize the specifiers by sorting them *)
496
let pat' = (List.stable_sort compare pat) in
497
let tgt' = (List.stable_sort compare tgt) in
499
(* if they are equal, they match with no further checking *)
500
if (pat' = tgt') then [] else
502
(* walk down the lists; don't walk the sorted lists because the *)
503
(* pattern must always be last, if it occurs *)
504
let rec loop pat tgt : binding list =
507
| [SpecPattern(name)], _ ->
508
(* final SpecPattern matches anything which comes after *)
509
(* record that future occurrences of @specifier(name) will yield this specifier *)
511
(trace "patchDebug" (dprintf "found specifier match for %s\n" name));
512
[BSpecifier(name, tgt)]
513
| (pspec :: prest), (tspec :: trest) ->
514
(unifySpecifier pspec tspec) @
519
(trace "patchDebug" (dprintf "mismatching specifier list length\n"));
527
and unifyTypeSpecifier (pat: typeSpecifier) (tgt: typeSpecifier) : binding list =
530
(trace "patchDebug" (dprintf "unifyTypeSpecifier\n"));
532
if (pat = tgt) then [] else
535
| Tnamed(s1), Tnamed(s2) -> (unifyString s1 s2)
536
| Tstruct(name1, None, _), Tstruct(name2, None, _) ->
537
(unifyString name1 name2)
538
| Tstruct(name1, Some(fields1), _), Tstruct(name2, Some(fields2), _) ->
539
(* ignoring extraAttrs b/c we're just trying to come up with a list
540
* of substitutions, and there's no unify_attributes function, and
541
* I don't care at this time about checking that they are equal .. *)
542
(unifyString name1 name2) @
543
(unifyList fields1 fields2 unifyField)
544
| Tunion(name1, None, _), Tstruct(name2, None, _) ->
545
(unifyString name1 name2)
546
| Tunion(name1, Some(fields1), _), Tunion(name2, Some(fields2), _) ->
547
(unifyString name1 name2) @
548
(unifyList fields1 fields2 unifyField)
549
| Tenum(name1, None, _), Tenum(name2, None, _) ->
550
(unifyString name1 name2)
551
| Tenum(name1, Some(items1), _), Tenum(name2, Some(items2), _) ->
552
(mustEq items1 items2); (* enum items *)
553
(unifyString name1 name2)
554
| TtypeofE(exp1), TtypeofE(exp2) ->
555
(unifyExpr exp1 exp2)
556
| TtypeofT(spec1, dtype1), TtypeofT(spec2, dtype2) ->
557
(unifySpecifiers spec1 spec2) @
558
(unifyDeclType dtype1 dtype2)
560
if verbose then (trace "patchDebug" (dprintf "mismatching typeSpecifiers\n"));
565
and unifyField (pat : field_group) (tgt : field_group) : binding list =
567
match pat,tgt with FIELD (spec1, list1), FIELD (spec2, list2) -> (
568
(unifySpecifiers spec1 spec2) @
569
(unifyList list1 list2 unifyNameExprOpt)
574
(trace "patchDebug" (dprintf "mismatching during type annotation\n"));
579
and unifyNameExprOpt (pat : name * expression option)
580
(tgt : name * expression option) : binding list =
583
| (name1, None), (name2, None) -> (unifyName name1 name2)
584
| (name1, Some(exp1)), (name2, Some(exp2)) ->
585
(unifyName name1 name2) @
586
(unifyExpr exp1 exp2)
590
and unifyName (pat : name) (tgt : name) : binding list =
592
match pat,tgt with (pstr, pdtype, pattrs, _ploc), (tstr, tdtype, tattrs, _tloc) ->
593
(mustEq pattrs tattrs);
594
(unifyString pstr tstr) @
595
(unifyDeclType pdtype tdtype)
598
and unifyInitDeclarators (pat : init_name list) (tgt : init_name list) : binding list =
602
(trace "patchDebug" (dprintf "unifyInitDeclarators, pat %d, tgt %d\n"
603
(List.length pat) (List.length tgt)));
607
| ((pdecl, piexpr) :: prest),
608
((tdecl, tiexpr) :: trest) ->
609
(unifyDeclarator pdecl tdecl) @
610
(unifyInitExpr piexpr tiexpr) @
611
(unifyInitDeclarators prest trest)
615
(trace "patchDebug" (dprintf "mismatching init declarators\n"));
620
and unifyDeclarators (pat : name list) (tgt : name list) : binding list =
621
(unifyList pat tgt unifyDeclarator)
623
and unifyDeclarator (pat : name) (tgt : name) : binding list =
626
(trace "patchDebug" (dprintf "unifyDeclarator\n"));
630
| (pname, pdtype, pattr, _ploc),
631
(tname, tdtype, tattr, _tloc) ->
632
(mustEq pattr tattr);
633
(unifyDeclType pdtype tdtype) @
634
(unifyString pname tname)
637
and unifyDeclType (pat : decl_type) (tgt : decl_type) : binding list =
640
(trace "patchDebug" (dprintf "unifyDeclType\n"));
641
(printDeclType pat tgt);
644
| JUSTBASE, JUSTBASE -> []
645
| PARENTYPE(pattr1, ptype, pattr2),
646
PARENTYPE(tattr1, ttype, tattr2) ->
647
(mustEq pattr1 tattr1);
648
(mustEq pattr2 tattr2);
649
(unifyDeclType ptype ttype)
650
| ARRAY(ptype, pattr, psz),
651
ARRAY(ttype, tattr, tsz) ->
652
(mustEq pattr tattr);
653
(unifyDeclType ptype ttype) @
657
(mustEq pattr tattr);
658
(unifyDeclType ptype ttype)
659
| PROTO(ptype, pformals, pva),
660
PROTO(ttype, tformals, tva) ->
662
(unifyDeclType ptype ttype) @
663
(unifySingleNames pformals tformals)
666
(trace "patchDebug" (dprintf "mismatching decl_types\n"));
671
and unifySingleNames (pat : single_name list) (tgt : single_name list) : binding list =
674
(trace "patchDebug" (dprintf "unifySingleNames, pat %d, tgt %d\n"
675
(List.length pat) (List.length tgt)));
679
| (pspec, pdecl) :: prest,
680
(tspec, tdecl) :: trest ->
681
(unifySpecifiers pspec tspec) @
682
(unifyDeclarator pdecl tdecl) @
683
(unifySingleNames prest trest)
686
(trace "patchDebug" (dprintf "mismatching single_name lists\n"));
691
and unifyString (pat : string) (tgt : string) : binding list =
693
(* equal? match with no further ado *)
694
if (pat = tgt) then [] else
696
(* is the pattern a variable? *)
697
if (isPatternVar pat) then
698
(* pat is actually "@name(blah)"; extract the 'blah' *)
699
let varname = (extractPatternVar pat) in
701
(* when substituted, this name becomes 'tgt' *)
703
(trace "patchDebug" (dprintf "found name match for %s\n" varname));
704
[BName(varname, tgt)]
708
(trace "patchDebug" (dprintf "mismatching names: %s and %s\n" pat tgt));
713
and unifyExpr (pat : expression) (tgt : expression) : binding list =
715
(* if they're equal, that's good enough *)
716
if (pat = tgt) then [] else
719
let ue = unifyExpr in
721
(* because of the equality check above, I can omit some cases *)
727
| BINARY(pop, pexp1, pexp2),
728
BINARY(top, texp1, texp2) ->
732
| QUESTION(p1, p2, p3),
733
QUESTION(t1, t2, t3) ->
737
| CAST((pspec, ptype), piexpr),
738
CAST((tspec, ttype), tiexpr) ->
739
(mustEq ptype ttype);
740
(unifySpecifiers pspec tspec) @
741
(unifyInitExpr piexpr tiexpr)
742
| CALL(pfunc, pargs),
743
CALL(tfunc, targs) ->
745
(unifyExprs pargs targs)
748
(unifyExprs pexprs texprs)
749
| EXPR_SIZEOF(pexpr),
750
EXPR_SIZEOF(texpr) ->
752
| TYPE_SIZEOF(pspec, ptype),
753
TYPE_SIZEOF(tspec, ttype) ->
754
(mustEq ptype ttype);
755
(unifySpecifiers pspec tspec)
756
| EXPR_ALIGNOF(pexpr),
757
EXPR_ALIGNOF(texpr) ->
759
| TYPE_ALIGNOF(pspec, ptype),
760
TYPE_ALIGNOF(tspec, ttype) ->
761
(mustEq ptype ttype);
762
(unifySpecifiers pspec tspec)
763
| INDEX(parr, pindex),
764
INDEX(tarr, tindex) ->
767
| MEMBEROF(pexpr, pfield),
768
MEMBEROF(texpr, tfield) ->
769
(mustEq pfield tfield);
771
| MEMBEROFPTR(pexpr, pfield),
772
MEMBEROFPTR(texpr, tfield) ->
773
(mustEq pfield tfield);
777
(mustEq pblock tblock);
779
| EXPR_PATTERN(name), _ ->
780
(* match, and contribute binding *)
782
(trace "patchDebug" (dprintf "found expr match for %s\n" name));
785
if (verbose && traceActive "patchDebug") then (
786
(trace "patchDebug" (dprintf "mismatching expression\n"));
793
and unifyInitExpr (pat : init_expression) (tgt : init_expression) : binding list =
796
Cprint.print_init_expression pat; Cprint.force_new_line ();
797
Cprint.print_init_expression tgt; Cprint.force_new_line ();
802
| NO_INIT, NO_INIT -> []
803
| SINGLE_INIT(pe), SINGLE_INIT(te) ->
805
| COMPOUND_INIT(plist),
806
COMPOUND_INIT(tlist) -> (
807
let rec loop plist tlist =
808
match plist, tlist with
809
| ((pwhat, piexpr) :: prest),
810
((twhat, tiexpr) :: trest) ->
811
(mustEq pwhat twhat);
812
(unifyInitExpr piexpr tiexpr) @
817
(trace "patchDebug" (dprintf "mismatching compound init exprs\n"));
825
(trace "patchDebug" (dprintf "mismatching init exprs\n"));
830
and unifyExprs (pat : expression list) (tgt : expression list) : binding list =
831
(unifyList pat tgt unifyExpr)
834
(* given the list of bindings 'b', substitute them into 'd' to yield a new definition *)
835
and substDefn (bindings : binding list) (defn : definition) : definition =
838
(trace "patchDebug" (dprintf "substDefn with %d bindings\n" (List.length bindings)));
841
(* apply the transformation *)
842
match (visitCabsDefinition (new substitutor bindings :> cabsVisitor) defn) with
843
| [d] -> d (* expect a singleton list *)
844
| _ -> (failwith "didn't get a singleton list where I expected one")