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

[MACROS] Issue 84: Semantic checks for unstable applications in enclo… #141

Closed
wants to merge 1 commit into from
Closed
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 @@ -168,4 +168,10 @@ package object api {
| "updateWith* call, and use that one in place of this .bag() call.""".stripMargin)
// Note: Don't catch this!
// This should terminate the program, because the stateful will be in an invalid state after throwing this.

class UnstableApplicationToEnclosingScopeException(id: String) extends InvalidProgramException( s"""
| The user defined function is performing an application in the enclosing scope: (`$id`).
| This may also be the result of write access to a mutable member of the enclosing scope
| (for instance setting a mutable variable or attribute).
""".stripMargin)
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package eu.stratosphere.emma.macros.program

import eu.stratosphere.emma.api.StatefulAccessedFromUdfException
import eu.stratosphere.emma.api.{UnstableApplicationToEnclosingScopeException, StatefulAccessedFromUdfException}
import eu.stratosphere.emma.macros.program.comprehension.ComprehensionAnalysis

private[emma] trait SemanticChecks extends ComprehensionAnalysis {
import universe._
import syntax._

val doSemanticChecks: Tree => Unit =
checkForStatefulAccessedFromUdf
val doSemanticChecks: Tree => Unit = { t =>
checkForStatefulAccessedFromUdf(t)
checkForUnstableApplicationsInEnclosingScope(t)
}


/**
* Checks that `.bag()` is not called from the UDF of an `updateWith*` on the same stateful that
Expand All @@ -21,4 +24,17 @@ private[emma] trait SemanticChecks extends ComprehensionAnalysis {
if (UDFs exists { _.closure(id.term) })
throw new StatefulAccessedFromUdfException()
}

/**
* Prohibits applications to functions / methods in the enclosing object / closure.
* This includes write references to mutable variables or properties (will
* become applications after type checking due to UAP).
*
* @param tree
*/
def checkForUnstableApplicationsInEnclosingScope(tree: Tree) = traverse(tree) {
case a @ Apply(Select(This(TypeName(_)), TermName(id)),_)
if !a.symbol.asTerm.isStable =>
throw new UnstableApplicationToEnclosingScopeException(id)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,84 @@ class SemanticChecksTest extends FlatSpec with Matchers with RuntimeUtil {
}
}

it should "check write access and application of outer methods / functions / variables / properties" in {
type E = UnstableApplicationToEnclosingScopeException
q"""
object SomeObject {
var x = 0
emma.parallelize {
List(23,42,99).foreach { y => x = x + y }
}
}
""" should failWith[E]

q"""
object SomeObject {
var x = 0
emma.parallelize {
List(23,42,99).foreach { y => x += y }
}
}
""" should failWith[E]

q"""
object SomeObject {
var x = 0
emma.parallelize {
x += 1
}
}
""" should failWith[E]

q"""
object SomeClosure {
var x = 0
emma.parallelize {
val lst = List(23,99,42) // disambiguation default arguments / Loc
x = lst.foldLeft(Int.MinValue)(Math.max(_,_))
}
}
""" should failWith[E]

q"""
class MyClass(var x:Int = 0) {
emma.parallelize {
val lst = List(23,99,42)
x = lst.foldLeft(Int.MinValue)(Math.max(_,_))
}
}
""" should failWith[E]

q"""
class MyClass {
private var _x:Int = 42
def x = _x
def x_=(newX:Int) { _x = newX}

emma.parallelize {
val lst = List(23,99,42)
x = lst.foldLeft(Int.MinValue)(Math.max(_,_))
}
}
""" should failWith[E]

noException should be thrownBy {tb.typecheck(withImports(
q"""
object MyObject {
def getFourtyTwo:Int = 42
emma.parallelize {
List(1,2,3,getFourtyTwo, 99).map(_ * 2)
}
}
"""))}
}

def failWith[E <: Throwable : ClassTag] =
include (implicitly[ClassTag[E]].runtimeClass.getSimpleName) compose { (tree: Tree) =>
intercept[ToolBoxError] { tb.typecheck(q"{ ..$imports; $tree }") }.getMessage
intercept[ToolBoxError] { tb.typecheck(withImports(tree)) }.getMessage
}

def withImports(tree: universe.Tree): universe.Tree = q"{ ..$imports; $tree }"
}

object SemanticChecksTest {
Expand Down