diff --git a/tests/heap_lang.ref b/tests/heap_lang.ref
index c0246453727e3d9baa9a73febe08a74cdbabce44..942c1aa93ebedde7266e52785607866515f6f2b5 100644
--- a/tests/heap_lang.ref
+++ b/tests/heap_lang.ref
@@ -68,11 +68,6 @@
     let: "val2" := fun2 "val1" in
     let: "val3" := fun3 "val2" in if: "val1" = "val2" then "val" else "val3"
   {{{ (x y : val) (z : Z), RET (x, y, #z); True }}}
-"not_cas_compare_safe"
-     : string
-The command has indeed failed with message:
-Ltac call to "wp_cas_suc" failed.
-Tactic failure: wp_cas_suc: Values are not safe to compare for CAS.
 "not_cas"
      : string
 The command has indeed failed with message:
diff --git a/tests/heap_lang.v b/tests/heap_lang.v
index 54d46b46ef9bc01636f03c15603096acb829056e..61d0e81a54c0c4a08971ce95362fbfe68a682b4e 100644
--- a/tests/heap_lang.v
+++ b/tests/heap_lang.v
@@ -166,13 +166,6 @@ End printing_tests.
 Section error_tests.
   Context `{heapG Σ}.
 
-  Check "not_cas_compare_safe".
-  Lemma not_cas_compare_safe (l : loc) (v : val) :
-    l ↦ v -∗ WP CAS #l v v {{ _, True }}.
-  Proof.
-    iIntros "H↦". Fail wp_cas_suc.
-  Abort.
-
   Check "not_cas".
   Lemma not_cas :
     (WP #() {{ _, True }})%I.
diff --git a/theories/heap_lang/lang.v b/theories/heap_lang/lang.v
index 30eb83c697b0b4b27ef576356895d4b2c908c073..322bd54490fe69f05a26db3ee7e94a300992c254 100644
--- a/theories/heap_lang/lang.v
+++ b/theories/heap_lang/lang.v
@@ -68,9 +68,6 @@ Inductive expr :=
 
 Bind Scope expr_scope with expr.
 
-Notation NONE := (InjL (Lit LitUnit)) (only parsing).
-Notation SOME x := (InjR x) (only parsing).
-
 Fixpoint is_closed (X : list string) (e : expr) : bool :=
   match e with
   | Var x => bool_decide (x ∈ X)
@@ -99,9 +96,6 @@ Inductive val :=
 
 Bind Scope val_scope with val.
 
-Notation NONEV := (InjLV (LitV LitUnit)) (only parsing).
-Notation SOMEV x := (InjRV x) (only parsing).
-
 Fixpoint of_val (v : val) : expr :=
   match v with
   | RecV f x e => Rec f x e
@@ -122,6 +116,38 @@ Fixpoint to_val (e : expr) : option val :=
   | _ => None
   end.
 
+(** We assume the following encoding of values to 64-bit words: The least 3
+significant bits of every word are a "tag", and we have 61 bits of payload,
+which is enough if all pointers are 8-byte-aligned (common on 64bit
+architectures). The tags have the following meaning:
+
+0: Payload is the data for a LitV (LitInt _).
+1: Payload is the data for a InjLV (LitV (LitInt _)).
+2: Payload is the data for a InjRV (LitV (LitInt _)).
+3: Payload is the data for a LitV (LitLoc _).
+4: Payload is the data for a InjLV (LitV (LitLoc _)).
+4: Payload is the data for a InjRV (LitV (LitLoc _)).
+6: Payload is one of the following finitely many values, which 61 bits are more
+   than enough to encode:
+   LitV LitUnit, InjLV (LitV LitUnit), InjRV (LitV LitUnit),
+   LitV (LitBool _), InjLV (LitV (LitBool _)), InjRV (LitV (LitBool _)).
+7: Value is boxed, i.e., payload is a pointer to some read-only memory area on
+   the heap which stores whether this is a RecV, PairV, InjLV or InjRV and the
+   relevant data for those cases. However, the boxed representation is never
+   used if any of the above representations could be used.
+
+Ignoring (as usual) the fact that we have to fit the infinite Z/loc into 61
+bits, this means every value is machine-word-sized and can hence be atomically
+read and written.  Also notice that the sets of boxed and unboxed values are
+disjoint. *)
+Definition val_is_unboxed (v : val) : Prop :=
+  match v with
+  | LitV _ => True
+  | InjLV (LitV _) => True
+  | InjRV (LitV _) => True
+  | _ => False
+  end.
+
 (** The state: heaps of vals. *)
 Definition state := gmap loc val.
 
@@ -367,53 +393,13 @@ Definition bin_op_eval (op : bin_op) (v1 v2 : val) : option val :=
   | _, _ => None
   end.
 
-(** Return whether it is possible to use CAS to compare vl (current value) with
-v1 (netest value).
-
-We assume the following encoding of values to 64-bit words: The least 3
-significant bits of every word are a "tag", and we have 61 bits of payload,
-which is enough if all pointers are 8-byte-aligned (commong on 64bit
-architectures). The tags have the following meaning:
-
-0: Payload is one of the following finitely many values, which 61 bits are more
-   than enough to encode: LitV LitUnit, LitV (LitBool _), NONEV, SOMEV (LitV
-   LitUnit), SOMEV (LitV (LitBool _)).
-1: Payload is the data for a LitV (LitInt _).
-2: Payload is the data for a SOMEV (LitV (LitInt _)).
-3: Payload is the data for a LitV (LitLoc _).
-4: Payload is the data for a SOMEV (LitV (LitLoc _)).
-5: Value is boxed, i.e., payload is a pointer to some read-only memory area on
-   the heap which stores whether this is a RecV, PairV, InjLV or InjRV and the
-   relevant data for those cases. However, the boxed representation is never
-   used if any of the above representations could be used.
-6: Unused.
-7: Unused.
-
-Ignoring (as usual) the fact that we have to fit the infinite Z/loc into 61
-bits, this means every value is machine-word-sized and can hence be atomically
-read and written. It also justifies the comparisons allowed for CAS: Whenever
-[vals_cas_compare_safe vl v1] holds, equality of the one-word representation of
-[vl] and [v1] is equivalent to equality of the abstract value represented. This
-is clear for [LitV _ == LitV _] and [SOMEV (LitV _) == SOMEV (LitV _)] because
-none of these are boxed. For [NONEV == v], we can't actually atomically load and
-compare the data for boxed values, but that's okay because we only have to know
-if they are equal to [NONEV] which is distinct from all boxed values.
- *)
+(** CAS just compares the word-sized representation of the two values, it cannot
+look into boxed data.  This works out fine if at least one of the to-be-compared
+values is unboxed (exploiting the fact that an unboxed and a boxed value can
+never be equal because these are disjoint sets).  *)
 Definition vals_cas_compare_safe (vl v1 : val) : Prop :=
-  match vl, v1 with
-  (* We allow comparing literals with each other, also when wrapped in a SOMEV. *)
-  | LitV _, LitV _ => True
-  | SOMEV (LitV _), SOMEV (LitV _) => True
-  (* We allow comparing NONEV to anything. *)
-  | NONEV, _ => True
-  | _, NONEV => True
-  (* We don't allow comparing anything else. *)
-  | _, _ => False
-  end.
-(** Just a sanity check. *)
-Lemma vals_cas_compare_safe_sym vl v1 :
-  vals_cas_compare_safe vl v1 → vals_cas_compare_safe v1 vl.
-Proof. rewrite /vals_cas_compare_safe. repeat case_match; done. Qed.
+  val_is_unboxed vl ∨ val_is_unboxed v1.
+Arguments vals_cas_compare_safe !_ !_ /.
 
 Inductive head_step : expr → state → expr → state → list (expr) → Prop :=
   | BetaS f x e1 e2 v2 e' σ :
diff --git a/theories/heap_lang/lib/increment.v b/theories/heap_lang/lib/increment.v
index d2db12190118428c6ce2cdb6dde671a1b563086f..b92acccd5269ddea4cb1e28a09d584c050e81c19 100644
--- a/theories/heap_lang/lib/increment.v
+++ b/theories/heap_lang/lib/increment.v
@@ -39,7 +39,8 @@ Section increment.
     (* Prove the atomic shift for CAS *)
     iAuIntro. iApply (aacc_aupd with "AU"); first done.
     iIntros (x') "H↦".
-    iApply (aacc_intro with "[H↦]"); [solve_ndisj|simpl;by auto with iFrame|iSplit].
+    iApply (aacc_intro with "[H↦]"); [solve_ndisj| |iSplit].
+    { iFrame. simpl. auto. }
     { iIntros "[_ $] !> $ !> //". }
     iIntros ([]) "H↦ !>".
     destruct (decide (#x' = #x)) as [[= ->]|Hx].
diff --git a/theories/heap_lang/notation.v b/theories/heap_lang/notation.v
index d991e108db625e970de4409058fc7f4ea3f67786..f8ef7a58517e001c25d35b3bcbeccdcbc9714547 100644
--- a/theories/heap_lang/notation.v
+++ b/theories/heap_lang/notation.v
@@ -144,6 +144,12 @@ Notation "e1 || e2" :=
   (If e1%E (Lit (LitBool true)) e2%E) (only parsing) : expr_scope.
 
 (** Notations for option *)
+Notation NONE := (InjL (Lit LitUnit)) (only parsing).
+Notation SOME x := (InjR x) (only parsing).
+
+Notation NONEV := (InjLV (LitV LitUnit)) (only parsing).
+Notation SOMEV x := (InjRV x) (only parsing).
+
 Notation "'match:' e0 'with' 'NONE' => e1 | 'SOME' x => e2 'end'" :=
   (Match e0 BAnon e1 x%bind e2)
   (e0, e1, x, e2 at level 200, only parsing) : expr_scope.
diff --git a/theories/heap_lang/proofmode.v b/theories/heap_lang/proofmode.v
index 17236f403ce7ba382d87045b2efb29ba50c42e59..a718a6633e79c147d1d472872cf1e220dd950eaf 100644
--- a/theories/heap_lang/proofmode.v
+++ b/theories/heap_lang/proofmode.v
@@ -406,7 +406,7 @@ Tactic Notation "wp_cas_fail" :=
     [iSolveTC
     |solve_mapsto ()
     |try congruence
-    |fast_done || fail "wp_cas_fail: Values are not safe to compare for CAS"
+    |try (fast_done || left; fast_done || right; fast_done) (* vals_cas_compare_safe *)
     |simpl; try wp_value_head]
   | |- envs_entails _ (twp ?s ?E ?e ?Q) =>
     first
@@ -415,7 +415,7 @@ Tactic Notation "wp_cas_fail" :=
       |fail 1 "wp_cas_fail: cannot find 'CAS' in" e];
     [solve_mapsto ()
     |try congruence
-    |fast_done || fail "wp_cas_fail: Values are not safe to compare for CAS"
+    |try (fast_done || left; fast_done || right; fast_done) (* vals_cas_compare_safe *)
     |wp_expr_simpl; try wp_value_head]
   | _ => fail "wp_cas_fail: not a 'wp'"
   end.
@@ -434,7 +434,7 @@ Tactic Notation "wp_cas_suc" :=
     [iSolveTC
     |solve_mapsto ()
     |try congruence
-    |fast_done || fail "wp_cas_suc: Values are not safe to compare for CAS"
+    |try (fast_done || left; fast_done || right; fast_done) (* vals_cas_compare_safe *)
     |pm_reflexivity
     |simpl; try wp_value_head]
   | |- envs_entails _ (twp ?E ?e ?Q) =>
@@ -444,7 +444,7 @@ Tactic Notation "wp_cas_suc" :=
       |fail 1 "wp_cas_suc: cannot find 'CAS' in" e];
     [solve_mapsto ()
     |try congruence
-    |fast_done || fail "wp_cas_suc: Values are not safe to compare for CAS"
+    |try (fast_done || left; fast_done || right; fast_done) (* vals_cas_compare_safe *)
     |pm_reflexivity
     |wp_expr_simpl; try wp_value_head]
   | _ => fail "wp_cas_suc: not a 'wp'"