Skip to content
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

Squid preparations #370

Merged
merged 4 commits into from
Jul 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import java.lang.annotation.Target;

/**
* A holder for the name of the field in the enclosing object, where a serialized code of a an
* A holder for the name of the field in the enclosing object, where a serialized code of an
* annotated method can be found.
* <p>
* For example
Expand All @@ -37,7 +37,7 @@
* Implies that `foo` contains a field named `applyQ$01` which returns the code of `apply` as
* a string.
* <p>
* Instances of this annotation are placed by by the `emma.lib` macro, please do not use this
* Instances of this annotation are placed by the `emma.lib` macro, please do not use this
* directly.
*/
@Retention(RetentionPolicy.RUNTIME)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,22 @@ trait RuntimeCompilerAware {
}

protected lazy val addContext = TreeTransform("RuntimeCompilerAware.addContext", tree => {
import u.Quasiquote
q"(env: $Env) => { implicit val e: $Env = env; $tree }"
// This is roughly equivalent to the following:
//import u.Quasiquote
//q"(env: $Env) => { implicit val e: $Env = env; $tree }"

// Note that instead of making an implicit ValDef inside the lambda,
// it would be good to make the parameter of the lambda implicit.
// However, showCode prints such code incorrectly. See
// https://github.com/scala/bug/issues/10936

val envSym = compiler.api.ParSym(compiler.api.Owner.encl, compiler.api.TermName.fresh("env"), Env)
val eValDef = compiler.api.ValDef(
compiler.api.ValSym(compiler.api.Owner.encl, compiler.api.TermName.fresh("e"), Env, u.Flag.IMPLICIT),
compiler.api.ParRef(envSym)
)
val body = compiler.api.Block(Seq(eValDef), tree)
compiler.api.Lambda(Seq(envSym), body)
})

