summaryrefslogtreecommitdiff
path: root/cil/src/check.ml
diff options
context:
space:
mode:
Diffstat (limited to 'cil/src/check.ml')
-rw-r--r--cil/src/check.ml1017
1 files changed, 0 insertions, 1017 deletions
diff --git a/cil/src/check.ml b/cil/src/check.ml
deleted file mode 100644
index 4dc8850..0000000
--- a/cil/src/check.ml
+++ /dev/null
@@ -1,1017 +0,0 @@
-(* MODIF: Loop constructor replaced by 3 constructors: While, DoWhile, For. *)
-
-(*
- *
- * Copyright (c) 2001-2002,
- * George C. Necula <necula@cs.berkeley.edu>
- * Scott McPeak <smcpeak@cs.berkeley.edu>
- * Wes Weimer <weimer@cs.berkeley.edu>
- * All rights reserved.
- *
- * 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.
- *
- * 3. The names of the contributors may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER
- * 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.
- *
- *)
-
-(* A consistency checker for CIL *)
-open Cil
-module E = Errormsg
-module H = Hashtbl
-open Pretty
-
-
-(* A few parameters to customize the checking *)
-type checkFlags =
- NoCheckGlobalIds (* Do not check that the global ids have the proper
- * hash value *)
-
-let checkGlobalIds = ref true
-
- (* Attributes must be sorted *)
-type ctxAttr =
- CALocal (* Attribute of a local variable *)
- | CAGlobal (* Attribute of a global variable *)
- | CAType (* Attribute of a type *)
-
-let valid = ref true
-
-let warn fmt =
- valid := false;
- Cil.warn fmt
-
-let warnContext fmt =
- valid := false;
- Cil.warnContext fmt
-
-let checkAttributes (attrs: attribute list) : unit =
- let rec loop lastname = function
- [] -> ()
- | Attr(an, _) :: resta ->
- if an < lastname then
- ignore (warn "Attributes not sorted");
- loop an resta
- in
- loop "" attrs
-
-
- (* Keep track of defined types *)
-let typeDefs : (string, typ) H.t = H.create 117
-
-
- (* Keep track of all variables names, enum tags and type names *)
-let varNamesEnv : (string, unit) H.t = H.create 117
-
- (* We also keep a map of variables indexed by id, to ensure that only one
- * varinfo has a given id *)
-let varIdsEnv: (int, varinfo) H.t = H.create 117
-
- (* And keep track of all varinfo's to check the uniqueness of the
- * identifiers *)
-let allVarIds: (int, varinfo) H.t = H.create 117
-
- (* Also keep a list of environments. We place an empty string in the list to
- * mark the start of a local environment (i.e. a function) *)
-let varNamesList : (string * int) list ref = ref []
-let defineName s =
- if s = "" then
- E.s (bug "Empty name\n");
- if H.mem varNamesEnv s then
- ignore (warn "Multiple definitions for %s\n" s);
- H.add varNamesEnv s ()
-
-let defineVariable vi =
- defineName vi.vname;
- varNamesList := (vi.vname, vi.vid) :: !varNamesList;
- (* Check the id *)
- if H.mem allVarIds vi.vid then
- ignore (warn "Id %d is already defined (%s)\n" vi.vid vi.vname);
- H.add allVarIds vi.vid vi;
- (* And register it in the current scope also *)
- H.add varIdsEnv vi.vid vi
-
-(* Check that a varinfo has already been registered *)
-let checkVariable vi =
- try
- (* Check in the current scope only *)
- if vi != H.find varIdsEnv vi.vid then
- ignore (warnContext "varinfos for %s not shared\n" vi.vname);
- with Not_found ->
- ignore (warn "Unknown id (%d) for %s\n" vi.vid vi.vname)
-
-
-let startEnv () =
- varNamesList := ("", -1) :: !varNamesList
-
-let endEnv () =
- let rec loop = function
- [] -> E.s (bug "Cannot find start of env")
- | ("", _) :: rest -> varNamesList := rest
- | (s, id) :: rest -> begin
- H.remove varNamesEnv s;
- H.remove varIdsEnv id;
- loop rest
- end
- in
- loop !varNamesList
-
-
-
-(* The current function being checked *)
-let currentReturnType : typ ref = ref voidType
-
-(* A map of labels in the current function *)
-let labels: (string, unit) H.t = H.create 17
-
-(* A list of statements seen in the current function *)
-let statements: stmt list ref = ref []
-
-(* A list of the targets of Gotos *)
-let gotoTargets: (string * stmt) list ref = ref []
-
-(*** TYPES ***)
-(* Cetain types can only occur in some contexts, so keep a list of context *)
-type ctxType =
- CTStruct (* In a composite type *)
- | CTUnion
- | CTFArg (* In a function argument type *)
- | CTFRes (* In a function result type *)
- | CTArray (* In an array type *)
- | CTPtr (* In a pointer type *)
- | CTExp (* In an expression, as the type of
- * the result of binary operators, or
- * in a cast *)
- | CTSizeof (* In a sizeof *)
- | CTDecl (* In a typedef, or a declaration *)
-
-let d_context () = function
- CTStruct -> text "CTStruct"
- | CTUnion -> text "CTUnion"
- | CTFArg -> text "CTFArg"
- | CTFRes -> text "CTFRes"
- | CTArray -> text "CTArray"
- | CTPtr -> text "CTPtr"
- | CTExp -> text "CTExp"
- | CTSizeof -> text "CTSizeof"
- | CTDecl -> text "CTDecl"
-
-
-(* Keep track of all tags that we use. For each tag remember also the info
- * structure and a flag whether it was actually defined or just used. A
- * forward declaration acts as a definition. *)
-type defuse =
- Defined (* We actually have seen a definition of this tag *)
- | Forward (* We have seen a forward declaration for it. This is done using
- * a GType with an empty type name *)
- | Used (* Only uses *)
-let compUsed : (int, compinfo * defuse ref) H.t = H.create 117
-let enumUsed : (string, enuminfo * defuse ref) H.t = H.create 117
-let typUsed : (string, typeinfo * defuse ref) H.t = H.create 117
-
-(* For composite types we also check that the names are unique *)
-let compNames : (string, unit) H.t = H.create 17
-
-
- (* Check a type *)
-let rec checkType (t: typ) (ctx: ctxType) =
- (* Check that it appears in the right context *)
- let rec checkContext = function
- TVoid _ -> ctx = CTPtr || ctx = CTFRes || ctx = CTDecl
- | TNamed (ti, a) -> checkContext ti.ttype
- | TArray _ ->
- (ctx = CTStruct || ctx = CTUnion
- || ctx = CTSizeof || ctx = CTDecl || ctx = CTArray || ctx = CTPtr)
- | TComp _ -> ctx <> CTExp
- | _ -> true
- in
- if not (checkContext t) then
- ignore (warn "Type (%a) used in wrong context. Expected context: %a"
- d_plaintype t d_context ctx);
- match t with
- (TVoid a | TBuiltin_va_list a) -> checkAttributes a
- | TInt (ik, a) -> checkAttributes a
- | TFloat (_, a) -> checkAttributes a
- | TPtr (t, a) -> checkAttributes a; checkType t CTPtr
-
- | TNamed (ti, a) ->
- checkAttributes a;
- if ti.tname = "" then
- ignore (warnContext "Using a typeinfo for an empty-named type\n");
- checkTypeInfo Used ti
-
- | TComp (comp, a) ->
- checkAttributes a;
- (* Mark it as a forward. We'll check it later. If we try to check it
- * now we might encounter undefined types *)
- checkCompInfo Used comp
-
-
- | TEnum (enum, a) -> begin
- checkAttributes a;
- checkEnumInfo Used enum
- end
-
- | TArray(bt, len, a) ->
- checkAttributes a;
- checkType bt CTArray;
- (match len with
- None -> ()
- | Some l -> begin
- let t = checkExp true l in
- match t with
- TInt((IInt|IUInt), _) -> ()
- | _ -> E.s (bug "Type of array length is not integer")
- end)
-
- | TFun (rt, targs, isva, a) ->
- checkAttributes a;
- checkType rt CTFRes;
- List.iter
- (fun (an, at, aa) ->
- checkType at CTFArg;
- checkAttributes aa) (argsToList targs)
-
-(* Check that a type is a promoted integral type *)
-and checkIntegralType (t: typ) =
- checkType t CTExp;
- match unrollType t with
- TInt _ -> ()
- | _ -> ignore (warn "Non-integral type")
-
-(* Check that a type is a promoted arithmetic type *)
-and checkArithmeticType (t: typ) =
- checkType t CTExp;
- match unrollType t with
- TInt _ | TFloat _ -> ()
- | _ -> ignore (warn "Non-arithmetic type")
-
-(* Check that a type is a promoted boolean type *)
-and checkBooleanType (t: typ) =
- checkType t CTExp;
- match unrollType t with
- TInt _ | TFloat _ | TPtr _ -> ()
- | _ -> ignore (warn "Non-boolean type")
-
-
-(* Check that a type is a pointer type *)
-and checkPointerType (t: typ) =
- checkType t CTExp;
- match unrollType t with
- TPtr _ -> ()
- | _ -> ignore (warn "Non-pointer type")
-
-
-and typeMatch (t1: typ) (t2: typ) =
- if typeSig t1 <> typeSig t2 then
- match unrollType t1, unrollType t2 with
- (* Allow free interchange of TInt and TEnum *)
- TInt (IInt, _), TEnum _ -> ()
- | TEnum _, TInt (IInt, _) -> ()
-
- | _, _ -> ignore (warn "Type mismatch:@! %a@!and %a@!"
- d_type t1 d_type t2)
-
-and checkCompInfo (isadef: defuse) comp =
- let fullname = compFullName comp in
- try
- let oldci, olddef = H.find compUsed comp.ckey in
- (* Check that it is the same *)
- if oldci != comp then
- ignore (warnContext "compinfo for %s not shared\n" fullname);
- (match !olddef, isadef with
- | Defined, Defined ->
- ignore (warnContext "Multiple definition of %s\n" fullname)
- | _, Defined -> olddef := Defined
- | Defined, _ -> ()
- | _, Forward -> olddef := Forward
- | _, _ -> ())
- with Not_found -> begin (* This is the first time we see it *)
- (* Check that the name is not empty *)
- if comp.cname = "" then
- E.s (bug "Compinfo with empty name");
- (* Check that the name is unique *)
- if H.mem compNames fullname then
- ignore (warn "Duplicate name %s" fullname);
- (* Add it to the map before we go on *)
- H.add compUsed comp.ckey (comp, ref isadef);
- H.add compNames fullname ();
- (* Do not check the compinfo unless this is a definition. Otherwise you
- * might run into undefined types. *)
- if isadef = Defined then begin
- checkAttributes comp.cattr;
- let fctx = if comp.cstruct then CTStruct else CTUnion in
- let rec checkField f =
- if not
- (f.fcomp == comp && (* Each field must share the self cell of
- * the host *)
- f.fname <> "") then
- ignore (warn "Self pointer not set in field %s of %s"
- f.fname fullname);
- checkType f.ftype fctx;
- (* Check the bitfields *)
- (match unrollType f.ftype, f.fbitfield with
- | TInt (ik, a), Some w ->
- checkAttributes a;
- if w < 0 || w >= bitsSizeOf (TInt(ik, a)) then
- ignore (warn "Wrong width (%d) in bitfield" w)
- | _, Some w ->
- ignore (E.error "Bitfield on a non integer type\n")
- | _ -> ());
- checkAttributes f.fattr
- in
- List.iter checkField comp.cfields
- end
- end
-
-
-and checkEnumInfo (isadef: defuse) enum =
- if enum.ename = "" then
- E.s (bug "Enuminfo with empty name");
- try
- let oldei, olddef = H.find enumUsed enum.ename in
- (* Check that it is the same *)
- if oldei != enum then
- ignore (warnContext "enuminfo for %s not shared\n" enum.ename);
- (match !olddef, isadef with
- Defined, Defined ->
- ignore (warnContext "Multiple definition of enum %s\n" enum.ename)
- | _, Defined -> olddef := Defined
- | Defined, _ -> ()
- | _, Forward -> olddef := Forward
- | _, _ -> ())
- with Not_found -> begin (* This is the first time we see it *)
- (* Add it to the map before we go on *)
- H.add enumUsed enum.ename (enum, ref isadef);
- checkAttributes enum.eattr;
- List.iter (fun (tn, _, _) -> defineName tn) enum.eitems;
- end
-
-and checkTypeInfo (isadef: defuse) ti =
- try
- let oldti, olddef = H.find typUsed ti.tname in
- (* Check that it is the same *)
- if oldti != ti then
- ignore (warnContext "typeinfo for %s not shared\n" ti.tname);
- (match !olddef, isadef with
- Defined, Defined ->
- ignore (warnContext "Multiple definition of type %s\n" ti.tname)
- | Defined, Used -> ()
- | Used, Defined ->
- ignore (warnContext "Use of type %s before its definition\n" ti.tname)
- | _, _ ->
- ignore (warnContext "Bug in checkTypeInfo for %s\n" ti.tname))
- with Not_found -> begin (* This is the first time we see it *)
- if ti.tname = "" then
- ignore (warnContext "typeinfo with empty name");
- checkType ti.ttype CTDecl;
- (* Add it to the map before we go on *)
- H.add typUsed ti.tname (ti, ref isadef);
- end
-
-(* Check an lvalue. If isconst then the lvalue appears in a context where
- * only a compile-time constant can appear. Return the type of the lvalue.
- * See the typing rule from cil.mli *)
-and checkLval (isconst: bool) (lv: lval) : typ =
- match lv with
- Var vi, off ->
- checkVariable vi;
- checkOffset vi.vtype off
-
- | Mem addr, off -> begin
- if isconst then
- ignore (warn "Memory operation in constant");
- let ta = checkExp false addr in
- match unrollType ta with
- TPtr (t, _) -> checkOffset t off
- | _ -> E.s (bug "Mem on a non-pointer")
- end
-
-(* Check an offset. The basetype is the type of the object referenced by the
- * base. Return the type of the lvalue constructed from a base value of right
- * type and the offset. See the typing rules from cil.mli *)
-and checkOffset basetyp : offset -> typ = function
- NoOffset -> basetyp
- | Index (ei, o) ->
- checkExpType false ei intType;
- begin
- match unrollType basetyp with
- TArray (t, _, _) -> checkOffset t o
- | t -> E.s (bug "typeOffset: Index on a non-array: %a" d_plaintype t)
- end
-
- | Field (fi, o) ->
- (* Now check that the host is shared propertly *)
- checkCompInfo Used fi.fcomp;
- (* Check that this exact field is part of the host *)
- if not (List.exists (fun f -> f == fi) fi.fcomp.cfields) then
- ignore (warn "Field %s not part of %s"
- fi.fname (compFullName fi.fcomp));
- checkOffset fi.ftype o
-
-and checkExpType (isconst: bool) (e: exp) (t: typ) =
- let t' = checkExp isconst e in (* compute the type *)
- if isconst then begin (* For initializers allow a string to initialize an
- * array of characters *)
- if typeSig t' <> typeSig t then
- match e, t with
- | _ -> typeMatch t' t
- end else
- typeMatch t' t
-
-(* Check an expression. isconst specifies if the expression occurs in a
- * context where only a compile-time constant can occur. Return the computed
- * type of the expression *)
-and checkExp (isconst: bool) (e: exp) : typ =
- E.withContext
- (fun _ -> dprintf "check%s: %a"
- (if isconst then "Const" else "Exp") d_exp e)
- (fun _ ->
- match e with
- | Const(CInt64 (_, ik, _)) -> TInt(ik, [])
- | Const(CChr _) -> charType
- | Const(CStr s) -> charPtrType
- | Const(CWStr s) -> TPtr(!wcharType,[])
- | Const(CReal (_, fk, _)) -> TFloat(fk, [])
- | Const(CEnum (_, _, ei)) -> TEnum(ei, [])
- | Lval(lv) ->
- if isconst then
- ignore (warn "Lval in constant");
- checkLval isconst lv
-
- | SizeOf(t) -> begin
- (* Sizeof cannot be applied to certain types *)
- checkType t CTSizeof;
- (match unrollType t with
- (TFun _ | TVoid _) ->
- ignore (warn "Invalid operand for sizeof")
- | _ ->());
- uintType
- end
- | SizeOfE(e) ->
- (* The expression in a sizeof can be anything *)
- let te = checkExp false e in
- checkExp isconst (SizeOf(te))
-
- | SizeOfStr s -> uintType
-
- | AlignOf(t) -> begin
- (* Sizeof cannot be applied to certain types *)
- checkType t CTSizeof;
- (match unrollType t with
- (TFun _ | TVoid _) ->
- ignore (warn "Invalid operand for sizeof")
- | _ ->());
- uintType
- end
- | AlignOfE(e) ->
- (* The expression in an AlignOfE can be anything *)
- let te = checkExp false e in
- checkExp isconst (AlignOf(te))
-
- | UnOp (Neg, e, tres) ->
- checkArithmeticType tres; checkExpType isconst e tres; tres
-
- | UnOp (BNot, e, tres) ->
- checkIntegralType tres; checkExpType isconst e tres; tres
-
- | UnOp (LNot, e, tres) ->
- let te = checkExp isconst e in
- checkBooleanType te;
- checkIntegralType tres; (* Must check that t is well-formed *)
- typeMatch tres intType;
- tres
-
- | BinOp (bop, e1, e2, tres) -> begin
- let t1 = checkExp isconst e1 in
- let t2 = checkExp isconst e2 in
- match bop with
- (Mult | Div) ->
- typeMatch t1 t2; checkArithmeticType tres;
- typeMatch t1 tres; tres
- | (Eq|Ne|Lt|Le|Ge|Gt) ->
- typeMatch t1 t2; checkArithmeticType t1;
- typeMatch tres intType; tres
- | Mod|BAnd|BOr|BXor ->
- typeMatch t1 t2; checkIntegralType tres;
- typeMatch t1 tres; tres
- | LAnd | LOr ->
- typeMatch t1 t2; checkBooleanType tres;
- typeMatch t1 tres; tres
- | Shiftlt | Shiftrt ->
- typeMatch t1 tres; checkIntegralType t1;
- checkIntegralType t2; tres
- | (PlusA | MinusA) ->
- typeMatch t1 t2; typeMatch t1 tres;
- checkArithmeticType tres; tres
- | (PlusPI | MinusPI | IndexPI) ->
- checkPointerType tres;
- typeMatch t1 tres;
- checkIntegralType t2;
- tres
- | MinusPP ->
- checkPointerType t1; checkPointerType t2;
- typeMatch t1 t2;
- typeMatch tres intType;
- tres
- end
- | AddrOf (lv) -> begin
- let tlv = checkLval isconst lv in
- (* Only certain types can be in AddrOf *)
- match unrollType tlv with
- | TVoid _ ->
- E.s (bug "AddrOf on improper type");
-
- | (TInt _ | TFloat _ | TPtr _ | TComp _ | TFun _ | TArray _ ) ->
- TPtr(tlv, [])
-
- | TEnum _ -> intPtrType
- | _ -> E.s (bug "AddrOf on unknown type")
- end
-
- | StartOf lv -> begin
- let tlv = checkLval isconst lv in
- match unrollType tlv with
- TArray (t,_, _) -> TPtr(t, [])
- | _ -> E.s (bug "StartOf on a non-array")
- end
-
- | CastE (tres, e) -> begin
- let et = checkExp isconst e in
- checkType tres CTExp;
- (* Not all types can be cast *)
- match unrollType et with
- TArray _ -> E.s (bug "Cast of an array type")
- | TFun _ -> E.s (bug "Cast of a function type")
- | TComp _ -> E.s (bug "Cast of a composite type")
- | TVoid _ -> E.s (bug "Cast of a void type")
- | _ -> tres
- end)
- () (* The argument of withContext *)
-
-and checkInit (i: init) : typ =
- E.withContext
- (fun _ -> dprintf "checkInit: %a" d_init i)
- (fun _ ->
- match i with
- SingleInit e -> checkExp true e
-(*
- | ArrayInit (bt, len, initl) -> begin
- checkType bt CTSizeof;
- if List.length initl > len then
- ignore (warn "Too many initializers in array");
- List.iter (fun i -> checkInitType i bt) initl;
- TArray(bt, Some (integer len), [])
- end
-*)
- | CompoundInit (ct, initl) -> begin
- checkType ct CTSizeof;
- (match unrollType ct with
- TArray(bt, Some (Const(CInt64(len, _, _))), _) ->
- let rec loopIndex i = function
- [] ->
- if i <> len then
- ignore (warn "Wrong number of initializers in array")
-
- | (Index(Const(CInt64(i', _, _)), NoOffset), ei) :: rest ->
- if i' <> i then
- ignore (warn "Initializer for index %s when %s was expected\n"
- (Int64.format "%d" i') (Int64.format "%d" i));
- checkInitType ei bt;
- loopIndex (Int64.succ i) rest
- | _ :: rest ->
- ignore (warn "Malformed initializer for array element")
- in
- loopIndex Int64.zero initl
- | TArray(_, _, _) ->
- ignore (warn "Malformed initializer for array")
- | TComp (comp, _) ->
- if comp.cstruct then
- let rec loopFields
- (nextflds: fieldinfo list)
- (initl: (offset * init) list) : unit =
- match nextflds, initl with
- [], [] -> () (* We are done *)
- | f :: restf, (Field(f', NoOffset), i) :: resti ->
- if f.fname <> f'.fname then
- ignore (warn "Expected initializer for field %s and found one for %s\n" f.fname f'.fname);
- checkInitType i f.ftype;
- loopFields restf resti
- | [], _ :: _ ->
- ignore (warn "Too many initializers for struct")
- | _ :: _, [] ->
- ignore (warn "Too few initializers for struct")
- | _, _ ->
- ignore (warn "Malformed initializer for struct")
- in
- loopFields
- (List.filter (fun f -> f.fname <> missingFieldName)
- comp.cfields)
- initl
-
- else (* UNION *)
- if comp.cfields == [] then begin
- if initl != [] then
- ignore (warn "Initializer for empty union not empty");
- end else begin
- match initl with
- [(Field(f, NoOffset), ei)] ->
- if f.fcomp != comp then
- ignore (bug "Wrong designator for union initializer");
- if !msvcMode && f != List.hd comp.cfields then
- ignore (warn "On MSVC you can only initialize the first field of a union");
- checkInitType ei f.ftype
-
- | _ ->
- ignore (warn "Malformed initializer for union")
- end
- | _ ->
- E.s (warn "Type of Compound is not array or struct or union"));
- ct
- end)
- () (* The arguments of withContext *)
-
-
-and checkInitType (i: init) (t: typ) : unit =
- let it = checkInit i in
- typeMatch it t
-
-and checkStmt (s: stmt) =
- E.withContext
- (fun _ ->
- (* Print context only for certain small statements *)
- match s.skind with
- (*Loop _*) While _ | DoWhile _ | For _ | If _ | Switch _ -> nil
- | _ -> dprintf "checkStmt: %a" d_stmt s)
- (fun _ ->
- (* Check the labels *)
- let checkLabel = function
- Label (ln, l, _) ->
- if H.mem labels ln then
- ignore (warn "Multiply defined label %s" ln);
- H.add labels ln ()
- | Case (e, _) -> checkExpType true e intType
- | _ -> () (* Not yet implemented *)
- in
- List.iter checkLabel s.labels;
- (* See if we have seen this statement before *)
- if List.memq s !statements then
- ignore (warn "Statement is shared");
- (* Remember that we have seen this one *)
- statements := s :: !statements;
- match s.skind with
- Break _ | Continue _ -> ()
- | Goto (gref, l) ->
- currentLoc := l;
- (* Find a label *)
- let lab =
- match List.filter (function Label _ -> true | _ -> false)
- !gref.labels with
- Label (lab, _, _) :: _ -> lab
- | _ ->
- ignore (warn "Goto to block without a label\n");
- "<missing label>"
- in
- (* Remember it as a target *)
- gotoTargets := (lab, !gref) :: !gotoTargets
-
-
- | Return (re,l) -> begin
- currentLoc := l;
- match re, !currentReturnType with
- None, TVoid _ -> ()
- | _, TVoid _ -> ignore (warn "Invalid return value")
- | None, _ -> ignore (warn "Invalid return value")
- | Some re', rt' -> checkExpType false re' rt'
- end
-(*
- | Loop (b, l, _, _) -> checkBlock b
-*)
- | While (e, b, l) ->
- currentLoc := l;
- let te = checkExp false e in
- checkBooleanType te;
- checkBlock b;
- | DoWhile (e, b, l) ->
- currentLoc := l;
- let te = checkExp false e in
- checkBooleanType te;
- checkBlock b;
- | For (bInit, e, bIter, b, l) ->
- currentLoc := l;
- checkBlock bInit;
- let te = checkExp false e in
- checkBooleanType te;
- checkBlock bIter;
- checkBlock b;
- | Block b -> checkBlock b
- | If (e, bt, bf, l) ->
- currentLoc := l;
- let te = checkExp false e in
- checkBooleanType te;
- checkBlock bt;
- checkBlock bf
- | Switch (e, b, cases, l) ->
- currentLoc := l;
- checkExpType false e intType;
- (* Remember the statements so far *)
- let prevStatements = !statements in
- checkBlock b;
- (* Now make sure that all the cases do occur in that block *)
- List.iter
- (fun c ->
- if not (List.exists (function Case _ -> true | _ -> false)
- c.labels) then
- ignore (warn "Case in switch statment without a \"case\"\n");
- (* Make sure it is in there *)
- let rec findCase = function
- | l when l == prevStatements -> (* Not found *)
- ignore (warnContext
- "Cannot find target of switch statement")
- | [] -> E.s (E.bug "Check: findCase")
- | c' :: rest when c == c' -> () (* Found *)
- | _ :: rest -> findCase rest
- in
- findCase !statements)
- cases;
- | TryFinally (b, h, l) ->
- currentLoc := l;
- checkBlock b;
- checkBlock h
-
- | TryExcept (b, (il, e), h, l) ->
- currentLoc := l;
- checkBlock b;
- List.iter checkInstr il;
- checkExpType false e intType;
- checkBlock h
-
- | Instr il -> List.iter checkInstr il)
- () (* argument of withContext *)
-
-and checkBlock (b: block) : unit =
- List.iter checkStmt b.bstmts
-
-
-and checkInstr (i: instr) =
- match i with
- | Set (dest, e, l) ->
- currentLoc := l;
- let t = checkLval false dest in
- (* Not all types can be assigned to *)
- (match unrollType t with
- TFun _ -> ignore (warn "Assignment to a function type")
- | TArray _ -> ignore (warn "Assignment to an array type")
- | TVoid _ -> ignore (warn "Assignment to a void type")
- | _ -> ());
- checkExpType false e t
-
- | Call(dest, what, args, l) ->
- currentLoc := l;
- let (rt, formals, isva) =
- match checkExp false what with
- TFun(rt, formals, isva, _) -> rt, formals, isva
- | _ -> E.s (bug "Call to a non-function")
- in
- (* Now check the return value*)
- (match dest, unrollType rt with
- None, TVoid _ -> ()
- | Some _, TVoid _ -> ignore (warn "void value is assigned")
- | None, _ -> () (* "Call of function is not assigned" *)
- | Some destlv, rt' ->
- let desttyp = checkLval false destlv in
- if typeSig desttyp <> typeSig rt then begin
- (* Not all types can be assigned to *)
- (match unrollType desttyp with
- TFun _ -> ignore (warn "Assignment to a function type")
- | TArray _ -> ignore (warn "Assignment to an array type")
- | TVoid _ -> ignore (warn "Assignment to a void type")
- | _ -> ());
- (* Not all types can be cast *)
- (match rt' with
- TArray _ -> ignore (warn "Cast of an array type")
- | TFun _ -> ignore (warn "Cast of a function type")
- | TComp _ -> ignore (warn "Cast of a composite type")
- | TVoid _ -> ignore (warn "Cast of a void type")
-
- | _ -> ())
- end);
- (* Now check the arguments *)
- let rec loopArgs formals args =
- match formals, args with
- [], _ when (isva || args = []) -> ()
- | (fn,ft,_) :: formals, a :: args ->
- checkExpType false a ft;
- loopArgs formals args
- | _, _ -> ignore (warn "Not enough arguments")
- in
- if formals = None then
- ignore (warn "Call to function without prototype\n")
- else
- loopArgs (argsToList formals) args
-
- | Asm _ -> () (* Not yet implemented *)
-
-let rec checkGlobal = function
- GAsm _ -> ()
- | GPragma _ -> ()
- | GText _ -> ()
- | GType (ti, l) ->
- currentLoc := l;
- E.withContext (fun _ -> dprintf "GType(%s)" ti.tname)
- (fun _ ->
- checkTypeInfo Defined ti;
- if ti.tname <> "" then defineName ti.tname)
- ()
-
- | GCompTag (comp, l) ->
- currentLoc := l;
- checkCompInfo Defined comp;
-
- | GCompTagDecl (comp, l) ->
- currentLoc := l;
- checkCompInfo Forward comp;
-
- | GEnumTag (enum, l) ->
- currentLoc := l;
- checkEnumInfo Defined enum
-
- | GEnumTagDecl (enum, l) ->
- currentLoc := l;
- checkEnumInfo Forward enum
-
- | GVarDecl (vi, l) ->
- currentLoc := l;
- (* We might have seen it already *)
- E.withContext (fun _ -> dprintf "GVarDecl(%s)" vi.vname)
- (fun _ ->
- (* If we have seen this vid already then it must be for the exact
- * same varinfo *)
- if H.mem varIdsEnv vi.vid then
- checkVariable vi
- else begin
- defineVariable vi;
- checkAttributes vi.vattr;
- checkType vi.vtype CTDecl;
- if not (vi.vglob &&
- vi.vstorage <> Register) then
- E.s (bug "Invalid declaration of %s" vi.vname)
- end)
- ()
-
- | GVar (vi, init, l) ->
- currentLoc := l;
- (* Maybe this is the first occurrence *)
- E.withContext (fun _ -> dprintf "GVar(%s)" vi.vname)
- (fun _ ->
- checkGlobal (GVarDecl (vi, l));
- (* Check the initializer *)
- begin match init.init with
- None -> ()
- | Some i -> ignore (checkInitType i vi.vtype)
- end;
- (* Cannot be a function *)
- if isFunctionType vi.vtype then
- E.s (bug "GVar for a function (%s)\n" vi.vname);
- )
- ()
-
-
- | GFun (fd, l) -> begin
- currentLoc := l;
- (* Check if this is the first occurrence *)
- let vi = fd.svar in
- let fname = vi.vname in
- E.withContext (fun _ -> dprintf "GFun(%s)" fname)
- (fun _ ->
- checkGlobal (GVarDecl (vi, l));
- (* Check that the argument types in the type are identical to the
- * formals *)
- let rec loopArgs targs formals =
- match targs, formals with
- [], [] -> ()
- | (fn, ft, fa) :: targs, fo :: formals ->
- if fn <> fo.vname || ft != fo.vtype || fa != fo.vattr then
- ignore (warnContext
- "Formal %s not shared (type + locals) in %s"
- fo.vname fname);
- loopArgs targs formals
-
- | _ ->
- E.s (bug "Type has different number of formals for %s"
- fname)
- in
- begin match vi.vtype with
- TFun (rt, args, isva, a) -> begin
- currentReturnType := rt;
- loopArgs (argsToList args) fd.sformals
- end
- | _ -> E.s (bug "Function %s does not have a function type"
- fname)
- end;
- ignore (fd.smaxid >= 0 || E.s (bug "smaxid < 0 for %s" fname));
- (* Now start a new environment, in a finally clause *)
- begin try
- startEnv ();
- (* Do the locals *)
- let doLocal tctx v =
- if v.vglob then
- ignore (warnContext
- "Local %s has the vglob flag set" v.vname);
- if v.vstorage <> NoStorage && v.vstorage <> Register then
- ignore (warnContext
- "Local %s has storage %a\n" v.vname
- d_storage v.vstorage);
- checkType v.vtype tctx;
- checkAttributes v.vattr;
- defineVariable v
- in
- List.iter (doLocal CTFArg) fd.sformals;
- List.iter (doLocal CTDecl) fd.slocals;
- statements := [];
- gotoTargets := [];
- checkBlock fd.sbody;
- H.clear labels;
- (* Now verify that we have scanned all targets *)
- List.iter
- (fun (lab, t) -> if not (List.memq t !statements) then
- ignore (warnContext
- "Target of \"goto %s\" statement does not appear in function body" lab))
- !gotoTargets;
- statements := [];
- gotoTargets := [];
- (* Done *)
- endEnv ()
- with e ->
- endEnv ();
- raise e
- end;
- ())
- () (* final argument of withContext *)
- end
-
-
-let checkFile flags fl =
- if !E.verboseFlag then ignore (E.log "Checking file %s\n" fl.fileName);
- valid := true;
- List.iter
- (function
- NoCheckGlobalIds -> checkGlobalIds := false)
- flags;
- iterGlobals fl (fun g -> try checkGlobal g with _ -> ());
- (* Check that for all struct/union tags there is a definition *)
- H.iter
- (fun k (comp, isadef) ->
- if !isadef = Used then
- begin
- valid := false;
- ignore (E.warn "Compinfo %s is referenced but not defined"
- (compFullName comp))
- end)
- compUsed;
- (* Check that for all enum tags there is a definition *)
- H.iter
- (fun k (enum, isadef) ->
- if !isadef = Used then
- begin
- valid := false;
- ignore (E.warn "Enuminfo %s is referenced but not defined"
- enum.ename)
- end)
- enumUsed;
- (* Clean the hashes to let the GC do its job *)
- H.clear typeDefs;
- H.clear varNamesEnv;
- H.clear varIdsEnv;
- H.clear allVarIds;
- H.clear compNames;
- H.clear compUsed;
- H.clear enumUsed;
- H.clear typUsed;
- varNamesList := [];
- if !E.verboseFlag then
- ignore (E.log "Finished checking file %s\n" fl.fileName);
- !valid
-