diff --git a/theories/heap_lang/tactics.v b/theories/heap_lang/tactics.v
index bf81b68a1a8eb51060fbfdde240294ce360775fd..97a7b0a7a02bf1eb9f8178f2c1747fd7cdc8f242 100644
--- a/theories/heap_lang/tactics.v
+++ b/theories/heap_lang/tactics.v
@@ -8,7 +8,8 @@ expressions into this type we can implementation substitution, closedness
 checking, atomic checking, and conversion into values, by computation. *)
 Module W.
 Inductive expr :=
-  | Val (v : val)
+  (* Value together with the original expression *)
+  | Val (v : val) (e : heap_lang.expr) (H : to_val e = Some v)
   | ClosedExpr (e : heap_lang.expr) `{!Closed [] e}
   (* Base lambda calculus *)
   | Var (x : string)
@@ -37,7 +38,7 @@ Inductive expr :=
 
 Fixpoint to_expr (e : expr) : heap_lang.expr :=
   match e with
-  | Val v => of_val v
+  | Val v e' _ => e'
   | ClosedExpr e _ => e
   | Var x => heap_lang.Var x
   | Rec f x e => heap_lang.Rec f x (to_expr e)
@@ -90,13 +91,16 @@ Ltac of_expr e :=
      let e0 := of_expr e0 in let e1 := of_expr e1 in let e2 := of_expr e2 in
      constr:(CAS e0 e1 e2)
   | to_expr ?e => e
-  | of_val ?v => constr:(Val v)
-  | _ => match goal with H : Closed [] e |- _ => constr:(@ClosedExpr e H) end
+  | of_val ?v => constr:(Val v (of_val v) (to_of_val v))
+  | _ => match goal with
+         | H : to_val e = Some ?v |- _ => constr:(Val v e H)
+         | H : Closed [] e |- _ => constr:(@ClosedExpr e H)
+         end
   end.
 
 Fixpoint is_closed (X : list string) (e : expr) : bool :=
   match e with
-  | Val _ | ClosedExpr _ _ => true
+  | Val _ _ _ | ClosedExpr _ _ => true
   | Var x => bool_decide (x ∈ X)
   | Rec f x e => is_closed (f :b: x :b: X) e
   | Lit _ => true
@@ -110,7 +114,7 @@ Fixpoint is_closed (X : list string) (e : expr) : bool :=
 Lemma is_closed_correct X e : is_closed X e → heap_lang.is_closed X (to_expr e).
 Proof.
   revert X.
-  induction e; naive_solver eauto using is_closed_of_val, is_closed_weaken_nil.
+  induction e; naive_solver eauto using is_closed_of_val, is_closed_to_val, is_closed_weaken_nil.
 Qed.
 
 (* We define [to_val (ClosedExpr _)] to be [None] since [ClosedExpr]
@@ -119,7 +123,7 @@ about apart from being closed. Notice that the reverse implication of
 [to_val_Some] thus does not hold. *)
 Fixpoint to_val (e : expr) : option val :=
   match e with
-  | Val v => Some v
+  | Val v _ _ => Some v
   | Rec f x e =>
      if decide (is_closed (f :b: x :b: []) e) is left H
      then Some (@RecV f x (to_expr e) (is_closed_correct _ _ H)) else None
@@ -142,7 +146,7 @@ Proof. intros [v ?]; exists v; eauto using to_val_Some. Qed.
 
 Fixpoint subst (x : string) (es : expr) (e : expr)  : expr :=
   match e with
-  | Val v => Val v
+  | Val v e H => Val v e H
   | ClosedExpr e H => @ClosedExpr e H
   | Var y => if decide (x = y) then es else Var y
   | Rec f y e =>
@@ -168,7 +172,7 @@ Lemma to_expr_subst x er e :
   to_expr (subst x er e) = heap_lang.subst x (to_expr er) (to_expr e).
 Proof.
   induction e; simpl; repeat case_decide;
-    f_equal; auto using subst_is_closed_nil, is_closed_of_val, eq_sym.
+    f_equal; eauto using subst_is_closed_nil, is_closed_of_val, is_closed_to_val, eq_sym.
 Qed.
 
 Definition atomic (e : expr) :=
@@ -213,11 +217,15 @@ Ltac solve_to_val :=
   match goal with
   | |- to_val ?e = Some ?v =>
      let e' := W.of_expr e in change (to_val (W.to_expr e') = Some v);
-     apply W.to_val_Some; simpl; unfold W.to_expr; unlock; reflexivity
+     apply W.to_val_Some; simpl; unfold W.to_expr; reflexivity
+  | |- IntoVal ?e ?v =>
+     let e' := W.of_expr e in change (to_val (W.to_expr e') = Some v);
+     apply W.to_val_Some; simpl; unfold W.to_expr; reflexivity
   | |- is_Some (to_val ?e) =>
      let e' := W.of_expr e in change (is_Some (to_val (W.to_expr e')));
      apply W.to_val_is_Some, (bool_decide_unpack _); vm_compute; exact I
   end.
+Hint Extern 10 (IntoVal _ _) => solve_to_val : typeclass_instances.
 
 Ltac solve_atomic :=
   match goal with