Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
L
lambda-rust
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Model registry
Operate
Environments
Monitor
Service Desk
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Iris
lambda-rust
Commits
1486a2ca
Commit
1486a2ca
authored
8 years ago
by
Robbert Krebbers
Browse files
Options
Downloads
Patches
Plain Diff
Turn CNT into a context in the notation.
parent
a7177cb8
No related branches found
No related tags found
No related merge requests found
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
theories/typing/lib/rc/rc.v
+1
-1
1 addition, 1 deletion
theories/typing/lib/rc/rc.v
theories/typing/programs.v
+30
-29
30 additions, 29 deletions
theories/typing/programs.v
with
31 additions
and
30 deletions
theories/typing/lib/rc/rc.v
+
1
−
1
View file @
1486a2ca
...
@@ -984,7 +984,7 @@ Section code.
...
@@ -984,7 +984,7 @@ Section code.
delete
[
#
1
;
"rc"
];;
return
:
[
"r"
]
.
delete
[
#
1
;
"rc"
];;
return
:
[
"r"
]
.
Lemma
rc_make_mut_type
ty
`{
!
TyWf
ty
}
clone
:
Lemma
rc_make_mut_type
ty
`{
!
TyWf
ty
}
clone
:
⊢
ᵣ
clone
@
:
(
fn
(
∀
α
:
lft
,
∅
;
&
shr
{
α
}
ty
)
→
ty
)
→
(
⊢
ᵣ
clone
@
:
fn
(
∀
α
:
lft
,
∅
;
&
shr
{
α
}
ty
)
→
ty
)
→
⊢
ᵣ
rc_make_mut
ty
clone
@
:
fn
(
∀
α
:
lft
,
∅
;
&
uniq
{
α
}
rc
ty
)
→
&
uniq
{
α
}
ty
.
⊢
ᵣ
rc_make_mut
ty
clone
@
:
fn
(
∀
α
:
lft
,
∅
;
&
uniq
{
α
}
rc
ty
)
→
&
uniq
{
α
}
ty
.
Proof
.
Proof
.
intros
Hclone
E
L
.
iApply
type_fn
;
[
solve_typing
..|]
.
iIntros
"/= !#"
.
intros
Hclone
E
L
.
iApply
type_fn
;
[
solve_typing
..|]
.
iIntros
"/= !#"
.
...
...
This diff is collapsed.
Click to expand it.
theories/typing/programs.v
+
30
−
29
View file @
1486a2ca
...
@@ -78,23 +78,24 @@ Section typing.
...
@@ -78,23 +78,24 @@ Section typing.
Global
Arguments
typed_read
_
_
_
%
T
_
%
T
_
%
T
.
Global
Arguments
typed_read
_
_
_
%
T
_
%
T
_
%
T
.
End
typing
.
End
typing
.
Notation
"'ELFT' E , 'LFT' L , 'TYPE' T ⊢ᵣ e
'CNT' C
"
:=
Notation
"'ELFT' E , 'LFT' L ,
'CNT' C ,
'TYPE' T ⊢ᵣ e"
:=
(
typed_body
E
L
C
T
e
)
(
at
level
20
,
e
at
level
200
)
.
(
typed_body
E
L
C
T
e
)
(
at
level
20
,
e
at
level
200
)
.
Notation
"'ELFT' E , 'LFT' L , 'TYPE' T ⊢ᵣ e ⊣ v , T'"
:=
Notation
"'ELFT' E , 'LFT' L , 'TYPE' T ⊢ᵣ e ⊣ v , T'"
:=
(
typed_instruction
E
L
T
e
(
λ
v
,
T'
))
(
at
level
20
,
e
at
level
200
)
.
(
typed_instruction
E
L
T
e
(
λ
v
,
T'
))
(
at
level
20
,
e
at
level
200
,
v
at
level
0
,
T'
at
level
200
)
.
Definition
typed_instruction_ty
`{
typeG
Σ
}
(
E
:
elctx
)
(
L
:
llctx
)
(
T
:
tctx
)
Definition
typed_instruction_ty
`{
typeG
Σ
}
(
E
:
elctx
)
(
L
:
llctx
)
(
T
:
tctx
)
(
e
:
expr
)
(
ty
:
type
)
:
iProp
Σ
:=
(
e
:
expr
)
(
ty
:
type
)
:
iProp
Σ
:=
ELFT
E
,
LFT
L
,
TYPE
T
⊢
ᵣ
e
⊣
v
,
[
v
◁
ty
]
.
ELFT
E
,
LFT
L
,
TYPE
T
⊢
ᵣ
e
⊣
v
,
[
v
◁
ty
]
.
Arguments
typed_instruction_ty
{_
_}
_
_
_
_
%
E
_
%
T
.
Arguments
typed_instruction_ty
{_
_}
_
_
_
_
%
E
_
%
T
.
Notation
"'ELFT' E , 'LFT' L , 'TYPE' T ⊢ᵣ e @: τ"
:=
Notation
"'ELFT' E , 'LFT' L , 'TYPE' T ⊢ᵣ e @: τ"
:=
(
typed_instruction_ty
E
L
T
e
τ
)
(
at
level
20
,
e
at
level
200
)
.
(
typed_instruction_ty
E
L
T
e
τ
)
(
at
level
20
,
e
,
τ
at
level
200
)
.
Definition
typed_val
`{
typeG
Σ
}
(
v
:
val
)
(
ty
:
type
)
:
Prop
:=
Definition
typed_val
`{
typeG
Σ
}
(
v
:
val
)
(
ty
:
type
)
:
Prop
:=
∀
E
L
,
typed_instruction_ty
E
L
[]
(
of_val
v
)
ty
.
∀
E
L
,
typed_instruction_ty
E
L
[]
(
of_val
v
)
ty
.
Arguments
typed_val
_
_
_
%
V
_
%
T
.
Arguments
typed_val
_
_
_
%
V
_
%
T
.
Notation
"⊢ᵣ v @: τ"
:=
(
typed_val
v
τ
)
(
at
level
20
,
v
at
level
200
)
.
Notation
"⊢ᵣ v @: τ"
:=
(
typed_val
v
τ
)
(
at
level
20
,
v
,
τ
at
level
200
)
.
Section
typing_rules
.
Section
typing_rules
.
Context
`{
typeG
Σ
}
.
Context
`{
typeG
Σ
}
.
...
@@ -102,15 +103,15 @@ Section typing_rules.
...
@@ -102,15 +103,15 @@ Section typing_rules.
(* This lemma is helpful when switching from proving unsafe code in Iris
(* This lemma is helpful when switching from proving unsafe code in Iris
back to proving it in the type system. *)
back to proving it in the type system. *)
Lemma
type_type
E
L
C
T
e
:
Lemma
type_type
E
L
C
T
e
:
ELFT
E
,
LFT
L
,
TYPE
T
⊢
ᵣ
e
CNT
C
-∗
(
ELFT
E
,
LFT
L
,
CNT
C
,
TYPE
T
⊢
ᵣ
e
)
-∗
ELFT
E
,
LFT
L
,
TYPE
T
⊢
ᵣ
e
CNT
C
.
ELFT
E
,
LFT
L
,
CNT
C
,
TYPE
T
⊢
ᵣ
e
.
Proof
.
done
.
Qed
.
Proof
.
done
.
Qed
.
(* TODO: Proof a version of this that substitutes into a compatible context...
(* TODO: Proof a version of this that substitutes into a compatible context...
if we really want to do that. *)
if we really want to do that. *)
Lemma
type_equivalize_lft
E
L
C
T
κ1
κ2
e
:
Lemma
type_equivalize_lft
E
L
C
T
κ1
κ2
e
:
ELFT
(
κ1
⊑
ₑ
κ2
)
::
(
κ2
⊑
ₑ
κ1
)
::
E
,
LFT
L
,
TYPE
T
⊢
ᵣ
e
CNT
C
-∗
(
ELFT
(
κ1
⊑
ₑ
κ2
)
::
(
κ2
⊑
ₑ
κ1
)
::
E
,
LFT
L
,
CNT
C
,
TYPE
T
⊢
ᵣ
e
)
-∗
ELFT
E
,
LFT
(
κ1
⊑
ₗ
[
κ2
])
::
L
,
TYPE
T
⊢
ᵣ
e
CNT
C
.
ELFT
E
,
LFT
(
κ1
⊑
ₗ
[
κ2
])
::
L
,
CNT
C
,
TYPE
T
⊢
ᵣ
e
.
Proof
.
Proof
.
iIntros
"He"
;
iIntros
(
tid
)
"#LFT #HE Htl [Hκ HL] HC HT"
.
iIntros
"He"
;
iIntros
(
tid
)
"#LFT #HE Htl [Hκ HL] HC HT"
.
iMod
(
lctx_equalize_lft
with
"LFT Hκ"
)
as
"[Hκ1 Hκ2]"
.
iMod
(
lctx_equalize_lft
with
"LFT Hκ"
)
as
"[Hκ1 Hκ2]"
.
...
@@ -119,9 +120,9 @@ Section typing_rules.
...
@@ -119,9 +120,9 @@ Section typing_rules.
Lemma
type_let'
E
L
T1
T2
(
T
:
tctx
)
C
xb
e
e'
:
Lemma
type_let'
E
L
T1
T2
(
T
:
tctx
)
C
xb
e
e'
:
Closed
(
xb
:
b
:
[])
e'
→
Closed
(
xb
:
b
:
[])
e'
→
ELFT
E
,
LFT
L
,
TYPE
T1
⊢
ᵣ
e
⊣
v
,
T2
v
-∗
(
ELFT
E
,
LFT
L
,
TYPE
T1
⊢
ᵣ
e
⊣
v
,
T2
v
)
-∗
(
∀
v
:
val
,
ELFT
E
,
LFT
L
,
TYPE
T2
v
++
T
⊢
ᵣ
subst'
xb
v
e'
CNT
C
)
-∗
(
∀
v
:
val
,
ELFT
E
,
LFT
L
,
CNT
C
,
TYPE
T2
v
++
T
⊢
ᵣ
subst'
xb
v
e'
)
-∗
ELFT
E
,
LFT
L
,
TYPE
T1
++
T
⊢
ᵣ
let
:
xb
:=
e
in
e'
CNT
C
.
ELFT
E
,
LFT
L
,
CNT
C
,
TYPE
T1
++
T
⊢
ᵣ
let
:
xb
:=
e
in
e'
.
Proof
.
Proof
.
iIntros
(
Hc
)
"He He'"
.
iIntros
(
tid
)
"#LFT #HE Htl HL HC HT"
.
rewrite
tctx_interp_app
.
iIntros
(
Hc
)
"He He'"
.
iIntros
(
tid
)
"#LFT #HE Htl HL HC HT"
.
rewrite
tctx_interp_app
.
iDestruct
"HT"
as
"[HT1 HT]"
.
wp_bind
e
.
iApply
(
wp_wand
with
"[He HL HT1 Htl]"
)
.
iDestruct
"HT"
as
"[HT1 HT]"
.
wp_bind
e
.
iApply
(
wp_wand
with
"[He HL HT1 Htl]"
)
.
...
@@ -138,10 +139,10 @@ Section typing_rules.
...
@@ -138,10 +139,10 @@ Section typing_rules.
for the following hypothesis. *)
for the following hypothesis. *)
Lemma
type_let
E
L
T
T'
T1
T2
C
xb
e
e'
:
Lemma
type_let
E
L
T
T'
T1
T2
C
xb
e
e'
:
Closed
(
xb
:
b
:
[])
e'
→
Closed
(
xb
:
b
:
[])
e'
→
ELFT
E
,
LFT
L
,
TYPE
T1
⊢
ᵣ
e
⊣
v
,
T2
v
→
(
ELFT
E
,
LFT
L
,
TYPE
T1
⊢
ᵣ
e
⊣
v
,
T2
v
)
→
tctx_extract_ctx
E
L
T1
T
T'
→
tctx_extract_ctx
E
L
T1
T
T'
→
(
∀
v
:
val
,
ELFT
E
,
LFT
L
,
TYPE
T2
v
++
T'
⊢
ᵣ
subst'
xb
v
e'
CNT
C
)
-∗
(
∀
v
:
val
,
ELFT
E
,
LFT
L
,
CNT
C
,
TYPE
T2
v
++
T'
⊢
ᵣ
subst'
xb
v
e'
)
-∗
ELFT
E
,
LFT
L
,
TYPE
T
⊢
ᵣ
let
:
xb
:=
e
in
e'
CNT
C
.
ELFT
E
,
LFT
L
,
CNT
C
,
TYPE
T
⊢
ᵣ
let
:
xb
:=
e
in
e'
.
Proof
.
Proof
.
unfold
tctx_extract_ctx
.
iIntros
(?
He
->
)
"?"
.
iApply
type_let'
;
last
done
.
unfold
tctx_extract_ctx
.
iIntros
(?
He
->
)
"?"
.
iApply
type_let'
;
last
done
.
iApply
He
.
iApply
He
.
...
@@ -149,16 +150,16 @@ Section typing_rules.
...
@@ -149,16 +150,16 @@ Section typing_rules.
Lemma
type_seq
E
L
T
T'
T1
T2
C
e
e'
:
Lemma
type_seq
E
L
T
T'
T1
T2
C
e
e'
:
Closed
[]
e'
→
Closed
[]
e'
→
ELFT
E
,
LFT
L
,
TYPE
T1
⊢
ᵣ
e
⊣
_,
T2
→
(
ELFT
E
,
LFT
L
,
TYPE
T1
⊢
ᵣ
e
⊣
_,
T2
)
→
tctx_extract_ctx
E
L
T1
T
T'
→
tctx_extract_ctx
E
L
T1
T
T'
→
ELFT
E
,
LFT
L
,
TYPE
T2
++
T'
⊢
ᵣ
e'
CNT
C
-∗
(
ELFT
E
,
LFT
L
,
CNT
C
,
TYPE
T2
++
T'
⊢
ᵣ
e'
)
-∗
ELFT
E
,
LFT
L
,
TYPE
T
⊢
ᵣ
e
;;
e'
CNT
C
.
ELFT
E
,
LFT
L
,
CNT
C
,
TYPE
T
⊢
ᵣ
e
;;
e'
.
Proof
.
iIntros
.
iApply
(
type_let
E
L
T
T'
T1
(
λ
_,
T2
));
auto
.
Qed
.
Proof
.
iIntros
.
iApply
(
type_let
E
L
T
T'
T1
(
λ
_,
T2
));
auto
.
Qed
.
Lemma
type_newlft
{
E
L
C
T
}
κs
e
:
Lemma
type_newlft
{
E
L
C
T
}
κs
e
:
Closed
[]
e
→
Closed
[]
e
→
(
∀
κ
,
ELFT
E
,
LFT
(
κ
⊑
ₗ
κs
)
::
L
,
TYPE
T
⊢
ᵣ
e
CNT
C
)
-∗
(
∀
κ
,
ELFT
E
,
LFT
(
κ
⊑
ₗ
κs
)
::
L
,
CNT
C
,
TYPE
T
⊢
ᵣ
e
)
-∗
ELFT
E
,
LFT
L
,
TYPE
T
⊢
ᵣ
Newlft
;;
e
CNT
C
.
ELFT
E
,
LFT
L
,
CNT
C
,
TYPE
T
⊢
ᵣ
Newlft
;;
e
.
Proof
.
Proof
.
iIntros
(
Hc
)
"He"
.
iIntros
(
tid
)
"#LFT #HE Htl HL HC HT"
.
iIntros
(
Hc
)
"He"
.
iIntros
(
tid
)
"#LFT #HE Htl HL HC HT"
.
iMod
(
lft_create
with
"LFT"
)
as
(
Λ
)
"[Htk #Hinh]"
;
first
done
.
iMod
(
lft_create
with
"LFT"
)
as
(
Λ
)
"[Htk #Hinh]"
;
first
done
.
...
@@ -171,8 +172,8 @@ Section typing_rules.
...
@@ -171,8 +172,8 @@ Section typing_rules.
Right now, we could take two. *)
Right now, we could take two. *)
Lemma
type_endlft
E
L
C
T1
T2
κ
κs
e
:
Lemma
type_endlft
E
L
C
T1
T2
κ
κs
e
:
Closed
[]
e
→
UnblockTctx
κ
T1
T2
→
Closed
[]
e
→
UnblockTctx
κ
T1
T2
→
ELFT
E
,
LFT
L
,
TYPE
T2
⊢
ᵣ
e
CNT
C
-∗
(
ELFT
E
,
LFT
L
,
CNT
C
,
TYPE
T2
⊢
ᵣ
e
)
-∗
ELFT
E
,
LFT
(
κ
⊑
ₗ
κs
)
::
L
,
TYPE
T1
⊢
ᵣ
Endlft
;;
e
CNT
C
.
ELFT
E
,
LFT
(
κ
⊑
ₗ
κs
)
::
L
,
CNT
C
,
TYPE
T1
⊢
ᵣ
Endlft
;;
e
.
Proof
.
Proof
.
iIntros
(
Hc
Hub
)
"He"
.
iIntros
(
tid
)
"#LFT #HE Htl [Hκ HL] HC HT"
.
iIntros
(
Hc
Hub
)
"He"
.
iIntros
(
tid
)
"#LFT #HE Htl [Hκ HL] HC HT"
.
iDestruct
"Hκ"
as
(
Λ
)
"(% & Htok & #Hend)"
.
iDestruct
"Hκ"
as
(
Λ
)
"(% & Htok & #Hend)"
.
...
@@ -194,8 +195,8 @@ Section typing_rules.
...
@@ -194,8 +195,8 @@ Section typing_rules.
Lemma
type_letpath
{
E
L
}
ty
C
T
T'
x
p
e
:
Lemma
type_letpath
{
E
L
}
ty
C
T
T'
x
p
e
:
Closed
(
x
:
b
:
[])
e
→
Closed
(
x
:
b
:
[])
e
→
tctx_extract_hasty
E
L
p
ty
T
T'
→
tctx_extract_hasty
E
L
p
ty
T
T'
→
(
∀
v
:
val
,
ELFT
E
,
LFT
L
,
TYPE
(
v
◁
ty
)
::
T'
⊢
ᵣ
subst'
x
v
e
CNT
C
)
-∗
(
∀
v
:
val
,
ELFT
E
,
LFT
L
,
CNT
C
,
TYPE
(
v
◁
ty
)
::
T'
⊢
ᵣ
subst'
x
v
e
)
-∗
ELFT
E
,
LFT
L
,
TYPE
T
⊢
ᵣ
let
:
x
:=
p
in
e
CNT
C
.
ELFT
E
,
LFT
L
,
CNT
C
,
TYPE
T
⊢
ᵣ
let
:
x
:=
p
in
e
.
Proof
.
iIntros
.
iApply
type_let
;
[
by
apply
type_path_instr
|
solve_typing
|
done
]
.
Qed
.
Proof
.
iIntros
.
iApply
type_let
;
[
by
apply
type_path_instr
|
solve_typing
|
done
]
.
Qed
.
Lemma
type_assign_instr
{
E
L
}
ty
ty1
ty1'
p1
p2
:
Lemma
type_assign_instr
{
E
L
}
ty
ty1
ty1'
p1
p2
:
...
@@ -220,8 +221,8 @@ Section typing_rules.
...
@@ -220,8 +221,8 @@ Section typing_rules.
Closed
[]
e
→
Closed
[]
e
→
tctx_extract_ctx
E
L
[
p1
◁
ty1
;
p2
◁
ty
]
T
T'
→
tctx_extract_ctx
E
L
[
p1
◁
ty1
;
p2
◁
ty
]
T
T'
→
typed_write
E
L
ty1
ty
ty1'
→
typed_write
E
L
ty1
ty
ty1'
→
ELFT
E
,
LFT
L
,
TYPE
(
p1
◁
ty1'
)
::
T'
⊢
ᵣ
e
CNT
C
-∗
(
ELFT
E
,
LFT
L
,
CNT
C
,
TYPE
(
p1
◁
ty1'
)
::
T'
⊢
ᵣ
e
)
-∗
ELFT
E
,
LFT
L
,
TYPE
T
⊢
ᵣ
p1
<-
p2
;;
e
CNT
C
.
ELFT
E
,
LFT
L
,
CNT
C
,
TYPE
T
⊢
ᵣ
p1
<-
p2
;;
e
.
Proof
.
iIntros
.
by
iApply
type_seq
;
first
apply
type_assign_instr
.
Qed
.
Proof
.
iIntros
.
by
iApply
type_seq
;
first
apply
type_assign_instr
.
Qed
.
Lemma
type_deref_instr
{
E
L
}
ty
ty1
ty1'
p
:
Lemma
type_deref_instr
{
E
L
}
ty
ty1
ty1'
p
:
...
@@ -246,8 +247,8 @@ Section typing_rules.
...
@@ -246,8 +247,8 @@ Section typing_rules.
tctx_extract_hasty
E
L
p
ty1
T
T'
→
tctx_extract_hasty
E
L
p
ty1
T
T'
→
typed_read
E
L
ty1
ty
ty1'
→
typed_read
E
L
ty1
ty
ty1'
→
ty
.(
ty_size
)
=
1
%
nat
→
ty
.(
ty_size
)
=
1
%
nat
→
(
∀
v
:
val
,
ELFT
E
,
LFT
L
,
TYPE
(
p
◁
ty1'
)
::
(
v
◁
ty
)
::
T'
⊢
ᵣ
subst'
x
v
e
CNT
C
)
-∗
(
∀
v
:
val
,
ELFT
E
,
LFT
L
,
CNT
C
,
TYPE
(
p
◁
ty1'
)
::
(
v
◁
ty
)
::
T'
⊢
ᵣ
subst'
x
v
e
)
-∗
ELFT
E
,
LFT
L
,
TYPE
T
⊢
ᵣ
let
:
x
:=
!
p
in
e
CNT
C
.
ELFT
E
,
LFT
L
,
CNT
C
,
TYPE
T
⊢
ᵣ
let
:
x
:=
!
p
in
e
.
Proof
.
iIntros
.
by
iApply
type_let
;
[
apply
type_deref_instr
|
solve_typing
|]
.
Qed
.
Proof
.
iIntros
.
by
iApply
type_let
;
[
apply
type_deref_instr
|
solve_typing
|]
.
Qed
.
Lemma
type_memcpy_iris
E
L
qL
tid
ty
ty1
ty1'
ty2
ty2'
(
n
:
Z
)
p1
p2
:
Lemma
type_memcpy_iris
E
L
qL
tid
ty
ty1
ty1'
ty2
ty2'
(
n
:
Z
)
p1
p2
:
...
@@ -294,8 +295,8 @@ Section typing_rules.
...
@@ -294,8 +295,8 @@ Section typing_rules.
typed_write
E
L
ty1
ty
ty1'
→
typed_write
E
L
ty1
ty
ty1'
→
typed_read
E
L
ty2
ty
ty2'
→
typed_read
E
L
ty2
ty
ty2'
→
Z
.
of_nat
(
ty
.(
ty_size
))
=
n
→
Z
.
of_nat
(
ty
.(
ty_size
))
=
n
→
ELFT
E
,
LFT
L
,
TYPE
(
p1
◁
ty1'
)
::
(
p2
◁
ty2'
)
::
T'
⊢
ᵣ
e
CNT
C
-∗
(
ELFT
E
,
LFT
L
,
CNT
C
,
TYPE
(
p1
◁
ty1'
)
::
(
p2
◁
ty2'
)
::
T'
⊢
ᵣ
e
)
-∗
ELFT
E
,
LFT
L
,
TYPE
T
⊢
ᵣ
p1
<-
{
n
}
!
p2
;;
e
CNT
C
.
ELFT
E
,
LFT
L
,
CNT
C
,
TYPE
T
⊢
ᵣ
p1
<-
{
n
}
!
p2
;;
e
.
Proof
.
iIntros
.
by
iApply
type_seq
;
first
eapply
(
type_memcpy_instr
ty
ty1
ty1'
)
.
Qed
.
Proof
.
iIntros
.
by
iApply
type_seq
;
first
eapply
(
type_memcpy_instr
ty
ty1
ty1'
)
.
Qed
.
End
typing_rules
.
End
typing_rules
.
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment