diff --git a/src/kernel_internals/typing/oneret.ml b/src/kernel_internals/typing/oneret.ml index 80708cb69689325ac5b9e5a75c4052ddabac5e5c..14e608a65c8d91c938fdcee8aea4103833077df3 100644 --- a/src/kernel_internals/typing/oneret.ml +++ b/src/kernel_internals/typing/oneret.ml @@ -120,18 +120,28 @@ let collect_returns (ca : Cil_types.code_annotation) = | _ -> [] let encapsulate_local_vars f = - (* we must ensure that our gotos to the end of the function do not - bypass declaration of objects with destructors, or there will be - issues when inserting the destructor calls. If needed, enclose - the main body (except return and the declaration of the retvar) - inside its own block. - *) + (* We must ensure that our gotos to the end of the function do not bypass + declarations of objects with destructors, or we may miss to insert the + corresponding destructor calls. Here is an example, with T a class: + + extern void Ctor(T* x); + + int f(void) { + goto L; + T __attribute__((__fc_destructor)) x; + Ctor(&x); + L: return 42; + } + + Treating such a special case may be hard and not sufficient. Then, although + coarse, our general solution is to enclose the function body (except the + return statement and the declaration of the retvar) inside a block. *) if List.exists (fun v -> Cil.hasAttribute Cabs2cil.frama_c_destructor v.vattr) f.sbody.blocals then begin - let module M = struct exception Found of (block * stmt) end in + let exception Found of (block Stack.t * stmt) in let vis = object val sb = Stack.create () inherit Cil.nopCilVisitor @@ -141,43 +151,49 @@ let encapsulate_local_vars f = method! vstmt s = match s.skind with - | Return _ -> raise (M.Found (Stack.top sb,s)); + | Return _ -> raise (Found (sb, s)); | _ -> DoChildren end in - let ret_block, ret = + let stack_block, ret_stmt = try ignore (visitCilBlock vis f.sbody); - Kernel.fatal "no return statement found inside %a" + Kernel.fatal "No return statement found inside %a" Cil_printer.pp_varinfo f.svar - with M.Found res -> res + with Found res -> res in - let ret_block_body = + let ret_block = Stack.top stack_block in + let ret_block_bstmts = List.filter - (fun s -> not (Cil_datatype.Stmt.equal ret s)) + (fun s -> not (Cil_datatype.Stmt.equal ret_stmt s)) ret_block.bstmts in + ret_block.bstmts <- ret_block_bstmts; + let s1 = Cil.mkStmt (Block f.sbody) in + let b = Cil.mkBlock [s1; ret_stmt] in + (* As we filtered [ret_stmt] out its original block, we now need to move the + declaration of the return variable into [b]. Before doing that, we must + remove such a declaration from its original block, which lies somewhere + in the stack of blocks [stack_block]. *) let retvar = - match ret.skind with - | Return(None, _) -> [] - | Return(Some {enode = Lval (Var v,NoOffset)}, _) -> [v] + match ret_stmt.skind with + | Return(None, _) -> None + | Return(Some {enode = Lval (Var v,NoOffset)}, _) -> Some v | Return _ -> Kernel.fatal "Return node in unexpected format after oneret call" | _ -> Kernel.fatal "find_return did not return Return node" in - let ret_block_locals = - List.filter - (fun v -> - not (List.exists - (fun v' -> Cil_datatype.Varinfo.equal v v') retvar)) - ret_block.blocals + let filter_vi vi block = + block.blocals <- + List.filter (fun vi' -> not (Cil_datatype.Varinfo.equal vi vi')) + block.blocals in - ret_block.bstmts <- ret_block_body; - ret_block.blocals <- ret_block_locals; - let s1 = Cil.mkStmt (Block f.sbody) in - let b = Cil.mkBlock [ s1; ret] in - b.blocals <- retvar; + let iter_block vi = Stack.iter (filter_vi vi) stack_block in + Option.iter iter_block retvar; + (* We can now safely add [retvar] into [b]'s declarations, and use [b] as + the new function's body block. *) + b.blocals <- Option.to_list retvar; f.sbody <- b end