From 8265a97e72c628c5915f9c4fabb5a00ac5aa9f76 Mon Sep 17 00:00:00 2001 From: Nicolas Voirol <voirol.nicolas@gmail.com> Date: Thu, 20 Oct 2016 18:47:39 +0200 Subject: [PATCH] Nicer transformers --- src/main/scala/inox/Program.scala | 4 +- src/main/scala/inox/ast/Definitions.scala | 4 +- src/main/scala/inox/ast/Identifier.scala | 2 +- src/main/scala/inox/ast/ProgramEncoder.scala | 60 +++++-- src/main/scala/inox/ast/TreeOps.scala | 153 +++++------------- src/main/scala/inox/ast/TypeOps.scala | 13 ++ .../inox/solvers/theories/BagEncoder.scala | 8 +- .../inox/solvers/theories/StringEncoder.scala | 8 +- .../inox/solvers/theories/TheoryEncoder.scala | 38 +---- .../solvers/unrolling/UnrollingSolver.scala | 10 +- .../inox/solvers/z3/NativeZ3Solver.scala | 10 +- 11 files changed, 127 insertions(+), 183 deletions(-) diff --git a/src/main/scala/inox/Program.scala b/src/main/scala/inox/Program.scala index 374464d05..69ce5b2e5 100644 --- a/src/main/scala/inox/Program.scala +++ b/src/main/scala/inox/Program.scala @@ -30,9 +30,7 @@ trait Program { val ctx = Program.this.ctx } - def transform(t: SymbolTransformer { - val transformer: TreeTransformer { val s: trees.type } - }): Program { val trees: t.t.type } = new Program { + def transform(t: SymbolTransformer { val s: trees.type }): Program { val trees: t.t.type } = new Program { val trees: t.t.type = t.t val symbols = t.transform(Program.this.symbols) val ctx = Program.this.ctx diff --git a/src/main/scala/inox/ast/Definitions.scala b/src/main/scala/inox/ast/Definitions.scala index f010ad44f..a9491af7c 100644 --- a/src/main/scala/inox/ast/Definitions.scala +++ b/src/main/scala/inox/ast/Definitions.scala @@ -136,9 +136,7 @@ trait Definitions { self: Trees => functions.map(p => prettyPrint(p._2, opts)).mkString("\n\n") } - def transform(trans: SelfTransformer): Symbols = new SymbolTransformer { - val transformer: trans.type = trans - }.transform(this) + def transform(trans: SelfTransformer): Symbols = SymbolTransformer(trans).transform(this) override def equals(that: Any): Boolean = that match { case sym: AbstractSymbols => functions == sym.functions && adts == sym.adts diff --git a/src/main/scala/inox/ast/Identifier.scala b/src/main/scala/inox/ast/Identifier.scala index 557de837f..5ca58353d 100644 --- a/src/main/scala/inox/ast/Identifier.scala +++ b/src/main/scala/inox/ast/Identifier.scala @@ -10,7 +10,7 @@ import inox.utils.{UniqueCounter, Positioned} class Identifier ( val name: String, val globalId: Int, - private val id: Int, + val id: Int, private val alwaysShowUniqueID: Boolean = false ) extends Ordered[Identifier] with Positioned { diff --git a/src/main/scala/inox/ast/ProgramEncoder.scala b/src/main/scala/inox/ast/ProgramEncoder.scala index e7cae628d..0f8edaf9d 100644 --- a/src/main/scala/inox/ast/ProgramEncoder.scala +++ b/src/main/scala/inox/ast/ProgramEncoder.scala @@ -3,22 +3,58 @@ package inox package ast -trait ProgramEncoder extends TreeBijection { +trait ProgramEncoder { self => val sourceProgram: Program - lazy val s: sourceProgram.trees.type = sourceProgram.trees - lazy val targetProgram: Program { val trees: t.type } = sourceProgram.transform(encoder) + val t: Trees - /* @nv XXX: ideally, we would want to replace `>>` by `override def andThen`, however this - * seems to break the scala compiler for some weird reason... */ - def >>(that: TreeBijection { val s: ProgramEncoder.this.t.type }): ProgramEncoder { - val sourceProgram: ProgramEncoder.this.sourceProgram.type + protected val extraFunctions: Seq[t.FunDef] = Seq.empty + protected val extraADTs: Seq[t.ADTDefinition] = Seq.empty + + /** Override point for more complex program transformations */ + protected def encodedProgram: Program { val trees: t.type } = { + sourceProgram.transform(SymbolTransformer(encoder)) + } + + lazy final val s: sourceProgram.trees.type = sourceProgram.trees + lazy final val targetProgram: Program { val trees: t.type } = { + encodedProgram.withFunctions(extraFunctions).withADTs(extraADTs) + } + + protected val encoder: TreeTransformer { val s: self.s.type; val t: self.t.type } + protected val decoder: TreeTransformer { val s: self.t.type; val t: self.s.type } + + def encode(vd: s.ValDef): t.ValDef = encoder.transform(vd) + def decode(vd: t.ValDef): s.ValDef = decoder.transform(vd) + + def encode(v: s.Variable): t.Variable = encoder.transform(v).asInstanceOf[t.Variable] + def decode(v: t.Variable): s.Variable = decoder.transform(v).asInstanceOf[s.Variable] + + def encode(e: s.Expr): t.Expr = encoder.transform(e) + def decode(e: t.Expr): s.Expr = decoder.transform(e) + + def encode(tpe: s.Type): t.Type = encoder.transform(tpe) + def decode(tpe: t.Type): s.Type = decoder.transform(tpe) + + def compose(that: ProgramEncoder { val t: self.s.type }): ProgramEncoder { + val sourceProgram: that.sourceProgram.type + val t: self.t.type + } = new ProgramEncoder { + val sourceProgram: that.sourceProgram.type = that.sourceProgram + val t: self.t.type = self.t + + val encoder = self.encoder compose that.encoder + val decoder = that.decoder compose self.decoder + } + + def andThen(that: ProgramEncoder { val sourceProgram: self.targetProgram.type }): ProgramEncoder { + val sourceProgram: self.sourceProgram.type val t: that.t.type } = new ProgramEncoder { - val sourceProgram: ProgramEncoder.this.sourceProgram.type = ProgramEncoder.this.sourceProgram + val sourceProgram: self.sourceProgram.type = self.sourceProgram val t: that.t.type = that.t - val encoder = ProgramEncoder.this.encoder andThen that.encoder - val decoder = that.decoder andThen ProgramEncoder.this.decoder + val encoder = self.encoder andThen that.encoder + val decoder = that.decoder andThen self.decoder } } @@ -30,8 +66,8 @@ object ProgramEncoder { val sourceProgram: p.type = p val t: p.trees.type = p.trees - object encoder extends p.trees.IdentitySymbolTransformer - object decoder extends p.trees.IdentitySymbolTransformer + protected object encoder extends p.trees.IdentityTreeTransformer + protected object decoder extends p.trees.IdentityTreeTransformer } } diff --git a/src/main/scala/inox/ast/TreeOps.scala b/src/main/scala/inox/ast/TreeOps.scala index 67de1df34..5aeeef5aa 100644 --- a/src/main/scala/inox/ast/TreeOps.scala +++ b/src/main/scala/inox/ast/TreeOps.scala @@ -21,7 +21,6 @@ trait TreeOps { self: Trees => trait IdentityTreeTransformer extends SelfTreeTransformer { override def transform(id: Identifier, tpe: s.Type): (Identifier, t.Type) = (id, tpe) - override def transform(v: s.Variable): t.Variable = v override def transform(vd: s.ValDef): t.ValDef = vd override def transform(e: s.Expr): t.Expr = e override def transform(tpe: s.Type): t.Type = tpe @@ -29,13 +28,9 @@ trait TreeOps { self: Trees => } trait IdentitySymbolTransformer extends SymbolTransformer { - /* For some reason, the scala compiler doesn't accept {{{ - * object transformer extends IdentityTreeTransformer - * }}} which would be nicer. */ - val transformer = new IdentityTreeTransformer {} + val s: self.type = self + val t: self.type = self - override protected final def transformFunction(fd: s.FunDef): t.FunDef = sys.error("unexpected") - override protected final def transformADT(adt: s.ADTDefinition): t.ADTDefinition = sys.error("unexpected") override def transform(syms: s.Symbols): t.Symbols = syms } @@ -69,15 +64,6 @@ trait TreeTransformer { def transform(id: Identifier, tpe: s.Type): (Identifier, t.Type) = (id, transform(tpe)) - def transform(v: s.Variable): t.Variable = { - val (id, tpe) = transform(v.id, v.tpe) - if ((id ne v.id) || (tpe ne v.tpe) || (s ne t)) { - t.Variable(id, tpe).copiedFrom(v) - } else { - v.asInstanceOf[t.Variable] - } - } - def transform(vd: s.ValDef): t.ValDef = { val (id, es, Seq(tpe), builder) = deconstructor.deconstruct(vd) val (newId, newTpe) = transform(id, tpe) @@ -101,7 +87,7 @@ trait TreeTransformer { var changed = false val newVs = for (v <- vs) yield { - val newV = transform(v) + val newV = transformVariable(v) if (v ne newV) changed = true newV } @@ -168,11 +154,18 @@ trait TreeTransformer { /* Type parameters can't be modified by transformed but they need to be * translated into the new tree definitions given by `t`. */ @inline - protected[ast] final def transformTypeParams(tparams: Seq[s.TypeParameterDef]): Seq[t.TypeParameterDef] = { + final def transformTypeParams(tparams: Seq[s.TypeParameterDef]): Seq[t.TypeParameterDef] = { if (s eq t) tparams.asInstanceOf[Seq[t.TypeParameterDef]] else tparams.map(tdef => t.TypeParameterDef(t.TypeParameter(tdef.id))) } + @inline + final def transformVariable(v: s.Variable): t.Variable = { + val (nid, ntpe) = transform(v.id, v.tpe) + if ((v.id ne nid) || (v.tpe ne ntpe) || (s ne t)) t.Variable(nid, ntpe).copiedFrom(v) + else v.asInstanceOf[t.Variable] + } + final def transform(fd: s.FunDef): t.FunDef = { new t.FunDef( fd.id, @@ -215,7 +208,6 @@ trait TreeTransformer { t2.transform(id1, tp1) } - override def transform(v: s.Variable): t.Variable = t2.transform(t1.transform(v)) override def transform(vd: s.ValDef): t.ValDef = t2.transform(t1.transform(vd)) override def transform(e: s.Expr): t.Expr = t2.transform(t1.transform(e)) override def transform(tpe: s.Type): t.Type = t2.transform(t1.transform(tpe)) @@ -250,115 +242,56 @@ trait TreeTransformer { } } -/** Symbol table transformer */ -trait SymbolTransformer { - val transformer: TreeTransformer - lazy val s: transformer.s.type = transformer.s - lazy val t: transformer.t.type = transformer.t - - def transform(id: Identifier, tpe: s.Type): (Identifier, t.Type) = transformer.transform(id, tpe) - def transform(v: s.Variable): t.Variable = transformer.transform(v) - def transform(vd: s.ValDef): t.ValDef = transformer.transform(vd) - def transform(e: s.Expr): t.Expr = transformer.transform(e) - def transform(tpe: s.Type): t.Type = transformer.transform(tpe) - def transform(flag: s.Flag): t.Flag = transformer.transform(flag) - - @inline - protected def transformTypeParams(tparams: Seq[s.TypeParameterDef]) = transformer.transformTypeParams(tparams) +/** Symbol table transformer base type */ +trait SymbolTransformer { self => + val s: Trees + val t: Trees - protected def transformFunction(fd: s.FunDef): t.FunDef = transformer.transform(fd) - protected def transformADT(adt: s.ADTDefinition): t.ADTDefinition = transformer.transform(adt) - - def transform(syms: s.Symbols): t.Symbols = t.NoSymbols - .withFunctions(syms.functions.values.toSeq.map(transformFunction)) - .withADTs(syms.adts.values.toSeq.map(transformADT)) + def transform(syms: s.Symbols): t.Symbols def compose(that: SymbolTransformer { - val transformer: TreeTransformer { val t: SymbolTransformer.this.s.type } + val t: self.s.type }): SymbolTransformer { - val transformer: TreeTransformer { - val s: that.s.type - val t: SymbolTransformer.this.t.type - } + val s: that.s.type + val t: self.t.type } = new SymbolTransformer { - val transformer = SymbolTransformer.this.transformer compose that.transformer - override def transform(syms: s.Symbols): t.Symbols = SymbolTransformer.this.transform(that.transform(syms)) + val s: that.s.type = that.s + val t: self.t.type = self.t + override def transform(syms: s.Symbols): t.Symbols = self.transform(that.transform(syms)) } def andThen(that: SymbolTransformer { - val transformer: TreeTransformer { val s: SymbolTransformer.this.t.type } + val s: self.t.type }): SymbolTransformer { - val transformer: TreeTransformer { - val s: SymbolTransformer.this.s.type - val t: that.t.type - } + val s: self.s.type + val t: that.t.type } = { // the scala compiler doesn't realize that this relation must hold here that compose this.asInstanceOf[SymbolTransformer { - val transformer: TreeTransformer { - val s: SymbolTransformer.this.s.type - val t: that.s.type - } + val s: self.s.type + val t: that.s.type }] } } -trait TreeBijection { - val s: Trees - val t: Trees - - val encoder: SymbolTransformer { val transformer: TreeTransformer { - val s: TreeBijection.this.s.type - val t: TreeBijection.this.t.type - }} - - val decoder: SymbolTransformer { val transformer: TreeTransformer { - val s: TreeBijection.this.t.type - val t: TreeBijection.this.s.type - }} - - def encode(vd: s.ValDef): t.ValDef = encoder.transform(vd) - def decode(vd: t.ValDef): s.ValDef = decoder.transform(vd) - - def encode(v: s.Variable): t.Variable = encoder.transform(v) - def decode(v: t.Variable): s.Variable = decoder.transform(v) - - def encode(e: s.Expr): t.Expr = encoder.transform(e) - def decode(e: t.Expr): s.Expr = decoder.transform(e) - - def encode(tpe: s.Type): t.Type = encoder.transform(tpe) - def decode(tpe: t.Type): s.Type = decoder.transform(tpe) - - def inverse: TreeBijection { - val s: TreeBijection.this.t.type - val t: TreeBijection.this.s.type - } = new TreeBijection { - val s: TreeBijection.this.t.type = TreeBijection.this.t - val t: TreeBijection.this.s.type = TreeBijection.this.s +trait SimpleSymbolTransformer extends SymbolTransformer { self => + protected def transformFunction(fd: s.FunDef): t.FunDef + protected def transformADT(adt: s.ADTDefinition): t.ADTDefinition - val encoder = TreeBijection.this.decoder - val decoder = TreeBijection.this.encoder - } - - def compose(that: TreeBijection { val t: TreeBijection.this.s.type }): TreeBijection { - val s: that.s.type - val t: TreeBijection.this.t.type - } = new TreeBijection { - val s: that.s.type = that.s - val t: TreeBijection.this.t.type = TreeBijection.this.t - - val encoder = TreeBijection.this.encoder compose that.encoder - val decoder = that.decoder compose TreeBijection.this.decoder - } + def transform(syms: s.Symbols): t.Symbols = t.NoSymbols + .withFunctions(syms.functions.values.toSeq.map(transformFunction)) + .withADTs(syms.adts.values.toSeq.map(transformADT)) +} - def andThen(that: TreeBijection { val s: TreeBijection.this.t.type }): TreeBijection { - val s: TreeBijection.this.s.type - val t: that.t.type - } = new TreeBijection { - val s: TreeBijection.this.s.type = TreeBijection.this.s - val t: that.t.type = that.t +object SymbolTransformer { + def apply(trans: TreeTransformer): SymbolTransformer { + val s: trans.s.type + val t: trans.t.type + } = new SimpleSymbolTransformer { + val s: trans.s.type = trans.s + val t: trans.t.type = trans.t - val encoder = TreeBijection.this.encoder andThen that.encoder - val decoder = that.decoder andThen TreeBijection.this.decoder + protected def transformFunction(fd: s.FunDef): t.FunDef = trans.transform(fd) + protected def transformADT(adt: s.ADTDefinition): t.ADTDefinition = trans.transform(adt) } } diff --git a/src/main/scala/inox/ast/TypeOps.scala b/src/main/scala/inox/ast/TypeOps.scala index d4006f908..1bc145426 100644 --- a/src/main/scala/inox/ast/TypeOps.scala +++ b/src/main/scala/inox/ast/TypeOps.scala @@ -187,6 +187,19 @@ trait TypeOps { } } + def greatestLowerBound(ts: Seq[Type]): Option[Type] = { + def oglb(ot1: Option[Type], t2: Option[Type]): Option[Type] = ot1 match { + case Some(t1) => greatestLowerBound(t1, t2.get) + case None => None + } + + if (ts.isEmpty) { + None + } else { + ts.map(Some(_)).reduceLeft(oglb) + } + } + def isSubtypeOf(t1: Type, t2: Type): Boolean = { leastUpperBound(t1, t2) == Some(t2) } diff --git a/src/main/scala/inox/solvers/theories/BagEncoder.scala b/src/main/scala/inox/solvers/theories/BagEncoder.scala index b91f3e52d..c7badaee2 100644 --- a/src/main/scala/inox/solvers/theories/BagEncoder.scala +++ b/src/main/scala/inox/solvers/theories/BagEncoder.scala @@ -71,10 +71,10 @@ trait BagEncoder extends TheoryEncoder { }) } - override val newFunctions = Seq(Get, Add, Union, Difference, Intersect, BagEquals) - override val newADTs = Seq(bagADT) + override val extraFunctions = Seq(Get, Add, Union, Difference, Intersect, BagEquals) + override val extraADTs = Seq(bagADT) - val treeEncoder = new SelfTreeTransformer { + protected object encoder extends SelfTreeTransformer { import sourceProgram._ override def transform(e: Expr): Expr = e match { @@ -117,7 +117,7 @@ trait BagEncoder extends TheoryEncoder { } } - val treeDecoder = new SelfTreeTransformer { + protected object decoder extends SelfTreeTransformer { import targetProgram._ override def transform(e: Expr): Expr = e match { diff --git a/src/main/scala/inox/solvers/theories/StringEncoder.scala b/src/main/scala/inox/solvers/theories/StringEncoder.scala index b37b9aaef..dac9759b9 100644 --- a/src/main/scala/inox/solvers/theories/StringEncoder.scala +++ b/src/main/scala/inox/solvers/theories/StringEncoder.scala @@ -81,8 +81,8 @@ trait StringEncoder extends TheoryEncoder { } })) - override val newFunctions = Seq(Size, Take, Drop, Slice, Concat) - override val newADTs = Seq(stringADT, stringNilADT, stringConsADT) + override val extraFunctions = Seq(Size, Take, Drop, Slice, Concat) + override val extraADTs = Seq(stringADT, stringNilADT, stringConsADT) private val stringBijection = new Bijection[String, Expr]() @@ -95,7 +95,7 @@ trait StringEncoder extends TheoryEncoder { v.toList.foldRight(StringNil()){ case (char, l) => StringCons(E(char), l) } } - val treeEncoder = new SelfTreeTransformer { + protected object encoder extends SelfTreeTransformer { override def transform(e: Expr): Expr = e match { case StringLiteral(v) => convertFromString(v) case StringLength(a) => Size(transform(a)).copiedFrom(e) @@ -113,7 +113,7 @@ trait StringEncoder extends TheoryEncoder { } } - val treeDecoder = new SelfTreeTransformer { + protected object decoder extends SelfTreeTransformer { override def transform(e: Expr): Expr = e match { case cc @ ADT(adt, args) if adt == StringNil || adt == StringCons => StringLiteral(convertToString(cc)).copiedFrom(cc) diff --git a/src/main/scala/inox/solvers/theories/TheoryEncoder.scala b/src/main/scala/inox/solvers/theories/TheoryEncoder.scala index 225aa13a4..b5d07dbcd 100644 --- a/src/main/scala/inox/solvers/theories/TheoryEncoder.scala +++ b/src/main/scala/inox/solvers/theories/TheoryEncoder.scala @@ -6,47 +6,15 @@ package theories import utils._ -trait TheoryEncoder extends ast.TreeBijection { - val sourceProgram: Program +trait TheoryEncoder extends ast.ProgramEncoder { self => lazy val trees: sourceProgram.trees.type = sourceProgram.trees - - lazy val s: trees.type = trees lazy val t: trees.type = trees - - lazy val targetProgram: Program { val trees: TheoryEncoder.this.trees.type } = { - sourceProgram.transform(encoder).withFunctions(newFunctions).withADTs(newADTs) - } - - import trees._ - - protected val treeEncoder: SelfTransformer - protected val treeDecoder: SelfTransformer - - lazy val encoder = new ast.SymbolTransformer { - val transformer: treeEncoder.type = treeEncoder - } - - lazy val decoder = new ast.SymbolTransformer { - val transformer: treeEncoder.type = treeEncoder - } - - val newFunctions: Seq[FunDef] = Seq.empty - val newADTs: Seq[ADTDefinition] = Seq.empty - - def >>(that: TheoryEncoder { val sourceProgram: TheoryEncoder.this.targetProgram.type }): TheoryEncoder { - val sourceProgram: TheoryEncoder.this.sourceProgram.type - } = new TheoryEncoder { - val sourceProgram: TheoryEncoder.this.sourceProgram.type = TheoryEncoder.this.sourceProgram - - protected val treeEncoder: SelfTransformer = TheoryEncoder.this.treeEncoder andThen that.treeEncoder - protected val treeDecoder: SelfTransformer = that.treeDecoder andThen TheoryEncoder.this.treeDecoder - } } trait NoEncoder extends TheoryEncoder { import trees._ - protected object treeEncoder extends IdentityTreeTransformer - protected object treeDecoder extends IdentityTreeTransformer + protected object encoder extends IdentityTreeTransformer + protected object decoder extends IdentityTreeTransformer } diff --git a/src/main/scala/inox/solvers/unrolling/UnrollingSolver.scala b/src/main/scala/inox/solvers/unrolling/UnrollingSolver.scala index 4aaa154b5..9929f0e05 100644 --- a/src/main/scala/inox/solvers/unrolling/UnrollingSolver.scala +++ b/src/main/scala/inox/solvers/unrolling/UnrollingSolver.scala @@ -21,7 +21,7 @@ object optFeelingLucky extends FlagOptionDef( object optUnrollAssumptions extends FlagOptionDef( "unrollassumptions", "Use unsat-assumptions to drive unfolding while remaining fair", false) -trait AbstractUnrollingSolver extends Solver { +trait AbstractUnrollingSolver extends Solver { self => import program._ import program.trees._ @@ -32,9 +32,9 @@ trait AbstractUnrollingSolver extends Solver { protected val encoder: ast.ProgramEncoder { val sourceProgram: program.type } - protected val theories: TheoryEncoder { val sourceProgram: AbstractUnrollingSolver.this.encoder.targetProgram.type } + protected val theories: TheoryEncoder { val sourceProgram: self.encoder.targetProgram.type } - protected lazy val programEncoder = encoder >> theories + protected lazy val programEncoder = encoder andThen theories protected lazy val s: programEncoder.s.type = programEncoder.s protected lazy val t: programEncoder.t.type = programEncoder.t @@ -56,11 +56,11 @@ trait AbstractUnrollingSolver extends Solver { protected val templates: Templates { val program: targetProgram.type - type Encoded = AbstractUnrollingSolver.this.Encoded + type Encoded = self.Encoded } protected val evaluator: DeterministicEvaluator with SolvingEvaluator { - val program: AbstractUnrollingSolver.this.program.type + val program: self.program.type } protected val underlying: AbstractSolver { diff --git a/src/main/scala/inox/solvers/z3/NativeZ3Solver.scala b/src/main/scala/inox/solvers/z3/NativeZ3Solver.scala index 2b62d3683..87f3f2183 100644 --- a/src/main/scala/inox/solvers/z3/NativeZ3Solver.scala +++ b/src/main/scala/inox/solvers/z3/NativeZ3Solver.scala @@ -9,8 +9,7 @@ import theories._ import z3.scala._ -trait NativeZ3Solver - extends AbstractUnrollingSolver { +trait NativeZ3Solver extends AbstractUnrollingSolver { self => import program._ import program.trees._ @@ -21,13 +20,12 @@ trait NativeZ3Solver type Encoded = Z3AST object theories extends { - val sourceProgram: NativeZ3Solver.this.encoder.targetProgram.type = - NativeZ3Solver.this.encoder.targetProgram + val sourceProgram: self.encoder.targetProgram.type = self.encoder.targetProgram } with StringEncoder protected object underlying extends { val program: targetProgram.type = targetProgram - val options = NativeZ3Solver.this.options + val options = self.options } with AbstractZ3Solver private lazy val z3 = underlying.z3 @@ -37,7 +35,7 @@ trait NativeZ3Solver } with Templates { import program.trees._ - type Encoded = NativeZ3Solver.this.Encoded + type Encoded = self.Encoded def asString(ast: Z3AST): String = ast.toString -- GitLab