Skip to content

Implement variable scopes, declarations and updates #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 76 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
32b7c4a
Remove `Expr.Bind` and introduce `Expr.VarDecl` and `Expr.Update`
sonmarcho Jul 6, 2022
c138d9e
Update InterpExpr for VarDecl and Update
sonmarcho Jul 7, 2022
5c764d9
Start updating the proofs in BottomUp
sonmarcho Jul 7, 2022
927114f
Prove the map lifting theorem for the Var, VarDecl and Update cases
sonmarcho Jul 8, 2022
c0fff19
Update the definition of EqState and fix the proofs
sonmarcho Jul 8, 2022
8bfc11e
Make blocks behave like variable scopes and start updating proofs
sonmarcho Jul 8, 2022
621fc6f
Factorize code a bit
sonmarcho Jul 11, 2022
2fb8ab4
Detail a comment about a problematic proof (EqInterp_Expr_EqState)
sonmarcho Jul 14, 2022
2c8c9df
Change the decreases measure in BottomUp
sonmarcho Jul 14, 2022
05dfee4
Simplify the proofs in BottomUp
sonmarcho Jul 14, 2022
756a2cf
Start working on an induction principle for Interp
sonmarcho Jul 15, 2022
3390532
Factorize some proofs in ExprInduction
sonmarcho Jul 15, 2022
05844d9
Factorize some proofs and make progress on ExprInduction
sonmarcho Jul 16, 2022
edaecaf
Make more progress on the proofs of ExprInduction.EqInterpRefl
sonmarcho Jul 18, 2022
5ecacfe
Make progress on the proofs of ExprInduction
sonmarcho Jul 19, 2022
0bf0167
Simplify InductIf_Succ
sonmarcho Jul 19, 2022
77ab624
Prove the ApplyLazy lemmas in ExprInduction.EqInterpRefl
sonmarcho Jul 19, 2022
a3ca9d2
Simplify the InductAbs proofs
sonmarcho Jul 19, 2022
cfb1e78
Make progress on ExprInduction
sonmarcho Jul 21, 2022
842118b
Prove the Pes_Satisfied lemma in ExprInduction
sonmarcho Jul 22, 2022
96e8288
Fix a termination proof in ExprInduction
sonmarcho Jul 22, 2022
91c0f93
Add a comment
sonmarcho Jul 22, 2022
b3a05ad
Cleanup a bit
sonmarcho Jul 22, 2022
b2ddeae
Start working on EqInterp_Scopes
sonmarcho Jul 22, 2022
7c9d8fe
Finish the proofs for EqInterp_Scopes
sonmarcho Jul 22, 2022
8bfb290
Add a comment for InterpBlock_Exprs
sonmarcho Jul 22, 2022
8612210
Make minor modifications
sonmarcho Jul 22, 2022
b7d863f
Identify failing proofs in BottomUp
sonmarcho Jul 23, 2022
9b25ae1
Fix BottomUp.dfy
sonmarcho Jul 25, 2022
d915a81
Start fixing the proofs in SimplifyEmptyBlocks
sonmarcho Jul 25, 2022
28f9735
Fix a small mistake
sonmarcho Jul 25, 2022
86a4441
Make progress on updating SimplifyEmptyBlocks
sonmarcho Jul 26, 2022
3b4348f
Make progress on EqInterpScopes
sonmarcho Jul 26, 2022
d72a822
Finish fixing the proofs in SimplifyEmptyBlocks
sonmarcho Jul 27, 2022
7ed8fe5
Make minor modifications
sonmarcho Jul 27, 2022
aa8049f
Make minor modifications
sonmarcho Jul 27, 2022
68b9c8a
Add a comment
sonmarcho Jul 27, 2022
f64bca8
Factorize EqInterpRefl and EqInterpScopes
sonmarcho Jul 27, 2022
ae13d93
Add comments
sonmarcho Jul 27, 2022
fb9d581
Inline InterpBlock in InterpExpr cleanup some proofs
sonmarcho Jul 27, 2022
ffdd9a2
Inline InterpLazy in InterpExpr and cleanup a bit ExprInduction
sonmarcho Jul 27, 2022
a230df2
Update some lemmas in ExprInduction
sonmarcho Jul 27, 2022
e3edb4f
Update a comment
sonmarcho Jul 27, 2022
eb0e35f
Denest some modules
sonmarcho Jul 27, 2022
e383151
Change some names
sonmarcho Jul 27, 2022
20bab4e
Update some comments
sonmarcho Jul 27, 2022
d0afed7
Update EqValue and fix an issue with InterpTernaryOp_Eq
sonmarcho Jul 27, 2022
09d79da
Make minor modifications
sonmarcho Jul 27, 2022
eaafcd2
Update Compiler.dfy
sonmarcho Jul 27, 2022
aa040b3
Add a comment
sonmarcho Jul 27, 2022
482b0c3
Update Value.HasType
sonmarcho Jul 27, 2022
13a69ee
readme: Improve build instructions
cpitclaudel Jul 22, 2022
e10c056
Update Compiler.dfy to use SimplifyEmptyBlocks
sonmarcho Jul 28, 2022
dbf6284
Update InterpLazy
sonmarcho Jul 28, 2022
b1c4ec1
Put ``Bind`` back in the AST
sonmarcho Jul 28, 2022
9078e5b
Add a comment
sonmarcho Jul 28, 2022
926640b
Make minor modifications
sonmarcho Jul 28, 2022
5af24e4
Make minor modifications
sonmarcho Jul 28, 2022
a2b3b9c
Update the translation
sonmarcho Jul 29, 2022
86a4f28
Update src/AST/Translator.dfy
sonmarcho Aug 4, 2022
31370cb
Update src/Semantics/Interp.dfy
sonmarcho Aug 4, 2022
bcbd5bc
Update src/Semantics/Interp.dfy
sonmarcho Aug 4, 2022
ae6859b
Rewrite Expr.Size in a recursive manner and make minor cleanup
sonmarcho Aug 4, 2022
ac02249
Cleanup
sonmarcho Aug 4, 2022
881231b
Remove the bind to vardecl desugaring from Translator.dfy
sonmarcho Aug 5, 2022
5409f32
Fix the compiled code
sonmarcho Aug 15, 2022
81b7a69
Cleanup
sonmarcho Aug 15, 2022
b8623af
Rename HasEqValue to ValueHasEq
sonmarcho Aug 15, 2022
3953e63
Cleanup
sonmarcho Aug 15, 2022
9e1af92
Add a comment to InterpBlock_Exprs
sonmarcho Aug 15, 2022
68ab8a6
Cleanup
sonmarcho Aug 15, 2022
0d9dfba
Add more cases to Translator.dfy
sonmarcho Aug 18, 2022
bc2dc8e
Fix TranslateVarDeclStmt
sonmarcho Aug 18, 2022
8e05617
Add loops to the AST and update the proofs
sonmarcho Aug 18, 2022
b94c363
Update Translator to handle conversions
sonmarcho Aug 18, 2022
c231d58
Update the project hierarchy in the README
sonmarcho Aug 25, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ A work-in-progress reimplementation of Dafny's compilers, in Dafny.
Trying it out
=============

Use ``make tests`` or ``make repl`` (and read through the `Makefile <./GNUmakefile>`__ to see all the steps).
Clone this repository as part of the ``compiler-bootstrap`` branch of the main Dafny repository, then use ``make tests`` or ``make repl`` (and read through the `Makefile <./GNUmakefile>`__ to see all the steps). Alternatively, if you cloned this repo on its own, ``make`` will complain and ask you how to select which Dafny to run for bootstrapping.

Overview
========
Expand Down Expand Up @@ -51,6 +51,14 @@ Project hierarchy
Operational semantics for the pure subset of Dafny
``Equiv.dfy``
Definition of program equivalence based on ``Interp.dfy``
``ExprInduction.dfy``
Induction principle based on ``Interp.dfy``
``InterpStateIneq.dfy``
Proof of a monotonicity property for the interpreter
``EqInterpScopes.dfy``
Proof that evaluating an expression with equivalent states (after roll back) leads to equivalent results
``EqInterpRefl.dfy``
Proof that evaluating an expression with states equivalent in the sense of ``Equiv.dfy`` leads to equivalent results
``Transforms/``
``Generic.dfy``
Basic definitions for AST transformers
Expand All @@ -61,6 +69,8 @@ Project hierarchy
Definition of a compiler pass
``EliminateNegatedBinops.dfy``
Simple demo pass
``SimplifyEmptyBlocks.dfy``
Pass which simplifies empty blocks
``REPL/``
``Repl.dfy``
Driver for a REPL build on top of ``Interp/``
Expand Down
12 changes: 12 additions & 0 deletions src/AST/Predicates.dfy
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,14 @@ abstract module Base {
case Bind(vars, vals, body) =>
&& Seq.All((e requires e in vals => All_Expr(e, P)), vals)
&& All_Expr(body, P)
case VarDecl(vdecls, ovals) =>
&& ovals.Some? ==> Seq.All((e requires e in ovals.value => All_Expr(e, P)), ovals.value)
case Update(vars, vals) =>
&& Seq.All((e requires e in vals => All_Expr(e, P)), vals)
case If(cond, thn, els) =>
All_Expr(cond, P) && All_Expr(thn, P) && All_Expr(els, P)
case Loop(guard, lbody) =>
All_Expr(guard, P) && All_Expr(lbody, P)
}
}

Expand All @@ -117,8 +123,14 @@ abstract module Base {
case Bind(vars, vals, body) =>
&& Seq.All((e requires e in vals => All_Expr(e, P)), vals)
&& All_Expr(body, P)
case VarDecl(vdecls, ovals) =>
&& ovals.Some? ==> Seq.All((e requires e in ovals.value => All_Expr(e, P)), ovals.value)
case Update(vars, vals) =>
&& Seq.All((e requires e in vals => All_Expr(e, P)), vals)
case If(cond, thn, els) =>
All_Expr(cond, P) && All_Expr(thn, P) && All_Expr(els, P)
case Loop(guard, lbody) =>
All_Expr(guard, P) && All_Expr(lbody, P)
}
}

Expand Down
174 changes: 114 additions & 60 deletions src/AST/Syntax.dfy
Original file line number Diff line number Diff line change
Expand Up @@ -31,63 +31,6 @@ module Types {
| Collection(finite: bool, kind: CollectionKind, eltType: Type)
| Function(args: seq<Type>, ret: Type) // TODO
| Class(classType: ClassType)
{
// TODO: remove?
predicate method NoLeftFunction()
{
match this {
case Unit => true
case Bool => true
case Char => true
case Int => true
case Real => true
case BigOrdinal => true
case BitVector(width: nat) => true
case Collection(finite: bool, kind: CollectionKind, eltType: Type) =>
&& eltType.NoLeftFunction()
&& match kind {
case Seq => true
case Set => true
case Multiset => true
case Map(kt) => kt.NoLeftFunction()
}
case Function(args: seq<Type>, ret: Type) => false
case Class(classType: ClassType) => false
}
}

// TODO: remove?
predicate method WellFormed() {
match this {
case Unit => true
case Bool => true
case Char => true
case Int => true
case Real => true
case BigOrdinal => true
case BitVector(width: nat) => true
case Collection(finite: bool, kind: CollectionKind, eltType: Type) =>
&& eltType.WellFormed()
// This condition is overly restrictive: we will do the general case later.
// For instance, maps can contain keys which don't have a decidable equality,
// and sequences can contain elements which also don't have a decidable equality
// (in which case we don't have a decidable equality over the sequences, but it
// is fine).
&& eltType.NoLeftFunction()
&& match kind {
case Seq => true
case Set => eltType.NoLeftFunction()
case Multiset => eltType.NoLeftFunction()
case Map(kt) => kt.WellFormed() && kt.NoLeftFunction()
}
case Function(args: seq<Type>, ret: Type) =>
&& (forall i | 0 <= i < |args| :: args[i].WellFormed())
&& ret.WellFormed()
case Class(classType: ClassType) =>
&& (forall i | 0 <= i < |classType.typeArgs| :: classType.typeArgs[i].WellFormed())
}
}
}

type T(!new,00,==) = Type
}
Expand Down Expand Up @@ -181,6 +124,7 @@ module UnaryOps {
module Exprs {
import Utils.Lib.Math
import Utils.Lib.Seq
import opened Utils.Lib.Datatypes

import Types
import UnaryOps
Expand Down Expand Up @@ -242,14 +186,33 @@ module Exprs {
/// caller's context. (In most cases, though, variables passed into an ``Abs``
/// are not mutated at all, because dafny lambdas are pure).

datatype TypedVar = TypedVar(name: string, ty: Types.Type)

// DISCUSS: if we use `Option<seq<Expr>>` in the `Expr.VarDecl` variant instead of introducing
// this auxiliary datatype, Dafny fails to prove termination of simple functions like ``Depth``.
datatype OptExprs =
| Some(value: seq<Expr>)
| None

datatype Expr =
| Var(name: string)
| Literal(lit: Literal)
| Abs(vars: seq<string>, body: Expr)
| Apply(aop: ApplyOp, args: seq<Expr>)
| Block(stmts: seq<Expr>)
| Bind(vars: seq<string>, vals: seq<Expr>, body: Expr)
| Bind(bvars: seq<TypedVar>, bvals: seq<Expr>, bbody: Expr)
| VarDecl(vdecls: seq<TypedVar>, ovals: OptExprs)
// DISCUSS: `ovals` may make `VarDecl` slightly redundant with `Update` (i.e., we
// can always decompose `VarDecl` with initialization expressions as `VarDecl` followed
// by `Update`). It is however useful for pretty printing purposes, and in the definition
// of ``Pure``: a variable declaration is pure, while an update isn't. This is useful
// because we desugar let-bindings to a scope containing an initialized variable declaration.
// We could make ``Pure1`` pattern match on `VarDecl` followed by `Update` operating on the
// same variables, but then we couldn't use the `Predicates.Deep.All_Expr` function to lift
// this definition.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we actually want to treat vardecls as pure?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the time I wrote this comment, I had removed the Bind construct from the AST (before putting it back later).
Following the discussion we had this morning about using let-bindings to make the proofs simpler, then converting them to scope + vardecl later in the compilation process: this comment is even less relevant now.
I didn't have much problems with the fact that this duplicates a bit Update so far (the additional work is limited), but today I see no problem in removing the optional initialization values from VarDecl.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note from chat: can't always decompose (var x := x + 1)

| Update(vars: seq<string>, vals: seq<Expr>)
| If(cond: Expr, thn: Expr, els: Expr) // DISCUSS: Lazy op node?
| Loop(guard: Expr, lbody: Expr)
{
function method Depth() : nat {
1 + match this {
Expand All @@ -268,8 +231,18 @@ module Exprs {
Seq.MaxF(var f := (e: Expr) requires e in vals => e.Depth(); f, vals, 0),
body.Depth()
)
case VarDecl(vdecls, ovals) =>
match ovals {
case Some(vals) =>
Seq.MaxF(var f := (e: Expr) requires e in ovals.value => e.Depth(); f, vals, 0)
case None => 0
}
case Update(vars, vals) =>
Seq.MaxF(var f := (e: Expr) requires e in vals => e.Depth(); f, vals, 0)
case If(cond, thn, els) =>
Math.Max(cond.Depth(), Math.Max(thn.Depth(), els.Depth()))
case Loop(guard, lbody) =>
Math.Max(guard.Depth(), lbody.Depth())
}
}

Expand All @@ -283,11 +256,79 @@ module Exprs {
case Apply(aop, exprs) => exprs
case Block(exprs) => exprs
case Bind(vars, vals, body) => vals + [body]
case VarDecl(vdecls, ovals) =>
match ovals {
case Some(vals) => vals
case None => []
}
case Update(vars, vals) => vals
case If(cond, thn, els) => [cond, thn, els]
case Loop(guard, lbody) => [guard, lbody]
}
}

function method Size() : nat
decreases this.Depth(), 0
{
1 + match this {
case Var(_) => 0
case Literal(lit) => 0
case Abs(vars, body) => body.Size()
case Apply(_, args) => Exprs_Size(args)
case Block(stmts) => Exprs_Size(stmts)
case Bind(vars, vals, body) => Exprs_Size(vals) + body.Size()
case VarDecl(vdecls, ovals) =>
match ovals {
case Some(vals) => Exprs_Size(vals)
case None => 0
}
case Update(vars, vals) => Exprs_Size(vals)
case If(cond, thn, els) => cond.Size() + thn.Size() + els.Size()
case Loop(guard, lbody) => guard.Size() + lbody.Size()
}
}

static function method MaxDepth(es: seq<Expr>) : nat {
Seq.MaxF((e: Expr) => e.Depth(), es, 0)
}

static function method Exprs_Size(es: seq<Expr>): nat
decreases MaxDepth(es), 1, |es|
{
// Proofs work better if we don't use FoldL
if es == [] then 0
else
assert es[0] in es;
es[0].Size() + Exprs_Size(es[1..])
}

static lemma Exprs_Size_Append(es: seq<Expr>, es': seq<Expr>)
ensures Exprs_Size(es + es') == Exprs_Size(es) + Exprs_Size(es')
{
if es == [] {
assert es + es' == es';
assert Exprs_Size(es) == 0;
}
else {
assert es + es' == [es[0]] + (es[1..] + es');
Exprs_Size_Append(es[1..], es');
}
}

static lemma Exprs_Size_Index(es: seq<Expr>, i: nat)
requires i < |es|
ensures es[i].Size() <= Exprs_Size(es)
{
if i == 0 {}
else {
assert es[i] == es[1..][i - 1];
Exprs_Size_Index(es[1..], i - 1);
}
}
}

const Exprs_Size := Expr.Exprs_Size

function method WellFormed(e: Expr): bool {
match e {
case Apply(Lazy(_), es) =>
Expand All @@ -304,6 +345,10 @@ module Exprs {
ty.Collection? && ty.finite
case Bind(vars, vals, _) =>
|vars| == |vals|
case VarDecl(vdecls, ovals) =>
ovals.Some? ==> |vdecls| == |ovals.value|
case Update(vars, vals) =>
|vars| == |vals|
case _ => true
}
}
Expand All @@ -322,8 +367,17 @@ module Exprs {
e'.Block? && |stmts| == |e'.stmts|
case If(cond, thn, els) =>
e'.If?
case Bind(vars, vals, body) =>
e'.Bind? && |vars| == |e'.vars| && |vals| == |e'.vals|
case Bind(bvars, bvals, bbody) =>
e'.Bind? && bvars == e'.bvars && |bvals| == |e'.bvals|
case VarDecl(vdecls, ovals) =>
// TODO(SMH): we might want not to check that the variable types are equal (there
// may be aliases)
e'.VarDecl? && vdecls == e'.vdecls && ovals.Some? == e'.ovals.Some? &&
(ovals.Some? ==> |ovals.value| == |e'.ovals.value|)
case Update(vars, vals) =>
e'.Update? && vars == e'.vars && |vals| == |e'.vals|
case Loop(guard, lbody) =>
e'.Loop?
}
}

Expand Down
Loading