diff --git a/src/plugins/qed/logic.mli b/src/plugins/qed/logic.mli
index f481bf9030c29a002df93d1d4996e8d67236adfe..96351d95a36f89d362d2afa30bf2afe4a592ed49 100644
--- a/src/plugins/qed/logic.mli
+++ b/src/plugins/qed/logic.mli
@@ -324,6 +324,9 @@ sig
   val e_forall : var list -> term -> term
   val e_exists : var list -> term -> term
   val e_lambda : var list -> term -> term
+  val e_close_forall : term -> term
+  val e_close_exists : term -> term
+  val e_close_lambda : term -> term
   val e_apply : term -> term list -> term
 
   val e_bind : binder -> var -> term -> term
diff --git a/src/plugins/qed/term.ml b/src/plugins/qed/term.ml
index 188886fc5ff3f2e249a3c361f148555afab81d7d..f6ba7a4013634b982f5183f44b039aa2d74e08d3 100644
--- a/src/plugins/qed/term.ml
+++ b/src/plugins/qed/term.ml
@@ -2143,6 +2143,38 @@ struct
     set mu (e_var x) (c_bvar k t) ;
     walk mu x lc
 
+  (* Alpha-convert free-variables in xs with the top-most bound variables *)
+  (* Only activate flag subset if lc.vars is a subset of xs *)
+  (* Warning: ~reversed:false needs to compute the length of xs *)
+  let lc_close_xs ?(subset = false) ~reversed xs (lc : lc_term) : lc_term =
+    let mu = cache () in
+    begin
+      if reversed then
+        let n = Bvars.order lc.bind in
+        List.iteri (fun i x ->
+            set mu (e_var x) (c_bvar (n+i) (tau_of_var x))
+          ) xs
+      else
+        let n = Bvars.order lc.bind + List.length xs -1 in
+        List.iteri (fun i x ->
+            set mu (e_var x) (c_bvar (n-i) (tau_of_var x))
+          ) xs
+    end;
+    (* if Vars.subset lc.vars xs then*)
+    if subset then
+      let rec walk mu lc =
+        if not (Vars.is_empty lc.vars) then
+          get mu (lc_alpha (walk mu)) lc
+        else lc in
+      walk mu lc
+    else
+      let xs = List.fold_left (fun xs x -> Vars.add x xs) Vars.empty xs in
+      let rec walk mu lc =
+        if Vars.intersect xs lc.vars then
+          get mu (lc_alpha (walk mu)) lc
+        else lc in
+      walk mu lc
+
   (* Alpha-convert top-most bound variable with free-variable x *)
   let lc_open x (lc : lc_term) : lc_term =
     let rec walk mu k lc =
@@ -2269,6 +2301,15 @@ struct
   let filter sigma e =
     Subst.filter sigma e || not (Bvars.is_empty e.bind)
 
+  let bind_qxs qxs e =
+    let qxs = List.filter (function
+        | Lambda, _ -> true
+        | Forall, x | Exists, x -> Vars.mem x e.vars
+      ) qxs in
+    let rxs = List.map (fun (_q, x) -> x) qxs in
+    let e = lc_close_xs ~reversed:true rxs e in
+    List.fold_left (fun e (q, x) -> c_bind q (tau_of_var x) e) e qxs
+
   let rec subst sigma alpha e =
     if filter sigma e then
       incache (Subst.cache sigma) sigma alpha e
@@ -2304,21 +2345,7 @@ struct
       let alpha = Intmap.add k (e_var x) alpha in
       let qs = (q,x) :: qs in
       bind sigma alpha qs a
-    | _ ->
-      (* HERE:
-         This final binding of variables could be parallelized
-         if Bvars is precise enough *)
-      List.fold_left
-        (fun e (q,x) ->
-           if Vars.mem x e.vars then
-             let t = tau_of_var x in
-             (* HERE:
-                possible to insert a recursive call to let-intro
-                it will use a new instance of e_subst_var that
-                will work on a different sigma *)
-             c_bind q t (lc_close x e)
-           else e
-        ) (subst sigma alpha e) qs
+    | _ -> bind_qxs qs (subst sigma alpha e)
 
   and apply sigma beta f vs =
     match f.repr, vs with
@@ -2504,13 +2531,47 @@ struct
 
   let e_close qs a = List.fold_left (fun b (q,x) -> e_bind q x b) a qs
 
-  let rec bind_xs q xs e =
-    match xs with [] -> e | x::xs -> e_bind q x (bind_xs q xs e)
+  let bind_xs q xs e =
+    let xs = match q with
+      | Lambda -> xs
+      | Forall | Exists -> List.filter (fun x -> Vars.mem x e.vars) xs
+    in (* let_intro_case have to be called sequentially because of cases like
+          '\forall x y, 42 = x + y -> P' where we detect two let-in variables in parralel
+          but we can only simplify one in practice.*)
+    let rec aux e xs =
+      let e, xs, changed = List.fold_right (fun x (e, xs', b) ->
+          match let_intro_case q x e with
+          | None -> e, x::xs', b
+          | Some v -> e_subst_var x v e, xs', true
+        ) xs (e, [], false) in
+      if changed then aux e xs else e, xs
+    in
+    let e, xs = aux e xs in
+    let e = lc_close_xs ~reversed:false xs e in
+    List.fold_right (fun x e -> c_bind q (tau_of_var x) e) xs e
 
   let e_forall = bind_xs Forall
   let e_exists = bind_xs Exists
   let e_lambda = bind_xs Lambda
 
+  let bind_all q e =
+    let rec aux e =
+      let e, changed = Vars.fold (fun x (e, b) ->
+          match let_intro_case q x e with
+          | None -> e, b
+          | Some v -> e_subst_var x v e, true
+        ) e.vars (e, false) in
+      if changed then aux e else e
+    in
+    let e = aux e in
+    let rxs = List.sort (fun x y -> POOL.compare y x) (Vars.elements e.vars) in
+    let e = lc_close_xs ~subset:true ~reversed:true rxs e in
+    List.fold_left (fun e x -> c_bind q (tau_of_var x) e) e rxs
+
+  let e_close_forall = bind_all Forall
+  let e_close_exists = bind_all Exists
+  let e_close_lambda = bind_all Lambda
+
   (* -------------------------------------------------------------------------- *)
   (* --- Iterators                                                          --- *)
   (* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/wp/Lang.ml b/src/plugins/wp/Lang.ml
index eabb6008c81ca45170de446d1244c53128f7e8e7..7869f466d863f94344ca26fb4337f80d7d76551e 100644
--- a/src/plugins/wp/Lang.ml
+++ b/src/plugins/wp/Lang.ml
@@ -825,7 +825,7 @@ struct
   let e_vars e = List.sort Var.compare (Vars.elements (vars e))
   let p_vars = e_vars
   let p_call = e_fun ~result:Prop
-  let p_close p = p_forall (p_vars p) p
+  let p_close = e_close_forall
 
   let occurs x t = Vars.mem x (vars t)
   let intersect a b = Vars.intersect (vars a) (vars b)
diff --git a/src/plugins/wp/tests/wp_acsl/oracle/struct_fields.res.oracle b/src/plugins/wp/tests/wp_acsl/oracle/struct_fields.res.oracle
index df29522c76ece81e57062b386000ae24c92c0429..2aa95174028d023e0f7831b5cb8d8e0656e045f2 100644
--- a/src/plugins/wp/tests/wp_acsl/oracle/struct_fields.res.oracle
+++ b/src/plugins/wp/tests/wp_acsl/oracle/struct_fields.res.oracle
@@ -4,9 +4,9 @@
 [rte:annot] annotating function foo
 [wp] 2 goals scheduled
 ---------------------------------------------
---- Context 'typed_foo' Cluster 'Init_S1_X' 
+--- Context 'typed_foo' Cluster 'Chunk' 
 ---------------------------------------------
-theory Init_S1_X
+theory Chunk
   (* use why3.BuiltIn.BuiltIn *)
   
   (* use bool.Bool *)
@@ -21,8 +21,18 @@ theory Init_S1_X
   
   (* use map.Map *)
   
-  type Init_S1_X =
-    | Init_S1_X1 (Init_F1_X_a:bool) (Init_F1_X_b:bool) (Init_F1_X_c:bool)
+  (* use frama_c_wp.memory.Memory *)
+  
+  (* use frama_c_wp.cint.Cint *)
+  
+  predicate is_sint8_chunk (m:addr -> int) =
+    forall a:addr. is_sint8 (get m a)
+  
+  predicate is_sint16_chunk (m:addr -> int) =
+    forall a:addr. is_sint16 (get m a)
+  
+  predicate is_sint32_chunk (m:addr -> int) =
+    forall a:addr. is_sint32 (get m a)
 end
 ---------------------------------------------
 --- Context 'typed_foo' Cluster 'S1_X' 
@@ -51,6 +61,27 @@ theory S1_X
     (is_sint8 (F1_X_a s) /\ is_sint16 (F1_X_b s)) /\ is_sint32 (F1_X_c s)
 end
 ---------------------------------------------
+--- Context 'typed_foo' Cluster 'Init_S1_X' 
+---------------------------------------------
+theory Init_S1_X
+  (* use why3.BuiltIn.BuiltIn *)
+  
+  (* use bool.Bool *)
+  
+  (* use int.Int *)
+  
+  (* use int.ComputerDivision *)
+  
+  (* use real.RealInfix *)
+  
+  (* use frama_c_wp.qed.Qed *)
+  
+  (* use map.Map *)
+  
+  type Init_S1_X =
+    | Init_S1_X1 (Init_F1_X_a:bool) (Init_F1_X_b:bool) (Init_F1_X_c:bool)
+end
+---------------------------------------------
 --- Context 'typed_foo' Cluster 'Compound' 
 ---------------------------------------------
 theory Compound
@@ -177,37 +208,6 @@ theory Compound
      separated p 3 q n ->
      Load_Init_S1_X p (havoc init1 init q n) = Load_Init_S1_X p init
 end
----------------------------------------------
---- Context 'typed_foo' Cluster 'Chunk' 
----------------------------------------------
-theory Chunk
-  (* use why3.BuiltIn.BuiltIn *)
-  
-  (* use bool.Bool *)
-  
-  (* use int.Int *)
-  
-  (* use int.ComputerDivision *)
-  
-  (* use real.RealInfix *)
-  
-  (* use frama_c_wp.qed.Qed *)
-  
-  (* use map.Map *)
-  
-  (* use frama_c_wp.memory.Memory *)
-  
-  (* use frama_c_wp.cint.Cint *)
-  
-  predicate is_sint8_chunk (m:addr -> int) =
-    forall a:addr. is_sint8 (get m a)
-  
-  predicate is_sint16_chunk (m:addr -> int) =
-    forall a:addr. is_sint16 (get m a)
-  
-  predicate is_sint32_chunk (m:addr -> int) =
-    forall a:addr. is_sint32 (get m a)
-end
 [wp:print-generated] 
   theory WP
     (* use why3.BuiltIn.BuiltIn *)
@@ -226,16 +226,15 @@ end
     
     (* use frama_c_wp.memory.Memory *)
     
-    (* use Init_S1_X *)
+    (* use Chunk *)
     
-    (* use Compound *)
+    (* use S1_X *)
     
-    (* use Chunk *)
+    (* use Compound *)
     
     goal wp_goal :
-      forall t:addr -> bool, x:Init_S1_X, t1:int -> int, t2:addr -> int, t3:
-       addr -> int, t4:addr -> int, a:addr.
-       Load_Init_S1_X a t = x ->
+      forall t:addr -> bool, t1:int -> int, t2:addr -> int, t3:addr -> int, t4:
+       addr -> int, a:addr.
        region (a.base) <= 0 ->
        is_sint16_chunk t3 ->
        is_sint32_chunk t4 ->