diff --git a/src/kernel_internals/typing/cabs2cil.ml b/src/kernel_internals/typing/cabs2cil.ml index ae7a41fd5008aace906238f7035fda8352b7dab1..5d267f9428f6d0848eb021cdf293489b2a87ffd9 100644 --- a/src/kernel_internals/typing/cabs2cil.ml +++ b/src/kernel_internals/typing/cabs2cil.ml @@ -125,20 +125,8 @@ let () = Cil_printer.register_shallow_attribute frama_c_destructor types of cabs2cil-introduced temp variables. *) let typeForInsertedVar: (Cil_types.typ -> Cil_types.typ) ref = ref (fun t -> t) -(** Like [typeForInsertedVar], but for casts. - * Casts in the source code are exempt from this hook. *) -let typeForInsertedCast: - (Cil_types.exp -> Cil_types.typ -> Cil_types.typ -> Cil_types.typ) ref = - ref (fun _ _ t -> t) - - let cabs_exp loc node = { expr_loc = loc; expr_node = node } -let bigger_length_args l1 l2 = - match l1, l2 with - | None, _ | _, None -> false - | Some l1, Some l2 -> List.length l1 > List.length l2 - let abort_context msg = let pos = fst (Cil.CurrentLoc.get ()) in let append fmt = @@ -708,23 +696,6 @@ let gotoTargetHash: (string, int) H.t = H.create 13 let gotoTargetNextAddr: int ref = ref 0 -(********** TRANSPARENT UNION ******) -(* Check if a type is a transparent union, and return the first field if it - * is *) -let isTransparentUnion (t: typ) : fieldinfo option = - match unrollType t with - | TComp (comp, _) when not comp.cstruct -> - (* Turn transparent unions into the type of their first field *) - if typeHasAttribute "transparent_union" t then begin - match comp.cfields with - | Some [] | None -> - abort_context - "Empty transparent union: %s" (compFullName comp) - | Some (f :: _) -> Some f - end else - None - | _ -> None - (* When we process an argument list, remember the argument index which has a * transparent union type, along with the original type. We need this to * process function definitions *) @@ -1439,33 +1410,6 @@ let integralPromotion = Cil.integralPromotion exceptions, also listed in 6.3.2.1:2 *) let dropQualifiers = Cil.type_remove_qualifier_attributes -(* true if the expression is known to be a boolean result, i.e. 0 or 1. *) -let rec is_boolean_result e = - Cil.(isBoolType (typeOf e)) || - match e.enode with - | Const _ -> - (match Cil.isInteger e with - | Some i -> - Integer.equal i Integer.zero || Integer.equal i Integer.one - | None -> false) - | CastE (_,e) -> is_boolean_result e - | BinOp((Lt | Gt | Le | Ge | Eq | Ne | LAnd | LOr),_,_,_) -> true - | BinOp((PlusA | PlusPI | MinusA | MinusPI | MinusPP | Mult - | Div | Mod | Shiftlt | Shiftrt | BAnd | BXor | BOr),_,_,_) -> false - | UnOp(LNot,_,_) -> true - | UnOp ((Neg | BNot),_,_) -> false - | Lval _ | SizeOf _ | SizeOfE _ | SizeOfStr _ | AlignOf _ - | AlignOfE _ | AddrOf _ | StartOf _ -> false - -(* Like Cil.mkCastT, but it calls typeForInsertedCast *) -let makeCastT ~(e: exp) ~(oldt: typ) ~(newt: typ) = - if need_cast oldt newt then - Cil.mkCastT ~oldt ~newt:(!typeForInsertedCast e oldt newt) e - else e - -let makeCast ~(e: exp) ~(newt: typ) = - makeCastT ~e ~oldt:(typeOf e) ~newt - let is_scalar_type t = match unrollType t with | TInt _ @@ -2182,7 +2126,7 @@ struct | Case (e, loc) -> (* If needed, convert e to type t, and check in case the label was too big *) - let e' = makeCast ~e ~newt:t in + let e' = mkCast ~newt:t e in let constFold = constFold true in let e'' = if theMachine.lowerConstants then constFold e' else e' in (match constFoldToInt e, constFoldToInt e'' with @@ -2661,208 +2605,9 @@ let cabsTypeAddAttributes = let get_qualifiers t = Cil.filter_qualifier_attributes (Cil.typeAttrs t) -(* Specify whether the cast is from the source code *) -let rec castTo ?context ?(fromsource=false) - (ot : typ) (nt : typ) (e : exp) : (typ * exp ) = - let dkey = Kernel.dkey_typing_cast in - Kernel.debug ~dkey "@[%t: castTo:%s %a->%a@\n@]" - Cil.pp_thisloc (if fromsource then "(source)" else "") - Cil_datatype.Typ.pretty ot Cil_datatype.Typ.pretty nt; - if not fromsource && not (need_cast ot nt) then begin - (* Do not put the cast if it is not necessary, unless it is from the - * source. *) - Kernel.debug ~dkey "no cast to perform"; - (ot, e) - end else begin - let nt = if fromsource then nt else !typeForInsertedCast e ot nt in - let result = (nt, if theMachine.insertImplicitCasts || fromsource then - Cil.mkCastT ~force:true ~oldt:ot ~newt:nt e else e) - in - let origin_error = - if fromsource then "explicit cast:" else " implicit cast:" - in - (* [BM] uncomment the following line to enable attributes static typing - ignore (check_strict_attributes true ot nt && check_strict_attributes false nt ot);*) - Kernel.debug ~dkey "@[castTo: ot=%a nt=%a\n result is %a@\n@]" - Cil_datatype.Typ.pretty ot Cil_datatype.Typ.pretty nt - Cil_printer.pp_exp (snd result); - (* Now see if we can have a cast here *) - match Cil.unrollType ot, Cil.unrollType nt with - | TNamed _, _ - | _, TNamed _ -> Kernel.fatal ~current:true "unrollType failed in castTo" - | t, TInt(IBool,_) when is_scalar_type t -> - if is_boolean_result e then begin - Kernel.debug ~dkey "Explicit cast to Boolean: %a" Cil_printer.pp_exp e; - result - end else begin - Kernel.debug ~dkey - "bool conversion by checking !=0: %a" Cil_printer.pp_exp e; - let cmp = Cil.mkBinOp ~loc:e.eloc Ne e (Cil.integer ~loc:e.eloc 0) in - let oldt = Cil.typeOf cmp in - nt, Cil.mkCastT ~oldt ~newt:nt cmp - end - | TInt(_,_), TInt(_,_) -> - (* We used to ignore attributes on integer-integer casts. Not anymore *) - (* if ikindo = ikindn then (nt, e) else *) - result - | TPtr (TFun (_,args,va,_),_), TPtr(TFun (_,args',va',_),_) -> - (* Checks on casting from a function type into another one. - We enforce at least the same number of arguments, and emit a warning - if types do not match. Note that empty argument lists are always - compatible. *) - if (va <> va' || bigger_length_args args args') && - (args <> None && args' <> None) - then - abort_context - "%s conversion between function types with \ - different number of arguments:@ %a@ and@ %a" - origin_error - Cil_datatype.Typ.pretty ot Cil_datatype.Typ.pretty nt; - if not (areCompatibleTypes ?context ot nt) then - Kernel.warning - ~wkey:Kernel.wkey_incompatible_types_call - ~current:true - "implicit conversion between incompatible function types:@ \ - %a@ and@ %a" - Cil_datatype.Typ.pretty ot Cil_datatype.Typ.pretty nt; - result - - | TFun _, TPtr(TFun _, _) -> - let clean_e = - match e.enode with - | Lval lv -> Cil.mkAddrOf ~loc:e.eloc lv - | _ -> e (* function decay into pointer anyway *) - in - castTo ?context ~fromsource (TPtr (ot, [])) nt clean_e - - (* accept converting a ptr to function to/from a ptr to void, even though - not really accepted by the standard. gcc supports it. though - *) - | TPtr (TFun _,_), TPtr (TVoid _, _) -> result - | TPtr (TVoid _, _), TPtr (TFun _,_) -> result - (* Taking numerical address or calling an absolute location. Also - accepted by gcc albeit with a warning. *) - | TInt _, TPtr (TFun _, _) -> result - - (* pointer to potential function type. Note that we do not - use unrollTypeDeep above in order to avoid needless divergence with - original type in the sources. - *) - | TPtr(TFun _,_), TPtr(TNamed(ti,nattr),pattr) -> - castTo ?context - ~fromsource ot (TPtr (Cil.typeAddAttributes nattr ti.ttype, pattr)) e - | TPtr(TNamed(ti,nattr),pattr), TPtr(TFun _,_) -> - castTo ?context - ~fromsource (TPtr (Cil.typeAddAttributes nattr ti.ttype, pattr)) nt e - - (* No other conversion implying a pointer to function - and a pointer to object are supported. *) - | TPtr (TFun _,_), TPtr _ -> - Kernel.warning - ~wkey:Kernel.wkey_incompatible_pointer_types - ~current:true - "casting function to %a" Cil_datatype.Typ.pretty nt; - result - | TPtr _, TPtr (TFun _,_) -> - Kernel.warning - ~wkey:Kernel.wkey_incompatible_pointer_types - ~current:true - "casting function from %a" Cil_datatype.Typ.pretty ot; - result - | _, TPtr (TFun _, _) -> - abort_context "%s cannot cast %a to function type" - origin_error Cil_datatype.Typ.pretty ot - | TPtr _, TPtr _ -> result - - | TInt _, TPtr _ -> result - - | TPtr _, TInt _ -> - if not fromsource - then - Kernel.warning - ~wkey:Kernel.wkey_int_conversion - ~current:true - "Conversion from a pointer to an integer without an explicit cast"; - result - - | TArray _, TPtr _ -> result - - | TArray(t1,_,_), TArray(t2,None,_) - when Cil_datatype.Typ.equal t1 t2 -> (nt, e) - - | TPtr _, TArray(_,_,_) -> - abort_context "%s cast over a non-scalar type %a" - origin_error Cil_datatype.Typ.pretty nt; - - | TEnum _, TInt _ -> result - | TFloat _, (TInt _|TEnum _) -> result - | (TInt _|TEnum _), TFloat _ -> result - | TFloat _, TFloat _ -> result - | TInt (ik,_), TEnum (ei,_) -> - (match e.enode with - | Const (CEnum { eihost = ei'}) - when ei.ename = ei'.ename && not fromsource && - Cil.bytesSizeOfInt ik = Cil.bytesSizeOfInt ei'.ekind - -> (nt,e) - | _ -> result) - | TEnum _, TEnum _ -> result - - | TEnum _, TPtr _ -> result - | TBuiltin_va_list _, (TInt _ | TPtr _) -> - result - - | (TInt _ | TPtr _), TBuiltin_va_list _ -> - Kernel.debug ~dkey ~current:true - "Casting %a to __builtin_va_list" Cil_datatype.Typ.pretty ot ; - result - - | TPtr _, TEnum _ -> - Kernel.debug ~dkey ~current:true - "Casting a pointer into an enumeration type" ; - result - - (* The expression is evaluated for its effects *) - | (TInt _ | TEnum _ | TPtr _ ), TVoid _ -> - Kernel.debug ~level:3 - "Casting a value into void: expr is evaluated for side effects"; - result - - (* Even casts between structs are allowed when we are only - * modifying some attributes *) - | TComp (comp1, _), TComp (comp2, _) when comp1.ckey = comp2.ckey -> - result - - (* If we try to pass a transparent union value to a function - * expecting a transparent union argument, the argument type would - * have been changed to the type of the first argument, and we'll - * see a cast from a union to the type of the first argument. Turn - * that into a field access *) - | TComp(_, _), _ -> begin - match isTransparentUnion ot with - | None -> - abort_context "%s %a -> %a" - origin_error Cil_datatype.Typ.pretty ot Cil_datatype.Typ.pretty nt - | Some fstfield -> begin - (* We do it now only if the expression is an lval *) - let e' = - match e.enode with - | Lval lv -> - new_exp ~loc:e.eloc - (Lval (addOffsetLval (Field(fstfield, NoOffset)) lv)) - | _ -> - abort_context - "%s transparent union expression is not an lval: %a\n" - origin_error - Cil_printer.pp_exp e - in - (* Continue casting *) - castTo ?context ~fromsource:fromsource fstfield.ftype nt e' - end - end - | _ -> - abort_context "%s cannot cast from %a to %a@\n" - origin_error Cil_datatype.Typ.pretty ot Cil_datatype.Typ.pretty nt - end +let castTo ?context ?(fromsource=false) + (oldt : typ) (newt : typ) (e : exp) : (typ * exp) = + mkCastTGen ?context ~force:fromsource ~fromsource ~oldt ~newt e (* Create and cache varinfo's for globals. Starts with a varinfo but if the * global has been declared already it might come back with another varinfo. @@ -3057,7 +2802,6 @@ let setupBuiltin ?(force_keep=false) name ?spec (resTyp, args_or_argtypes, isva) if force_keep then v.vattr <- Cil.addAttribute (Attr ("FC_BUILTIN",[])) v.vattr; v -;; (* builtin is never ghost *) let memoBuiltin ?force_keep ?spec name proto = @@ -6045,7 +5789,7 @@ and doExp local_env if isIntegralType t then let tres = integralPromotion t in let e'' = - new_exp ~loc (UnOp(Neg, makeCastT ~e:e' ~oldt:t ~newt:tres, tres)) + new_exp ~loc (UnOp(Neg, mkCastT ~oldt:t ~newt:tres e', tres)) in finishExp r se e'' tres else @@ -6061,7 +5805,7 @@ and doExp local_env if isIntegralType t then let tres = integralPromotion t in let e'' = - new_exp ~loc (UnOp(BNot, makeCastT ~e:e' ~oldt:t ~newt:tres, tres)) + new_exp ~loc (UnOp(BNot, mkCastT ~oldt:t ~newt:tres e', tres)) in finishExp r se e'' tres else @@ -6071,7 +5815,7 @@ and doExp local_env let (r, se, e, t as v) = doExp (no_paren_local_env local_env) asconst e what in if isIntegralType t then let newt = integralPromotion t in - let e' = makeCastT ~e ~oldt:t ~newt in + let e' = mkCastT ~oldt:t ~newt e in finishExp r se e' newt else if isArithmeticType t then @@ -7511,7 +7255,7 @@ and doExp local_env end in finishExp [] (unspecified_chunk empty) - (makeCast ~e:(integer ~loc addrval) ~newt:voidPtrType) voidPtrType + (mkCast ~newt:voidPtrType (integer ~loc addrval)) voidPtrType end | Cabs.EXPR_PATTERN _ -> @@ -7689,8 +7433,8 @@ and doBinOp loc (bop: binop) (e1: exp) (t1: typ) (e2: exp) (t2: typ) = (* Keep the operator since it is arithmetic *) tres, optConstFoldBinOp loc false bop - (makeCastT ~e:e1 ~oldt:t1 ~newt:tres) - (makeCastT ~e:e2 ~oldt:t2 ~newt:tres) + (mkCastT ~oldt:t1 ~newt:tres e1) + (mkCastT ~oldt:t2 ~newt:tres e2) tres end else abort_context @@ -7702,8 +7446,8 @@ and doBinOp loc (bop: binop) (e1: exp) (t1: typ) (e2: exp) (t2: typ) = (* Keep the operator since it is arithmetic *) intType, optConstFoldBinOp loc false bop - (makeCastT ~e:e1 ~oldt:t1 ~newt:tres) - (makeCastT ~e:e2 ~oldt:t2 ~newt:tres) + (mkCastT ~oldt:t1 ~newt:tres e1) + (mkCastT ~oldt:t2 ~newt:tres e2) intType in let doIntegralArithmetic () = @@ -7713,8 +7457,8 @@ and doBinOp loc (bop: binop) (e1: exp) (t1: typ) (e2: exp) (t2: typ) = | TInt _ -> tres, optConstFoldBinOp loc false bop - (makeCastT ~e:e1 ~oldt:t1 ~newt:tres) - (makeCastT ~e:e2 ~oldt:t2 ~newt:tres) + (mkCastT ~oldt:t1 ~newt:tres e1) + (mkCastT ~oldt:t2 ~newt:tres e2) tres | _ -> Kernel.fatal @@ -7735,8 +7479,8 @@ and doBinOp loc (bop: binop) (e1: exp) (t1: typ) (e2: exp) (t2: typ) = arguments with incompatible types to a common type *) let e1', e2' = if not (areCompatibleTypes t1p t2p) then - makeCastT ~e:e1 ~oldt:t1 ~newt:Cil.voidPtrType, - makeCastT ~e:e2 ~oldt:t2 ~newt:Cil.voidPtrType + mkCastT ~oldt:t1 ~newt:Cil.voidPtrType e1, + mkCastT ~oldt:t2 ~newt:Cil.voidPtrType e2 else e1, e2 in intType, @@ -7748,7 +7492,7 @@ and doBinOp loc (bop: binop) (e1: exp) (t1: typ) (e2: exp) (t2: typ) = { e1 with enode = AddrOf (addOffsetLval (Index (e2,NoOffset)) lv) } | _ -> optConstFoldBinOp loc false PlusPI e1 - (makeCastT ~e:e2 ~oldt:t2 ~newt:(integralPromotion t2)) t1 + (mkCastT ~oldt:t2 ~newt:(integralPromotion t2) e2) t1 in match bop with | (Mult|Div) -> doArithmetic () @@ -7763,8 +7507,8 @@ and doBinOp loc (bop: binop) (e1: exp) (t1: typ) (e2: exp) (t2: typ) = let t2' = integralPromotion t2 in t1', optConstFoldBinOp loc false bop - (makeCastT ~e:e1 ~oldt:t1 ~newt:t1') - (makeCastT ~e:e2 ~oldt:t2 ~newt:t2') + (mkCastT ~oldt:t1 ~newt:t1' e1) + (mkCastT ~oldt:t2 ~newt:t2' e2) t1' | (PlusA|MinusA) when isArithmeticType t1 && isArithmeticType t2 -> doArithmetic () @@ -7778,7 +7522,7 @@ and doBinOp loc (bop: binop) (e1: exp) (t1: typ) (e2: exp) (t2: typ) = | MinusA when isPointerType t1 && isIntegralType t2 -> t1, optConstFoldBinOp loc false MinusPI e1 - (makeCastT ~e:e2 ~oldt:t2 ~newt:(integralPromotion t2)) t1 + (mkCastT ~oldt:t2 ~newt:(integralPromotion t2) e2) t1 | MinusA when isPointerType t1 && isPointerType t2 -> if areCompatibleTypes (* C99 6.5.6:3 *) (Cil.type_remove_qualifier_attributes_deep t1) @@ -7791,9 +7535,9 @@ and doBinOp loc (bop: binop) (e1: exp) (t1: typ) (e2: exp) (t2: typ) = (* Two special cases for comparisons with the NULL pointer. We are a bit more permissive. *) | (Le|Lt|Ge|Gt|Eq|Ne) when isPointerType t1 && isZero e2 -> - pointerComparison e1 t1 (makeCast ~e:e2 ~newt:t1) t1 + pointerComparison e1 t1 (mkCast ~newt:t1 e2) t1 | (Le|Lt|Ge|Gt|Eq|Ne) when isPointerType t2 && isZero e1 -> - pointerComparison (makeCast ~e:e1 ~newt:t2) t2 e2 t2 + pointerComparison (mkCast ~newt:t2 e1) t2 e2 t2 | (Le|Lt|Ge|Gt|Eq|Ne) when isPointerType t1 && isPointerType t2 -> pointerComparison e1 t1 e2 t2 @@ -8449,7 +8193,7 @@ and doInit local_env asconst add_implicit_ensures preinit so acc initl = asconst oneinit (AExp(Some so.soTyp)) in let r = Cil_datatype.Lval.Set.of_list r in - let init_expr = makeCastT ~e:oneinit' ~oldt:t' ~newt:so.soTyp in + let init_expr = mkCastT ~oldt:t' ~newt:so.soTyp oneinit' in let preinit' = setOneInit preinit so.soOff (SinglePre (init_expr, r)) in (* Move on *) advanceSubobj so; @@ -9024,7 +8768,7 @@ and createLocal ghost ((_, sto, _, _) as specs) in let setlen = se0 +++ (mkStmtOneInstr ~ghost ~valid_sid - (Set(var savelen, makeCast ~e:len ~newt:savelen.vtype, + (Set(var savelen, mkCast ~newt:savelen.vtype len, CurrentLoc.get ())), [],[],[]) in @@ -9054,8 +8798,7 @@ and createLocal ghost ((_, sto, _, _) as specs) (Local_init (vi,AssignInit (SingleInit - (makeCast ~e:(new_exp ~loc (Lval(var tmp))) - ~newt:vi.vtype)), + (mkCast ~newt:vi.vtype (new_exp ~loc (Lval(var tmp))))), CurrentLoc.get ())), [],[var vi],[var tmp]) end @@ -9491,8 +9234,7 @@ and doDecl local_env (isglobal: bool) : Cabs.definition -> chunk = function defaultChunk ~ghost loc (i2c (mkStmtOneInstr ~ghost:local_env.is_ghost ~valid_sid - (Set ((Mem (makeCast ~e:(integer ~loc 0) - ~newt:intPtrType), + (Set ((Mem (mkCast ~newt:intPtrType (integer ~loc 0)), NoOffset), integer ~loc 0, loc)),[],[],[])) in @@ -9580,7 +9322,7 @@ and doDecl local_env (isglobal: bool) : Cabs.definition -> chunk = function match unrollType !currentReturnType with | TVoid _ -> [], None | (TInt _ | TEnum _ | TFloat _ | TPtr _) as rt -> - let res = Some (makeCastT ~e:(zero ~loc) ~oldt:intType ~newt:rt) in + let res = Some (mkCastT ~oldt:intType ~newt:rt (zero ~loc)) in if !currentFunctionFDEC.svar.vname = "main" then [],res else begin @@ -9595,7 +9337,7 @@ and doDecl local_env (isglobal: bool) : Cabs.definition -> chunk = function On the other hand, *( T* )0 is. We're not supposed to get there anyway. *) let null_ptr = - makeCastT ~e:(zero ~loc) ~oldt:intType ~newt:(TPtr(rt,[])) + mkCastT ~oldt:intType ~newt:(TPtr(rt,[])) (zero ~loc) in let res = Some (new_exp ~loc (Lval (mkMem ~addr:null_ptr ~off:NoOffset))) @@ -10104,7 +9846,7 @@ and doStatement local_env (s : Cabs.statement) : chunk = if not (Cil.isIntegralType et) then Kernel.error ~once:true ~current:true "Switch on a non-integer expression."; let et' = Cil.integralPromotion et in - let e' = makeCastT ~e:e' ~oldt:et ~newt:et' in + let e' = mkCastT ~oldt:et ~newt:et' e' in enter_break_env (); let s' = doStatement local_env s in exit_break_env (); @@ -10182,7 +9924,7 @@ and doStatement local_env (s : Cabs.statement) : chunk = | Some (switchv, switch) -> (* We have already generated this one *) (se @@@ (i2c(mkStmtOneInstr ~ghost ~valid_sid - (Set (var switchv, makeCast ~e:e' ~newt:intType, loc')), + (Set (var switchv, mkCast ~newt:intType e', loc')), [],[],[]), ghost)) @@@ (s2c(mkStmt ~ghost ~valid_sid (Goto (ref switch, loc'))), ghost) @@ -10213,7 +9955,7 @@ and doStatement local_env (s : Cabs.statement) : chunk = (se @@@ (i2c (mkStmtOneInstr ~ghost ~valid_sid - (Set (var switchv, makeCast ~e:e' ~newt:intType, loc')), + (Set (var switchv, mkCast ~newt:intType e', loc')), [],[],[]), ghost)) @@@ (s2c switch, ghost) diff --git a/src/kernel_internals/typing/cabs2cil.mli b/src/kernel_internals/typing/cabs2cil.mli index 21b1100134556d614dd3af4d174aae60852eb592..13d8b9107f4f18b5d2a082173bfa14482d7459fa 100644 --- a/src/kernel_internals/typing/cabs2cil.mli +++ b/src/kernel_internals/typing/cabs2cil.mli @@ -160,18 +160,6 @@ val fc_local_static: string types of cabs2cil-introduced temp variables. *) val typeForInsertedVar: (Cil_types.typ -> Cil_types.typ) ref -(** Like [typeForInsertedVar], but for casts. - [typeForInsertedCast expr original_type destination_type] - returns the type into which [expr], which has type [original_type] and - whose type must be converted into [destination_type], must be casted. - - By default, returns [destination_type]. - - This applies only to implicit casts. Casts already present - in the source code are exempt from this hook. *) -val typeForInsertedCast: - (Cil_types.exp -> Cil_types.typ -> Cil_types.typ -> Cil_types.typ) ref - (** [fresh_global prefix] creates a variable name not clashing with any other globals and starting with [prefix] *) val fresh_global : string -> string diff --git a/src/kernel_services/ast_queries/cil.ml b/src/kernel_services/ast_queries/cil.ml index 17d4701d6fd10e0a505892ab4d5a9ac49046a1fb..94be68f46f229218626cd984f50d41fd1bad28f6 100644 --- a/src/kernel_services/ast_queries/cil.ml +++ b/src/kernel_services/ast_queries/cil.ml @@ -70,6 +70,14 @@ let () = Log.set_current_source (fun () -> fst (CurrentLoc.get ())) let pp_thisloc fmt = Location.pretty fmt (CurrentLoc.get ()) +let abort_context msg = + let pos = fst (CurrentLoc.get ()) in + let append fmt = + Format.pp_print_newline fmt (); + Errorloc.pp_context_from_file fmt pos + in + Kernel.abort ~current:true ~append msg + let voidType = Cil_const.voidType let intType = TInt(IInt,[]) let uintType = TInt(IUInt,[]) @@ -3587,6 +3595,7 @@ let rec isLogicRealType t = isLogicRealType (unroll_ltdef ty) | Lvar _ | Ltype _ | Larrow _ -> false +(* ISO 6.2.5.18 *) let isArithmeticType t = match unrollTypeSkel t with (TInt _ | TEnum _ | TFloat _) -> true @@ -3597,13 +3606,6 @@ let isLongDoubleType t = | TFloat(FLongDouble,_) -> true | _ -> false -let isScalarType t= - match unrollTypeSkel t with - | TInt _ | TEnum _ | TFloat _ | TPtr _ -> true - | _ -> false - -let isArithmeticOrPointerType = isScalarType - let rec isLogicArithmeticType t = match t with | Ctype t -> isArithmeticType t @@ -3617,6 +3619,10 @@ let isFunctionType t = | TFun _ -> true | _ -> false +(* ISO 6.2.5.1 *) +let isObjectType t = + not (isFunctionType t) + let isLogicFunctionType t = Logic_const.isLogicCType isFunctionType t let isFunPtrType t = @@ -3631,6 +3637,28 @@ let isPointerType t = | TPtr _ -> true | _ -> false +(* ISO 6.2.5.21 *) +let isScalarType t = isArithmeticType t || isPointerType t + +let isArithmeticOrPointerType = isScalarType + + +(********** TRANSPARENT UNION ******) +(* Check if a type is a transparent union, and return the first field if it + * is *) +let isTransparentUnion (t: typ) : fieldinfo option = + match unrollType t with + | TComp (comp, _) when not comp.cstruct -> + (* Turn transparent unions into the type of their first field *) + if typeHasAttribute "transparent_union" t then begin + match comp.cfields with + | Some [] | None -> + abort_context "Empty transparent union: %s" (compFullName comp) + | Some (f :: _) -> Some f + end else + None + | _ -> None + let rec isTypeTagType t = match t with | Ltype ({lt_name = "typetag"},[]) -> true @@ -3698,15 +3726,15 @@ let rec typeOf (e: exp) : typ = | Const(CEnum {eival=v}) -> typeOf v (* l-values used as r-values lose their qualifiers (C99 6.3.2.1:2) *) - | Lval(lv) -> type_remove_qualifier_attributes (typeOfLval lv) + | Lval lv -> type_remove_qualifier_attributes (typeOfLval lv) | SizeOf _ | SizeOfE _ | SizeOfStr _ -> theMachine.typeOfSizeOf | AlignOf _ | AlignOfE _ -> theMachine.typeOfSizeOf | UnOp (_, _, t) -> t | BinOp (_, _, _, t) -> t | CastE (t, _) -> t - | AddrOf (lv) -> TPtr(typeOfLval lv, []) - | StartOf (lv) -> + | AddrOf lv -> TPtr(typeOfLval lv, []) + | StartOf lv -> match unrollType (typeOfLval lv) with | TArray (t,_,attrs) -> TPtr(t, attrs) | _ -> Kernel.fatal ~current:true "typeOf: StartOf on a non-array" @@ -5896,42 +5924,101 @@ let same_int64 ?(machdep=true) e1 e2 = | Some i, Some i' -> Integer.equal i i' | _ -> false +(* how type qualifiers must be checked *) +type qualifier_check_context = + | Identical (* identical qualifiers. *) + | IdenticalToplevel (* ignore at toplevel, use Identical when going under a + pointer. *) + | Covariant (* first type can have const-qualifications + the second doesn't have. *) + | CovariantToplevel + (* accepts everything for current type, use Covariant when going under a + pointer. *) + | Contravariant (* second type can have const-qualifications + the first doesn't have. *) + | ContravariantToplevel + (* accepts everything for current type, use Contravariant when going under + a pointer. *) -let mkCastT ?(force=false) ~(oldt: typ) ~(newt: typ) e = - let loc = e.eloc in - (* Issue #!1546 - let force = force || - (* see warning of need_cast function: - [false] as default value for that option is not safe... *) - (match e.enode with | Const(CEnum _) -> false | _ -> true) - in *) - if need_cast ~force oldt newt then begin - let mk_cast exp = (* to new type [newt] *) - new_exp ~loc (CastE(type_remove_qualifier_attributes newt,exp)) - in - let normalized_type = type_remove_attributes_for_c_cast (unrollType newt) in - (* Watch out for constants and cast of cast to pointer *) - match normalized_type, e.enode with - (* In the case were we have a representation for the literal, - explicitly add the cast. *) - | TInt(newik, []), Const(CInt64(i, _, None)) -> - kinteger64 ~loc ~kind:newik i - | TPtr _, CastE (_, e') -> - (match unrollType (typeOf e'), e'.enode with - | (TPtr _ as typ''), _ -> - (* Old cast can be removed...*) - if need_cast ~force newt typ'' then mk_cast e' - else (* In fact, both casts can be removed. *) e' - | _, Const(CInt64 (i, _, _)) when Integer.is_zero i -> mk_cast e' - | _ -> mk_cast e) - | _ -> - (* Do not remove old casts because they are conversions !!! *) - mk_cast e - end else - e +let qualifier_context_fun_arg = function + | Identical | IdenticalToplevel -> IdenticalToplevel + | Covariant | CovariantToplevel -> ContravariantToplevel + | Contravariant | ContravariantToplevel -> CovariantToplevel + +let qualifier_context_fun_ret = function + | Identical | IdenticalToplevel -> IdenticalToplevel + | Covariant | CovariantToplevel -> CovariantToplevel + | Contravariant | ContravariantToplevel -> ContravariantToplevel + +let qualifier_context_ptr = function + | Identical | IdenticalToplevel -> Identical + | Covariant | CovariantToplevel -> Covariant + | Contravariant | ContravariantToplevel -> Contravariant + +let included_qualifiers ?(context=Identical) a1 a2 = + let a1 = filter_qualifier_attributes a1 in + let a2 = filter_qualifier_attributes a2 in + let a1 = dropAttribute "restrict" a1 in + let a2 = dropAttribute "restrict" a2 in + let a1_no_cv = dropAttributes ["const"; "volatile"] a1 in + let a2_no_cv = dropAttributes ["const"; "volatile"] a2 in + let is_equal = Cil_datatype.Attributes.equal a1 a2 in + if is_equal then true + else begin + match context with + | Identical -> false + | Covariant -> Cil_datatype.Attributes.equal a1_no_cv a2 + | Contravariant -> Cil_datatype.Attributes.equal a1 a2_no_cv + | CovariantToplevel | ContravariantToplevel | IdenticalToplevel -> true + end + +(* precondition: t1 and t2 must be "compatible" as per combineTypes, i.e. + you must have called [combineTypes t1 t2] before calling this function. *) +let rec have_compatible_qualifiers_deep ?(context=Identical) t1 t2 = + match unrollType t1, unrollType t2 with + | TFun (tres1, Some args1, _, _), TFun (tres2, Some args2, _, _) -> + have_compatible_qualifiers_deep + ~context:(qualifier_context_fun_ret context) tres1 tres2 && + let context = qualifier_context_fun_arg context in + List.for_all2 (fun (_, t1', a1) (_, t2', a2) -> + have_compatible_qualifiers_deep ~context t1' t2' && + included_qualifiers ~context a1 a2) + args1 args2 + | TPtr (t1', a1), TPtr (t2', a2) + | TArray (t1', _, a1), TArray (t2', _, a2) -> + (included_qualifiers ~context a1 a2) && + let context = qualifier_context_ptr context in + have_compatible_qualifiers_deep ~context t1' t2' + | _, _ -> included_qualifiers ~context (typeAttrs t1) (typeAttrs t2) + + +(* true if the expression is known to be a boolean result, i.e. 0 or 1. *) +let rec is_boolean_result e = + (isBoolType (typeOf e)) || + match e.enode with + | Const _ -> + (match isInteger e with + | Some i -> + Integer.equal i Integer.zero || Integer.equal i Integer.one + | None -> false) + | CastE (_, e) -> is_boolean_result e + | BinOp ((Lt | Gt | Le | Ge | Eq | Ne | LAnd | LOr), _, _, _) -> true + | BinOp + ((PlusA | PlusPI | MinusA | MinusPI | MinusPP | Mult + | Div | Mod | Shiftlt | Shiftrt | BAnd | BXor | BOr), _, _, _) -> false + | UnOp (LNot, _, _) -> true + | UnOp ((Neg | BNot), _, _) -> false + | Lval _ | SizeOf _ | SizeOfE _ | SizeOfStr _ | AlignOf _ + | AlignOfE _ | AddrOf _ | StartOf _ -> false + + +(** A hook into the code that creates casts. By default this + returns the new type. + Casts in the source code are exempt from this hook. *) +let typeForInsertedCast: + (Cil_types.exp -> Cil_types.typ -> Cil_types.typ -> Cil_types.typ) ref = + ref (fun _ _ t -> t) -let mkCast ?force ~(newt: typ) e = - mkCastT ?force ~oldt:(typeOf e) ~newt e exception Cannot_combine of string @@ -6072,12 +6159,9 @@ let combineTypesGen ?emitwith (combF : combineFunction) (* They are not structurally equal. But perhaps they are equal if we evaluate them. Check first machine independent comparison. *) let checkEqualSize (machdep: bool) = - let size_t = theMachine.typeOfSizeOf in - let size_t_oldsz' = mkCast ~force:false ~newt:size_t oldsz' in - let size_t_sz' = mkCast ~force:false ~newt:size_t sz' in - ExpStructEqSized.equal - (constFold machdep size_t_oldsz') - (constFold machdep size_t_sz') + match constFoldToInt ~machdep oldsz', constFoldToInt ~machdep sz' with + | Some m, Some n -> m = n + | _ -> ExpStructEqSized.equal oldsz' sz' in if checkEqualSize false then oldsz @@ -6201,74 +6285,6 @@ let combineTypes ?(strictReturnTypes=false) what (oldt: typ) (t: typ) : typ = (***************** Compatibility ******) - -(* how type qualifiers must be checked *) -type qualifier_check_context = - | Identical (* identical qualifiers. *) - | IdenticalToplevel (* ignore at toplevel, use Identical when going under a - pointer. *) - | Covariant (* first type can have const-qualifications - the second doesn't have. *) - | CovariantToplevel - (* accepts everything for current type, use Covariant when going under a - pointer. *) - | Contravariant (* second type can have const-qualifications - the first doesn't have. *) - | ContravariantToplevel - (* accepts everything for current type, use Contravariant when going under - a pointer. *) - -let qualifier_context_fun_arg = function - | Identical | IdenticalToplevel -> IdenticalToplevel - | Covariant | CovariantToplevel -> ContravariantToplevel - | Contravariant | ContravariantToplevel -> CovariantToplevel - -let qualifier_context_fun_ret = function - | Identical | IdenticalToplevel -> IdenticalToplevel - | Covariant | CovariantToplevel -> CovariantToplevel - | Contravariant | ContravariantToplevel -> ContravariantToplevel - -let qualifier_context_ptr = function - | Identical | IdenticalToplevel -> Identical - | Covariant | CovariantToplevel -> Covariant - | Contravariant | ContravariantToplevel -> Contravariant - -let included_qualifiers ?(context=Identical) a1 a2 = - let a1 = filter_qualifier_attributes a1 in - let a2 = filter_qualifier_attributes a2 in - let a1 = dropAttribute "restrict" a1 in - let a2 = dropAttribute "restrict" a2 in - let a1_no_cv = dropAttributes ["const"; "volatile"] a1 in - let a2_no_cv = dropAttributes ["const"; "volatile"] a2 in - let is_equal = Cil_datatype.Attributes.equal a1 a2 in - if is_equal then true - else begin - match context with - | Identical -> false - | Covariant -> Cil_datatype.Attributes.equal a1_no_cv a2 - | Contravariant -> Cil_datatype.Attributes.equal a1 a2_no_cv - | CovariantToplevel | ContravariantToplevel | IdenticalToplevel -> true - end - -(* precondition: t1 and t2 must be "compatible" as per combineTypes, i.e. - you must have called [combineTypes t1 t2] before calling this function. *) -let rec have_compatible_qualifiers_deep ?(context=Identical) t1 t2 = - match unrollType t1, unrollType t2 with - | TFun (tres1, Some args1, _, _), TFun (tres2, Some args2, _, _) -> - have_compatible_qualifiers_deep - ~context:(qualifier_context_fun_ret context) tres1 tres2 && - let context = qualifier_context_fun_arg context in - List.for_all2 (fun (_, t1', a1) (_, t2', a2) -> - have_compatible_qualifiers_deep ~context t1' t2' && - included_qualifiers ~context a1 a2) - args1 args2 - | TPtr (t1', a1), TPtr (t2', a2) - | TArray (t1', _, a1), TArray (t2', _, a2) -> - (included_qualifiers ~context a1 a2) && - let context = qualifier_context_ptr context in - have_compatible_qualifiers_deep ~context t1' t2' - | _, _ -> included_qualifiers ~context (typeAttrs t1) (typeAttrs t2) - let compatibleTypes ?strictReturnTypes ?context t1 t2 = let r = combineTypes ?strictReturnTypes CombineOther t1 t2 in (* C99, 6.7.3 §9: "... to be compatible, both shall have the identically @@ -6283,9 +6299,258 @@ let areCompatibleTypes ?strictReturnTypes ?context t1 t2 = ignore (compatibleTypes ?strictReturnTypes ?context t1 t2); true with Cannot_combine _ -> false +(******************** CASTING *****) + + +let checkCast ?context ?(fromsource=false) = + let rec default_rec oldt newt = + let dkey = Kernel.dkey_typing_cast in + let error s = + if fromsource + then abort_context s + else Kernel.fatal ~current:true s + in + match unrollType oldt, unrollType newt with + | TNamed _, _ + | _, TNamed _ -> Kernel.fatal ~current:true "unrollType failed in checkCast" + | t, TInt (IBool, _) when isScalarType t -> () + | TInt _, TInt _ -> () + | TFloat _, TInt _ -> (* ISO 6.3.1.4.1 *) () + | TInt _, TFloat _ -> (* ISO 6.3.1.4.2 *) () + | TFloat _, TFloat _ -> (* ISO 6.3.1.5.1 *) () + + (* ISO 6.4.4.3 is only about enum constant but ccomp is more permissive. *) + | TEnum _, (TInt _ | TFloat _ | TPtr _ | TEnum _) + | TFloat _ , TEnum _ -> () + | TPtr _, TEnum _ -> + Kernel.debug ~dkey ~current:true + "Casting a pointer into an enumeration type" + | TInt _, TEnum _ -> () + + | _, TVoid _ -> + (* ISO 6.3.2.2 *) + Kernel.debug ~level:3 + "Casting a value into void: expr is evaluated for side effects" + | TPtr (t, _), TPtr (TVoid _, _) when isObjectType t -> + (* ISO 6.3.2.3.1 *) () + | TPtr (TVoid _, _), TPtr (t, _) when isObjectType t -> + (* ISO 6.3.2.3.1 *) () + | TInt _, TPtr _ -> (* ISO 6.3.2.3.5 *) () + | TPtr _, TInt _ -> (* ISO 6.3.2.3.6 *) + if not fromsource && newt != theMachine.upointType + then + Kernel.warning + ~wkey:Kernel.wkey_int_conversion + ~current:true + "Conversion from a pointer to an integer without an explicit cast" + | TPtr (t1, _), TPtr (t2, _) when isObjectType t1 && isObjectType t2 -> + (* ISO 6.3.2.3.7 *) () + | TPtr (t1, _), TPtr (t2, _) when isFunctionType t1 && isFunctionType t2 -> + (* ISO 6.3.2.3.8 *) + if not (areCompatibleTypes ?context oldt newt) + then + Kernel.warning + ~wkey:Kernel.wkey_incompatible_types_call + ~current:true + "implicit conversion between incompatible function types:@ \ + %a@ and@ %a" + Cil_datatype.Typ.pretty oldt Cil_datatype.Typ.pretty newt + + (* accept converting a ptr to function to/from a ptr to void, even though + not really accepted by the standard. + Main compilers supports it. *) + | TPtr (TFun _, _), TPtr (TVoid _, _) -> () + | TPtr (TVoid _, _), TPtr (TFun _, _) -> () + + | TFun _, TPtr (TFun _, _) -> (* ISO 6.3.2.1.4 *) () + + | TArray (t1, _, _), TPtr (t2, _) -> + (* ISO 6.3.2.1.3 *) + if not (areCompatibleTypes ?context t1 t2) + then + Kernel.warning + ~wkey:Kernel.wkey_incompatible_types_call + ~current:true + "conversion between incompatible from array type to pointer type:@ \ + %a@ and@ %a" + Cil_datatype.Typ.pretty oldt Cil_datatype.Typ.pretty newt + + | TArray (t1, _, _), TArray (t2, _, _) -> + if not (areCompatibleTypes ?context t1 t2) + then + Kernel.warning + ~wkey:Kernel.wkey_incompatible_types_call + ~current:true + "conversion between incompatible array types :@ \ + %a@ and@ %a" + Cil_datatype.Typ.pretty oldt Cil_datatype.Typ.pretty newt + + (* pointer to potential function type. Note that we do not + use unrollTypeDeep above in order to avoid needless divergence with + original type in the sources. + *) + | TPtr (TFun _, _), TPtr (TNamed (ti, nattr), pattr) -> + default_rec oldt (TPtr (typeAddAttributes nattr ti.ttype, pattr)) + | TPtr (TNamed (ti, nattr), pattr), TPtr (TFun _, _) -> + default_rec (TPtr (typeAddAttributes nattr ti.ttype, pattr)) newt + + | TFloat _, TPtr _ + | TPtr _, TFloat _ -> + (* ISO 6.5.4.4 *) + error "illegal conversion between float and pointers" + + (* No other conversion implying a pointer to function + and a pointer to object are supported. *) + | TPtr (t1, _), TPtr (t2, _) when isFunctionType t1 && isObjectType t2 -> + Kernel.warning + ~wkey:Kernel.wkey_incompatible_pointer_types + ~current:true + "casting function to %a" Cil_datatype.Typ.pretty newt + | TPtr (t1, _), TPtr (t2, _) when isFunctionType t2 && isObjectType t1 -> + Kernel.warning + ~wkey:Kernel.wkey_incompatible_pointer_types + ~current:true + "casting function from %a" Cil_datatype.Typ.pretty oldt + + + | _, TPtr (t1, _) when isFunctionType t1 -> + error "cannot cast %a to function type" + Cil_datatype.Typ.pretty oldt + + | t1, t2 when isArithmeticType t1 && isArithmeticType t2 -> + (* ISO 6.5.16.1.1#1 *) () + + + | TComp _, TComp _ when areCompatibleTypes oldt newt -> + (* ISO 6.5.16.1.1#2*) () + + (* If we try to pass a transparent union value to a function + expecting a transparent union argument, the argument type would + have been changed to the type of the first argument, and we'll + see a cast from a union to the type of the first argument. Turn + that into a field access *) + | TComp (_, _), _ -> + begin + match isTransparentUnion oldt with + | None -> + abort_context "cast from %a to %a" + Cil_datatype.Typ.pretty oldt Cil_datatype.Typ.pretty newt + | Some _ -> () + end + + | TBuiltin_va_list _, (TInt _ | TPtr _) -> () + + | (TInt _ | TPtr _), TBuiltin_va_list _ -> + Kernel.debug ~dkey ~current:true + "Casting %a to __builtin_va_list" Cil_datatype.Typ.pretty oldt + + | _, t1 when fromsource && not (isScalarType t1) -> + (* ISO 6.5.4.2 *) + error "Cast over a non-scalar type %a" + Cil_datatype.Typ.pretty newt + + | _ -> + error "cannot cast from %a to %a@\n" + Cil_datatype.Typ.pretty oldt Cil_datatype.Typ.pretty newt + in default_rec + +let rec castReduce fromsource force = + let dkey = Kernel.dkey_typing_cast in + let rec rec_default oldt newt e = + let loc = e.eloc in + let normalized_newt = type_remove_attributes_for_c_cast (unrollType newt) in + let res e = new_exp ~loc (CastE (type_remove_qualifier_attributes newt, e)) in + match unrollType oldt, normalized_newt, e.enode with + (* In the case were we have a representation for the literal, + explicitly add the cast. *) + | _, TInt (newik, []), Const (CInt64 (i, _, None)) -> + (* ISO 6.3.1.3.2 *) kinteger64 ~loc ~kind:newik i + + | _, TPtr _, CastE (_, e') -> + begin + match unrollType (typeOf e'), e'.enode with + | (TPtr _ as typ''), _ -> + (* Old cast can be removed...*) + if need_cast ~force newt typ'' then res e' + else (* In fact, both casts can be removed. *) e' + | _, Const (CInt64 (i, _, _)) when Integer.is_zero i -> res e' + | _ -> res e + end + + | TInt (ik, _), TEnum (ei, _), Const (CEnum { eihost = ei'}) when + ei.ename = ei'.ename && not fromsource && + bytesSizeOfInt ik = bytesSizeOfInt ei'.ekind -> e + + | TFun _, TPtr (TFun _, _), Lval lv -> mkAddrOf ~loc lv + + | t, TInt (IBool, _), _ when isScalarType t -> + if is_boolean_result e then begin + Kernel.debug ~dkey "Explicit cast to Boolean: %a" !pp_exp_ref e; + res e + end else begin + Kernel.debug ~dkey + "bool conversion by checking !=0: %a" !pp_exp_ref e; + let cmp = mkBinOp ~loc Ne e (integer ~loc 0) in + let oldt = typeOf cmp in + rec_default oldt newt cmp + end + + | TComp _, _, _ -> + begin + match isTransparentUnion oldt with + | None -> + abort_context "cast from %a to %a" + Cil_datatype.Typ.pretty oldt Cil_datatype.Typ.pretty newt + | Some fstfield -> + (* We do it now only if the expression is an lval *) + let e' = + match e.enode with + | Lval lv -> + new_exp ~loc:e.eloc + (Lval (addOffsetLval (Field (fstfield, NoOffset)) lv)) + | _ -> + Kernel.fatal ~current:true + "castTo: transparent union expression is not an lval: %a\n" + Cil_datatype.Exp.pretty e + + in + (* Continue casting *) + rec_default fstfield.ftype newt e' + end + + | _ -> res e + in rec_default + +and mkCastTGen ?(check=true) ?context ?(fromsource=false) ?(force=false) + ~(oldt: typ) ~(newt: typ) e = + let dkey = Kernel.dkey_typing_cast in + if + (if fromsource + then not (need_cast ~force oldt newt) + else not (need_cast ~force:false oldt newt)) + then + begin + Kernel.debug ~dkey "no cast to perform"; + let returned_type = + match newt with + | TNamed _ -> newt + | _ -> oldt + in + (returned_type, e) + end + else + let newt = if fromsource then newt else !typeForInsertedCast e oldt newt in + if check then checkCast ?context ~fromsource oldt newt; + (newt, castReduce fromsource force oldt newt e) + +and mkCastT ?(check=true) ?(force=false) ~(oldt: typ) ~(newt: typ) e = + snd (mkCastTGen ~check ~context:Identical ~force ~oldt ~newt e) + +and mkCast ?(check=true) ?force ~(newt: typ) e = + mkCastT ~check ?force ~oldt:(typeOf e) ~newt e (* TODO: unify this with doBinOp in Cabs2cil. *) -let mkBinOp ~loc op e1 e2 = +and mkBinOp ~loc op e1 e2 = let t1 = typeOf e1 in let t2 = typeOf e2 in let machdep = false in @@ -6317,7 +6582,7 @@ let mkBinOp ~loc op e1 e2 = let compare_pointer op ?cast1 ?cast2 e1 e2 = let do_cast e = function | None -> e - | Some t' -> mkCastT ~force:false ~oldt:(typeOf e) ~newt:t' e + | Some t' -> mkCast ~force:false ~newt:t' e in let e1, e2 = if need_cast ~force:true (typeOf e1) (typeOf e2) then diff --git a/src/kernel_services/ast_queries/cil.mli b/src/kernel_services/ast_queries/cil.mli index ed7a2d90c3bacd176d6bfedd45e46a4b0f203ea8..202240172646694cf72d8211023f7a858620703c 100644 --- a/src/kernel_services/ast_queries/cil.mli +++ b/src/kernel_services/ast_queries/cil.mli @@ -587,6 +587,11 @@ val isFunPtrType: typ -> bool @since 18.0-Argon *) val isLogicFunPtrType: logic_type -> bool +(** Check if a type is a transparent union, and return the first field + + @since Frama-C+dev *) +val isTransparentUnion : typ -> fieldinfo option + (** True if the argument is the type for reified C types. *) val isTypeTagType: logic_type -> bool @@ -1150,17 +1155,67 @@ val mkString: loc:location -> string -> exp *) val need_cast: ?force:bool -> typ -> typ -> bool +(** [typeForInsertedCast expr original_type destination_type] + returns the type into which [expr], which has type [original_type] and + whose type must be converted into [destination_type], must be casted. + + By default, returns [destination_type]. + + This applies only to implicit casts. Casts already present + in the source code are exempt from this hook. + + @since Frama-C+dev*) +val typeForInsertedCast: (exp -> typ -> typ -> typ) ref + +(** [checkCast context fromsource oldt newt] emits a warning or an error + if the cast from [oldt] to [newt] is invalid. + Otherwise, doesn't make anything. + [fromsource] is [false] (default) if the cast is not from the source. + Check [areCompatibleTypes] documentation for [context]. + + Suspicious cases that only emits a warning : + - Implicit cast from a pointer to an integer. + - Cast from a pointer to a function type to another pointer to a function + type when the function types are not compatible. + - Cast from an array to a pointer/array when types are not compatible. + - Cast, in both directions, between pointer to an object type and pointer + to a function type. + + @since Frama-C+dev*) +val checkCast: + ?context:qualifier_check_context -> ?fromsource:bool -> typ -> typ -> unit + + +(** Generic version of {!Cil.mkCastT}. + Construct a cast when having the old type of the expression. + [fromsource] is [false] (default) if the cast is not from the source. + If [check] is [true] (default), we check that the cast is valid, + fail if the cast is invalid. + If the new type is the same as the old type, then no cast is added, + unless [force] is [true] (default is [false]). + Casting from [oldt] to [newt]. + Take the expression as argument and can modify it. + Return the new type and the new expression. + + @since Frama-C+dev +*) +val mkCastTGen: ?check:bool -> ?context:qualifier_check_context -> + ?fromsource:bool -> ?force:bool -> oldt:typ -> newt:typ -> exp -> typ * exp + (** Construct a cast when having the old type of the expression. If the new type is the same as the old type, then no cast is added, unless [force] - is [true] (default is [false]) + is [true] (default is [false]). + Fail if the cast is invalid. @before 23.0-Vanadium different order of arguments. + @before Frama-C+dev no [check] argument, it was always [false]. *) -val mkCastT: ?force:bool -> oldt:typ -> newt:typ -> exp -> exp +val mkCastT: ?check:bool -> ?force:bool -> oldt:typ -> newt:typ -> exp -> exp (** Like {!Cil.mkCastT} but uses typeOf to get [oldt] @before 23.0-Vanadium different order of arguments. + @before Frama-C+dev no [check] argument, it was always [false]. *) -val mkCast: ?force:bool -> newt:typ -> exp -> exp +val mkCast: ?check:bool -> ?force:bool -> newt:typ -> exp -> exp (** Equivalent to [stripCasts] for terms. *) val stripTermCasts: term -> term diff --git a/src/kernel_services/ast_queries/logic_typing.ml b/src/kernel_services/ast_queries/logic_typing.ml index 13ea19b232fa3826cfcc3e388b49a4c67132d3a5..4cfddc62f891ddba3b715a74d7d4d96e67a2cd96 100644 --- a/src/kernel_services/ast_queries/logic_typing.ml +++ b/src/kernel_services/ast_queries/logic_typing.ml @@ -1351,53 +1351,21 @@ struct end - let rec c_cast_to ot nt e = + let c_cast_to ot nt e = if is_same_c_type ot nt then (ot, e) - else begin - let result = (nt, mk_cast e (Ctype nt)) in - match ot, nt with - | TNamed(r, _), _ -> c_cast_to r.ttype nt e - | _, TNamed(r, _) -> c_cast_to ot r.ttype e - | TInt(_ikindo,_), TInt(_ikindn,_) -> - result - | TInt _, TPtr _ -> result - | TPtr _, TInt _ -> result - | ((TArray (told,_,_) | TPtr (told,_)), - (TPtr (tnew,_) | TArray(tnew,_,_))) - when is_same_c_type told tnew -> result - | (TPtr _ | TArray _), (TPtr _ | TArray _) - when isLogicNull e -> result - | TPtr _, TPtr _ when isVoidPtrType nt -> (nt, e) - | TPtr (t1,_), TPtr (t2,_) when - Cil.isFunctionType t1 && - Cil.isFunctionType t2 && - is_compatible_funtype t1 t2 -> result - | TEnum _, TInt _ -> result - | TFloat _, (TInt _|TEnum _) -> result - | (TInt _|TEnum _), TFloat _ -> result - | TFloat _, TFloat _ -> result - | TInt _, TEnum _ -> result - | TEnum _, TEnum _ -> result - | TEnum _, TPtr _ -> result - | TBuiltin_va_list _, (TInt _ | TPtr _) -> - result - | (TInt _ | TPtr _), TBuiltin_va_list _ -> - Kernel.debug ~level:3 "Casting %a to __builtin_va_list" - Cil_printer.pp_typ ot; - result - | TPtr _, TEnum _ -> - Kernel.debug ~level:3 "Casting a pointer into an enumeration type"; - result - | (TInt _ | TEnum _ | TPtr _ ), TVoid _ -> - (ot, e) - | TComp (comp1, _), TComp (comp2, _) - when comp1.ckey = comp2.ckey -> - nt, e - | _ -> - Kernel.fatal ~current:true - "Logic_typing.c_cast_to: %a -> %a@." - Cil_printer.pp_typ ot Cil_printer.pp_typ nt - end + else + begin + Cil.checkCast ot nt; + match Cil.unrollType ot, Cil.unrollType nt with + | TPtr _, TPtr _ when isVoidPtrType nt -> + nt, e + | (TInt _ | TEnum _ | TPtr _ ), TVoid _ -> + ot, e + | TComp (comp1, _), TComp (comp2, _) when comp1.ckey = comp2.ckey -> + nt, e + | _ -> nt, mk_cast e (Ctype nt) + end + (* for overloading: raised when an arguments list does not fit a formal parameter list *) diff --git a/src/plugins/variadic/standard.ml b/src/plugins/variadic/standard.ml index 16c4a9e8c6bc82d96fc80b0af123a0ec641ab783..98eefe0444d8a2b8313c137f22f55668bff17537 100644 --- a/src/plugins/variadic/standard.ml +++ b/src/plugins/variadic/standard.ml @@ -138,7 +138,7 @@ let cast_arg i paramtyp exp = (i + 1) pretty_typ argtyp pretty_typ paramtyp end; - Cil.mkCast ~force:false ~newt:paramtyp exp + Cil.mkCast ~check:false ~force:false ~newt:paramtyp exp (* cast a list of args to the tparams list of types and remove unused args *) diff --git a/tests/syntax/Enum_repr.ml b/tests/syntax/Enum_repr.ml index 907d611a59531e83826bbde6cf7e45831311cc4d..3346cb44b0cf42d98bf275a8489e244f30513bf6 100644 --- a/tests/syntax/Enum_repr.ml +++ b/tests/syntax/Enum_repr.ml @@ -1,13 +1,13 @@ open Cil_types let warn_cast = - let typeForInsertedCast = !Cabs2cil.typeForInsertedCast in + let typeForInsertedCast = !Cil.typeForInsertedCast in fun e t1 t2 -> Kernel.feedback ~source:(fst e.eloc) "Inserted implicit cast from %a to %a" Printer.pp_typ t1 Printer.pp_typ t2; typeForInsertedCast e t1 t2 -let () = Cabs2cil.typeForInsertedCast := warn_cast +let () = Cil.typeForInsertedCast := warn_cast let run () = let f = Ast.get () in diff --git a/tests/syntax/inserted_casts.ml b/tests/syntax/inserted_casts.ml index ebd466c230473c94bf6623deb3dffb01e1017fea..87e83b394c134b127227ba1b3d14565b5fd1aa19 100644 --- a/tests/syntax/inserted_casts.ml +++ b/tests/syntax/inserted_casts.ml @@ -11,4 +11,4 @@ let print_warning e ot nt = nt ;; -Cabs2cil.typeForInsertedCast := print_warning +Cil.typeForInsertedCast := print_warning diff --git a/tests/syntax/oracle/array_cast_bts1099.res.oracle b/tests/syntax/oracle/array_cast_bts1099.res.oracle index 09ba17a47e269d5a40446c613f67d7d35cd6f4fd..4d39dbf0d7d154c649c738f8b024cd5518ecde1d 100644 --- a/tests/syntax/oracle/array_cast_bts1099.res.oracle +++ b/tests/syntax/oracle/array_cast_bts1099.res.oracle @@ -1,6 +1,6 @@ [kernel] Parsing array_cast_bts1099.i (no preprocessing) [kernel] array_cast_bts1099.i:12: User Error: - explicit cast: cast over a non-scalar type t + Cast over a non-scalar type t 10 int tab1[4]; 11 u* p = &tab1; 12 t* p2 = (t) p; diff --git a/tests/syntax/oracle/wrong-assignment.res.oracle b/tests/syntax/oracle/wrong-assignment.res.oracle index 85766b5401ffebf7cee24786897212ae7241fedc..e3ed14ddfe7d6fbd0482c6d74157fd906d72f0a4 100644 --- a/tests/syntax/oracle/wrong-assignment.res.oracle +++ b/tests/syntax/oracle/wrong-assignment.res.oracle @@ -1,6 +1,6 @@ [kernel] Parsing wrong-assignment.i (no preprocessing) [kernel] wrong-assignment.i:13: User Error: - implicit cast: ebool -> _Bool + cast from ebool to _Bool 11 void d() { 12 // this assignment should be rejected 13 c.a = b;