Commit 46b315e0 authored by Robbert Krebbers's avatar Robbert Krebbers

Functions as first-class values.

parent bfd313b7
...@@ -210,7 +210,7 @@ Section a_wp_rules. ...@@ -210,7 +210,7 @@ Section a_wp_rules.
Qed. Qed.
Lemma awp_atomic (ev : val) R Φ : Lemma awp_atomic (ev : val) R Φ :
(R - R', R' AWP ev #() @ R' {{ w, R' - R Φ w }}) - (R - R', R' AWP ev #() @ R' {{ w, R' - R Φ w }}) -
AWP a_atomic ev @ R {{ Φ }}. AWP a_atomic ev @ R {{ Φ }}.
Proof. Proof.
iIntros "Hwp". rewrite awp_eq /awp_def. wp_lam. wp_pures. iIntros "Hwp". rewrite awp_eq /awp_def. wp_lam. wp_pures.
......
...@@ -136,7 +136,9 @@ Notation "'whileVᶜ' ( cnd ) { e }" := (a_while (LamV <> cnd) (LamV <> e)) ...@@ -136,7 +136,9 @@ Notation "'whileVᶜ' ( cnd ) { e }" := (a_while (LamV <> cnd) (LamV <> e))
format "'[v' 'whileVᶜ' ( cnd ) { '/ ' '[' e ']' '/' } ']'") : expr_scope. format "'[v' 'whileVᶜ' ( cnd ) { '/ ' '[' e ']' '/' } ']'") : expr_scope.
Definition a_call: val := λ: "f" "arg", Definition a_call: val := λ: "f" "arg",
"a" ←ᶜ "arg" ; a_atomic (λ: <>, "f" "a"). (* sequence point before a function call *)
"fa" ←ᶜ ("f" ||| "arg");
a_atomic (λ: <>, (Fst "fa") (Snd "fa")).
Notation "'callᶜ' f a" := Notation "'callᶜ' f a" :=
(a_call f a)%E (a_call f a)%E
(at level 10, f, a at level 9, (at level 10, f, a at level 9,
...@@ -506,17 +508,22 @@ Section proofs. ...@@ -506,17 +508,22 @@ Section proofs.
iIntros "HI #Hinv". iApply a_while_spec. by iApply (a_whileV_inv_spec with "HI Hinv"). iIntros "HI #Hinv". iApply a_while_spec. by iApply (a_whileV_inv_spec with "HI Hinv").
Qed. Qed.
Lemma a_call_spec R Ψ Φ (f : val) ea : Lemma a_call_spec R Ψ1 Ψ2 Φ ef ea :
AWP ea @ R {{ Ψ }} - AWP ef @ R {{ Ψ1 }} -
( a, Ψ a - U (R - AWP f a {{ v, R Φ v }})) - AWP ea @ R {{ Ψ2 }} -
AWP call f ea @ R {{ Φ }}. ( f a, Ψ1 f - Ψ2 a - U (R - AWP f a {{ v, R Φ v }})) -
AWP call ef ea @ R {{ Φ }}.
Proof. Proof.
iIntros "Ha Hfa". iIntros "H1 H2 H".
awp_apply (a_wp_awp R with "Ha"); iIntros (va) "Ha". awp_lam. awp_pures. awp_apply (a_wp_awp with "H2"); iIntros (vf) "H2".
iApply a_seq_bind_spec'. iApply (awp_wand with "Ha"). awp_apply (a_wp_awp with "H1"); iIntros (va) "H1".
iIntros (a) "HΨ". iSpecialize ("Hfa" with "HΨ"). iModIntro. awp_pures. awp_lam. awp_pures.
iApply awp_atomic. iNext. iIntros "HR". iExists True%I. iSplit; first done. iApply a_seq_bind_spec'. iApply (awp_par with "H1 H2").
awp_pures. iApply (awp_wand with "[-]"); first by iApply "Hfa". eauto. iIntros "!>" (f a) "H1 H2 !>". iSpecialize ("H" with "H1 H2"); iModIntro.
awp_pures.
iApply awp_atomic. iIntros "HR". iSpecialize ("H" with "HR").
iExists True%I. iModIntro; iSplit; first done. awp_pures.
iApply (awp_wand with "H"); eauto.
Qed. Qed.
Lemma a_un_op_spec R Φ op e : Lemma a_un_op_spec R Φ op e :
...@@ -618,7 +625,7 @@ Section proofs. ...@@ -618,7 +625,7 @@ Section proofs.
iApply awp_bind. iApply (awp_par with "Ha1 Ha2"). iNext. iApply awp_bind. iApply (awp_par with "Ha1 Ha2"). iNext.
iIntros (v1 v2) "Hv1 Hv2 !>". awp_pures. iIntros (v1 v2) "Hv1 Hv2 !>". awp_pures.
iApply awp_atomic. iApply awp_atomic.
iIntros "!> $". iDestruct ("HΦ" with "Hv1 Hv2") as (cl v w ->) "(Hl & % & HΦ)". iIntros "$ !>". iDestruct ("HΦ" with "Hv1 Hv2") as (cl v w ->) "(Hl & % & HΦ)".
simplify_eq/=. iExists True%I. iSplit; first done. awp_pures. simplify_eq/=. iExists True%I. iSplit; first done. awp_pures.
iApply awp_bind. iApply a_load_spec. iApply awp_ret. iApply wp_value. iApply awp_bind. iApply a_load_spec. iApply awp_ret. iApply wp_value.
iExists cl, v; iFrame. iSplit; first done. iExists cl, v; iFrame. iSplit; first done.
......
...@@ -8,7 +8,7 @@ Definition factorial : val := λ: "n", ...@@ -8,7 +8,7 @@ Definition factorial : val := λ: "n",
"r" mut 1; "r" mut 1;
"c" mut 0; "c" mut 0;
while (∗ᶜ (a_ret "c") < a_ret "n") { while (∗ᶜ (a_ret "c") < a_ret "n") {
call incr (a_ret "c"); call (a_ret incr) (a_ret "c");
a_ret "r" = ∗ᶜ (a_ret "r") * ∗ᶜ (a_ret "c") a_ret "r" = ∗ᶜ (a_ret "r") * ∗ᶜ (a_ret "c")
}; };
∗ᶜ(a_ret "r"). ∗ᶜ(a_ret "r").
...@@ -32,7 +32,7 @@ Section factorial_spec. ...@@ -32,7 +32,7 @@ Section factorial_spec.
iLöb as "IH" forall (n k Hk). iApply a_whileV_spec; iNext. iLöb as "IH" forall (n k Hk). iApply a_whileV_spec; iNext.
vcg. iIntros "**". case_bool_decide. vcg. iIntros "**". case_bool_decide.
+ iLeft. iSplit; eauto. iModIntro. vcg. + iLeft. iSplit; eauto. iModIntro. vcg.
iIntros "Hc Hr !> $". iApply (incr_spec with "Hc"); iIntros "Hc". iIntros "Hc Hr !> $ !>". iApply (incr_spec with "Hc"); iIntros "Hc".
vcg_continue. iIntros "Hc Hr !>". vcg_continue. iIntros "Hc Hr !>".
assert (fact k * S k = fact (S k)) as -> by (simpl; lia). assert (fact k * S k = fact (S k)) as -> by (simpl; lia).
iApply ("IH" $! n (S k) with "[%] Hc Hr"). lia. iApply ("IH" $! n (S k) with "[%] Hc Hr"). lia.
......
...@@ -8,9 +8,9 @@ Section tests_vcg. ...@@ -8,9 +8,9 @@ Section tests_vcg.
Lemma test_invoke_1 (l: cloc) R : Lemma test_invoke_1 (l: cloc) R :
l C #42 - l C #42 -
AWP call c_id (∗ᶜ ♯ₗl) @ R {{ v, v = #42 l C #42 }}%I. AWP call (a_ret c_id) (∗ᶜ ♯ₗl) @ R {{ v, v = #42 l C #42 }}%I.
Proof. Proof.
iIntros "Hl". vcg. iIntros "Hl !> $". iIntros "Hl". vcg. iIntros "Hl !> $ !>".
awp_lam. vcg. iIntros "Hl". vcg_continue. eauto. awp_lam. vcg. iIntros "Hl". vcg_continue. eauto.
Qed. Qed.
...@@ -20,17 +20,17 @@ Section tests_vcg. ...@@ -20,17 +20,17 @@ Section tests_vcg.
(a_ret "v1") + (a_ret "v2"). (a_ret "v1") + (a_ret "v2").
Lemma test_invoke_2 R : Lemma test_invoke_2 R :
AWP call plus_pair (21 ||| 21) @ R {{ v, v = #42 }}%I. AWP call (a_ret plus_pair) (21 ||| 21) @ R {{ v, v = #42 }}%I.
Proof. Proof.
iIntros. vcg. iIntros "!> $". awp_lam. vcg. by vcg_continue. iIntros. vcg. iIntros "!> $ !>". awp_lam. vcg. by vcg_continue.
Qed. Qed.
Lemma test_invoke_3 (l : cloc) R : Lemma test_invoke_3 (l : cloc) R :
l C #21 - l C #21 -
AWP call plus_pair (∗ᶜ ♯ₗl ||| ∗ᶜ ♯ₗl) @ R AWP call (a_ret plus_pair) (∗ᶜ ♯ₗl ||| ∗ᶜ ♯ₗl) @ R
{{ v, v = #42 l C #21 }}%I. {{ v, v = #42 l C #21 }}%I.
Proof. Proof.
iIntros. vcg. iIntros "Hl !> $". awp_lam. vcg. iIntros. vcg. iIntros "Hl !> $ !>". awp_lam. vcg.
iIntros "Hl". vcg_continue; eauto. iIntros "Hl". vcg_continue; eauto.
Qed. Qed.
End tests_vcg. End tests_vcg.
...@@ -5,7 +5,7 @@ Definition inc : val := λ: "l", ...@@ -5,7 +5,7 @@ Definition inc : val := λ: "l",
a_ret "l" += 1 ; 1. a_ret "l" += 1 ; 1.
Definition par_inc : val := λ: "l", Definition par_inc : val := λ: "l",
call inc (a_ret "l") + call inc (a_ret "l"). call (a_ret inc) (a_ret "l") + call (a_ret inc) (a_ret "l").
Section par_inc. Section par_inc.
Context `{amonadG Σ, !inG Σ (frac_authR natR)}. Context `{amonadG Σ, !inG Σ (frac_authR natR)}.
...@@ -25,9 +25,9 @@ Section par_inc. ...@@ -25,9 +25,9 @@ Section par_inc.
iApply (awp_insert_res _ _ par_inc_inv with "[Hγ Hl]"). iApply (awp_insert_res _ _ par_inc_inv with "[Hγ Hl]").
{ iExists 0%nat. iFrame. } { iExists 0%nat. iFrame. }
iAssert ( (own γ (!{1 / 2} 0%nat) - iAssert ( (own γ (!{1 / 2} 0%nat) -
AWP call inc (a_ret (cloc_to_val cl)) @ par_inc_inv R AWP call (a_ret inc) (a_ret (cloc_to_val cl)) @ par_inc_inv R
{{ v, v = #1 own γ (!{1 / 2} 1%nat) }}))%I as "#H". {{ v, v = #1 own γ (!{1 / 2} 1%nat) }}))%I as "#H".
{ iIntros "!> Hγ'". vcg; iIntros "!> [HR $]". iDestruct "HR" as (n') "[Hl Hγ]". { iIntros "!> Hγ'". vcg; iIntros "!> [HR $] !>". iDestruct "HR" as (n') "[Hl Hγ]".
iApply awp_fupd. iApply (inc_spec with "[$]"); iIntros "Hl". iApply awp_fupd. iApply (inc_spec with "[$]"); iIntros "Hl".
iMod (own_update_2 with "Hγ Hγ'") as "[Hγ Hγ']". iMod (own_update_2 with "Hγ Hγ'") as "[Hγ Hγ']".
{ apply frac_auth_update, (nat_local_update _ _ (S n') 1); lia. } { apply frac_auth_update, (nat_local_update _ _ (S n') 1); lia. }
......
...@@ -73,7 +73,7 @@ Inductive dcexpr : Type := ...@@ -73,7 +73,7 @@ Inductive dcexpr : Type :=
| dCPar : dcexpr dcexpr dcexpr | dCPar : dcexpr dcexpr dcexpr
| dCWhile : dcexpr dcexpr dcexpr | dCWhile : dcexpr dcexpr dcexpr
| dCWhileV : dcexpr dcexpr dcexpr | dCWhileV : dcexpr dcexpr dcexpr
| dCCall : val dcexpr dcexpr | dCCall : dcexpr dcexpr dcexpr
| dCUnknown : expr dcexpr. | dCUnknown : expr dcexpr.
Fixpoint dcexpr_size (de : dcexpr) : nat := Fixpoint dcexpr_size (de : dcexpr) : nat :=
...@@ -81,8 +81,9 @@ Fixpoint dcexpr_size (de : dcexpr) : nat := ...@@ -81,8 +81,9 @@ Fixpoint dcexpr_size (de : dcexpr) : nat :=
| dCRet _ | dCUnknown _ => 1 | dCRet _ | dCUnknown _ => 1
| dCSeqBind _ de1 de2 | dCMutBind _ de1 de2 |dCAlloc de1 de2 | dCStore de1 de2 | dCSeqBind _ de1 de2 | dCMutBind _ de1 de2 |dCAlloc de1 de2 | dCStore de1 de2
| dCBinOp _ de1 de2 | dCPreBinOp _ de1 de2 | dCPar de1 de2 | dCBinOp _ de1 de2 | dCPreBinOp _ de1 de2 | dCPar de1 de2
| dCWhile de1 de2 | dCWhileV de1 de2 => S (dcexpr_size de1 + dcexpr_size de2) | dCWhile de1 de2 | dCWhileV de1 de2
| dCLoad de | dCUnOp _ de | dCCall _ de => S (dcexpr_size de) | dCCall de1 de2 => S (dcexpr_size de1 + dcexpr_size de2)
| dCLoad de | dCUnOp _ de => S (dcexpr_size de)
end. end.
Definition dloc_of_dval (dv : dval) : option dloc := Definition dloc_of_dval (dv : dval) : option dloc :=
...@@ -144,10 +145,10 @@ Fixpoint dcexpr_wf (E : known_locs) (de : dcexpr) : bool := ...@@ -144,10 +145,10 @@ Fixpoint dcexpr_wf (E : known_locs) (de : dcexpr) : bool :=
match de with match de with
| dCRet de => dexpr_wf E de | dCRet de => dexpr_wf E de
| dCSeqBind _ de1 de2 | dCMutBind _ de1 de2 => dcexpr_wf E de1 && dcexpr_wf E de2 | dCSeqBind _ de1 de2 | dCMutBind _ de1 de2 => dcexpr_wf E de1 && dcexpr_wf E de2
| dCLoad de1 | dCUnOp _ de1 | dCCall _ de1 => dcexpr_wf E de1 | dCLoad de1 | dCUnOp _ de1 => dcexpr_wf E de1
| dCAlloc de1 de2 | dCStore de1 de2 | dCBinOp _ de1 de2 | dCAlloc de1 de2 | dCStore de1 de2 | dCBinOp _ de1 de2
| dCPreBinOp _ de1 de2 | dCWhile de1 de2 | dCWhileV de1 de2 | dCPar de1 de2 => | dCPreBinOp _ de1 de2 | dCWhile de1 de2 | dCWhileV de1 de2
dcexpr_wf E de1 && dcexpr_wf E de2 | dCPar de1 de2 | dCCall de1 de2 => dcexpr_wf E de1 && dcexpr_wf E de2
| dCUnknown e => true | dCUnknown e => true
end. end.
...@@ -254,7 +255,7 @@ Fixpoint dcexpr_interp (E : known_locs) (de : dcexpr) : expr := ...@@ -254,7 +255,7 @@ Fixpoint dcexpr_interp (E : known_locs) (de : dcexpr) : expr :=
| dCPar de1 de2 => dcexpr_interp E de1 ||| dcexpr_interp E de2 | dCPar de1 de2 => dcexpr_interp E de1 ||| dcexpr_interp E de2
| dCWhile de1 de2 => while (dcexpr_interp E de1) { dcexpr_interp E de2 } | dCWhile de1 de2 => while (dcexpr_interp E de1) { dcexpr_interp E de2 }
| dCWhileV de1 de2 => whileV (dcexpr_interp E de1) { dcexpr_interp E de2 } | dCWhileV de1 de2 => whileV (dcexpr_interp E de1) { dcexpr_interp E de2 }
| dCCall fv de => call fv (dcexpr_interp E de) | dCCall de1 de2 => call (dcexpr_interp E de1) (dcexpr_interp E de2)
| dCUnknown e1 => e1 | dCUnknown e1 => e1
end. end.
...@@ -782,7 +783,7 @@ Fixpoint dce_subst (E: known_locs) (x: string) (dv : dval) (dce : dcexpr) : dcex ...@@ -782,7 +783,7 @@ Fixpoint dce_subst (E: known_locs) (x: string) (dv : dval) (dce : dcexpr) : dcex
| dCPar de1 de2 => dCPar (dce_subst E x dv de1) (dce_subst E x dv de2) | dCPar de1 de2 => dCPar (dce_subst E x dv de1) (dce_subst E x dv de2)
| dCWhile de1 de2 => dCWhile (dce_subst E x dv de1) (dce_subst E x dv de2) | dCWhile de1 de2 => dCWhile (dce_subst E x dv de1) (dce_subst E x dv de2)
| dCWhileV de1 de2 => dCWhileV de1 de2 | dCWhileV de1 de2 => dCWhileV de1 de2
| dCCall v de1 => dCCall v (dce_subst E x dv de1) | dCCall de1 de2 => dCCall (dce_subst E x dv de1) (dce_subst E x dv de2)
| dCUnknown e => dCUnknown (subst x (dval_interp E dv) e) | dCUnknown e => dCUnknown (subst x (dval_interp E dv) e)
end. end.
......
...@@ -219,8 +219,9 @@ Instance into_dcexpr_whileV E E' E'' e1 e2 de1 de2 : ...@@ -219,8 +219,9 @@ Instance into_dcexpr_whileV E E' E'' e1 e2 de1 de2 :
IntoDCExpr E E' e1 de1 IntoDCExpr E' E'' e2 de2 IntoDCExpr E E' e1 de1 IntoDCExpr E' E'' e2 de2
IntoDCExpr E E'' (whileV(e1) { e2 }) (dCWhileV de1 de2). IntoDCExpr E E'' (whileV(e1) { e2 }) (dCWhileV de1 de2).
Proof. solve_into_dcexpr2. Qed. Proof. solve_into_dcexpr2. Qed.
Instance into_dcexpr_call e1 E E' f de1 : Instance into_dcexpr_call E E' E'' e1 e2 de1 de2 :
IntoDCExpr E E' e1 de1 IntoDCExpr E E' (call (Val f) e1) (dCCall f de1). IntoDCExpr E E' e1 de1 IntoDCExpr E' E'' e2 de2
Proof. intros [-> ??]; by split. Qed. IntoDCExpr E E'' (call e1 e2) (dCCall de1 de2).
Proof. solve_into_dcexpr2. Qed.
Instance into_dcexpr_unknown E e : IntoDCExpr E E e (dCUnknown e) | 100. Instance into_dcexpr_unknown E e : IntoDCExpr E E e (dCUnknown e) | 100.
Proof. done. Qed. Proof. done. Qed.
...@@ -121,6 +121,12 @@ Section vcg. ...@@ -121,6 +121,12 @@ Section vcg.
(cl C[LLvl] w - vcg_continuation E Φ v)) (cl C[LLvl] w - vcg_continuation E Φ v))
end%I. end%I.
Definition vcg_call (E : known_locs) (dv1 dv2 : dval)
(m : denv) (R : iProp Σ) (Φ : known_locs denv dval iProp Σ) : iProp Σ :=
(wand_denv_interp E m $ U (
R -
AWP (dval_interp E dv1) (dval_interp E dv2) {{ v, R vcg_continuation E Φ v }}))%I.
Fixpoint vcg (E : known_locs) (n : nat) (m : denv) (de : dcexpr) Fixpoint vcg (E : known_locs) (n : nat) (m : denv) (de : dcexpr)
(R : iProp Σ) (Φ : known_locs denv dval iProp Σ) : iProp Σ := (R : iProp Σ) (Φ : known_locs denv dval iProp Σ) : iProp Σ :=
match de, n with match de, n with
...@@ -216,10 +222,19 @@ Section vcg. ...@@ -216,10 +222,19 @@ Section vcg.
end end
end end
| dCWhile de1 de2, S n => vcg_awp_continuation R E (dCWhileV de1 de2) m Φ | dCWhile de1 de2, S n => vcg_awp_continuation R E (dCWhileV de1 de2) m Φ
| dCCall ef de1, S n => vcg E n m de1 R $ λ E m dv, | dCCall de1 de2, S n =>
wand_denv_interp E m $ U ( match forward E n m de1 with
R - AWP ef (dval_interp E dv) | Some (m', mNew, dv1) =>
{{ v, R vcg_continuation E Φ v }}) vcg E n m' de2 R $ λ E m'' dv2,
vcg_call E dv1 dv2 (denv_merge mNew m'') R Φ
| None =>
match forward E n m de2 with
| Some (m', mNew, dv2) =>
vcg E n m' de1 R $ λ E m'' dv1,
vcg_call E dv1 dv2 (denv_merge mNew m'') R Φ
| None => vcg_awp_continuation R E de m Φ
end
end
| _, S p => vcg_awp_continuation R E de m Φ | _, S p => vcg_awp_continuation R E de m Φ
end%I. end%I.
...@@ -611,13 +626,33 @@ Section vcg_spec. ...@@ -611,13 +626,33 @@ Section vcg_spec.
- (* whileV *) - (* whileV *)
by iDestruct (vcg_awp_continuation_correct with "Hm H") as "?". by iDestruct (vcg_awp_continuation_correct with "Hm H") as "?".
- (* call *) - (* call *)
iApply (a_call_spec with "[H Hm]"). destruct (forward _ _ m _) as [[[m' mNew] dv1]|] eqn:?; last first.
{ iApply ("IH" with "[] [] Hm H"); eauto. } + destruct (forward _ _ m de2) as [[[m' mNew] dv2]|] eqn:?; last first.
iIntros (w) "H"; iDestruct "H" as (E' dv m' -> ???) "[Hm' H]". { by iDestruct (vcg_awp_continuation_correct with "Hm H") as "?". }
iDestruct (wand_denv_interp_spec with "H Hm'") as "H". iDestruct (forward_correct with "Hm") as "[Hm' H2]"; eauto.
iIntros "!> HR". iDestruct ("H" with "HR") as "H". iApply (a_call_spec with "[H Hm'] H2").
iApply (awp_wand with "H"); iIntros (v') "[$ H]". { iApply ("IH" with "[] [] Hm' H"); eauto. }
by iApply vcg_continuation_mono. iIntros (f a) "H [-> HmNew]";
iDestruct "H" as (E' dv m'' -> ???) "[Hm'' H]".
iDestruct (wand_denv_interp_spec with "H [Hm'' HmNew]") as "H".
{ iApply denv_merge_interp; eauto using denv_wf_mono.
iFrame "Hm''". iApply (denv_interp_mono with "HmNew"); eauto. }
iIntros "!> HR". iSpecialize ("H" with "HR"); iModIntro.
rewrite (dval_interp_mono E E'); eauto.
iApply (awp_wand with "H"); iIntros (w) "[$ H]".
by iApply vcg_continuation_mono.
+ iDestruct (forward_correct with "Hm") as "[Hm' H1]"; eauto.
iApply (a_call_spec with "H1 [H Hm']").
{ iApply ("IH" with "[] [] Hm' H"); eauto. }
iIntros (f a) "[-> HmNew] H";
iDestruct "H" as (E' dv m'' -> ???) "[Hm'' H]".
iDestruct (wand_denv_interp_spec with "H [Hm'' HmNew]") as "H".
{ iApply denv_merge_interp; eauto using denv_wf_mono.
iFrame "Hm''". iApply (denv_interp_mono with "HmNew"); eauto. }
iIntros "!> HR". iSpecialize ("H" with "HR"); iModIntro.
rewrite (dval_interp_mono E E'); eauto.
iApply (awp_wand with "H"); iIntros (w) "[$ H]".
by iApply vcg_continuation_mono.
- (* unknown *) - (* unknown *)
by iDestruct (vcg_awp_continuation_correct with "Hm H") as "?". by iDestruct (vcg_awp_continuation_correct with "Hm H") as "?".
Qed. Qed.
...@@ -677,3 +712,4 @@ Arguments vcg_store _ _ _ _ _ _ _ /. ...@@ -677,3 +712,4 @@ Arguments vcg_store _ _ _ _ _ _ _ /.
Arguments vcg_bin_op _ _ _ _ _ _ _ _ /. Arguments vcg_bin_op _ _ _ _ _ _ _ _ /.
Arguments vcg_un_op _ _ _ _ _ _ _ /. Arguments vcg_un_op _ _ _ _ _ _ _ /.
Arguments vcg_pre_bin_op _ _ _ _ _ _ _ _ /. Arguments vcg_pre_bin_op _ _ _ _ _ _ _ _ /.
Arguments vcg_call _ _ _ _ _ _ _ _ /.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment