Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
I
inox
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
LARA
inox
Commits
05592e92
Commit
05592e92
authored
9 years ago
by
Nicolas Voirol
Browse files
Options
Downloads
Patches
Plain Diff
Documentation for path and evaluator
parent
f4d79c32
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
src/main/scala/leon/evaluators/Evaluator.scala
+19
-0
19 additions, 0 deletions
src/main/scala/leon/evaluators/Evaluator.scala
src/main/scala/leon/purescala/Path.scala
+79
-1
79 additions, 1 deletion
src/main/scala/leon/purescala/Path.scala
with
98 additions
and
1 deletion
src/main/scala/leon/evaluators/Evaluator.scala
+
19
−
0
View file @
05592e92
...
@@ -125,12 +125,29 @@ object Evaluator {
...
@@ -125,12 +125,29 @@ object Evaluator {
*/
*/
private
val
checkCache
:
MutableMap
[
CaseClass
,
CheckStatus
]
=
MutableMap
.
empty
private
val
checkCache
:
MutableMap
[
CaseClass
,
CheckStatus
]
=
MutableMap
.
empty
/* Status of invariant checking
*
* For a given invariant, its checking status can be either
* - Complete(success) : checking has been performed previously and
* resulted in a value of `success`.
* - Pending : invariant is currently being checked somewhere
* in the program. If it fails, the failure is
* assumed to be bubbled up to all relevant failure
* points.
* - NoCheck : invariant has never been seen before. Discovering
* NoCheck for an invariant will automatically update
* the status to `Pending` as this creates a checking
* obligation.
*/
sealed
abstract
class
CheckStatus
{
sealed
abstract
class
CheckStatus
{
/* The invariant was invalid and this particular case class can't exist */
def
isFailure
:
Boolean
=
this
match
{
def
isFailure
:
Boolean
=
this
match
{
case
Complete
(
status
)
=>
!
status
case
Complete
(
status
)
=>
!
status
case
_
=>
false
case
_
=>
false
}
}
/* The invariant has never been checked before and the checking obligation
* therefore falls onto the first caller of this method. */
def
isRequired
:
Boolean
=
this
==
NoCheck
def
isRequired
:
Boolean
=
this
==
NoCheck
}
}
...
@@ -138,6 +155,7 @@ object Evaluator {
...
@@ -138,6 +155,7 @@ object Evaluator {
case
object
Pending
extends
CheckStatus
case
object
Pending
extends
CheckStatus
case
object
NoCheck
extends
CheckStatus
case
object
NoCheck
extends
CheckStatus
/* Status of the invariant checking for `cc` */
def
invariantCheck
(
cc
:
CaseClass
)
:
CheckStatus
=
synchronized
{
def
invariantCheck
(
cc
:
CaseClass
)
:
CheckStatus
=
synchronized
{
if
(!
cc
.
ct
.
classDef
.
hasInvariant
)
Complete
(
true
)
if
(!
cc
.
ct
.
classDef
.
hasInvariant
)
Complete
(
true
)
else
checkCache
.
get
(
cc
).
getOrElse
{
else
checkCache
.
get
(
cc
).
getOrElse
{
...
@@ -146,6 +164,7 @@ object Evaluator {
...
@@ -146,6 +164,7 @@ object Evaluator {
}
}
}
}
/* Update the cache with the invariant check result for `cc` */
def
invariantResult
(
cc
:
CaseClass
,
success
:
Boolean
)
:
Unit
=
synchronized
{
def
invariantResult
(
cc
:
CaseClass
,
success
:
Boolean
)
:
Unit
=
synchronized
{
checkCache
(
cc
)
=
Complete
(
success
)
checkCache
(
cc
)
=
Complete
(
success
)
}
}
...
...
This diff is collapsed.
Click to expand it.
src/main/scala/leon/purescala/Path.scala
+
79
−
1
View file @
05592e92
...
@@ -20,7 +20,16 @@ object Path {
...
@@ -20,7 +20,16 @@ object Path {
def
apply
(
path
:
Seq
[
Expr
])
:
Path
=
new
Path
(
path
.
map
(
Right
(
_
)))
def
apply
(
path
:
Seq
[
Expr
])
:
Path
=
new
Path
(
path
.
map
(
Right
(
_
)))
}
}
/** Encodes path conditions */
/** Encodes path conditions
*
* Paths are encoded as an (ordered) series of let-bindings and boolean
* propositions. A path is satisfiable iff all propositions are true
* in the context of the provided let-bindings.
*
* This encoding enables let-bindings over types for which equality is
* not defined, whereas previous encoding of let-bindings into equals
* could introduce non-sensical equations.
*/
class
Path
private
[
purescala
](
class
Path
private
[
purescala
](
private
[
purescala
]
val
elements
:
Seq
[
Either
[(
Identifier
,
Expr
)
,
Expr
]])
{
private
[
purescala
]
val
elements
:
Seq
[
Either
[(
Identifier
,
Expr
)
,
Expr
]])
{
...
@@ -31,9 +40,22 @@ class Path private[purescala](
...
@@ -31,9 +40,22 @@ class Path private[purescala](
def
--
(
ids
:
Set
[
Identifier
])
=
new
Path
(
elements
.
filterNot
(
_
.
left
.
exists
(
p
=>
ids
(
p
.
_1
))))
def
--
(
ids
:
Set
[
Identifier
])
=
new
Path
(
elements
.
filterNot
(
_
.
left
.
exists
(
p
=>
ids
(
p
.
_1
))))
/** Appends `that` path at the end of `this` */
def
merge
(
that
:
Path
)
=
new
Path
(
elements
++
that
.
elements
)
def
merge
(
that
:
Path
)
=
new
Path
(
elements
++
that
.
elements
)
/** Transforms all expressions inside the path
*
* Expressions in a path appear both on the right-hand side of let binders
* and in boolean path conditions.
*/
def
map
(
f
:
Expr
=>
Expr
)
=
new
Path
(
elements
.
map
(
_
.
left
.
map
{
case
(
id
,
e
)
=>
id
->
f
(
e
)
}.
right
.
map
(
f
)))
def
map
(
f
:
Expr
=>
Expr
)
=
new
Path
(
elements
.
map
(
_
.
left
.
map
{
case
(
id
,
e
)
=>
id
->
f
(
e
)
}.
right
.
map
(
f
)))
/** Splits the path on predicate `p`
*
* The path is split into
* 1. the sub-path containing all conditions that pass `p` (and all let-bindings), and
* 2. the sequence of conditions that didn't pass `p`.
*/
def
partition
(
p
:
Expr
=>
Boolean
)
:
(
Path
,
Seq
[
Expr
])
=
{
def
partition
(
p
:
Expr
=>
Boolean
)
:
(
Path
,
Seq
[
Expr
])
=
{
val
(
passed
,
failed
)
=
elements
.
partition
{
val
(
passed
,
failed
)
=
elements
.
partition
{
case
Right
(
e
)
=>
p
(
e
)
case
Right
(
e
)
=>
p
(
e
)
...
@@ -43,16 +65,38 @@ class Path private[purescala](
...
@@ -43,16 +65,38 @@ class Path private[purescala](
(
new
Path
(
passed
),
failed
.
flatMap
(
_
.
right
.
toOption
))
(
new
Path
(
passed
),
failed
.
flatMap
(
_
.
right
.
toOption
))
}
}
/** Check if the path is empty
*
* A path is empty iff it contains no let-bindings and its path condition is trivial.
*/
def
isEmpty
=
elements
.
filter
{
def
isEmpty
=
elements
.
filter
{
case
Right
(
BooleanLiteral
(
true
))
=>
false
case
Right
(
BooleanLiteral
(
true
))
=>
false
case
_
=>
true
case
_
=>
true
}.
isEmpty
}.
isEmpty
/** Returns the negation of a path
*
* Path negation requires folding the path into a proposition and negating it.
* However, all external let-binders can be preserved in the resulting path, thus
* avoiding let-binding dupplication in future path foldings.
*/
def
negate
:
Path
=
{
def
negate
:
Path
=
{
val
(
outers
,
rest
)
=
elements
.
span
(
_
.
isLeft
)
val
(
outers
,
rest
)
=
elements
.
span
(
_
.
isLeft
)
new
Path
(
outers
:+
Right
(
not
(
fold
[
Expr
](
BooleanLiteral
(
true
),
let
,
Constructors
.
and
(
_
,
_
))(
rest
))))
new
Path
(
outers
:+
Right
(
not
(
fold
[
Expr
](
BooleanLiteral
(
true
),
let
,
Constructors
.
and
(
_
,
_
))(
rest
))))
}
}
/** Returns a new path which depends ONLY on provided ids.
*
* Let-bindings obviously depend on some `id` if it corresponds to the bound
* identifier. An expression depends on an `id` iff the identifier is
* contained within the expression.
*
* This method makes little sense on its own and will typically be used from
* within a fixpoint computation where the `ids` set is iteratively computed
* by performing [[filterByIds]] calls on some (unchaning) base [[Path]].
*
* @see [[leon.purescala.FunctionClosure.close]] for an example usecase.
*/
def
filterByIds
(
ids
:
Set
[
Identifier
])
:
Path
=
{
def
filterByIds
(
ids
:
Set
[
Identifier
])
:
Path
=
{
def
containsIds
(
ids
:
Set
[
Identifier
])(
e
:
Expr
)
:
Boolean
=
exists
{
def
containsIds
(
ids
:
Set
[
Identifier
])(
e
:
Expr
)
:
Boolean
=
exists
{
case
Variable
(
id
)
=>
ids
.
contains
(
id
)
case
Variable
(
id
)
=>
ids
.
contains
(
id
)
...
@@ -66,6 +110,7 @@ class Path private[purescala](
...
@@ -66,6 +110,7 @@ class Path private[purescala](
new
Path
(
newElements
)
new
Path
(
newElements
)
}
}
/** Free variables within the path */
lazy
val
variables
:
Set
[
Identifier
]
=
fold
[
Set
[
Identifier
]](
Set
.
empty
,
lazy
val
variables
:
Set
[
Identifier
]
=
fold
[
Set
[
Identifier
]](
Set
.
empty
,
(
id
,
e
,
res
)
=>
res
-
id
++
variablesOf
(
e
),
(
e
,
res
)
=>
res
++
variablesOf
(
e
)
(
id
,
e
,
res
)
=>
res
-
id
++
variablesOf
(
e
),
(
e
,
res
)
=>
res
++
variablesOf
(
e
)
)(
elements
)
)(
elements
)
...
@@ -75,20 +120,44 @@ class Path private[purescala](
...
@@ -75,20 +120,44 @@ class Path private[purescala](
def
isBound
(
id
:
Identifier
)
:
Boolean
=
bindings
.
exists
(
p
=>
p
.
_1
==
id
)
def
isBound
(
id
:
Identifier
)
:
Boolean
=
bindings
.
exists
(
p
=>
p
.
_1
==
id
)
/** Fold the path elements
*
* This function takes two combiner functions, one for let bindings and one
* for proposition expressions.
*/
private
def
fold
[
T
](
base
:
T
,
combineLet
:
(
Identifier
,
Expr
,
T
)
=>
T
,
combineCond
:
(
Expr
,
T
)
=>
T
)
private
def
fold
[
T
](
base
:
T
,
combineLet
:
(
Identifier
,
Expr
,
T
)
=>
T
,
combineCond
:
(
Expr
,
T
)
=>
T
)
(
elems
:
Seq
[
Either
[(
Identifier
,
Expr
)
,
Expr
]])
:
T
=
elems
.
foldRight
(
base
)
{
(
elems
:
Seq
[
Either
[(
Identifier
,
Expr
)
,
Expr
]])
:
T
=
elems
.
foldRight
(
base
)
{
case
(
Left
((
id
,
e
)),
res
)
=>
combineLet
(
id
,
e
,
res
)
case
(
Left
((
id
,
e
)),
res
)
=>
combineLet
(
id
,
e
,
res
)
case
(
Right
(
e
),
res
)
=>
combineCond
(
e
,
res
)
case
(
Right
(
e
),
res
)
=>
combineCond
(
e
,
res
)
}
}
/** Folds the path elements over a distributive proposition combinator [[combine]]
*
* Certain combiners can be distributive over let-binding folds. Namely, one
* requires that `combine(let a = b in e1, e2)` be equivalent to
* `let a = b in combine(e1, e2)` (where a \not\in FV(e2)). This is the case for
* - conjunction [[and]]
* - implication [[implies]]
*/
private
def
distributiveClause
(
base
:
Expr
,
combine
:
(
Expr
,
Expr
)
=>
Expr
)
:
Expr
=
{
private
def
distributiveClause
(
base
:
Expr
,
combine
:
(
Expr
,
Expr
)
=>
Expr
)
:
Expr
=
{
val
(
outers
,
rest
)
=
elements
.
span
(
_
.
isLeft
)
val
(
outers
,
rest
)
=
elements
.
span
(
_
.
isLeft
)
val
inner
=
fold
[
Expr
](
base
,
let
,
combine
)(
rest
)
val
inner
=
fold
[
Expr
](
base
,
let
,
combine
)(
rest
)
fold
[
Expr
](
inner
,
let
,
(
_
,
_
)
=>
scala
.
sys
.
error
(
"Should never happen!"
))(
outers
)
fold
[
Expr
](
inner
,
let
,
(
_
,
_
)
=>
scala
.
sys
.
error
(
"Should never happen!"
))(
outers
)
}
}
/** Folds the path into a conjunct with the expression `base` */
def
and
(
base
:
Expr
)
=
distributiveClause
(
base
,
Constructors
.
and
(
_
,
_
))
def
and
(
base
:
Expr
)
=
distributiveClause
(
base
,
Constructors
.
and
(
_
,
_
))
/** Fold the path into an implication of `base`, namely `path ==> base` */
def
implies
(
base
:
Expr
)
=
distributiveClause
(
base
,
Constructors
.
implies
(
_
,
_
))
def
implies
(
base
:
Expr
)
=
distributiveClause
(
base
,
Constructors
.
implies
(
_
,
_
))
/** Folds the path into a `require` wrapping the expression `body`
*
* The function takes additional optional parameters
* - [[pre]] if one wishes to mix a pre-existing precondition into the final
* [[leon.purescala.Expressions.Require]], and
* - [[post]] for mixing a postcondition ([[leon.purescala.Expressions.Ensuring]]) in.
*/
def
specs
(
body
:
Expr
,
pre
:
Expr
=
BooleanLiteral
(
true
),
post
:
Expr
=
NoTree
(
BooleanType
))
=
{
def
specs
(
body
:
Expr
,
pre
:
Expr
=
BooleanLiteral
(
true
),
post
:
Expr
=
NoTree
(
BooleanType
))
=
{
val
(
outers
,
rest
)
=
elements
.
span
(
_
.
isLeft
)
val
(
outers
,
rest
)
=
elements
.
span
(
_
.
isLeft
)
val
cond
=
fold
[
Expr
](
BooleanLiteral
(
true
),
let
,
Constructors
.
and
(
_
,
_
))(
rest
)
val
cond
=
fold
[
Expr
](
BooleanLiteral
(
true
),
let
,
Constructors
.
and
(
_
,
_
))(
rest
)
...
@@ -110,9 +179,18 @@ class Path private[purescala](
...
@@ -110,9 +179,18 @@ class Path private[purescala](
fold
[
Expr
](
full
,
let
,
(
_
,
_
)
=>
scala
.
sys
.
error
(
"Should never happen!"
))(
outers
)
fold
[
Expr
](
full
,
let
,
(
_
,
_
)
=>
scala
.
sys
.
error
(
"Should never happen!"
))(
outers
)
}
}
/** Folds the path into the associated boolean proposition */
lazy
val
toClause
:
Expr
=
and
(
BooleanLiteral
(
true
))
lazy
val
toClause
:
Expr
=
and
(
BooleanLiteral
(
true
))
/** Like [[toClause]] but doesn't simplify final path through constructors
* from [[leon.purescala.Constructors]] */
lazy
val
fullClause
:
Expr
=
fold
[
Expr
](
BooleanLiteral
(
true
),
Let
(
_
,
_
,
_
),
And
(
_
,
_
))(
elements
)
lazy
val
fullClause
:
Expr
=
fold
[
Expr
](
BooleanLiteral
(
true
),
Let
(
_
,
_
,
_
),
And
(
_
,
_
))(
elements
)
/** Folds the path into a boolean proposition where let-bindings are
* replaced by equations.
*
* CAUTION: Should only be used once INSIDE the solver!!
*/
lazy
val
toPath
:
Expr
=
andJoin
(
elements
.
map
{
lazy
val
toPath
:
Expr
=
andJoin
(
elements
.
map
{
case
Left
((
id
,
e
))
=>
Equals
(
id
.
toVariable
,
e
)
case
Left
((
id
,
e
))
=>
Equals
(
id
.
toVariable
,
e
)
case
Right
(
e
)
=>
e
case
Right
(
e
)
=>
e
...
...
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