oplss2024/ahmed/notes.typ
2024-06-03 15:00:06 -04:00

166 lines
4.6 KiB
Plaintext

#import "@preview/prooftrees:0.1.0": *
#set page(width: 5in, height: 8in, margin: 0.4in)
#let ifthenelse(e, e1, e2) = $"if" #e "then" #e1 "else" #e2$
#let subst(x, v, e) = $#e [#v\/#x]$
#let isValue(x) = $#x "value"$
#let mapstostar = $op(arrow.r.long.bar)^*$
#let safe = $"safe"$
#let db(x) = $bracket.l.double #x bracket.r.double$
= Mon. Jun 3 \@ 14:00
== Logical relations
Logical relations are used to prove things about programs
- Unary equivalence
- Example: Prove termination of the STLC
- Example: Prove type soundness / safety (these lectures)
- Binary equivalence
- Example: are two programs going to behave the same when you put them in some different context? (*contextual equivalence*)
-
Representation independence
- Example: suppose u implement a stack and have 2 different implementations. Prove that it doesn't matter which implementation you use.
- Parametricity for existential types "there exists a stack such that these properties are true"
- Parametricity
- Security levels of data in a security programming language
- Establish that high-security data never flows to low-security observers (*non-interference*)
You can also relate source terms to target terms in different languages.
This can be used to prove compiler correctness.
== Type safety / soundness
Unary realizability relations for FFI. "all well-behaved terms from language A can be used in language B without causing problems with either language"
== STLC
Statics
- $tau ::= "bool" | tau_1 arrow.r tau_2$
- $e ::= x | "true" | "false" | ifthenelse(e, e_1, e_2) | lambda (x : tau) . e | e_1 " " e_2$
- $v ::= "true" | "false" | lambda (x:tau) . e$
- $E ::= [dot] | "if" E "then" e_1 "else" e_2 | E " " e_2 | v " " E$
Operational semantics
#let mapsto = $arrow.r.long.bar$
#rect[$e mapsto e'$]
#tree(
axi[],
uni[$ifthenelse("true", e_1, e_2) mapsto e_1$]
)
#tree(
axi[],
uni[$ifthenelse("false", e_1, e_2) mapsto e_2$]
)
#tree(
axi[],
uni[$(lambda (x:tau) e) v mapsto subst(x, v, e)$]
)
#tree(
axi[$e mapsto e'$],
uni[$E[e] mapsto E[e']$]
)
Contexts:
$Gamma ::= dot | Gamma , x : A$
#rect[$Gamma tack.r e : tau$]
#tree(
axi[],
uni[$Gamma tack.r "true" : "bool"$]
)
#tree(
axi[],
uni[$Gamma tack.r "false" : "bool"$]
)
#tree(
axi[$Gamma tack.r e : "bool"$],
axi[$Gamma tack.r e_1 : tau$],
axi[$Gamma tack.r e_2 : tau$],
tri[$Gamma tack.r ifthenelse(e, e_1, e_2) : tau$]
)
#tree(
axi[$Gamma(x) = tau$],
uni[$Gamma tack.r x : tau$]
)
#tree(
axi[$Gamma , x : tau tack.r e : tau_2$],
uni[$Gamma tack.r lambda (x : tau_1) . e : tau_1 arrow.r tau_2 $]
)
#tree(
axi[$Gamma tack.r e_1 : tau_2 arrow.r tau$],
axi[$Gamma tack.r e_2 : tau_2$],
bin[$Gamma tack.r e_1 e_2 : tau$],
)
#quote(block: true, attribution: [Milner])[
Well-typed programs do not go wrong
]
What does "going wrong" mean? It means getting stuck. You can't take another step and it's not a value.
*Definition (Type Soundness).* If $dot tack.r e : tau$, then
$forall(e' . e mapstostar e')$ either $isValue(e')$ or $exists e'' . e' mapsto e''$
*Definition (Progress).* If $dot tack.r e : tau$ then either $isValue(e)$ or $exists e' . (e mapsto e')$.
*Definition (Preservation).* If $dot tack.r e : tau$ and $e mapsto e'$ then $dot tack.r e' : tau$.
Progress and preservation are a _technique_ for proving type soundness, by just using the two functions over and over.
== Logical relations
$ P_tau(e) $
A unary relation on any expression $e$ and its corresponding type $tau$.
(There are also binary relations $R_tau (e_1, e_2)$)
The property we're interested in is: $safe(e)$
- If $dot tack.r e:tau$ then $safe(e)$
*Definition (safe).* $safe(e) :equiv forall e' . (e mapstostar e') arrow.r.double isValue(e') or exists e'' . (e' mapsto e'')$
Common technique:
- Focus on the values, when do values belong to the relation, when are they safe?
- Then check expressions. When do expressions belong to the relation, and when are they safe?
$ V db(tau_1) = { v | ... } \
V db("bool") = { "true" , "false" } \
V db(tau_1 arrow.r tau_2) = { lambda (x : tau_1) . e | ... }
$
Can't have just arbitrary $lambda (x : tau_1) . e$ under $V db(tau_1 arrow.r tau_2)$.
The body also needs to be well-typed.
$
V db(tau_1 arrow.r tau_2) = { lambda (x : tau_1) . e | forall v in V db(tau_1) . subst(x, v, e) in Epsilon db(tau_2) } \
Epsilon db(tau) = { e | forall e' . e mapstostar e' and "irreducible"(e') arrow.r.double e' in V db(tau)}
$
#rect[
*Example of code from Rust.*
Unsafe code blocks are an example of a something that may not be _syntactically well-formed_, but are still represented by logical relations because they behave the same at the boundary.
]