Program Semantics

advertisement
Program Analysis and Verification - Lecture 2
Program Semantics
Lecturer: Noam Rinetzky
Summary By: Gal Rotem and Michal Faktor
This summary is based on the 24.2.2014 lecture, the lesson presentation and online book.
Motivation
- Verifying absence of bugs.
For example, Apple recently had a bug in the SSL key exchange verification. The line 'goto fail' repeated
twice, causing the jump to be always executed. This error resulted in an unreachable code segment, which
could have been verified ahead.
- Proving functional correctness
Program analysis & verification
Given a program that has an assertion assert(p) at program point pc, we would like to prove that the
assertion is true (p is always true when the program reaches pc). Unfortunately, the problem is generally
undecidable.
An assertion can be either:
ο‚·
ο‚·
ο‚·
Always satisfied (true for all inputs)
Never satisfied (false for all inputs)
Sometimes satisfied (false for some inputs and false for some)
An assertion p holds for program P at program point pc if when ever the program reaches pc p is satisfied.
An analysis is sound if it only reports that assertions that hold indeed do. In particular, it never reports that
an assertion that may not be satisfied as one that holds.
The main idea is to verify that assertions hold is to use over approximation: we have an exact set of
configurations/behavior/states and we want to prove that it has certain properties are true with regard to
this set. As we mentioned before, this problem s is undecidable. However, if we can prove that a larger set,
i.e., one that contains the original set, has these properties, then we can be sure that so does the exact set
that we started from.
For example, we would like to the properties of all reachable states of a program P starting from the set of
all possible initial states, and to prove that P never reaches a 'bad' state. When preforming an overapproximation of the set of reachable states, i.e., considering a larger set then the reachable ones, we still
would like to show that the set of reachable states does not overlap with the set of bad states. If we can
prove this, then we know that the program never reaches a bad state. However if we failed to do so then the
program might still never reach a bad state but we have failed to detect it due to over approximation. In
other words, over approximation enables us to circumvent the undecidability problem by allowing the
analysis to be imprecise, i.e. the analysis might fail to detect that certain true properties of a program
indeed old. The challenge is to develop analyses that are sound and useful, i.e., not to imprecise.
Syntax VS Semantics
Syntax in the form that a program is written and Semantics is the meaning of the program.
We will mark the meaning of a program P (=the semantics of P) using "semantic braces"- βŸ¦π‘·βŸ§
"Standard" semantics – "state transformer" semantics
We can describe the meaning of a program using a state machine where ach operation is translated into a
state transition. We can start from a certain initial state and use the state machine until reaching a final
state. In slide 19, we can see a run that reaches an accepting state.
We would like to find the properties of all reachable states. So, instead of using a set-transformer, we will
use a set-of-states-transformer. In slide 25, we indicated the set of all possible states. We can see that as
the code in the else clause is unreachable, the set of states is empty – {}.
We reached a final set-of-states which is an accepting one. In any reachable state y set are equal to 42.
The sets of states we used here are infinite, and thus it might not be possible to represent them in the
machine memory. Hence, we would like to describe the sets in a bounded way:
"Abstract-state transformer" semantics
An abstract semantics is more compact, but less informative, than the set of states semantics. Each abstract
state represents a set of concrete states, i.e., the actual states used in the state-transformer semantics. For
example, we can map each variable either to E (if it is even), O (odd), or T (stands for top, meaning we don't
know whether it is odd or even). For example, the abstract state in which all the variables are mapped to E
represents all the program state in which all the variable of the programs have an even.
We can see in slide 30, that our final state indicates that both y and x are even. We received an imprecise
abstraction- a set which indeed a superset that contains the set of reachable states of the original problem,
but doesn’t guarantee that the assertion holds. For example, if at the previous semantics we could assert
that in same phase of the program y==42, now we cannot, however, we can assert that y is even, which
means that we can assert that assert(y%2==0) holds and that assert(y==33) [or any other odd] ) does not.
We will note that this semantics is sound-when we explore the abstract states we cover the set of reachable
states. But it may be imprecise and, as a result, might overlap with the set of bad states even though the
program never reaches a bad state (thus the analysis might raise "false alarms").
Programming Languages
Syntax is the way we write the program.
ο‚·
ο‚·
The syntax of the language can be described using BNF notation
Different parsing technique can be used
Semantics is the meaning of the program- what the program does.
Program Semantics
There are several types of program semantics, and the use of each depends on the problem we are
addressing. Examples:
ο‚· Operational: State-transformer. Variations: set-of-states transformer, trace transformer (where
trace describes the run of the program)
ο‚· Axiomatic: Predicate-transformer
ο‚· Denotational: Representing the meanings of programs as mathematical function. For example: the
semantics of a program that computes a factorial is the factorial function.
What semantics do we want?
ο‚· We want the semantics to capture the aspects of computation we care about. I.e., it should be able
to represent the properties we are interested of.
ο‚· We want the semantics to hide irrelevant details and use the most abstract level we can. For
example, the specific way the program counter works might not be of interesting to us.
o The highest level of abstraction is- "fully abstract". When using a fully abstract semantics, if
we have 2 programs that for every input evaluates the same output, then they have the
same “semantics”. I.e., if we have two different programs that compute the factorial
function, e.g., one using a loop and one using recursion, there meaning in a fully abstract
denotational semantics would be the factorial function.
ο‚· Compositional: a a compositional semantics, defines the semantics in a modular way, the program’s
meaning is defined by the meaning of its (syntactical) parts
Formal semantics
"Formal semantics is concerned with rigorously specifying the meaning, or behavior, of programs, pieces of
hardware, etc."
"This theory allows a program to be manipulated like a formula- that is to say, its properties can be
calculated."
Why formal semantics?
ο‚· Allows us to define the meaning of the program in an implementation-independent way
ο‚· Allows to automatically generate interpreters (and hopefully in the future, full-fledged compilers(
ο‚· Makes verification and debugging more precise: if you don’t know what the program does, how do
you know it is incorrect?
Levels of abstractions and applications
π‘†π‘‘π‘Žπ‘‘π‘–π‘ π΄π‘›π‘Žπ‘™π‘¦π‘ π‘–π‘ 
(π‘Žπ‘π‘ π‘‘π‘Ÿπ‘Žπ‘π‘‘ π‘ π‘’π‘šπ‘Žπ‘›π‘‘π‘–π‘π‘ )
βŠ‘
(πΆπ‘œπ‘›π‘π‘Ÿπ‘’π‘‘π‘’) π‘ƒπ‘Ÿπ‘œπ‘”π‘Ÿπ‘Žπ‘š π‘†π‘’π‘šπ‘Žπ‘›π‘‘π‘–π‘π‘ 
βŠ‘
π΄π‘ π‘ π‘’π‘šπ‘π‘™π‘¦ − 𝐿𝑒𝑣𝑒𝑙 π‘†π‘’π‘šπ‘Žπ‘‘π‘–π‘π‘ 
(π‘†π‘šπ‘Žπ‘™π‘™ − 𝑠𝑑𝑒𝑝)
Going up the arrow =
Going to an higher
abstraction level
Semantic description methods
ο‚· Operational semantics: describes how the program operates on every state in the system, for
instance by using a state transformer. We will learn two kinds of operational semantics:
o Natural semantics (big step) [G. Kahn]
o Structural semantics (small step) [G. Plotkin]
ο‚· Denotational semantics [D. Scott, C. Strachy]: the program meaning is described by mathematical
objects- as functions or relations between input and output.
ο‚· Axiomatic semantics [C. A. R. Hoare, R. Floyd]: the program meaning is defined by what we can
prove on the program. This semantics is used as a logical tool to prove properties of the program by
its effect on assertions.
Operational Semantics
In the following, we consider a simple programming language called “While”.
First, we define the programming language’s abstract syntax. (Here we use the term abstract syntax in the
same way as in the compilation course):
π‘Ž ∷= 𝑛|π‘₯|π‘Ž1 + π‘Ž2 |π‘Ž1 ⋆ π‘Ž2 |π‘Ž1 − π‘Ž2
𝑏 ∷= π‘‘π‘Ÿπ‘’π‘’|π‘“π‘Žπ‘™π‘ π‘’|π‘Ž1 = π‘Ž2 |π‘Ž1 ≤ π‘Ž2 |¬π‘|𝑏1 ∧ 𝑏2
𝑆 ∷= π‘₯ ≔ π‘Ž|π‘ π‘˜π‘–π‘|𝑆1 ; 𝑆2 |𝑖𝑓 𝑏 π‘‘β„Žπ‘’π‘› 𝑆1 𝑒𝑙𝑠𝑒 𝑆2 |π‘€β„Žπ‘–π‘™π‘’ 𝑏 π‘‘π‘œ 𝑆
We can use the abstract syntax and represent programs using “parse trees”, i.e., their ASTs.
The abstract syntax is ambiguous. In slide 55 we can see 2 AST that matches the same statement. The
concrete syntax must provide sufficient information so unique trees will be constructed. The ambiguity can
be resolved by adding brackets and by defining the precedence of the operators rather straightforwardly,
and hence we will ignore it.
Example: AST for the While program: "while (y≥z) do z:=z+1" is the following:
Syntactic categories:
The program’s text (syntax) is comprised of the following syntactic categories:
𝑛 ∈ π‘π‘’π‘š
Numerals (the representation of the numbers)
π‘₯ ∈ π‘‰π‘Žπ‘Ÿ
Program variables
π‘Ž ∈ 𝐴𝑒π‘₯𝑝
Arithmetic expressions
𝑏 ∈ 𝐡𝑒π‘₯𝑝
Boolean expressions
𝑆 ∈ π‘†π‘‘π‘š
Statements
Using the syntactic categories we can define the semantic domains (or categories):
Z
πΌπ‘›π‘‘π‘’π‘”π‘’π‘Ÿπ‘  {0,1 − 1,2, −2, … }
T
π‘‡π‘Ÿπ‘’π‘‘β„Ž π‘£π‘Žπ‘™π‘’π‘’π‘  {𝑓𝑓, 𝑑𝑑}
State
π‘‰π‘Žπ‘Ÿ → 𝑍
The semantics domains give a meaning to each syntactic category.
Note that there is a difference between integers and numerals: numerals syntactic entities and are used to
write numbers as text in the program. Integers are semantic entities and are used to represent actual integer
numbers in Z. Formally, we should use different notations to distinguish between the two. However, for
brevity, use the same (decimal) notation for both and distinguish between the two by context. A similar
distinction exists for Booleans.
The state of the program records the values of its variables. Formally, it is a mapping between (the names
of) program variables and their (integer) value.
For example: 𝑠 = [π‘₯ ↦ 5, 𝑦 ↦ 7, 𝑧 ↦ 0] is a state where the values of variable x, y, and z are 5, 7, and 0,
respectively.
We can perform lookup and update on the state:
Lookup: 𝑠 π‘₯ = 5
returns the value of x in state s
Update: 𝑠[π‘₯ ↦ 6] = [π‘₯ ↦ 6, 𝑦 ↦ 7, 𝑧 ↦ 0] is the same state as s except that x is mapped to 6.
Note we are performing a destructive update: when updating the value of a variable we forget the previous
mapping.
Additional state manipulations examples:
ο‚· [π‘₯ ↦ 1, 𝑦 ↦ 7, 𝑧 ↦ 16]𝑦 = 7
ο‚· [π‘₯ ↦ 1, 𝑦 ↦ 7, 𝑧 ↦ 16]𝑑 = undefined
ο‚· [π‘₯ ↦ 1, 𝑦 ↦ 7, 𝑧 ↦ 16][π‘₯ ↦ 5] = [π‘₯ ↦ 5, 𝑦 ↦ 7, 𝑧 ↦ 16]
ο‚· [π‘₯ ↦ 1, 𝑦 ↦ 7, 𝑧 ↦ 16][π‘₯ ↦ 5]π‘₯ = 5
ο‚· [π‘₯ ↦ 1, 𝑦 ↦ 7, 𝑧 ↦ 16][π‘₯ ↦ 5]𝑦 = 7
Semantics of arithmetic expressions
Arithmetic expressions are side-effect free, they are evaluated in a state but do not change it.
We define the meaning of arithmetic expressions as a total function π’œ that takes two arguments: a syntactic
expression and a state.
The semantic function π“βŸ¦π‘¨π’†π’™π’‘βŸ§: 𝑺𝒕𝒂𝒕𝒆 → 𝒁 is defined by induction on the syntax tree:
π’œβŸ¦π‘›βŸ§π‘  = 𝑛
(Note: we have 2 different n-s here. In green-the representation of the number in the
programming language, and in red- the real integer)
π’œβŸ¦π‘₯βŸ§π‘  = 𝑠 π‘₯
(Performing lookup)
π’œβŸ¦π‘Ž1 + π‘Ž2 βŸ§π‘  = π’œβŸ¦π‘Ž1 βŸ§π‘  + π’œβŸ¦π‘Ž2 βŸ§π‘ 
π’œβŸ¦π‘Ž1 − π‘Ž2 βŸ§π‘  = π’œβŸ¦π‘Ž1 βŸ§π‘  − π’œβŸ¦π‘Ž2 βŸ§π‘ 
π’œβŸ¦π‘Ž1 ⋆ π‘Ž2 βŸ§π‘  = π’œβŸ¦π‘Ž1 βŸ§π‘  × π’œβŸ¦π‘Ž2 βŸ§π‘ 
π’œβŸ¦(π‘Ž1 )βŸ§π‘  = π’œβŸ¦π‘Ž1 βŸ§π‘ 
(⋆ is the syntactic multiplication operator, and x denotes the actual
semantic multiplication of integers)
(This definition is not really needed as in our simple language we effectively use the
program’s AST.)
π’œβŸ¦−π‘Ž1 βŸ§π‘  = 0 − π’œβŸ¦π‘Ž1 βŸ§π‘ 
This definition is compositional: the sematic is defined in a modular way: the meaning of an expression is
defined by the meaning of its parts.
Properties can be proved by structural induction.
Arithmetic expression exercises:
1. Suppose 𝑠 π‘₯ = 3. Then: π’œβŸ¦π‘₯ + 1βŸ§π‘  = π’œβŸ¦π‘₯βŸ§π‘  + π’œβŸ¦1βŸ§π‘  = 3 + 1 = 4
2. The meaning of π’œβŸ¦π‘₯ + 1 + 1βŸ§π‘  and π’œβŸ¦π‘₯ + 2βŸ§π‘  is identical:
π’œβŸ¦π‘₯ + 1 + 1βŸ§π‘  = π’œβŸ¦π‘₯βŸ§π‘  + π’œβŸ¦1 + 1βŸ§π‘  = 𝑠 π‘₯ + 2
π’œβŸ¦π‘₯ + 2βŸ§π‘  = π’œβŸ¦π‘₯βŸ§π‘  + π’œβŸ¦2βŸ§π‘  = 𝑠 π‘₯ + 2
Semantics of Boolean expressions:
Boolean expressions are, as well, side-effect free.
The semantic function π“‘βŸ¦π‘©π’†π’™π’‘βŸ§: 𝑺𝒕𝒂𝒕𝒆 → 𝑻 is defined by induction on the syntax tree:
π“‘βŸ¦π‘‘π‘Ÿπ‘’π‘’βŸ§π‘  = 𝑑𝑑
π“‘βŸ¦π‘“π‘Žπ‘™π‘ π‘’βŸ§π‘  = 𝑓𝑓
𝑑𝑑 𝑖𝑓 π’œβŸ¦π‘Ž1 βŸ§π‘  = π’œβŸ¦π‘Ž2 βŸ§π‘ 
π“‘βŸ¦π‘Ž1 = π‘Ž2 βŸ§π‘  = {𝑓𝑓
π‘œπ‘‘β„Žπ‘’π‘Ÿπ‘€π‘–π‘ π‘’
𝑑𝑑 𝑖𝑓 π’œβŸ¦π‘Ž1 βŸ§π‘  ≤ π’œβŸ¦π‘Ž2 βŸ§π‘ 
π“‘βŸ¦π‘Ž1 ≤ π‘Ž2 βŸ§π‘  = {𝑓𝑓
π‘œπ‘‘β„Žπ‘’π‘Ÿπ‘€π‘–π‘ π‘’
𝑑𝑑 𝑖𝑓 π“‘βŸ¦π‘βŸ§π‘  = 𝑓𝑓
π“‘βŸ¦¬π‘βŸ§π‘  = {𝑓𝑓
𝑖𝑓 π“‘βŸ¦π‘βŸ§π‘  = 𝑑𝑑
𝑑𝑑 𝑖𝑓 π“‘βŸ¦π‘1 βŸ§π‘  = 𝑑𝑑 π‘Žπ‘›π‘‘ π“‘βŸ¦π‘2 βŸ§π‘  = 𝑑𝑑
π“‘βŸ¦π‘1 ∧ 𝑏2 βŸ§π‘  = {𝑓𝑓
π‘œπ‘‘β„Žπ‘’π‘Ÿπ‘€π‘–π‘ π‘’
The operational semantic is concerned with how to execute programs, i.e., how statements modify the state
and how to define transition relation between configurations. We are interested in how the states are
modified during the execution of the statement.
There are 2 different approaches to operational semantics:
ο‚·
ο‚·
Natural semantics: describes how the overall results of executions are obtained. It is also called "bigstep" semantics. Intuitively, it s defined as a relation between an input state and an output state.
Structural operational semantics: describes how the individual steps of the computations take place.
So-called "small-step" semantic.
Natural operating semantics (NS)
ο‚·
ο‚·
ο‚·
This semantics was developed by Gilles Kahn and it is also known as "Big/large step semantics"
It is defined as a relation between configurations ⟨𝑺, 𝒔⟩ → 𝒔′ where the arrow represent execution
of all steps used to compute statement S starting from state s and ending in state s’.
There are 2 kinds of configurations:
ο‚·
o ⟨𝑆, 𝑠⟩ denotes that a statement S is about to execute on state s
o 𝑠
A terminal (final) state
Transitions: ⟨𝑆, 𝑠⟩ → 𝑠′ means that the execution of S from state s terminates in (result) state s'.
The semantics is capable of only describing executions that terminates. Thus, using this semantics
we cannot describe infinite (non-terminating) computations.
ο‚·
We define → using rules of form:
premise
side condition
⟨𝑆1 ,𝑠1 ⟩→𝑠1′ ,…,⟨𝑆𝑛 ,𝑠𝑛 ⟩→𝑠𝑛 ′
⟨𝑆,𝑠⟩→𝑠′
𝑖𝑓 …
conclusion
Where 𝑆1 , … , 𝑆𝑛 are immediate constituents of S or are statements constructed from the immediate
constituents of S. The side condition specifies when we can apply the rule.
Natural semantics for While
[π‘Žπ‘ π‘ π‘›π‘  ]
⟨π‘₯ ≔ π‘Ž, 𝑠⟩ → 𝑠[π‘₯ ↦ π’œβŸ¦π‘ŽβŸ§π‘ ]
⟨π‘ π‘˜π‘–π‘, 𝑠⟩ → 𝑠
[π‘ π‘˜π‘–π‘π‘›π‘  ]
[π‘π‘œπ‘šπ‘π‘›π‘  ]
⟨𝑆1 ;𝑆2 ,𝑠⟩→𝑠′′
⟨𝑖𝑓 𝑏 π‘‘β„Žπ‘’π‘› 𝑆1 𝑒𝑙𝑠𝑒 𝑆2 ,𝑠⟩→𝑠′
⟨𝑆2 ,𝑠⟩→𝑠′
𝑓𝑓
[𝑖𝑓𝑛𝑠 ]
axiom
⟨𝑆1 ,𝑠⟩→𝑠′ ,⟨𝑆2 ,𝑠′⟩→𝑠′′
⟨𝑆1 ,𝑠⟩→𝑠′
𝑑𝑑
[𝑖𝑓𝑛𝑠
]
axioms
⟨𝑖𝑓 𝑏 π‘‘β„Žπ‘’π‘› 𝑆1 𝑒𝑙𝑠𝑒 𝑆2
𝑓𝑓
[π‘€β„Žπ‘–π‘™π‘’π‘›π‘  ]
𝑑𝑑
[π‘€β„Žπ‘–π‘™π‘’π‘›π‘ 
]
𝑖𝑓 β„¬βŸ¦π‘βŸ§π‘  = 𝑑𝑑
𝑖𝑓 β„¬βŸ¦π‘βŸ§π‘  = 𝑓𝑓
,𝑠⟩→𝑠′
⟨π‘€β„Žπ‘–π‘™π‘’ 𝑏 π‘‘π‘œ 𝑆, 𝑠⟩ → 𝑠
⟨𝑆,𝑠⟩→𝑠′ ,⟨π‘€β„Žπ‘–π‘™π‘’ 𝑏 π‘‘π‘œ 𝑆,𝑠′⟩→𝑠′′
⟨π‘€β„Žπ‘–π‘™π‘’ 𝑏 π‘‘π‘œ 𝑆,𝑠⟩→𝑠′′
If the side condition holds,
we can apply the rule
𝑖𝑓 β„¬βŸ¦π‘βŸ§π‘  = 𝑓𝑓
𝑖𝑓 β„¬βŸ¦π‘βŸ§π‘  = 𝑑𝑑
We note that the while rule, in case b doesn’t hold, indicates that the state remains the same. In case b
holds, the while rule is defined recursively, and not by using an immediate constituent. In other words, the
derivation rule for while is non-compositional.
Example: Let 𝑠0 be the state that assigns zero to all program variables. We show that the execution of
statement 𝑖𝑓 π‘₯ = 0 π‘‘β„Žπ‘’π‘› π‘₯ ≔ π‘₯ + 1 𝑒𝑙𝑠𝑒 π‘ π‘˜π‘–π‘ ends with state 𝑠0 [π‘₯ ↦ 1].
using [π‘Žπ‘ π‘ π‘›π‘  ] and the definition of π’œ : ⟨π‘₯ ≔ π‘₯ + 1, 𝑠0 ⟩ → 𝑠0 [π‘₯ ↦ 1] (1)
using [π‘ π‘˜π‘–π‘π‘›π‘  ] : ⟨π‘ π‘˜π‘–π‘, 𝑠0 ⟩ → 𝑠0 (2)
using [π‘π‘œπ‘šπ‘π‘›π‘  ], (1) and (2) :
⟨π‘ π‘˜π‘–π‘,𝑠0 ⟩→𝑠0 ,⟨π‘₯≔π‘₯+1,𝑠0 ⟩→𝑠0 [π‘₯↦1]
⟨π‘ π‘˜π‘–π‘,π‘₯≔π‘₯+1,𝑠0 ⟩→𝑠0 [π‘₯↦1]
𝑑𝑑 ]
using 𝑠0 definition, [𝑖𝑓𝑛𝑠
and (1) :
⟨π‘₯≔π‘₯+1,𝑠0 ⟩→𝑠0 [π‘₯↦1]
⟨𝑖𝑓 π‘₯=0 π‘‘β„Žπ‘’π‘› π‘₯≔π‘₯+1 𝑒𝑙𝑠𝑒 π‘ π‘˜π‘–π‘,𝑠0 ⟩→𝑠0 [π‘₯↦1]
𝑖𝑓 β„¬βŸ¦π‘₯ = 0βŸ§π‘ 0 = 𝑑𝑑
Derivation trees
When we use the axioms and rules to derive a transition ⟨𝑆, 𝑠⟩ → 𝑠′, we obtain a derivation tree.
The root of the derivation tree is ⟨𝑆, 𝑠⟩ → 𝑠′ and the leaves are instances of axioms. The internal nodes are
conclusions of instantiated rules and have the corresponding premises as their immediate children.
We write the derivation tree with the root at the bottom. Therefore, the children are above their parents
(and the premises are above the conclusions, as we originally defined). We build the tree from the root
(bottom) upwards.
Example: Assume
𝑠0 = [π‘₯ ↦ 5, 𝑦 ↦ 7, 𝑧 ↦ 0]
𝑠1 = [π‘₯ ↦ 5, 𝑦 ↦ 7, 𝑧 ↦ 5]
𝑠2 = [π‘₯ ↦ 7, 𝑦 ↦ 7, 𝑧 ↦ 5]
𝑠3 = [π‘₯ ↦ 7, 𝑦 ↦ 5, 𝑧 ↦ 5]
steps 3 and 4: applications of [π‘Žπ‘ π‘ π‘›π‘  ] axiom, so these is a leaves
⟨𝑧 ≔ π‘₯, 𝑠0 ⟩ → 𝑠1
⟨π‘₯ ≔ 𝑦, 𝑠0 ⟩ → 𝑠2
step 3: applying [π‘π‘œπ‘šπ‘π‘›π‘  ]
rule
⟨(𝑧 ≔ π‘₯; π‘₯ ≔ 𝑦), 𝑠0 ⟩ → 𝑠2
step 2: application of [π‘Žπ‘ π‘ π‘›π‘  ] axiom,
so this is a leaf
⟨𝑦 ≔ 𝑧, 𝑠2 ⟩ → 𝑠3
step 1: applying [π‘π‘œπ‘šπ‘π‘›π‘  ] rule
⟨(𝑧 ≔ π‘₯; π‘₯ ≔ 𝑦); 𝑦 ≔ 𝑧, 𝑠0 ⟩ → 𝑠3
We note that in deterministic semantics, we should have only one derivation possibility in each step. If the
semantics isn't deterministic, we can have several possibilities and every one that succeeds is a possible
meaning of the program.
In the While language, as defined so far, both derivation tree and output state are unique.
Top-down evaluation example
We now consider a program that compute the factorial of x, assuming that x = 2.
Denoted by W
𝑦 ≔ 1; π‘€β„Žπ‘–π‘™π‘’ ¬(π‘₯ = 1) π‘‘π‘œ (𝑦 ≔ 𝑦 ∗ π‘₯; π‘₯ ≔ π‘₯ − 1).
First, we use only the derivations rules and build the tree from the root to the leaves, without computing the
values in red. Then we compute the values in red in the specified order, and we can see that the states after
(4) from the leaves
(6)
[π‘Žπ‘ π‘ π‘›π‘  ]are computed
[π‘Žπ‘ π‘ π‘›π‘  ]
(5) (top) down.
(7)
a transition
⟨𝑦 ≔ 𝑦 ∗ π‘₯; 𝑠[𝑦 ↦ 1]⟩ → 𝑠[𝑦 ↦ 2]
[π‘π‘œπ‘šπ‘π‘›π‘  ]
⟨π‘₯ ≔ π‘₯ − 1; 𝑠[𝑦 ↦ 2]⟩ → 𝑠[𝑦 ↦ 2][π‘₯ ↦ 1]
𝑓𝑓
(3)
[π‘€β„Žπ‘–π‘™π‘’π‘›π‘  ]
⟨𝑦 ≔ 𝑦 ∗ π‘₯; π‘₯ ≔ π‘₯ − 1, 𝑠[𝑦 ↦ 1]⟩ → 𝑠[𝑦 ↦ 2][π‘₯ ↦ 1]
(8)
[π‘Žπ‘ π‘ π‘›π‘  ]
(1)
⟨𝑦 ≔ 1; 𝑠⟩ → 𝑠[𝑦 ↦ 1]
(9)
(10)
⟨π‘Š, 𝑠[𝑦 ↦ 2][π‘₯ ↦ 1]⟩ → 𝑠[𝑦 ↦ 2, π‘₯ ↦ 1]
𝑑𝑑
[π‘€β„Žπ‘–π‘™π‘’π‘›π‘ 
] (2)
(11)
⟨π‘Š; 𝑠[𝑦 ↦ 1]⟩ → 𝑠[𝑦 ↦ 2, π‘₯ ↦ 1]
[π‘π‘œπ‘šπ‘π‘›π‘  ]
(12)
⟨𝑦 ≔ 1; π‘€β„Žπ‘–π‘™π‘’ ¬(π‘₯ = 1) π‘‘π‘œ (𝑦 ≔ 𝑦 ∗ π‘₯; π‘₯ ≔ π‘₯ − 1), 𝑠⟩ → 𝑠[𝑦 ↦ 2][π‘₯ ↦ 1]
This example shows how the semantics can compute the derivation tree and not only assert that a given
derivation tree is constructed according to the rules.
Program termination
Given a statement S and input s:
ο‚·
ο‚·
S terminates on s if there exists a state s' s.t ⟨𝑆, 𝑠⟩ → 𝑠 ′
S loops on s if there is no a state s' s.t ⟨𝑆, 𝑠⟩ → 𝑠′
Given a statement S:
ο‚·
ο‚·
S always terminates if for every input state s, S terminates on s.
S always loops if for every input state s, S loops on s
Semantic equivalence
𝑆1 and 𝑆2 are semantically equivalent if for all s and s': < 𝑆1 , 𝑠 >→ 𝑠′ iff < 𝑆2 , 𝑠 >→ 𝑠′
Intuitively, if 𝑆1 and 𝑆2 are semantically equivalent and we can't reach from s to s' in 𝑆1 we won't be able to
do so in 𝑆2 as well. Note that we require that both program terminate exactly on the same input states.
For example: the statement π‘€β„Žπ‘–π‘™π‘’ 𝑏 π‘‘π‘œ 𝑆 is semantically equivalent to
𝑖𝑓 𝑏 π‘‘β„Žπ‘’π‘› (𝑆; π‘€β„Žπ‘–π‘™π‘’ 𝑏 π‘‘π‘œ 𝑆) 𝑒𝑙𝑠𝑒 π‘ π‘˜π‘–π‘.
Proof: Lemma 2.5 in the book, pages 26-27
Properties of natural semantics
Equivalence of program constructs
ο‚·
ο‚·
ο‚·
π‘ π‘˜π‘–π‘; π‘ π‘˜π‘–π‘ is semantically equivalent to π‘ π‘˜π‘–π‘
((𝑆1 ; 𝑆2 ); 𝑆3 ) is semantically equivalent to (𝑆1 ; (𝑆2 ; 𝑆3 ))
The proof is in slide 80. The main idea is to show that for every state s, if with one statement we
reach state s', then we will reach the same state s' using the other statement (and vise versa). In
each direction we construct 2 derivation trees. One tree is constructed under the assumption that
from state s we reach state s', and thus will give us a set of rule applications, which we use in the
construction of the other tree.
Example: (π‘₯ ≔ 5; 𝑦 ≔ π‘₯ ∗ 8) is semantically equivalent to (π‘₯ ≔ 5; 𝑦 ≔ 40)
The semantics for While is deterministic
Theorem: for all statements S and states 𝑠1 , 𝑠2: 𝑖𝑓 ⟨𝑆, 𝑠⟩ → 𝑠1 π‘Žπ‘›π‘‘ ⟨𝑆, 𝑠⟩ → 𝑠2 π‘‘β„Žπ‘’π‘› 𝑠1 = 𝑠2
Proof: in the book, pages 29-30. The proof uses induction on the shape of derivation trees. First, prove that
the property holds for all simple derivation trees by showing it holds for axioms (the tree has single node in
these cases) and then prove that the property holds for all composite trees:
For each rule assume that the property holds for its premises (induction hypothesis) and prove it holds for
the conclusion of the rule.
The semantic function 𝑺𝒏𝒔
Using the natural (operational) semantics, we can assign the meaning to any statement S as a partial
function from State to State:
𝑆𝑛𝑠 : π‘†π‘‘π‘š → (π‘†π‘‘π‘Žπ‘‘π‘’ β†ͺ π‘†π‘‘π‘Žπ‘‘π‘’)
(A different way to define the semantics function would be𝑆𝑛𝑠 : (π‘†π‘‘π‘š × π‘†π‘‘π‘Žπ‘‘π‘’) →
π‘†π‘‘π‘Žπ‘‘π‘’.
Both ways are equivalent, because of currying)
𝑠′
𝑖𝑓 ⟨𝑆, 𝑠⟩ → 𝑠′
𝑆𝑛𝑠 βŸ¦π‘†βŸ§π‘  = {
𝑒𝑛𝑑𝑒𝑓𝑖𝑛𝑒𝑑 π‘œπ‘‘β„Žπ‘’π‘Ÿπ‘€π‘–π‘ π‘’
In natural semantics for While, if ⟨𝑆, 𝑠⟩ terminates then there is a state s' s.t ⟨𝑆, 𝑠⟩ → 𝑠′, and this state is
unique. Otherwise, 𝑆𝑛𝑠 βŸ¦π‘†βŸ§π‘  is undefined. The latter happens because we cannot construct a derivation tree
⟨𝑆, 𝑠⟩ → 𝑠′ for any s’.
Examples: 𝑆𝑛𝑠 βŸ¦π‘ π‘˜π‘–π‘βŸ§π‘  = 𝑠
𝑆𝑛𝑠 ⟦π‘₯ ≔ 1βŸ§π‘  = 𝑠[π‘₯ ↦ 1]
𝑆𝑛𝑠 βŸ¦π‘€β„Žπ‘–π‘™π‘’ π‘‘π‘Ÿπ‘’π‘’ π‘‘π‘œ π‘ π‘˜π‘–π‘βŸ§π‘  = 𝑒𝑛𝑑𝑒𝑓𝑖𝑛𝑒𝑑. The execution of this statement doesn't terminate, and so its
meaning is undefined in the natural semantics.
Structural operating semantics (SOS)
ο‚·
ο‚·
This semantics was developed by Gordon Plotkin and it is also called "small step semantics".
The semantics is defined as a transition relation between configurations ⟨𝑺, 𝒔⟩ ⇒ ⟨𝑺′, 𝒔′⟩ . However,
here, the (double) arrow represents an execution of the first step.
ο‚·
ο‚·
ο‚·
ο‚·
There are 2 kinds of configurations:
o ⟨𝑆, 𝑠⟩ denotes a statement S which is about to execute on state s
o 𝑠
denotes terminal (final) state
Transitions are written ⟨𝑆, 𝑠⟩ ⇒ 𝛾, where:
o 𝛾 = ⟨𝑆 ′ , 𝑠′⟩ if the execution of S from s is not completed and the remaining computation
proceeds from intermediate configuration 𝛾.
o 𝛾 = 𝑠′ if the execution of S from s has terminated and the final state is s'.
⟨𝑆, 𝑠⟩ is stuck if there is no 𝛾 s.t ⟨𝑆, 𝑠⟩ ⇒ 𝛾. A configuration can be stuck if we don't have a rule in the
semantics that tells us how to proceed from ⟨𝑆, 𝑠⟩. For example: if we have 'if' without 'else'
afterwards, and the semantics does not define what happens if the condition does not hold then the
execution gets stuck if the 'if' condition doesn't hold.
If ⟨𝑆, 𝑠⟩ ⇒ ⟨𝑆 ′ , 𝑠′⟩ then s and s' might be equal, i.e., we can stay in the same state after the partial
execution. Note however that the statement changes. This can happens, e.g., when evaluating the
condition of a an if statement.
Structural semantics for While
[π‘Žπ‘ π‘ π‘†π‘‚π‘† ]
⟨π‘₯ ≔ π‘Ž, 𝑠⟩ ⇒ 𝑠[π‘₯ ↦ π’œβŸ¦π‘ŽβŸ§π‘ ]
[π‘ π‘˜π‘–π‘π‘†π‘‚π‘† ]
Axioms (atomic/primitive operations)
⟨π‘ π‘˜π‘–π‘, 𝑠⟩ ⇒ 𝑠
1
[π‘π‘œπ‘šπ‘π‘†π‘‚π‘†
]
⟨𝑆1 ,𝑠⟩⇒⟨𝑆′1 ,𝑠′⟩
⟨𝑆1 ;𝑆2 ,𝑠⟩⇒⟨𝑆′1 ;𝑆2 ,𝑠′⟩
This happens when the execution of 𝑆1 from s is not completed, and thus
we proceed to 𝑆′1
2
[π‘π‘œπ‘šπ‘π‘†π‘‚π‘†
]
⟨𝑆1 ,𝑠⟩⇒𝑠′
⟨𝑆1 ;𝑆2 ,𝑠⟩⇒⟨𝑆2 ,𝑠′⟩
This happens when the execution of 𝑆1 from s has terminated, and thus
we proceed to 𝑆2 . This means that 𝑆1 was an atomic operation.
𝑑𝑑
[𝑖𝑓𝑆𝑂𝑆
]
𝑓𝑓
[𝑖𝑓𝑆𝑂𝑆 ]
⟨𝑖𝑓 𝑏 π‘‘β„Žπ‘’π‘› 𝑆1 𝑒𝑙𝑠𝑒 𝑆2 , 𝑠⟩ ⇒ ⟨𝑆1 , 𝑠⟩
𝑖𝑓 β„¬βŸ¦π‘βŸ§π‘  = 𝑑𝑑
⟨𝑖𝑓 𝑏 π‘‘β„Žπ‘’π‘› 𝑆1 𝑒𝑙𝑠𝑒 𝑆2 , 𝑠⟩ ⇒ ⟨𝑆2 , 𝑠⟩
𝑖𝑓 β„¬βŸ¦π‘βŸ§π‘  = 𝑓𝑓
The if rule applies some sort of
rewriting to the program
[π‘€β„Žπ‘–π‘™π‘’π‘†π‘‚π‘† ] ⟨π‘€β„Žπ‘–π‘™π‘’ 𝑏 π‘‘π‘œ 𝑆, 𝑠⟩ ⇒
βŸ¨π‘–π‘“ 𝑏 π‘‘β„Žπ‘’π‘›
𝑆; π‘€β„Žπ‘–π‘™π‘’ 𝑏 π‘‘π‘œ 𝑆
𝑒𝑙𝑠𝑒
π‘ π‘˜π‘–π‘, π‘ βŸ©
When we perform the execution of a while statement, we unfold one level each time, by replacing it
with 'if'. We stop unfolding when we the condition b will be false, and then we fold back and remain
with the state we reached when b was false (as then, we perform skip).
Derivation sequences
A derivation sequence of a statement S starting in state s is either:
ο‚·
ο‚·
A finite sequence 𝛾0 , 𝛾1 , 𝛾2 , … , π›Ύπ‘˜ such that:
1. 𝛾0 = ⟨𝑆, 𝑠⟩
2. 𝛾𝑖 ⇒ 𝛾𝑖+1
3. π›Ύπ‘˜ is either stuck configuration or a final state
An infinite sequence𝛾0 , 𝛾1 , 𝛾2 , … such that
1. 𝛾0 = ⟨𝑆, 𝑠⟩
2. 𝛾𝑖 ⇒ 𝛾𝑖+1
Notations:
ο‚·
ο‚·
𝛾0 ⇒π‘˜ π›Ύπ‘˜
𝛾0 ⇒∗ 𝛾
𝛾0 derives π›Ύπ‘˜ in k steps. (⇒π‘˜ is the composition of ⇒ k times)
𝛾0 derives 𝛾 in a finite (possibly zero) number of steps. (⇒π‘˜∗ is the reflexive transition
closure of composition of ⇒ k times)
For each step there is a corresponding derivation tree.
Note that SOS can define executions that don't terminate.
We can see a derivation sequence example in slide 88. Each step in the sequence should be computed
(alternatively, formally proved to be constructed according to the rules of the semantics) using a derivation
tree. The slide contains a derivation tree for the first step, and the rules that are being used there, from
1
2
bottom-up are: [π‘π‘œπ‘šπ‘π‘†π‘‚π‘†
] , [π‘π‘œπ‘šπ‘π‘†π‘‚π‘†
] and [π‘Žπ‘ π‘ π‘†π‘‚π‘† ].
Evaluation via derivation sequences
ο‚· For any While statement S and state s it is always possible to find at least one derivation sequence
from ⟨𝑆, 𝑠⟩: apply axioms and rules forever or until a terminal or stuck configuration is reached.
ο‚· Proposition: there are no stuck configurations in While
In slide 90 we can see a derivation sequences example for factorial (n!).
Note that each step should be computed using a derivation tree (as one step is proved in slide 88)
Program termination
Given a statement S and input s:
ο‚·
ο‚·
ο‚·
S terminates on s if there exists a finite derivation sequence starting at ⟨𝑆, 𝑠⟩ (This finite derivation
sequence ends with either stuck configuration or a final state)
S terminates successfully on s if there exists a finite derivation sequence starting at ⟨𝑆, 𝑠⟩ leading to a
final state
S loops on s if there exists an infinite derivation sequence starting at ⟨𝑆, 𝑠⟩
Properties of structural operational semantics
ο‚· 𝑆1 and 𝑆2 are semantically equivalent if:
o For all s and 𝛾 which is either final or stuck, ⟨𝑆1 , 𝑠⟩ ⇒∗ 𝛾 iff ⟨𝑆2 , 𝑠⟩ ⇒∗ 𝛾
 (we are using here the ⇒∗ notation, as the length of the two derivation sequences
may be different)
o For all s, there is an infinite derivation sequence starting at ⟨𝑆1 , 𝑠⟩ iff there is an infinite
derivation sequence starting at ⟨𝑆2 , 𝑠⟩ (𝑆1 loops on s iff 𝑆2 loops on s).
ο‚· Theorem: While is deterministic: if ⟨𝑆, 𝑠⟩ ⇒∗ 𝑠1 and ⟨𝑆, 𝑠⟩ ⇒∗ 𝑠2 then 𝑠1 = 𝑠2
Sequential composition
Lemma: if ⟨𝑆1 ; 𝑆2 , 𝑠⟩ ⇒π‘˜ 𝑠′′ then there exists s' and k=m+n s.t ⟨𝑆1 , 𝑠⟩ ⇒π‘š 𝑠′ and ⟨𝑆2 , 𝑠′⟩ ⇒𝑛 𝑠′′
The proof (pages 37-38, Lemma 2.19) uses induction on the length of the derivation sequences.
ο‚·
If k=0 the result holds vacuously.
ο‚·
Proving that the property holds for all other derivation sequences, is done by induction- assuming
that the lemma holds for all π’Œ ≤ π’ŒπŸŽ and showing that it holds for sequences of length π’ŒπŸŽ + 𝟏
The semantic function 𝑺𝑺𝑢𝑺
The meaning of a statement S is defined as a partial function from State to State:
𝑆𝑆𝑂𝑆 : π‘†π‘‘π‘š → (π‘†π‘‘π‘Žπ‘‘π‘’ β†ͺ π‘†π‘‘π‘Žπ‘‘π‘’)
𝑠′
𝑆𝑆𝑂𝑆 βŸ¦π‘†βŸ§π‘  = {
𝑒𝑛𝑑𝑒𝑓𝑖𝑛𝑒𝑑
𝑖𝑓 ⟨𝑆, 𝑠⟩ ⇒∗ 𝑠′
𝑒𝑙𝑠𝑒
Note that when defining the semantic function we are interested only in terminating executions, i.e., ones
which produce a final state. There is no meaning to the “output of an infinite execution”.
We note that the semantic of While (as defined so far) is deterministic and hence 𝑆𝑆𝑂𝑆 is well-defined.
Examples:
𝑆𝑆𝑂𝑆 βŸ¦π‘ π‘˜π‘–π‘βŸ§π‘  = 𝑠
𝑆𝑆𝑂𝑆 ⟦π‘₯ ≔ 1βŸ§π‘  = 𝑠[π‘₯ ↦ 1]
𝑆𝑆𝑂𝑆 βŸ¦π‘€β„Žπ‘–π‘™π‘’ π‘‘π‘Ÿπ‘’π‘’ π‘‘π‘œ π‘ π‘˜π‘–π‘βŸ§π‘  = 𝑒𝑛𝑑𝑒𝑓𝑖𝑛𝑒𝑑
An equivalence result
We have seen two definitions of the semantics of While using a natural semantics and a structural
operational semantics.
Theorem: For every statement S of While 𝑆𝑛𝑠 βŸ¦π‘†βŸ§ = 𝑆𝑆𝑂𝑆 βŸ¦π‘†βŸ§
Proof in the book, pages 40-43. The proof consists of 2 lemmas:
ο‚·
ο‚·
For every statement S of While and states s and s' we have ⟨𝑆, 𝑠⟩ → 𝑠′ implies ⟨𝑆, 𝑠⟩ ⇒∗ 𝑠′ (lemma
2.27)
The proof is by induction on the shape of the derivation tree for ⟨𝑆, 𝑠⟩ → 𝑠′
We get that if 𝑆𝑛𝑠 βŸ¦π‘†βŸ§π‘  = 𝑠′ then 𝑆𝑆𝑂𝑆 βŸ¦π‘†βŸ§π‘  = 𝑠′ (1)
For every statement S of While, states s and s' and natural number k, we have that ⟨𝑆, 𝑠⟩ ⇒π‘˜ 𝑠′
implies ⟨𝑆, 𝑠⟩ → 𝑠′ (lemma 2.2.28)
The proof is by induction on the length of the derivation sequence
We get that if 𝑆𝑆𝑂𝑆 βŸ¦π‘†βŸ§π‘  = 𝑠′ then 𝑆𝑛𝑠 βŸ¦π‘†βŸ§π‘  = 𝑠′ (2)
Then, from (1) and (2) we get that 𝑆𝑛𝑠 βŸ¦π‘†βŸ§ = 𝑆𝑆𝑂𝑆 βŸ¦π‘†βŸ§. In particular, if one semantic function is defined on a
state s then so is the other one, and therefore, if one is not defined on a state s then neither is the other.
Note: the claim is true even though structural semantics allows defining infinite computations.
Download