/** Adds a [[File]] to the classpath. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ trait DataBag[A] extends Serializable {
*
* @param n number of elements to return
* @param o the implicit [[Ordering]] of elements
* @return an ordered (descending) [[List]] of the bottom `n` elements
* @return an ordered (descending) [[List]] of the top `n` elements
*/
def top(n: Int)(implicit o: Ordering[A]): List[A] =
fold(Top(n, o))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ trait MutableBag[K, V] {
*/
def update[M: Meta](ms: DataBag[Group[K, M]])(f: UpdateFunction[M]): DataBag[(K, V)]

/** Returns the underluing data of this `MutableBag` as an (immutable) `DataBag`. */
/** Returns the underlying data of this `MutableBag` as an (immutable) `DataBag`. */
def bag(): DataBag[(K, V)]

/** Creates a copy of this `MutableBag`. Can be used for by-value assignment. */
Expand Down
10 changes: 8 additions & 2 deletions emma-language/src/main/scala/org/emmalanguage/ast/Bindings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,12 @@ trait Bindings { this: AST =>
* Creates a type-checked binding definition.
* @param lhs Must be a binding symbol.
* @param rhs The value of this binding (empty by default), owned by `lhs`.
* @param alwaysSetTpt If false, then we only set tpt if the type of `lhs` is different than
* what would be inferred from the `rhs` later. If true, we always write
* out the type (this is necessary when passing a tree to Squid).
* @return `[val|var] lhs [= rhs]`.
*/
def apply(lhs: u.TermSymbol, rhs: u.Tree = Empty()): u.ValDef = {
def apply(lhs: u.TermSymbol, rhs: u.Tree = Empty(), alwaysSetTpt: Boolean = false): u.ValDef = {
assert(is.defined(lhs), s"$this LHS is not defined")
assert(is.binding(lhs), s"$this LHS $lhs is not a binding")
assert(has.nme(lhs), s"$this LHS $lhs has no name")
Expand All @@ -109,7 +112,10 @@ trait Bindings { this: AST =>
|(lhs: `$lhs`, rhs:\n`${u.showCode(rhs)}`\n)
|""".stripMargin.trim)
(Owner.at(lhs)(rhs),
if (lhs.info =:= rhs.tpe.dealias.widen) TypeQuote.empty
// We don't want ValDefs to write out the type if it can be inferred, because of the
// "inaccessible types made explicit issue".
// See https://github.com/emmalanguage/emma/issues/234#issuecomment-260111399
if (lhs.info =:= rhs.tpe.dealias.widen && !alwaysSetTpt) TypeQuote.empty
else TypeQuote(lhs.info))
} else {
assert(lhs.isParameter, s"$this RHS cannot be empty")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,8 @@ trait Terms { this: AST =>
val pts = params.map(_.info)
val tpe = Type.fun(pts, body.tpe)
val sym = TermSym.free(TermName.lambda, tpe)
val als = for ((p, t) <- params zip pts) yield ParSym(sym, p.name, t)
val als = for ((p, t) <- params zip pts)
yield ParSym(sym, p.name, t, if (p.isImplicit) u.Flag.IMPLICIT else u.NoFlags)
val rhs = Sym.subst(sym, params zip als)(body)
val fun = u.Function(als.map(ParDef(_)).toList, rhs)
setSymbol(fun, sym)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,14 @@ trait Values { this: AST =>
* Creates a type-checked value definition.
* @param lhs Must be a value symbol.
* @param rhs The RHS of this value, owned by `lhs`.
* @param alwaysSetTpt See BindingDef.apply
* @return `val lhs = rhs`.
*/
def apply(lhs: u.TermSymbol, rhs: u.Tree): u.ValDef = {
def apply(lhs: u.TermSymbol, rhs: u.Tree, alwaysSetTpt: Boolean = false): u.ValDef = {
assert(is.defined(lhs), s"$this LHS is not defined")
assert(is.value(lhs), s"$this LHS $lhs is not a value")
assert(is.defined(rhs), s"$this RHS is not defined")
BindingDef(lhs, rhs)
BindingDef(lhs, rhs, alwaysSetTpt)
}

def unapply(bind: u.ValDef): Option[(u.TermSymbol, u.Tree)] = bind match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ private[comprehension] trait Combination extends Common {
for (i <- 1 to 2) yield tuple2.member(api.TermName(s"_$i")).asTerm
}

// TODO: Split conjunctive filter predicates.

/**
* Introduces combinators instead of comprehensions.
* In addition to the monad ops, it recognizes crosses and equi joins.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,19 @@ private[core] trait DCE extends Common {
// Decide for each ValDef whether it is needed.
// Start with the refs in the expr and refs in the DefDefs, then go through the ValDefs backwards.
val liveRefs = vals.foldRight(refs(expr) | refsInDefs) {
// If we already know that the lhs is `live` then we add the refs on the rhs to `live`.
case (core.ValDef(lhs, rhs), live)
if live(lhs) =>
live | refs(rhs)
// When we have a DefCall that is returning a unit, then treat this val as used
case (core.ValDef(lhs, rhs @ api.DefCall(_, method, _, _)), live)
if maybeMutable(method) => live | refs(rhs) + lhs
case (core.ValDef(lhs, rhs), live) =>
if (live(lhs)) live | refs(rhs) else live
if maybeMutable(method) =>
live | refs(rhs) + lhs
// Always treat implicit vals as used
case (core.ValDef(lhs, rhs), live)
if lhs.isImplicit =>
live | refs(rhs) + lhs
case (_, live) => live
}

// Retain only those ValDefs which are referenced.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,18 @@ private[core] trait Reduce extends Common {
api.BottomUp.inherit({ // accumulate trivial ValDef bindings in scope
case core.Let(vals, _, _) =>
vals.foldLeft(Map.empty[u.Symbol, u.Tree])((valDefs, valDef) => valDef match {
case core.ValDef(x, [email protected](y)) => valDefs + (x -> valDefs.getOrElse(y, a))
case core.ValDef(x, [email protected](_)) => valDefs + (x -> a)
case core.ValDef(x, [email protected](y)) if !x.isImplicit => valDefs + (x -> valDefs.getOrElse(y, a))
case core.ValDef(x, [email protected](_)) if !x.isImplicit => valDefs + (x -> a)
case _ => valDefs
})
})(mclose).transformWith({
case Attr.inh(core.Ref(x), valDefs :: _)
if valDefs contains x => valDefs(x)
case Attr.none(core.Let(vals, defs, expr)) =>
core.Let(vals.filter(_.rhs match {
core.Let(vals.filter((vd: u.ValDef) => vd.symbol.isImplicit || (vd.rhs match {
case core.Atomic(_) => false
case _ => true
}), defs, expr)
})), defs, expr)
})._tree)

/** Merges two closed trivial ValDef maps preserving the closure. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ private[compiler] trait Source extends Common
Foreach2Loop.transform
))

/** Removes implicit lists consisting of the following symbols. */
/** Removes implicit parameter lists having the given types. */
def removeImplicits(types: Set[u.Type]) = TreeTransform("Source.removeImplicits", {

object Matching {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -825,9 +825,31 @@ private[compiler] trait FoldForestFusion extends Common {
// Build a new data-flow graph to determine the topological order of vals.
if (fusedIndex.isEmpty) let else {
val index = valIndex ++ fusedIndex -- fusedMonadOps
val dataFlow = index.mapValues(_.collect {
val dataFlow0 = index.mapValues(_.collect {
case core.Ref(x) if index.contains(x) => x
}.toSet)

// We add edges from all non-implicit ValDef to all implicit ValDefs.
// This is necessary, because implicits are not resolved at this point in the pipeline,
// so we don't see the real dependencies from just looking at the refs.
// Note that unfortunately this can still go wrong in certain cases:
// - implicit dependencies between implicit ValDefs
// - an implicit ValDef has a dependency on a non-implicit ValDef
// But it works for common cases, e.g., with addContext.
val implicitValSyms = vals.filter(_.symbol.isImplicit).map(_.symbol.asTerm)
val dataFlow =
if (implicitValSyms.isEmpty) {
dataFlow0
} else {
dataFlow0.map { case (vdsym, edges) =>
if (vdsym.isImplicit) {
(vdsym, edges)
} else {
(vdsym, edges ++ implicitValSyms.toSet)
}
}
}

val sortedVals = for {
lhs <- topoSort(dataFlow).get
} yield core.ValDef(lhs, index(lhs))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ trait BaseCompilerSpec extends FreeSpec with Matchers with PropertyChecks with T

/**
* Combines a sequence of `transformations` into a pipeline with pre- and post-processing.
* If the IT maven profile is set, then it also checks if the resulting code is valid by compiling it.
* If the compile-spec-pipelines maven profile is set, then it also checks if the resulting code is
* valid by compiling it.
*/
protected def pipeline(
typeCheck: Boolean = false, withPre: Boolean = true, withPost: Boolean = true
Expand Down Expand Up @@ -161,7 +162,7 @@ object BaseCompilerSpec {

/**
* Whether spec pipelines should try a toolbox compilation at the end, to potentially catch more bugs.
* (Set by the IT maven profile; makes specs run about 5 times slower.)
* (Set by the compile-spec-pipelines maven profile; makes specs run about 5 times slower.)
*/
lazy val compileSpecPipelines = props
.getProperty("compile-spec-pipelines")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ class DCESpec extends BaseCompilerSpec {
}
}

"don't eliminate unit valdefs" - {
"println" in {
"don't eliminate" - {
"unit valdefs" in {
val act = dcePipeline(reify {
println("alma")
val x = 5
Expand All @@ -84,7 +84,21 @@ class DCESpec extends BaseCompilerSpec {

act shouldBe alphaEqTo(exp)
}
}
}

"implicit valdefs" in {
val act = dcePipeline(reify {
implicit val x = 5
val y = 6
y
})

val exp = idPipeline(reify {
implicit val x = 5
val y = 6
y
})

act shouldBe alphaEqTo(exp)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,22 @@ class ReduceSpec extends BaseCompilerSpec {
act shouldBe alphaEqTo(exp)
}

"does not eliminate implicit ValDefs" in {
val act = actPipeline(reify {
val xs = this.xs
implicit val i = 8
xs
})

val exp = expPipeline(reify {
val xs = this.xs
implicit val i = 8
xs
})

act shouldBe alphaEqTo(exp)
}

"inlines" - {
"lambdas in the enclosing let block" in {
val act = actPipeline(reify {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ object util {
outputPath.toString
}).acquireAndGet(identity)

/** Creates a demp output path with the given `suffix`. */
/** Creates a temp output path with the given `suffix`. */
def tempPath(suffix: String) = FilenameUtils
.separatorsToUnix(Paths.get(s"$basePath/$suffix").toString)

Expand Down