300 likes | 311 Views
Learn about exceptions, their syntax, semantics, and compilation in programming languages. Follow stepwise development from Arithmetic Expressions to Adding Exceptions and Jumps. Detailed examples, compiler correctness theorems, and implementation insights provided. Discover the impact on program execution flow and the importance of proper exception handling for robust software development.
E N D
What Is An Exception? An event within a computation that causes termination in a non-standard way. Examples: • Division by zero; • Stack overflow; • Null pointer.
This Talk • Most modern languages support programming with exceptions, e.g. using throw and catch; • The compilation of such exception primitives is traditionally viewed as an advanced topic; • We give a simple explanation and verification, using elementary functional techniques.
Step 1 - Arithmetic Expressions Syntax: data Expr = Val Int | Add Expr Expr Semantics: eval :: Expr Int eval (Val n) = n eval (Add x y) = eval x + eval y
Virtual machine: type Stack = [Int] data Op = PUSH Int | ADD type Code = [Op] Compiler: comp :: Expr Code comp (Val n) = [PUSH n] comp (Add x y) = comp x ++ comp y ++ [ADD]
eval Expr Int comp [] Code Stack exec [] Compiler Correctness Theorem: exec s (comp e) = eval e : s
Proof: by induction, using a distribution lemma: exec s (xs ++ ys) = exec (exec s xs) ys However, we can avoid this lemma and shorten the proof by 60% by generalising the theorem: exec s (comp e ++ ops) = exec (eval e : s) ops
Step 2 - Adding Exceptions Syntax: data Expr = ••• | Throw | Catch Expr Expr Semantics: eval :: Expr Maybe Int eval (Val n) = Just n eval (Throw) = Nothing eval (Add x y) = eval xeval y eval (Catch x h) = eval x eval h
Examples: eval Add (Val 1) (Val 2) Just 3 eval Add Throw (Val 2) Nothing eval Catch (Val 1) (Val 2) Just 1 eval Catch Throw (Val 2) Just 2
Virtual machine: data Op = ••• | THROW | MARK Code | UNMARK type Stack = [Item] data Item = VAL Int | HAN Code Compiler: comp (Throw) = [THROW] comp (Catch x h) = [MARK (comp h)] ++ comp x ++ [UNMARK]
How Is THROW Executed? Informally, we must: • Unwind the stack seeking a handler; • Execute the first handler found, if any; • Skip to the next part of the computation.
Implementation: unwind :: Stack Code Stack unwind [] ops = [] unwind (VAL n : s) ops = unwind s ops unwind (HAN h : s) ops = exec s (h ++ skip ops) skip :: Code Code skip [] = [] skip (UNMARK : ops) = ops skip (MARK h : ops) = skip (skip ops) skip (op : ops) = skip ops
Example 1 + (catch (2 + throw) 3) Code Stack
Example 1 + (catch (2 + throw) 3) Code Stack PUSH 1 MARK [PUSH 3] PUSH 2 THROW ADD UNMARK ADD
Example 1 + (catch (2 + throw) 3) Code Stack MARK [PUSH 3] PUSH 2 THROW ADD UNMARK ADD VAL 1
Example 1 + (catch (2 + throw) 3) Code Stack PUSH 2 THROW ADD UNMARK ADD HAN [PUSH 3] VAL 1
Example 1 + (catch (2 + throw) 3) Code Stack THROW ADD UNMARK ADD VAL 2 HAN [PUSH 3] VAL 1
Example 1 + (catch (2 + throw) 3) Code Stack THROW ADD UNMARK ADD HAN [PUSH 3] VAL 1
Example 1 + (catch (2 + throw) 3) Code Stack PUSH 3 THROW ADD UNMARK ADD VAL 1
Example 1 + (catch (2 + throw) 3) Code Stack THROW ADD UNMARK ADD VAL 3 VAL 1
Example 1 + (catch (2 + throw) 3) Code Stack ADD UNMARK ADD VAL 3 VAL 1
Example 1 + (catch (2 + throw) 3) Code Stack UNMARK ADD VAL 3 VAL 1
Example 1 + (catch (2 + throw) 3) Code Stack ADD VAL 3 VAL 1
Example 1 + (catch (2 + throw) 3) Code Stack VAL 4
Compiler Correctness eval Expr Maybe Int comp conv Code Stack exec [] where conv Nothing = [] conv (Just n) = [VAL n]
As previously, we generalise to an arbitrary initial stack and arbitrary additional code. Theorem: exec s (comp e ++ ops) = exec s (trans (eval e) : ops) where trans :: Maybe Int Op trans Nothing = THROW trans (Just n) = PUSH n
Proof: by induction, using a skipping lemma: skip (comp e ++ ops) = skip ops Notes: • The proof is 3.5 pages of simple calculation; • The quickcheck tool was very useful as an aid to simplifying the definitions and results.
Step 3 - Adding Jumps Basic idea: Catch x h See the paper for further details. is now compiled to MARK a code for x UNMARK JUMP b a: code for h b: remaining code
Summary • Explanation and verification of the compilation of exceptions using stack unwinding. • Stepwise development to aid understanding: 1 - Arithmetic expressions; 2 - Adding exceptions; 3 - Adding jumps.
Further Work • Mechanical verification; • Modular compilers; • Calculating the compiler; • Generalising the language; • Compiler optimisations.