diff --git a/src/plugins/wp/cfgAnnot.ml b/src/plugins/wp/cfgAnnot.ml
index a921d7ac21a0e8021a796aaa960b970eebf4a4a0..d26c4ef545c2f186914e7a08169f08d500705ce4 100644
--- a/src/plugins/wp/cfgAnnot.ml
+++ b/src/plugins/wp/cfgAnnot.ml
@@ -178,6 +178,25 @@ let get_disjoint_behaviors kf =
        L.preproc_annot L.labels_fct_pre @@ Ast_info.disjoint_behaviors spec bs
     ) spec.spec_disjoint_behaviors
 
+let normalize_terminates p =
+  let module L = NormAtLabels in
+  L.preproc_annot L.labels_fct_pre @@
+  Logic_const.pat (p.ip_content.tp_statement, BuiltinLabel Pre)
+
+let get_terminates kf =
+  Option.map
+    (fun p -> WpPropId.mk_terminates_id kf Kglobal p, normalize_terminates p)
+    (Annotations.funspec kf).spec_terminates
+
+let get_terminates_goal kf =
+  match (Annotations.funspec kf).spec_terminates with
+  | Some p ->
+      normalize_terminates p
+  | None when Kernel_function.is_definition kf ->
+      Logic_const.pfalse
+  | None ->
+      Logic_const.ptrue
+
 (* -------------------------------------------------------------------------- *)
 (* --- Contracts                                                          --- *)
 (* -------------------------------------------------------------------------- *)
@@ -189,6 +208,7 @@ type contract = {
   contract_exit : WpPropId.pred_info list ;
   contract_smoke : WpPropId.pred_info list ;
   contract_assigns : Cil_types.assigns ;
+  contract_terminates : Cil_types.predicate ;
 }
 
 let assigns_upper_bound behaviors =
@@ -264,13 +284,15 @@ module CallContract = WpContext.StaticGenerator(Kernel_function)
           | WritesAny -> WritesAny
           | Writes froms -> Writes (normalize_froms Normal froms)
         in
+        let terminates = get_terminates_goal kf in
         {
           contract_cond = List.rev !wcond ;
           contract_hpre = List.rev !whpre ;
           contract_post = List.rev !wpost ;
           contract_exit = List.rev !wexit ;
           contract_smoke = [] ;
-          contract_assigns = assigns
+          contract_assigns = assigns ;
+          contract_terminates = terminates ;
         }
     end)
 
@@ -383,6 +405,7 @@ let get_code_assertions ?(smoking=false) kf stmt =
 (* -------------------------------------------------------------------------- *)
 
 type loop_contract = {
+  loop_terminates: predicate option;
   (* to be verified at loop entry *)
   loop_established: WpPropId.pred_info list;
   (* to be assumed for loop current *)
@@ -396,6 +419,7 @@ type loop_contract = {
 }
 
 let reverse_loop_contract l = {
+  loop_terminates = l.loop_terminates ;
   loop_established = List.rev l.loop_established ;
   loop_invariants = List.rev l.loop_invariants ;
   loop_preserved = List.rev l.loop_preserved ;
@@ -439,9 +463,16 @@ module LoopContract = WpContext.StaticGenerator(CodeKey)
             | AVariant(term, None) ->
                 let vpos , vdec =
                   WpStrategy.mk_variant_properties kf stmt ca term in
-                { l with loop_preserved =
-                           normalize_annot vdec ::
-                           normalize_annot vpos ::
+                let intro_terminates (pid, v) =
+                  pid,
+                  match (Annotations.funspec kf).spec_terminates with
+                  | None -> v
+                  | Some t -> Logic_const.pimplies (normalize_terminates t, v)
+                in
+                { l with loop_terminates = None ;
+                         loop_preserved =
+                           intro_terminates (normalize_annot vdec) ::
+                           intro_terminates (normalize_annot vpos) ::
                            l.loop_preserved }
             | AAssigns(_,WritesAny) ->
                 let asgn = WpPropId.mk_loop_any_assigns_info stmt in
@@ -457,6 +488,7 @@ module LoopContract = WpContext.StaticGenerator(CodeKey)
                 end
             | _ -> l
           end stmt {
+          loop_terminates = Some Logic_const.pfalse ;
           loop_established = [] ;
           loop_invariants = [] ;
           loop_preserved = [] ;
@@ -465,12 +497,21 @@ module LoopContract = WpContext.StaticGenerator(CodeKey)
         }
     end)
 
-let get_loop_contract ?(smoking=false) kf stmt =
+let get_loop_contract ?(smoking=false) ?terminates kf stmt =
   let lc = LoopContract.get (kf,stmt) in
-  if smoking && not (WpReached.is_dead_code stmt) then
-    let g = smoke kf ~id:"dead_loop" ~unreachable:stmt () in
-    { lc with loop_smoke = g :: lc.loop_smoke }
-  else lc
+  let lc_smoke = if smoking && not (WpReached.is_dead_code stmt) then
+      let g = smoke kf ~id:"dead_loop" ~unreachable:stmt () in
+      { lc with loop_smoke = g :: lc.loop_smoke }
+    else lc
+  in
+  match lc_smoke.loop_terminates, terminates with
+  | None, _->
+      lc_smoke
+  | Some _, None ->
+      { lc_smoke with loop_terminates = None }
+  | Some loop_terminates, Some terminates ->
+      let prop = Logic_const.pimplies(terminates, loop_terminates) in
+      { lc_smoke with loop_terminates = Some prop }
 
 (* -------------------------------------------------------------------------- *)
 (* --- Clear Tablesnts                                                    --- *)
diff --git a/src/plugins/wp/cfgAnnot.mli b/src/plugins/wp/cfgAnnot.mli
index 1ca50e9f0ce2ca26a0462d03491ae619278641dd..0c42f7aab3cbccaa2fde6d5345715203b1cc5fef 100644
--- a/src/plugins/wp/cfgAnnot.mli
+++ b/src/plugins/wp/cfgAnnot.mli
@@ -56,6 +56,8 @@ val get_behavior_goals :
 val get_complete_behaviors : kernel_function -> pred_info list
 val get_disjoint_behaviors : kernel_function -> pred_info list
 
+val get_terminates : kernel_function -> pred_info option
+
 (* -------------------------------------------------------------------------- *)
 (* --- Property Accessors : Assertions                                    --- *)
 (* -------------------------------------------------------------------------- *)
@@ -76,6 +78,7 @@ val get_stmt_assigns : kernel_function -> stmt -> assigns_full_info list
 (* -------------------------------------------------------------------------- *)
 
 type loop_contract = {
+  loop_terminates: predicate option;
   (** to be verified at loop entry *)
   loop_established: pred_info list;
   (** to be assumed for loop current *)
@@ -88,7 +91,7 @@ type loop_contract = {
   loop_assigns: assigns_full_info list;
 }
 
-val get_loop_contract : ?smoking:bool ->
+val get_loop_contract : ?smoking:bool -> ?terminates:predicate ->
   kernel_function -> stmt -> loop_contract
 
 (* -------------------------------------------------------------------------- *)
@@ -102,6 +105,7 @@ type contract = {
   contract_exit : pred_info list ;
   contract_smoke : pred_info list ;
   contract_assigns : assigns ;
+  contract_terminates : predicate ;
 }
 
 val get_call_contract : ?smoking:stmt -> kernel_function -> stmt -> contract
diff --git a/src/plugins/wp/cfgCalculus.ml b/src/plugins/wp/cfgCalculus.ml
index 697b3eb75e3b8dd4b7f3aa1ce01b892aa447453c..953aab3bce73223ada331ddb1d509f05e5b44e3b 100644
--- a/src/plugins/wp/cfgCalculus.ml
+++ b/src/plugins/wp/cfgCalculus.ml
@@ -139,6 +139,7 @@ struct
     body: Cfg.automaton option;
     succ: Cfg.vertex -> Cfg.G.edge list;
     dead: stmt -> bool ;
+    terminates: WpPropId.pred_info option ;
     we: W.t_env;
     wp: W.t_prop option Vhash.t; (* None is used for non-dag detection *)
     mutable wk: W.t_prop; (* end point *)
@@ -186,6 +187,11 @@ struct
   let prove_property env (p : WpPropId.pred_info) w =
     if is_selected ~goal:true env p then W.add_goal env.we p w else w
 
+  let prove_subproperty env (p : WpPropId.pred_info) prop stmt source w =
+    if is_selected ~goal:true env p
+    then W.add_subgoal env.we p prop stmt source w
+    else w
+
   (* --- Decomposition of WP Rules --- *)
 
   let rec wp (env:env) (a:vertex) : W.t_prop =
@@ -235,11 +241,17 @@ struct
           is_default_bhv m &&
           WpLog.SmokeTests.get () &&
           WpLog.SmokeDeadloop.get () in
-        loop env a s (CfgAnnot.get_loop_contract ~smoking m.kf s)
+        let terminates = Option.map snd env.terminates in
+        loop env a s (CfgAnnot.get_loop_contract ~smoking ?terminates m.kf s)
     | Edges -> successors env a
 
   (* Compute loops *)
   and loop env a s (lc : CfgAnnot.loop_contract) : W.t_prop =
+    let insert_terminates w = match env.terminates, lc.loop_terminates with
+      | None, _ | _, None -> w (* no terminates goal or nothing to prove *)
+      | Some t, Some prop -> prove_subproperty env t prop s FromCode w
+    in
+    insert_terminates @@
     List.fold_right (prove_property env) lc.loop_established @@
     List.fold_right (use_assigns env) lc.loop_assigns @@
     W.label env.we None (Clabels.loop_current s) @@
@@ -319,10 +331,15 @@ struct
         ~pexit:c.contract_exit
         ~assigns:c.contract_assigns
         ~p_post ~p_exit in
-    if is_default_bhv env.mode then
-      let pre = List.filter (is_selected_callpre env) c.contract_cond in
-      W.call_goal_precond env.we s kf es ~pre w_call
-    else w_call
+    let w_pre = if is_default_bhv env.mode then
+        let pre = List.filter (is_selected_callpre env) c.contract_cond in
+        W.call_goal_precond env.we s kf es ~pre w_call
+      else w_call
+    in
+    match env.terminates with
+    | Some p when is_default_bhv env.mode && is_selected ~goal:true env p ->
+        W.call_terminates env.we p s kf es ~callee_t:c.contract_terminates w_pre
+    | _ -> w_pre
 
   let do_complete_disjoint env w =
     if not (is_default_bhv env.mode) then w
@@ -378,6 +395,12 @@ struct
         env.wk <- if exits then do_exit env ~formals b w else w ;
         wp env cfg.entry_point
 
+  let do_terminates env w =
+    match env.terminates with
+    | Some p when is_default_bhv env.mode && is_selected ~goal:true env p ->
+        prove_property env (fst p, Logic_const.ptrue) w
+    | _ -> w
+
   (* Putting everything together *)
   let compute ~mode ~props =
     let kf = mode.kf in
@@ -392,9 +415,10 @@ struct
          WpLog.SmokeTests.get () &&
          WpLog.SmokeDeadcode.get ()
       then CfgInfos.smoking infos else (fun _ -> false) in
+    let terminates = CfgAnnot.get_terminates kf in
     let env = {
       mode ; props ; body ;
-      succ ; dead ;
+      succ ; dead ; terminates ;
       we = W.new_env kf ;
       wp = Vhash.create 32 ;
       wk = W.empty ;
@@ -409,6 +433,7 @@ struct
       do_preconditions env ~formals bhv @@
       do_complete_disjoint env @@
       do_funbehavior env ~formals ~exits bhv @@
+      do_terminates env @@
       W.empty
     end
 
diff --git a/src/plugins/wp/cfgDump.ml b/src/plugins/wp/cfgDump.ml
index 798d85ac72a8951197527fa8d11c9672bf87f8b1..a7592df0bfb6c2f3d939956d2358c1b833dad9da 100644
--- a/src/plugins/wp/cfgDump.ml
+++ b/src/plugins/wp/cfgDump.ml
@@ -106,6 +106,19 @@ let add_goal env (pid,pred) k =
   Format.fprintf !out "  %a -> %a [ style=dotted ] ;@." pretty u pretty k ;
   merge env u k
 
+let add_subgoal env (pid, t_pred) prop stmt _source k =
+  let u = node () in
+  if Wp_parameters.debug_atleast 1 then
+    Format.fprintf !out "  %a [ color=red , label=\"Prove %a @@ %a (%a)\" ] ;@."
+      pretty u
+      Printer.pp_predicate t_pred
+      Cil_printer.pp_stmt stmt
+      Printer.pp_predicate prop
+  else
+    Format.fprintf !out "  %a [ color=red , label=\"Prove %a\" ] ;@."
+      pretty u WpPropId.pp_propid pid ;
+  merge env u k
+
 let pp_assigns fmt = function
   | Cil_types.WritesAny -> Format.pp_print_string fmt " \\everything"
   | Cil_types.Writes [] -> Format.pp_print_string fmt " \\nothing"
@@ -223,7 +236,20 @@ let call_goal_precond env _stmt kf _es ~pre k =
           (fun (_,p) -> Format.fprintf fmt "\n@[<hov 2>Requires %a ;@]"
               Printer.pp_predicate p) pre
     end ;
-  ignore pre ;
+  Format.fprintf !out "  %a -> %a [ style=dotted ] ;@." pretty u pretty k ;
+  merge env u k
+
+let call_terminates env (_gpid, prop) _stmt kf _es ~callee_t k =
+  let _ = callee_t in
+  let u = node () in
+  Format.fprintf !out "  %a [ color=red , label=\"Prove PreCond %a%t\" ] ;@."
+    pretty u Kernel_function.pretty kf
+    begin fun fmt ->
+      if Wp_parameters.debug_atleast 1 then
+        Format.fprintf fmt "\n@[<hov 2>Terminates if %a[%s] ==> %a[%a];@]"
+          Printer.pp_predicate prop "Caller"
+          Printer.pp_predicate callee_t Kernel_function.pretty kf
+    end ;
   Format.fprintf !out "  %a -> %a [ style=dotted ] ;@." pretty u pretty k ;
   merge env u k
 
diff --git a/src/plugins/wp/cfgInfos.ml b/src/plugins/wp/cfgInfos.ml
index 50ce82a8271924c9a7cef07741647fb3fb0cad87..0bb9bfc65f1412121bdb5b75d644628ff5a08279 100644
--- a/src/plugins/wp/cfgInfos.ml
+++ b/src/plugins/wp/cfgInfos.ml
@@ -108,13 +108,14 @@ let selected_call ~bhv ~prop kf =
 let selected_clause ~prop name getter kf =
   getter kf <> [] && selected_name ~prop name
 
-let selected_terminates kf =
+let selected_terminates ~prop kf =
   match Annotations.terminates kf with
-  | None -> ()
+  | None ->
+      false
   | Some ip ->
-      let loc = ip.ip_content.tp_statement.pred_loc in
-      Wp_parameters.warning ~source:(fst loc)
-        "Terminates not implemented yet (skipped)."
+      let tk_name = "@terminates" in
+      let tp_names = WpPropId.user_pred_names ip.Cil_types.ip_content in
+      WpPropId.are_selected_names prop (tk_name :: tp_names)
 
 let selected_disjoint_complete kf ~bhv ~prop =
   selected_default ~bhv &&
@@ -255,8 +256,8 @@ let compile Key.{ kf ; smoking ; bhv ; prop } =
   Option.iter
     begin fun (cfg : Cfg.automaton) ->
       (* Spec Iteration *)
-      selected_terminates kf ;
-      if selected_disjoint_complete kf ~bhv ~prop ||
+      if selected_terminates ~prop kf ||
+         selected_disjoint_complete kf ~bhv ~prop ||
          (List.exists (selected_bhv ~smoking ~bhv ~prop) behaviors)
       then infos.annots <- true ;
       (* Stmt Iteration *)
diff --git a/src/plugins/wp/cfgWP.ml b/src/plugins/wp/cfgWP.ml
index ba8d3245e87db470fa5b710e25c811b79803fa4e..5f0767c667b169e7b5eff49b5007636e14a92a0b 100644
--- a/src/plugins/wp/cfgWP.ml
+++ b/src/plugins/wp/cfgWP.ml
@@ -55,7 +55,7 @@ struct
 
   type target =
     | Gprop of P.t
-    | Geffect of P.t * Stmt.t * effect_source
+    | Gsource of P.t * Stmt.t * effect_source
     | Gposteffect of P.t
 
   module TARGET =
@@ -65,33 +65,33 @@ struct
       | FromCode -> 1 | FromCall -> 2 | FromReturn -> 3
     let hash = function
       | Gprop p | Gposteffect p -> P.hash p
-      | Geffect(p,s,e) -> P.hash p * 37 + 41 * Stmt.hash s + hsrc e
+      | Gsource(p,s,e) -> P.hash p * 37 + 41 * Stmt.hash s + hsrc e
     let compare g1 g2 =
       if g1 == g2 then 0 else
         match g1,g2 with
         | Gprop p1 , Gprop p2 -> P.compare p1 p2
         | Gprop _ , _ -> (-1)
         | _ , Gprop _ -> 1
-        | Geffect(p1,s1,e1) , Geffect(p2,s2,e2) ->
+        | Gsource(p1,s1,e1) , Gsource(p2,s2,e2) ->
             let c = P.compare p1 p2 in
             if c <> 0 then c else
               let c = Stmt.compare s1 s2 in
               if c <> 0 then c else
                 hsrc e1 - hsrc e2
-        | Geffect _ , _ -> (-1)
-        | _ , Geffect _ -> 1
+        | Gsource _ , _ -> (-1)
+        | _ , Gsource _ -> 1
         | Gposteffect p1 , Gposteffect p2 -> P.compare p1 p2
     let equal g1 g2 = (compare g1 g2 = 0)
-    let prop_id = function Gprop p | Gposteffect p | Geffect(p,_,_) -> p
-    let source = function Gprop _ | Gposteffect _ -> None | Geffect(_,s,e) -> Some(s,e)
+    let prop_id = function Gprop p | Gposteffect p | Gsource(p,_,_) -> p
+    let source = function Gprop _ | Gposteffect _ -> None | Gsource(_,s,e) -> Some(s,e)
     let is_smoke_test = function
       | Gprop p -> WpPropId.is_smoke_test p
-      | Gposteffect _ | Geffect _ -> false
+      | Gposteffect _ | Gsource _ -> false
     let pretty fmt = function
       | Gprop p -> WpPropId.pretty fmt p
-      | Geffect(p,s,FromCode) -> Format.fprintf fmt "%a at sid:%d" WpPropId.pretty p s.sid
-      | Geffect(p,s,FromCall) -> Format.fprintf fmt "Call %a at sid:%d" WpPropId.pretty p s.sid
-      | Geffect(p,s,FromReturn) -> Format.fprintf fmt "Return %a at sid:%d" WpPropId.pretty p s.sid
+      | Gsource(p,s,FromCode) -> Format.fprintf fmt "%a at sid:%d" WpPropId.pretty p s.sid
+      | Gsource(p,s,FromCall) -> Format.fprintf fmt "Call %a at sid:%d" WpPropId.pretty p s.sid
+      | Gsource(p,s,FromReturn) -> Format.fprintf fmt "Return %a at sid:%d" WpPropId.pretty p s.sid
       | Gposteffect p -> Format.fprintf fmt "%a post-effect" WpPropId.pretty p
   end
 
@@ -507,6 +507,18 @@ struct
          let vcs = add_vc (Gprop gpid) ~warn goal wp.vcs in
          { wp with vcs = vcs })
 
+  let add_subgoal wenv (gpid, _) predicate stmt source wp = in_wenv wenv wp
+      (fun env wp ->
+         let outcome = Warning.catch
+             ~severe:true ~effect:"Degenerated goal"
+             (L.pred `Positive env) predicate in
+         let warn,goal = match outcome with
+           | Warning.Result(warn,goal) -> warn,goal
+           | Warning.Failed warn -> warn,F.p_false
+         in
+         let vcs = add_vc (Gsource(gpid, stmt, source)) ~warn goal wp.vcs in
+         { wp with vcs = vcs })
+
   let add_assigns wenv (gpid,ainfo) wp = in_wenv wenv wp
       begin fun env wp ->
         let outcome = Warning.catch
@@ -577,7 +589,7 @@ struct
          in
          let target = match sloc with
            | None -> Gprop e.e_pid
-           | Some stmt -> Geffect(e.e_pid,stmt,source)
+           | Some stmt -> Gsource(e.e_pid,stmt,source)
          in
          Gmap.add target group vcs
       ) effects vcs
@@ -593,7 +605,7 @@ struct
       (fun e vcs ->
          let target = match stmt with
            | None -> Gprop e.e_pid
-           | Some s -> Geffect(e.e_pid,s,FromCode)
+           | Some s -> Gsource(e.e_pid,s,FromCode)
          in
          add_vc target ?warn F.p_false vcs)
       effects vcs
@@ -1026,6 +1038,52 @@ struct
                  ) wp.vcs pre
              in { wp with vcs = vcs })
 
+  (* -------------------------------------------------------------------------- *)
+  (* --- WP RULE : call terminates                                          --- *)
+  (* -------------------------------------------------------------------------- *)
+
+  let call_terminates wenv (gpid, caller_t) stmt kf es ~callee_t wp =
+    in_wenv wenv wp
+      (fun env wp ->
+         let gid = Gsource(gpid, stmt, FromCall) in
+         let outcome = Warning.catch
+             ~severe:true ~effect:"Can not prove call termination"
+             (L.pred `Positive env) caller_t
+         in
+         match outcome with
+         | Warning.Failed warn ->
+             let vcs = add_vc gid ~warn p_false wp.vcs in
+             { wp with vcs = vcs }
+         | Warning.Result(warn, caller_t) ->
+             let sigma = L.current env in
+             let outcome = Warning.catch
+                 ~severe:true ~effect:"Can not prove call preconditions"
+                 (List.map (C.exp sigma)) es in
+             match outcome with
+             | Warning.Failed warn2 ->
+                 let warn = W.union warn warn2 in
+                 let vcs = add_vc gid ~warn p_false wp.vcs in
+                 { wp with vcs = vcs }
+             | Warning.Result(warn2, vs) ->
+                 let warn = W.union warn warn2 in
+                 let init = L.mem_at env Clabels.init in
+                 let call = L.call kf vs in
+                 let call_e = L.mk_env ~here:sigma () in
+                 let call_f = L.call_pre init call sigma in
+                 let outcome = Warning.catch
+                     ~severe:true ~effect:"Can not prove call precondition"
+                     (L.in_frame call_f (L.pred `Positive call_e)) callee_t in
+                 match outcome with
+                 | Warning.Result(warn3,callee_t) ->
+                     let warn = W.union warn warn3 in
+                     let goal = p_imply caller_t callee_t in
+                     let vcs = add_vc gid ~warn goal wp.vcs in
+                     { wp with vcs = vcs }
+                 | Warning.Failed warn3 ->
+                     let warn = W.union warn warn3 in
+                     let vcs = add_vc gid ~warn p_false wp.vcs in
+                     { wp with vcs = vcs })
+
   (* -------------------------------------------------------------------------- *)
   (* --- WP RULE : call postcondition                                       --- *)
   (* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/wp/mcfg.ml b/src/plugins/wp/mcfg.ml
index 568b894770929317b5078add8bfb06d9979f5055..15905e4eab2aecfcec837c07a2690c903bf33cab 100644
--- a/src/plugins/wp/mcfg.ml
+++ b/src/plugins/wp/mcfg.ml
@@ -67,6 +67,8 @@ module type S = sig
   val add_axiom : WpPropId.prop_id -> LogicUsage.logic_lemma -> unit
   val add_hyp  : t_env -> WpPropId.pred_info -> t_prop -> t_prop
   val add_goal : t_env -> WpPropId.pred_info -> t_prop -> t_prop
+  val add_subgoal : t_env -> WpPropId.pred_info ->
+    predicate -> stmt -> WpPropId.effect_source -> t_prop -> t_prop
 
   val add_assigns : t_env -> WpPropId.assigns_info -> t_prop -> t_prop
 
@@ -101,6 +103,10 @@ module type S = sig
     pre: WpPropId.pred_info list ->
     t_prop -> t_prop
 
+  val call_terminates : t_env -> WpPropId.pred_info ->
+    stmt -> kernel_function -> exp list ->
+    callee_t:predicate -> t_prop -> t_prop
+
   val call : t_env -> stmt ->
     lval option -> kernel_function -> exp list ->
     pre:     WpPropId.pred_info list ->
diff --git a/src/plugins/wp/wpPropId.ml b/src/plugins/wp/wpPropId.ml
index c3312d5f84313af0f14bb82cdfc8351ddd311322..7d30a86199f42b9353311eee209fd5e65edcfbf8 100644
--- a/src/plugins/wp/wpPropId.ml
+++ b/src/plugins/wp/wpPropId.ml
@@ -44,6 +44,7 @@ type prop_kind =
   | PKVarPos      (** computation related to a loop variant being positive *)
   | PKAFctOut     (** computation related to the function assigns on normal termination *)
   | PKAFctExit    (** computation related to the function assigns on exit termination *)
+  | PKTerminates  (** computation related to the termination *)
   | PKSmoke      (** expected to fail *)
   | PKPre of kernel_function * stmt * Property.t
   (** precondition for function
@@ -177,6 +178,9 @@ let mk_pre_id kf ki b p =
 let mk_post_id kf ki b p =
   mk_prop PKProp (Property.ip_of_ensures kf ki b p)
 
+let mk_terminates_id kf kinstr p =
+  mk_prop PKTerminates (Property.ip_of_terminates kf kinstr p)
+
 let mk_stmt_post_id kf s b p =
   mk_prop PKProp (Property.ip_of_ensures kf (Kstmt s) b p)
 
@@ -214,6 +218,7 @@ let kind_order = function
   | PKCheck -> 9
   | PKTactic -> 10
   | PKSmoke -> 11
+  | PKTerminates -> 12
 
 let compare_kind k1 k2 = match k1, k2 with
     PKPre (kf1, ki1, p1), PKPre (kf2, ki2, p2) ->
@@ -328,7 +333,7 @@ end = struct
 
   let basename_of_prop_id p =
     match p.p_kind , p.p_prop with
-    | (PKTactic | PKCheck | PKProp | PKPropLoop | PKSmoke) , p ->
+    | (PKTactic | PKCheck | PKProp | PKPropLoop | PKSmoke | PKTerminates) , p ->
         base_id_prop_txt p
     | PKEstablished , p -> base_id_prop_txt p ^ "_established"
     | PKPreserved , p -> base_id_prop_txt p ^ "_preserved"
@@ -411,7 +416,8 @@ struct
 
   let get_prop_id_base p =
     match p.p_kind , p.p_prop with
-    | (PKTactic | PKCheck | PKProp | PKPropLoop | PKSmoke) , p -> get_ip p
+    | (PKTactic | PKCheck | PKProp | PKPropLoop | PKSmoke | PKTerminates) , p ->
+        get_ip p
     | PKEstablished , p -> get_ip p ^ "_established"
     | PKPreserved , p -> get_ip p ^ "_preserved"
     | PKVarDecr , p -> get_ip p ^ "_decrease"
@@ -563,6 +569,7 @@ let label_of_kind = function
   | PKVarPos -> "Positive"
   | PKAFctOut -> "Function assigns"
   | PKAFctExit -> "Exit assigns"
+  | PKTerminates -> "Terminates"
   | PKPre(kf,_,_) ->
       Printf.sprintf "Precondition for '%s'" (Kernel_function.get_name kf)
 
@@ -579,7 +586,7 @@ struct
     | None -> ()
     | Some(k,n) -> fprintf fmt " (%d/%d)" (succ k) n
   let pp_subprop fmt p = match p.p_kind with
-    | PKProp | PKTactic | PKCheck | PKPropLoop | PKSmoke -> ()
+    | PKProp | PKTactic | PKCheck | PKPropLoop | PKSmoke | PKTerminates -> ()
     | PKEstablished -> pp_print_string fmt " (established)"
     | PKPreserved -> pp_print_string fmt " (preserved)"
     | PKVarDecr -> pp_print_string fmt " (decrease)"
@@ -657,6 +664,7 @@ let propid_hints hs p =
   | PKVarPos , _ -> add_required hs "positive"
   | PKAFctOut , _ -> add_required hs "return"
   | PKAFctExit , _ -> add_required hs "exit"
+  | PKTerminates , _ -> add_required hs "terminates"
   | PKPre(kf,st,_) , _ ->
       add_required hs ("precond-" ^ Kernel_function.get_name kf) ;
       stmt_hints hs st
@@ -731,7 +739,7 @@ let prop_id_keys p =
 
 let pp_goal_kind fmt = function
   | PKTactic | PKSmoke | PKCheck
-  | PKProp | PKPropLoop | PKAFctOut | PKAFctExit
+  | PKProp | PKPropLoop | PKAFctOut | PKAFctExit | PKTerminates
   | PKPre _ -> ()
   | PKEstablished -> Format.pp_print_string fmt "Establishment of "
   | PKPreserved -> Format.pp_print_string fmt "Preservation of "
@@ -1040,14 +1048,15 @@ let split_map f pid gs =
 
 let subproofs id = match id.p_kind with
   | PKCheck -> 0
-  | PKProp | PKSmoke | PKTactic | PKPre _ | PKPropLoop -> 1
+  | PKProp | PKSmoke | PKTactic | PKPre _ | PKPropLoop | PKTerminates -> 1
   | PKEstablished | PKPreserved
   | PKVarDecr | PKVarPos
   | PKAFctExit | PKAFctOut -> 2
 
 let subproof_idx id = match id.p_kind with
   | PKCheck -> (-1) (* 0/0 *)
-  | PKProp | PKTactic | PKPre _ | PKSmoke | PKPropLoop -> 0 (* 1/1 *)
+  | PKProp | PKTactic | PKPre _ | PKSmoke | PKPropLoop
+  | PKTerminates -> 0 (* 1/1 *)
   | PKPreserved  -> 0 (* 1/2 *)
   | PKEstablished-> 1 (* 2/2 *)
   | PKVarDecr    -> 0 (* 1/2 *)
@@ -1097,7 +1106,8 @@ let get_induction p =
       | IPAssigns {ias_kf; ias_kinstr=Kstmt stmt} -> Some (ias_kf, stmt)
       | _ -> None
   in match p.p_kind with
-  | PKCheck|PKSmoke |PKAFctOut|PKAFctExit|PKPre _ | PKTactic -> None
+  | PKCheck|PKSmoke |PKAFctOut|PKAFctExit|PKPre _ | PKTactic| PKTerminates ->
+      None
   | PKProp ->
       let loop_stmt_opt = match get_stmt (property_of_id p) with
         | None -> None
diff --git a/src/plugins/wp/wpPropId.mli b/src/plugins/wp/wpPropId.mli
index 20ba4ab98d8f7742b304325fe97835e4e2273322..643b69d72d03055c3323e9dda7e53b95be818221 100644
--- a/src/plugins/wp/wpPropId.mli
+++ b/src/plugins/wp/wpPropId.mli
@@ -100,6 +100,7 @@ type prop_kind =
   | PKVarPos      (** computation related to a loop variant being positive *)
   | PKAFctOut     (** computation related to the function assigns on normal termination *)
   | PKAFctExit    (** computation related to the function assigns on exit termination *)
+  | PKTerminates  (** computation related to the termination *)
   | PKSmoke      (** smoke property *)
   | PKPre of kernel_function * stmt * Property.t (** precondition for function
                                                      at stmt, property of the require. Many information that should come
@@ -196,6 +197,9 @@ val mk_pre_id : kernel_function -> kinstr -> funbehavior ->
 val mk_post_id : kernel_function -> kinstr -> funbehavior ->
   termination_kind * identified_predicate -> prop_id
 
+val mk_terminates_id :
+  kernel_function -> kinstr -> identified_predicate -> prop_id
+
 val mk_stmt_post_id : kernel_function -> stmt -> funbehavior ->
   termination_kind * identified_predicate -> prop_id