aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Petrashko <dark@d-d.me>2016-07-15 13:22:06 +0200
committerGitHub <noreply@github.com>2016-07-15 13:22:06 +0200
commitf37e45a516ca97a27fed279c5da26574d2fe77db (patch)
tree268a2283e96ce363bf4fd9bca9d7a6be6e90ef7f
parent18a1c206295fc85c795da48b3e737b88a2bb56e5 (diff)
parent7e00c724273d432c8900c0e8ec852bb77357958e (diff)
downloaddotty-f37e45a516ca97a27fed279c5da26574d2fe77db.tar.gz
dotty-f37e45a516ca97a27fed279c5da26574d2fe77db.tar.bz2
dotty-f37e45a516ca97a27fed279c5da26574d2fe77db.zip
Merge pull request #1291 from nicolasstucki/implement-scala-dynamic
Add scala.Dynamic support.
-rw-r--r--src/dotty/tools/dotc/core/Definitions.scala4
-rw-r--r--src/dotty/tools/dotc/core/StdNames.scala1
-rw-r--r--src/dotty/tools/dotc/core/TypeOps.scala25
-rw-r--r--src/dotty/tools/dotc/core/Types.scala3
-rw-r--r--src/dotty/tools/dotc/reporting/Reporter.scala30
-rw-r--r--src/dotty/tools/dotc/typer/Applications.scala10
-rw-r--r--src/dotty/tools/dotc/typer/Dynamic.scala71
-rw-r--r--src/dotty/tools/dotc/typer/ProtoTypes.scala2
-rw-r--r--src/dotty/tools/dotc/typer/TypeAssigner.scala9
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala29
-rw-r--r--tests/neg/dynamicApplyDynamicTest1.scala7
-rw-r--r--tests/neg/dynamicApplyDynamicTest2.scala7
-rw-r--r--tests/neg/dynamicApplyDynamicTest3.scala7
-rw-r--r--tests/neg/dynamicApplyDynamicTest4.scala10
-rw-r--r--tests/neg/dynamicApplyDynamicTest5.scala9
-rw-r--r--tests/neg/dynamicApplyDynamicTest6.scala9
-rw-r--r--tests/neg/dynamicApplyDynamicTest7.scala9
-rw-r--r--tests/neg/dynamicApplyDynamicTest8.scala9
-rw-r--r--tests/neg/dynamicApplyDynamicTest9.scala9
-rw-r--r--tests/neg/dynamicApplyNamedDynamicTest1.scala7
-rw-r--r--tests/neg/dynamicApplyNamedDynamicTest2.scala10
-rw-r--r--tests/neg/dynamicApplyNamedDynamicTest3.scala7
-rw-r--r--tests/neg/dynamicApplyNamedDynamicTest4.scala7
-rw-r--r--tests/neg/dynamicApplyNamedDynamicTest5.scala9
-rw-r--r--tests/neg/dynamicApplyNamedDynamicTest6.scala9
-rw-r--r--tests/neg/dynamicApplyNamedDynamicTest7.scala9
-rw-r--r--tests/neg/dynamicApplyNamedDynamicTest8.scala9
-rw-r--r--tests/neg/dynamicDynamicImplicitsTest1.scala13
-rw-r--r--tests/neg/dynamicDynamicImplicitsTest2.scala13
-rw-r--r--tests/neg/dynamicDynamicImplicitsTest3.scala13
-rw-r--r--tests/neg/dynamicNoImport.scala13
-rw-r--r--tests/neg/dynamicSelectDynamicTest1.scala10
-rw-r--r--tests/neg/dynamicSelectDynamicTest2.scala7
-rw-r--r--tests/neg/dynamicSelectDynamicTest3.scala7
-rw-r--r--tests/neg/dynamicSelectDynamicTest4.scala11
-rw-r--r--tests/neg/dynamicSelectDynamicTest5.scala7
-rw-r--r--tests/neg/dynamicSelectDynamicTest6.scala11
-rw-r--r--tests/neg/dynamicSelectDynamicTest7.scala9
-rw-r--r--tests/neg/dynamicUpdateDynamicTest1.scala7
-rw-r--r--tests/neg/dynamicUpdateDynamicTest2.scala9
-rw-r--r--tests/neg/dynamicUpdateDynamicTest3.scala9
-rw-r--r--tests/neg/dynamicUpdateDynamicTest4.scala9
-rw-r--r--tests/pos/dynamicExtendsNoImport.scala7
-rw-r--r--tests/run/dynamicDynamicTests.scala164
44 files changed, 626 insertions, 20 deletions
diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala
index 8cd810889..6d183fe40 100644
--- a/src/dotty/tools/dotc/core/Definitions.scala
+++ b/src/dotty/tools/dotc/core/Definitions.scala
@@ -424,8 +424,10 @@ class Definitions {
def Product_productArity(implicit ctx: Context) = Product_productArityR.symbol
lazy val Product_productPrefixR = ProductClass.requiredMethodRef(nme.productPrefix)
def Product_productPrefix(implicit ctx: Context) = Product_productPrefixR.symbol
- lazy val LanguageModuleRef = ctx.requiredModule("dotty.language")
+ lazy val LanguageModuleRef = ctx.requiredModule("dotty.language")
def LanguageModuleClass(implicit ctx: Context) = LanguageModuleRef.symbol.moduleClass.asClass
+ lazy val Scala2LanguageModuleRef = ctx.requiredModule("scala.language")
+ def Scala2LanguageModuleClass(implicit ctx: Context) = Scala2LanguageModuleRef.symbol.moduleClass.asClass
lazy val NonLocalReturnControlType: TypeRef = ctx.requiredClassRef("scala.runtime.NonLocalReturnControl")
lazy val ClassTagType = ctx.requiredClassRef("scala.reflect.ClassTag")
diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala
index f9ede23c5..f47ab1744 100644
--- a/src/dotty/tools/dotc/core/StdNames.scala
+++ b/src/dotty/tools/dotc/core/StdNames.scala
@@ -383,6 +383,7 @@ object StdNames {
val delayedInit: N = "delayedInit"
val delayedInitArg: N = "delayedInit$body"
val drop: N = "drop"
+ val dynamics: N = "dynamics"
val dummyApply: N = "<dummy-apply>"
val elem: N = "elem"
val emptyValDef: N = "emptyValDef"
diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala
index 80e0fc6f1..10b0f5615 100644
--- a/src/dotty/tools/dotc/core/TypeOps.scala
+++ b/src/dotty/tools/dotc/core/TypeOps.scala
@@ -483,17 +483,19 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
*/
def featureEnabled(owner: ClassSymbol, feature: TermName): Boolean = {
def toPrefix(sym: Symbol): String =
- if (sym eq defn.LanguageModuleClass) "" else toPrefix(sym.owner) + sym.name + "."
+ if (!sym.exists || (sym eq defn.LanguageModuleClass) || (sym eq defn.Scala2LanguageModuleRef)) ""
+ else toPrefix(sym.owner) + sym.name + "."
def featureName = toPrefix(owner) + feature
- def hasImport(implicit ctx: Context): Boolean = (
- ctx.importInfo != null
- && ( (ctx.importInfo.site.widen.typeSymbol eq owner)
- && ctx.importInfo.originals.contains(feature)
- ||
- { var c = ctx.outer
- while (c.importInfo eq ctx.importInfo) c = c.outer
- hasImport(c)
- }))
+ def hasImport(implicit ctx: Context): Boolean = {
+ if (ctx.importInfo == null || (ctx.importInfo.site.widen.typeSymbol ne owner)) false
+ else if (ctx.importInfo.excluded.contains(feature)) false
+ else if (ctx.importInfo.originals.contains(feature)) true
+ else {
+ var c = ctx.outer
+ while (c.importInfo eq ctx.importInfo) c = c.outer
+ hasImport(c)
+ }
+ }
def hasOption = ctx.base.settings.language.value exists (s => s == featureName || s == "_")
hasImport(ctx.withPhase(ctx.typerPhase)) || hasOption
}
@@ -505,6 +507,9 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
def scala2Mode =
featureEnabled(defn.LanguageModuleClass, nme.Scala2)
+ def dynamicsEnabled =
+ featureEnabled(defn.Scala2LanguageModuleClass, nme.dynamics)
+
def testScala2Mode(msg: String, pos: Position) = {
if (scala2Mode) migrationWarning(msg, pos)
scala2Mode
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index fa402f9fc..11da27265 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -3221,6 +3221,9 @@ object Types {
object ErrorType extends ErrorType
+ /* Type used to track Select nodes that could not resolve a member and their qualifier is a scala.Dynamic. */
+ object TryDynamicCallType extends ErrorType
+
/** Wildcard type, possibly with bounds */
abstract case class WildcardType(optBounds: Type) extends CachedGroundType with TermType {
def derivedWildcardType(optBounds: Type)(implicit ctx: Context) =
diff --git a/src/dotty/tools/dotc/reporting/Reporter.scala b/src/dotty/tools/dotc/reporting/Reporter.scala
index e4169b1fd..bddfd2f68 100644
--- a/src/dotty/tools/dotc/reporting/Reporter.scala
+++ b/src/dotty/tools/dotc/reporting/Reporter.scala
@@ -12,6 +12,7 @@ import config.Printers
import java.lang.System.currentTimeMillis
import core.Mode
import interfaces.Diagnostic.{ERROR, WARNING, INFO}
+import dotty.tools.dotc.core.Symbols.Symbol
object Reporter {
class Error(msgFn: => String, pos: SourcePosition) extends Diagnostic(msgFn, pos, ERROR)
@@ -68,6 +69,29 @@ trait Reporting { this: Context =>
def featureWarning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
reporter.report(new FeatureWarning(msg, pos))
+ def featureWarning(feature: String, featureDescription: String, isScala2Feature: Boolean,
+ featureUseSite: Symbol, required: Boolean, pos: SourcePosition): Unit = {
+ val req = if (required) "needs to" else "should"
+ val prefix = if (isScala2Feature) "scala." else "dotty."
+ val fqname = prefix + "language." + feature
+
+ val explain = {
+ if (reporter.isReportedFeatureUseSite(featureUseSite)) ""
+ else {
+ reporter.reportNewFeatureUseSite(featureUseSite)
+ s"""|
+ |This can be achieved by adding the import clause 'import $fqname'
+ |or by setting the compiler option -language:$feature.
+ |See the Scala docs for value $fqname for a discussion
+ |why the feature $req be explicitly enabled.""".stripMargin
+ }
+ }
+
+ val msg = s"$featureDescription $req be enabled\nby making the implicit value $fqname visible.$explain"
+ if (required) error(msg, pos)
+ else reporter.report(new FeatureWarning(msg, pos))
+ }
+
def warning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
reporter.report(new Warning(msg, pos))
@@ -172,7 +196,7 @@ abstract class Reporter extends interfaces.ReporterResult {
/** Report a diagnostic */
def doReport(d: Diagnostic)(implicit ctx: Context): Unit
- /** Whether very long lines can be truncated. This exists so important
+ /** Whether very long lines can be truncated. This exists so important
* debugging information (like printing the classpath) is not rendered
* invisible due to the max message length.
*/
@@ -206,6 +230,10 @@ abstract class Reporter extends interfaces.ReporterResult {
*/
def errorsReported = hasErrors
+ private[this] var reportedFeaturesUseSites = Set[Symbol]()
+ def isReportedFeatureUseSite(featureTrait: Symbol): Boolean = reportedFeaturesUseSites.contains(featureTrait)
+ def reportNewFeatureUseSite(featureTrait: Symbol): Unit = reportedFeaturesUseSites += featureTrait
+
val unreportedWarnings = new mutable.HashMap[String, Int] {
override def default(key: String) = 0
}
diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala
index aba073f3d..6e78a570d 100644
--- a/src/dotty/tools/dotc/typer/Applications.scala
+++ b/src/dotty/tools/dotc/typer/Applications.scala
@@ -87,11 +87,12 @@ object Applications {
import Applications._
-trait Applications extends Compatibility { self: Typer =>
+trait Applications extends Compatibility { self: Typer with Dynamic =>
import Applications._
import tpd.{ cpy => _, _ }
import untpd.cpy
+ import Dynamic.isDynamicMethod
/** @tparam Arg the type of arguments, could be tpd.Tree, untpd.Tree, or Type
* @param methRef the reference to the method of the application
@@ -554,6 +555,13 @@ trait Applications extends Compatibility { self: Typer =>
fun1.tpe match {
case ErrorType => tree.withType(ErrorType)
+ case TryDynamicCallType =>
+ tree match {
+ case tree @ Apply(Select(qual, name), args) if !isDynamicMethod(name) =>
+ typedDynamicApply(qual, name, args, pt)(tree)
+ case _ =>
+ handleUnexpectedFunType(tree, fun1)
+ }
case _ => methPart(fun1).tpe match {
case funRef: TermRef =>
tryEither { implicit ctx =>
diff --git a/src/dotty/tools/dotc/typer/Dynamic.scala b/src/dotty/tools/dotc/typer/Dynamic.scala
new file mode 100644
index 000000000..aeb3cca8c
--- /dev/null
+++ b/src/dotty/tools/dotc/typer/Dynamic.scala
@@ -0,0 +1,71 @@
+package dotty.tools
+package dotc
+package typer
+
+import dotty.tools.dotc.ast.Trees.NamedArg
+import dotty.tools.dotc.ast.tpd._
+import dotty.tools.dotc.ast.untpd
+import dotty.tools.dotc.core.Constants.Constant
+import dotty.tools.dotc.core.Contexts.Context
+import dotty.tools.dotc.core.Names.Name
+import dotty.tools.dotc.core.StdNames._
+import dotty.tools.dotc.core.Types._
+import dotty.tools.dotc.core.Mode
+import dotty.tools.dotc.core.Decorators._
+
+object Dynamic {
+ def isDynamicMethod(name: Name): Boolean =
+ name == nme.applyDynamic || name == nme.selectDynamic || name == nme.updateDynamic || name == nme.applyDynamicNamed
+}
+
+/** Translates selection that does not typecheck according to the scala.Dynamic rules:
+ * foo.bar(baz) = quux ~~> foo.selectDynamic(bar).update(baz, quux)
+ * foo.bar = baz ~~> foo.updateDynamic("bar")(baz)
+ * foo.bar(x = bazX, y = bazY, baz, ...) ~~> foo.applyDynamicNamed("bar")(("x", bazX), ("y", bazY), ("", baz), ...)
+ * foo.bar(baz0, baz1, ...) ~~> foo.applyDynamic(bar)(baz0, baz1, ...)
+ * foo.bar ~~> foo.selectDynamic(bar)
+ *
+ * The first matching rule of is applied.
+ */
+trait Dynamic { self: Typer with Applications =>
+
+ /** Translate selection that does not typecheck according to the normal rules into a applyDynamic/applyDynamicNamed.
+ * foo.bar(baz0, baz1, ...) ~~> foo.applyDynamic(bar)(baz0, baz1, ...)
+ * foo.bar(x = bazX, y = bazY, baz, ...) ~~> foo.applyDynamicNamed("bar")(("x", bazX), ("y", bazY), ("", baz), ...)
+ */
+ def typedDynamicApply(qual: untpd.Tree, name: Name, args: List[untpd.Tree], pt: Type)(original: untpd.Apply)(
+ implicit ctx: Context): Tree = {
+ def isNamedArg(arg: untpd.Tree): Boolean = arg match { case NamedArg(_, _) => true; case _ => false }
+ val dynName = if (args.exists(isNamedArg)) nme.applyDynamicNamed else nme.applyDynamic
+ if (dynName == nme.applyDynamicNamed && untpd.isWildcardStarArgList(args)) {
+ ctx.error("applyDynamicNamed does not support passing a vararg parameter", original.pos)
+ original.withType(ErrorType)
+ } else {
+ def namedArgTuple(name: String, arg: untpd.Tree) = untpd.Tuple(List(Literal(Constant(name)), arg))
+ def namedArgs = args.map {
+ case NamedArg(argName, arg) => namedArgTuple(argName.toString, arg)
+ case arg => namedArgTuple("", arg)
+ }
+ val args1 = if (dynName == nme.applyDynamic) args else namedArgs
+ typedApply(untpd.Apply(coreDynamic(qual, dynName, name), args1), pt)
+ }
+ }
+
+ /** Translate selection that does not typecheck according to the normal rules into a selectDynamic.
+ * foo.bar ~~> foo.selectDynamic(bar)
+ *
+ * Note: inner part of translation foo.bar(baz) = quux ~~> foo.selectDynamic(bar).update(baz, quux) is achieved
+ * through an existing transformation of in typedAssign [foo.bar(baz) = quux ~~> foo.bar.update(baz, quux)].
+ */
+ def typedDynamicSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree =
+ typedApply(coreDynamic(tree.qualifier, nme.selectDynamic, tree.name), pt)
+
+ /** Translate selection that does not typecheck according to the normal rules into a updateDynamic.
+ * foo.bar = baz ~~> foo.updateDynamic(bar)(baz)
+ */
+ def typedDynamicAssign(qual: untpd.Tree, name: Name, rhs: untpd.Tree, pt: Type)(implicit ctx: Context): Tree =
+ typedApply(untpd.Apply(coreDynamic(qual, nme.updateDynamic, name), rhs), pt)
+
+ private def coreDynamic(qual: untpd.Tree, dynName: Name, name: Name)(implicit ctx: Context): untpd.Apply =
+ untpd.Apply(untpd.Select(qual, dynName), Literal(Constant(name.toString)))
+}
diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala
index 3a13212a3..a430d5f75 100644
--- a/src/dotty/tools/dotc/typer/ProtoTypes.scala
+++ b/src/dotty/tools/dotc/typer/ProtoTypes.scala
@@ -438,6 +438,8 @@ object ProtoTypes {
(if (theMap != null) theMap else new WildApproxMap).mapOver(tp)
}
+ @sharable object AssignProto extends UncachedGroundType with MatchAlways
+
private[ProtoTypes] class WildApproxMap(implicit ctx: Context) extends TypeMap {
def apply(tp: Type) = wildApprox(tp, this)
}
diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala
index 0344ae6c6..1394d2e3e 100644
--- a/src/dotty/tools/dotc/typer/TypeAssigner.scala
+++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala
@@ -199,11 +199,16 @@ trait TypeAssigner {
def selectionType(site: Type, name: Name, pos: Position)(implicit ctx: Context): Type = {
val mbr = site.member(name)
if (reallyExists(mbr)) site.select(name, mbr)
- else {
+ else if (site.derivesFrom(defn.DynamicClass) && !Dynamic.isDynamicMethod(name)) {
+ TryDynamicCallType
+ } else {
if (!site.isErroneous) {
ctx.error(
if (name == nme.CONSTRUCTOR) d"$site does not have a constructor"
- else d"$name is not a member of $site", pos)
+ else if (site.derivesFrom(defn.DynamicClass)) {
+ d"$name is not a member of $site\n" +
+ "possible cause: maybe a wrong Dynamic method signature?"
+ } else d"$name is not a member of $site", pos)
}
ErrorType
}
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index 4e2842da7..b9d5e3817 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -57,11 +57,12 @@ object Typer {
assert(tree.pos.exists, s"position not set for $tree # ${tree.uniqueId}")
}
-class Typer extends Namer with TypeAssigner with Applications with Implicits with Checking {
+class Typer extends Namer with TypeAssigner with Applications with Implicits with Dynamic with Checking {
import Typer._
import tpd.{cpy => _, _}
import untpd.cpy
+ import Dynamic.isDynamicMethod
/** A temporary data item valid for a single typed ident:
* The set of all root import symbols that have been
@@ -315,7 +316,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
def asSelect(implicit ctx: Context): Tree = {
val qual1 = typedExpr(tree.qualifier, selectionProto(tree.name, pt, this))
if (tree.name.isTypeName) checkStable(qual1.tpe, qual1.pos)
- typedSelect(tree, pt, qual1)
+ val select = typedSelect(tree, pt, qual1)
+ pt match {
+ case _: FunProto | AssignProto => select
+ case _ =>
+ if (select.tpe eq TryDynamicCallType) typedDynamicSelect(tree, pt)
+ else select
+ }
}
def asJavaSelectFromTypeTree(implicit ctx: Context): Tree = {
@@ -479,7 +486,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
val appliedUpdate = cpy.Apply(fn)(wrappedUpdate, (args map untpd.TypedSplice) :+ tree.rhs)
typed(appliedUpdate, pt)
case lhs =>
- val lhsCore = typedUnadapted(lhs)
+ val lhsCore = typedUnadapted(lhs, AssignProto)
def lhs1 = typed(untpd.TypedSplice(lhsCore))
def canAssign(sym: Symbol) = // allow assignments from the primary constructor to class fields
sym.is(Mutable, butNot = Accessor) ||
@@ -507,6 +514,12 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case _ =>
reassignmentToVal
}
+ case TryDynamicCallType =>
+ tree match {
+ case Assign(Select(qual, name), rhs) if !isDynamicMethod(name) =>
+ typedDynamicAssign(qual, name, rhs, pt)
+ case _ => reassignmentToVal
+ }
case tpe =>
reassignmentToVal
}
@@ -1091,7 +1104,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
.withType(dummy.nonMemberTermRef)
checkVariance(impl1)
if (!cls.is(AbstractOrTrait) && !ctx.isAfterTyper) checkRealizableBounds(cls.typeRef, cdef.pos)
- assignType(cpy.TypeDef(cdef)(name, impl1, Nil), cls)
+ val cdef1 = assignType(cpy.TypeDef(cdef)(name, impl1, Nil), cls)
+ if (ctx.phase.isTyper && cdef1.tpe.derivesFrom(defn.DynamicClass) && !ctx.dynamicsEnabled) {
+ val isRequired = parents1.exists(_.tpe.isRef(defn.DynamicClass))
+ ctx.featureWarning(nme.dynamics.toString, "extension of type scala.Dynamic", isScala2Feature = true,
+ cls, isRequired, cdef.pos)
+ }
+ cdef1
// todo later: check that
// 1. If class is non-abstract, it is instantiatable:
@@ -1686,7 +1705,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
tree match {
case _: MemberDef | _: PackageDef | _: Import | _: WithoutTypeOrPos[_] => tree
case _ => tree.tpe.widen match {
- case ErrorType =>
+ case _: ErrorType =>
tree
case ref: TermRef =>
pt match {
diff --git a/tests/neg/dynamicApplyDynamicTest1.scala b/tests/neg/dynamicApplyDynamicTest1.scala
new file mode 100644
index 000000000..90f4493eb
--- /dev/null
+++ b/tests/neg/dynamicApplyDynamicTest1.scala
@@ -0,0 +1,7 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic
+
+object DynamicTest {
+ new Foo().bazApply() // error
+}
diff --git a/tests/neg/dynamicApplyDynamicTest2.scala b/tests/neg/dynamicApplyDynamicTest2.scala
new file mode 100644
index 000000000..aa0e44405
--- /dev/null
+++ b/tests/neg/dynamicApplyDynamicTest2.scala
@@ -0,0 +1,7 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic
+
+object DynamicTest {
+ new Foo().bazApply("abc", 1) // error
+}
diff --git a/tests/neg/dynamicApplyDynamicTest3.scala b/tests/neg/dynamicApplyDynamicTest3.scala
new file mode 100644
index 000000000..61d3c9677
--- /dev/null
+++ b/tests/neg/dynamicApplyDynamicTest3.scala
@@ -0,0 +1,7 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic
+
+object DynamicTest {
+ new Foo().bazApply _ // error // error
+}
diff --git a/tests/neg/dynamicApplyDynamicTest4.scala b/tests/neg/dynamicApplyDynamicTest4.scala
new file mode 100644
index 000000000..4573bd08d
--- /dev/null
+++ b/tests/neg/dynamicApplyDynamicTest4.scala
@@ -0,0 +1,10 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic {
+ def selectDynamic(name: String): String = ???
+ def applyDynamicNamed(name: String)(args: Any*): String = ???
+}
+
+object DynamicTest {
+ new Foo().bazApply() // error
+}
diff --git a/tests/neg/dynamicApplyDynamicTest5.scala b/tests/neg/dynamicApplyDynamicTest5.scala
new file mode 100644
index 000000000..d1d9cdd69
--- /dev/null
+++ b/tests/neg/dynamicApplyDynamicTest5.scala
@@ -0,0 +1,9 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic {
+ def applyDynamic(name: String)(args: String*): String = ???
+}
+
+object DynamicTest {
+ new Foo().bazApply(1, 2, 3) // error // error // error
+}
diff --git a/tests/neg/dynamicApplyDynamicTest6.scala b/tests/neg/dynamicApplyDynamicTest6.scala
new file mode 100644
index 000000000..865858ff3
--- /dev/null
+++ b/tests/neg/dynamicApplyDynamicTest6.scala
@@ -0,0 +1,9 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic {
+ def applyDynamic(name: String)(args: String*): String = ???
+}
+
+object DynamicTest {
+ def test: Int = new Foo().bazApply() // error
+}
diff --git a/tests/neg/dynamicApplyDynamicTest7.scala b/tests/neg/dynamicApplyDynamicTest7.scala
new file mode 100644
index 000000000..7985dd09e
--- /dev/null
+++ b/tests/neg/dynamicApplyDynamicTest7.scala
@@ -0,0 +1,9 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic {
+ def applyDynamic(name: Int)(args: String*): String = ???
+}
+
+object DynamicTest {
+ def test: String = new Foo().bazApply() // error
+}
diff --git a/tests/neg/dynamicApplyDynamicTest8.scala b/tests/neg/dynamicApplyDynamicTest8.scala
new file mode 100644
index 000000000..ee8192d76
--- /dev/null
+++ b/tests/neg/dynamicApplyDynamicTest8.scala
@@ -0,0 +1,9 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic {
+ def applyDynamicNamed(name: String)(args: (String, Int)*): String = ???
+}
+
+object DynamicTest {
+ def test: String = new Foo().bazApply("1" -> 2) // error
+}
diff --git a/tests/neg/dynamicApplyDynamicTest9.scala b/tests/neg/dynamicApplyDynamicTest9.scala
new file mode 100644
index 000000000..bc4f94d5e
--- /dev/null
+++ b/tests/neg/dynamicApplyDynamicTest9.scala
@@ -0,0 +1,9 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic {
+ def applyDynamicNamed(name: String)(args: (String, Int)*): String = ???
+}
+
+object DynamicTest {
+ new Foo().applyDynamic("bar")("1" -> 2) // error
+}
diff --git a/tests/neg/dynamicApplyNamedDynamicTest1.scala b/tests/neg/dynamicApplyNamedDynamicTest1.scala
new file mode 100644
index 000000000..a51cd2c7b
--- /dev/null
+++ b/tests/neg/dynamicApplyNamedDynamicTest1.scala
@@ -0,0 +1,7 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic
+
+object DynamicTest {
+ new Foo().bazApply(a = "abc", b = 1) // error
+}
diff --git a/tests/neg/dynamicApplyNamedDynamicTest2.scala b/tests/neg/dynamicApplyNamedDynamicTest2.scala
new file mode 100644
index 000000000..ca801e77c
--- /dev/null
+++ b/tests/neg/dynamicApplyNamedDynamicTest2.scala
@@ -0,0 +1,10 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic {
+ def selectDynamic(name: String): Array[String] = ???
+ def applyDynamic(name: String)(args: Any*): String = ???
+}
+
+object DynamicTest {
+ new Foo().bazApply(a = "abc", b = 1) // error
+}
diff --git a/tests/neg/dynamicApplyNamedDynamicTest3.scala b/tests/neg/dynamicApplyNamedDynamicTest3.scala
new file mode 100644
index 000000000..b8ec41c6d
--- /dev/null
+++ b/tests/neg/dynamicApplyNamedDynamicTest3.scala
@@ -0,0 +1,7 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic
+
+object DynamicTest {
+ new Foo().bazApply("abc", b = 1) // error
+}
diff --git a/tests/neg/dynamicApplyNamedDynamicTest4.scala b/tests/neg/dynamicApplyNamedDynamicTest4.scala
new file mode 100644
index 000000000..983cbc599
--- /dev/null
+++ b/tests/neg/dynamicApplyNamedDynamicTest4.scala
@@ -0,0 +1,7 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic
+
+object DynamicTest {
+ new Foo().bazApply("abc", 4, b = 1, b = "bcd") // error
+}
diff --git a/tests/neg/dynamicApplyNamedDynamicTest5.scala b/tests/neg/dynamicApplyNamedDynamicTest5.scala
new file mode 100644
index 000000000..3a48d38d8
--- /dev/null
+++ b/tests/neg/dynamicApplyNamedDynamicTest5.scala
@@ -0,0 +1,9 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic {
+ def applyDynamicNamed(name: String)(args: String*): String = ???
+}
+
+object DynamicTest {
+ new Foo().bazApply("abc", 4, b = 1, b = "bcd") // error // error // error // error
+}
diff --git a/tests/neg/dynamicApplyNamedDynamicTest6.scala b/tests/neg/dynamicApplyNamedDynamicTest6.scala
new file mode 100644
index 000000000..4c39842af
--- /dev/null
+++ b/tests/neg/dynamicApplyNamedDynamicTest6.scala
@@ -0,0 +1,9 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic {
+ def applyDynamicNamed(name: Int)(args: Any*): String = ???
+}
+
+object DynamicTest {
+ new Foo().bazApply("abc", 4, b = 1, b = "bcd") // error
+}
diff --git a/tests/neg/dynamicApplyNamedDynamicTest7.scala b/tests/neg/dynamicApplyNamedDynamicTest7.scala
new file mode 100644
index 000000000..c29339f6d
--- /dev/null
+++ b/tests/neg/dynamicApplyNamedDynamicTest7.scala
@@ -0,0 +1,9 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic {
+ def applyDynamicNamed(name: String)(args: Any*): String = ???
+}
+
+object DynamicTest {
+ def test: Int = new Foo().bazApply("abc", 4, b = 1, b = "bcd") // error
+}
diff --git a/tests/neg/dynamicApplyNamedDynamicTest8.scala b/tests/neg/dynamicApplyNamedDynamicTest8.scala
new file mode 100644
index 000000000..e07b04ca1
--- /dev/null
+++ b/tests/neg/dynamicApplyNamedDynamicTest8.scala
@@ -0,0 +1,9 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic {
+ def applyDynamic(name: String)(args: Any*): String = ???
+}
+
+object DynamicTest {
+ new Foo().applyDynamicNamed("abc")() // error
+}
diff --git a/tests/neg/dynamicDynamicImplicitsTest1.scala b/tests/neg/dynamicDynamicImplicitsTest1.scala
new file mode 100644
index 000000000..b044336aa
--- /dev/null
+++ b/tests/neg/dynamicDynamicImplicitsTest1.scala
@@ -0,0 +1,13 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic {
+ def selectDynamic(name: String): String = ???
+}
+
+object DynamicTest {
+ implicit class Bar(foo: Foo) {
+ def bazSelect: Int = ???
+ }
+
+ def baz: String = new Foo().bazSelect // error
+}
diff --git a/tests/neg/dynamicDynamicImplicitsTest2.scala b/tests/neg/dynamicDynamicImplicitsTest2.scala
new file mode 100644
index 000000000..3229d80e6
--- /dev/null
+++ b/tests/neg/dynamicDynamicImplicitsTest2.scala
@@ -0,0 +1,13 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic {
+ def applyDynamic(name: String)(args: Any*): String = ???
+}
+
+object DynamicTest {
+ implicit class Bar(foo: Foo) {
+ def bazApply: Int = ???
+ }
+
+ def baz: String = new Foo().bazApply("") // error
+}
diff --git a/tests/neg/dynamicDynamicImplicitsTest3.scala b/tests/neg/dynamicDynamicImplicitsTest3.scala
new file mode 100644
index 000000000..6c26b9500
--- /dev/null
+++ b/tests/neg/dynamicDynamicImplicitsTest3.scala
@@ -0,0 +1,13 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic {
+ def applytDynamicNamed(name: String)(args: Any*): String = ???
+}
+
+object DynamicTest {
+ implicit class Bar(foo: Foo) {
+ def bazApply: Int = ???
+ }
+
+ def baz: String = new Foo().bazApply(a = "") // error
+}
diff --git a/tests/neg/dynamicNoImport.scala b/tests/neg/dynamicNoImport.scala
new file mode 100644
index 000000000..c009d91a4
--- /dev/null
+++ b/tests/neg/dynamicNoImport.scala
@@ -0,0 +1,13 @@
+class Foo extends scala.Dynamic // error
+trait Bar extends scala.Dynamic // error
+object Baz extends scala.Dynamic // error
+
+package A {
+ import scala.language.dynamics
+ package B {
+ import scala.language.{ dynamics => _ }
+ class Foo extends scala.Dynamic // error
+ trait Bar extends scala.Dynamic // error
+ object Baz extends scala.Dynamic // error
+ }
+}
diff --git a/tests/neg/dynamicSelectDynamicTest1.scala b/tests/neg/dynamicSelectDynamicTest1.scala
new file mode 100644
index 000000000..a45671ad1
--- /dev/null
+++ b/tests/neg/dynamicSelectDynamicTest1.scala
@@ -0,0 +1,10 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic
+
+object DynamicTest {
+ def testSelect = {
+ new Foo().bazSelect // error
+ ()
+ }
+}
diff --git a/tests/neg/dynamicSelectDynamicTest2.scala b/tests/neg/dynamicSelectDynamicTest2.scala
new file mode 100644
index 000000000..9ba19a14d
--- /dev/null
+++ b/tests/neg/dynamicSelectDynamicTest2.scala
@@ -0,0 +1,7 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic
+
+object DynamicTest {
+ def testSelect = new Foo().bazSelect // error
+}
diff --git a/tests/neg/dynamicSelectDynamicTest3.scala b/tests/neg/dynamicSelectDynamicTest3.scala
new file mode 100644
index 000000000..acdad7033
--- /dev/null
+++ b/tests/neg/dynamicSelectDynamicTest3.scala
@@ -0,0 +1,7 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic
+
+object DynamicTest {
+ new Foo().bazSelect // error
+}
diff --git a/tests/neg/dynamicSelectDynamicTest4.scala b/tests/neg/dynamicSelectDynamicTest4.scala
new file mode 100644
index 000000000..7295a43fd
--- /dev/null
+++ b/tests/neg/dynamicSelectDynamicTest4.scala
@@ -0,0 +1,11 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic {
+ def selectDynamic(name: String): String = ???
+}
+
+object DynamicTest {
+ def testSelect: Int = {
+ new Foo().bazSelect // error
+ }
+}
diff --git a/tests/neg/dynamicSelectDynamicTest5.scala b/tests/neg/dynamicSelectDynamicTest5.scala
new file mode 100644
index 000000000..17836efe4
--- /dev/null
+++ b/tests/neg/dynamicSelectDynamicTest5.scala
@@ -0,0 +1,7 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic
+
+object DynamicTest {
+ new Foo().bazSelectUpdate(6) = "a" // error
+}
diff --git a/tests/neg/dynamicSelectDynamicTest6.scala b/tests/neg/dynamicSelectDynamicTest6.scala
new file mode 100644
index 000000000..8df9bf492
--- /dev/null
+++ b/tests/neg/dynamicSelectDynamicTest6.scala
@@ -0,0 +1,11 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic {
+ def selectDynamic(name: Int): String = ???
+}
+
+object DynamicTest {
+ def testSelect: String = {
+ new Foo().bazSelect // error
+ }
+}
diff --git a/tests/neg/dynamicSelectDynamicTest7.scala b/tests/neg/dynamicSelectDynamicTest7.scala
new file mode 100644
index 000000000..4b4207844
--- /dev/null
+++ b/tests/neg/dynamicSelectDynamicTest7.scala
@@ -0,0 +1,9 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic {
+ def applyDynamic(name: String)(args: Any*): String = ???
+}
+
+object DynamicTest {
+ new Foo().selectDynamic("bar") // error
+}
diff --git a/tests/neg/dynamicUpdateDynamicTest1.scala b/tests/neg/dynamicUpdateDynamicTest1.scala
new file mode 100644
index 000000000..8eba9fe60
--- /dev/null
+++ b/tests/neg/dynamicUpdateDynamicTest1.scala
@@ -0,0 +1,7 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic
+
+object DynamicTest {
+ new Foo().bazUpdate = 42 // error
+}
diff --git a/tests/neg/dynamicUpdateDynamicTest2.scala b/tests/neg/dynamicUpdateDynamicTest2.scala
new file mode 100644
index 000000000..8bb8a9537
--- /dev/null
+++ b/tests/neg/dynamicUpdateDynamicTest2.scala
@@ -0,0 +1,9 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic {
+ def updateDynamic(name: String)(value: String): Unit = ???
+}
+
+object DynamicTest {
+ new Foo().bazUpdate = 42 // error
+}
diff --git a/tests/neg/dynamicUpdateDynamicTest3.scala b/tests/neg/dynamicUpdateDynamicTest3.scala
new file mode 100644
index 000000000..22008dda4
--- /dev/null
+++ b/tests/neg/dynamicUpdateDynamicTest3.scala
@@ -0,0 +1,9 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic {
+ def updateDynamic(name: String)(value: Int): Unit = ???
+}
+
+object DynamicTest {
+ def test: Int = new Foo().bazUpdate = 42 // error
+}
diff --git a/tests/neg/dynamicUpdateDynamicTest4.scala b/tests/neg/dynamicUpdateDynamicTest4.scala
new file mode 100644
index 000000000..3cdd32b3c
--- /dev/null
+++ b/tests/neg/dynamicUpdateDynamicTest4.scala
@@ -0,0 +1,9 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic {
+ def updateDynamic(name: Int)(value: Int): Unit = ???
+}
+
+object DynamicTest {
+ new Foo().bazUpdate = 42 // error
+}
diff --git a/tests/pos/dynamicExtendsNoImport.scala b/tests/pos/dynamicExtendsNoImport.scala
new file mode 100644
index 000000000..51f3719c5
--- /dev/null
+++ b/tests/pos/dynamicExtendsNoImport.scala
@@ -0,0 +1,7 @@
+
+package foo {
+ import scala.language.dynamics
+ class Bar extends scala.Dynamic
+}
+
+class Baz extends foo.Bar
diff --git a/tests/run/dynamicDynamicTests.scala b/tests/run/dynamicDynamicTests.scala
new file mode 100644
index 000000000..3f8da8298
--- /dev/null
+++ b/tests/run/dynamicDynamicTests.scala
@@ -0,0 +1,164 @@
+import scala.language.dynamics
+
+class Foo extends scala.Dynamic {
+ def selectDynamic(name: String): String = "selectDynamic(" + name + ")"
+ def applyDynamic(name: String)(args: Any*): String = "applyDynamic(" + name + ")" + args.mkString("(", ", ", ")")
+ def applyDynamicNamed(name: String)(args: (String, Any)*): String = "applyDynamicNamed(" + name + ")" + args.mkString("(", ", ", ")")
+ def updateDynamic(name: String)(value: Any): String = "updateDynamic(" + name + ")(" + value + ")"
+}
+
+class Bar(self: String) extends scala.Dynamic {
+ def selectDynamic(name: String): Bar = new Bar(self + ".selectDynamic(" + name + ")")
+ def applyDynamic(name: String)(args: Any*): Bar = new Bar(self + ".applyDynamic(" + name + ")" + args.mkString("(", ", ", ")"))
+ def applyDynamicNamed(name: String)(args: (String, Any)*): Bar = new Bar(self + ".applyDynamicNamed(" + name + ")" + args.mkString("(", ", ", ")"))
+ def updateDynamic(name: String)(value: Any): Bar = new Bar(self + ".updateDynamic(" + name + ")(" + value + ")")
+ def update(key: Int, value: Int): Bar = new Bar(self + ".update(" + key + ", " + value + ")")
+ override def toString = self
+}
+
+class Baz extends scala.Dynamic {
+ def selectDynamic(name: String): String = "selectDynamic(" + name + ")"
+ def applyDynamic(name: String)(args: String*): String = "applyDynamic(" + name + ")" + args.mkString("(", ", ", ")")
+ def applyDynamicNamed(name: String)(args: (String, Any)*): String = "applyDynamicNamed(" + name + ")" + args.mkString("(", ", ", ")")
+ def updateDynamic(name: String)(value: String): String = "updateDynamic(" + name + ")(" + value + ")"
+}
+
+object Test {
+ implicit class StringUpdater(str: String) {
+ def update(name: String, v: String) = s"$str.update(" + name + ", " + v + ")"
+ }
+
+ var failed = false
+
+ def assertEquals(expected: String, actual: Any): Unit = {
+ if (expected != actual.toString) {
+ println("Error: expected <" + expected + "> but was <" + actual.toString + ">")
+ failed = true
+ }
+ }
+
+ def main(args: Array[String]): Unit = {
+ runFooTests1()
+ runFooTests2()
+ runBarTests()
+ runBazTests()
+ assert(!failed)
+ }
+
+ /** Test the basics of the transfomation rules. */
+ def runFooTests1() = {
+ val foo = new Foo
+
+ assertEquals("selectDynamic(bazSelect)", foo.bazSelect)
+
+ assertEquals("applyDynamic(bazApply)()", foo.bazApply())
+ assertEquals("applyDynamic(bazApply)(1)", foo.bazApply(1))
+ assertEquals("applyDynamic(bazApply)(1, 2, 3)", foo.bazApply(1, 2, 3))
+ assertEquals("applyDynamic(bazApply)(1, 2, a)", foo.bazApply(1, 2, "a"))
+ assertEquals("applyDynamic(bazApply)(1, 2, a)", foo.bazApply(List(1, 2, "a"): _*))
+
+ assertEquals("applyDynamicNamed(bazApply)((a,1))", foo.bazApply(a = 1))
+ assertEquals("applyDynamicNamed(bazApply)((a,1), (b,2))", foo.bazApply(a = 1, b = 2))
+ assertEquals("applyDynamicNamed(bazApply)((a,1), (,0))", foo.bazApply(a = 1, 0))
+ assertEquals("applyDynamicNamed(bazApply)((a,1), (a,5))", foo.bazApply(a = 1, a = 5))
+ assertEquals("applyDynamicNamed(bazApply)((,d), (a,1), (,5), (a,c))", foo.bazApply("d", a = 1, 5, a = 'c'))
+
+ assertEquals("updateDynamic(bazUpdate)(abc)", foo.bazUpdate = "abc")
+
+ assertEquals("selectDynamic(bazSelectUpdate).update(key, value)", foo.bazSelectUpdate("key") = "value")
+ }
+
+ /** Test implicit conversions kick in before dynamic calls. */
+ def runFooTests2() = {
+ implicit class Bar(foo: Foo) {
+ def bazSelect: String = "Bar.bazSelect"
+ def bazApply(args: Any*): String = args.mkString("Bar.bazApply(", ", ", ")")
+ def bazApplyNamed(a: Int = -1, b: String = "-1"): String = "Bar.bazApplyNamed(" + a + ", " + b + ")"
+ }
+
+ val foo = new Foo
+
+ assertEquals("Bar.bazSelect", foo.bazSelect)
+
+ assertEquals("Bar.bazApply()", foo.bazApply())
+ assertEquals("Bar.bazApply(1)", foo.bazApply(1))
+ assertEquals("Bar.bazApply(1, 2, 3)", foo.bazApply(1, 2, 3))
+ assertEquals("Bar.bazApply(1, 2, a)", foo.bazApply(1, 2, "a"))
+
+ assertEquals("Bar.bazApplyNamed(1, -1)", foo.bazApplyNamed(a = 1))
+ assertEquals("Bar.bazApplyNamed(1, 2)", foo.bazApplyNamed(a = 1, b = "2"))
+ assertEquals("Bar.bazApplyNamed(1, abc)", foo.bazApplyNamed(a = 1, "abc"))
+
+ assertEquals("selectDynamic(bazSelectUpdate).update(key, value)", foo.bazSelectUpdate("key") = "value")
+ }
+
+ /** Test cains of dynamic calls. */
+ def runBarTests() = {
+ val bar = new Bar("bar")
+
+ // dynamics combined with themselfs
+ assertEquals("bar.selectDynamic(select1).selectDynamic(select2).selectDynamic(select3)",
+ bar.select1.select2.select3)
+ assertEquals("bar.applyDynamic(apply1)().applyDynamic(apply2)().applyDynamic(apply3)()",
+ bar.apply1().apply2().apply3())
+ assertEquals("bar.applyDynamic(apply1)(1).applyDynamic(apply2)(1, 2).applyDynamic(apply3)(1, 2, 3)",
+ bar.apply1(1).apply2(1, 2).apply3(1, 2, 3))
+ assertEquals("bar.applyDynamicNamed(apply1)((a,1)).applyDynamicNamed(apply2)((,1), (b,2))",
+ bar.apply1(a = 1).apply2(1, b = 2))
+ assertEquals("bar.updateDynamic(update1)(1).updateDynamic(update2)(4)", (bar.update1 = 1).update2 = 4)
+ assertEquals("bar.selectDynamic(update1).update(1, 2).selectDynamic(update2).update(3, 4)",
+ (bar.update1(1) = 2).update2(3) = 4)
+
+ // selectDynamic combined with every other dynamic
+ assertEquals("bar.applyDynamic(apply1)().selectDynamic(select1)", bar.apply1().select1)
+ assertEquals("bar.selectDynamic(select1).applyDynamic(apply2)()", bar.select1.apply2())
+ assertEquals("bar.applyDynamicNamed(apply1)((a,1)).selectDynamic(select1)", bar.apply1(a = 1).select1)
+ assertEquals("bar.selectDynamic(select1).applyDynamicNamed(apply1)((a,1))", bar.select1.apply1(a = 1))
+ assertEquals("bar.updateDynamic(update1)(1).selectDynamic(select1)", (bar.update1 = 1).select1)
+ assertEquals("bar.selectDynamic(select1).updateDynamic(update1)(1)", bar.select1.update1 = 1)
+ assertEquals("bar.selectDynamic(update1).update(0, 1).selectDynamic(select1)", (bar.update1(0) = 1).select1)
+ assertEquals("bar.selectDynamic(select1).selectDynamic(update1).update(0, 1)", bar.select1.update1(0) = 1)
+
+ // applyDynamic combined with every remaninig dynamic
+ assertEquals("bar.applyDynamic(apply1)().applyDynamicNamed(apply2)((a,1))", bar.apply1().apply2(a = 1))
+ assertEquals("bar.applyDynamicNamed(apply1)((a,1)).applyDynamic(apply2)()", bar.apply1(a = 1).apply2())
+ assertEquals("bar.applyDynamic(apply1)().updateDynamic(update1)(1)", bar.apply1().update1 = 1)
+ assertEquals("bar.updateDynamic(update1)(1).applyDynamic(apply1)()", (bar.update1 = 1).apply1())
+ assertEquals("bar.applyDynamic(apply1)().selectDynamic(update1).update(0, 1)", bar.apply1().update1(0) = 1)
+ assertEquals("bar.selectDynamic(update1).update(0, 1).applyDynamic(apply1)()", (bar.update1(0) = 1).apply1())
+
+ // applyDynamicNamed combined with every remaninig dynamic
+ assertEquals("bar.applyDynamicNamed(apply1)((a,1)).updateDynamic(update1)(1)", bar.apply1(a = 1).update1 = 1)
+ assertEquals("bar.updateDynamic(update1)(1).applyDynamicNamed(apply1)((a,1))", (bar.update1 = 1).apply1(a = 1))
+ assertEquals("bar.applyDynamicNamed(apply1)((a,1)).selectDynamic(update1).update(0, 1)", bar.apply1(a = 1).update1(0) = 1)
+ assertEquals("bar.selectDynamic(update1).update(0, 1).applyDynamicNamed(apply1)((a,1))", (bar.update1(0) = 1).apply1(a = 1))
+
+ // updateDynamic combined selectDynamic(_).update(_, _)
+ assertEquals("bar.updateDynamic(update1)(1).selectDynamic(update2).update(0, 1)", (bar.update1 = 1).update2(0) = 1)
+ assertEquals("bar.selectDynamic(update1).update(0, 1).updateDynamic(update2)(1)", (bar.update1(0) = 1).update2 = 1)
+ }
+
+ /** Test implicit conversion in the arguments of the dynamic call. */
+ def runBazTests() = {
+ implicit def intToString(n: Int): String = n.toString
+
+ val baz = new Baz
+
+ assertEquals("applyDynamic(bazApply)()", baz.bazApply())
+ assertEquals("applyDynamic(bazApply)(1)", baz.bazApply(1))
+ assertEquals("applyDynamic(bazApply)(1, 2, 3)", baz.bazApply(1, 2, 3))
+ assertEquals("applyDynamic(bazApply)(1, 2, a)", baz.bazApply(1, 2, "a"))
+
+ assertEquals("applyDynamicNamed(bazApply)((a,1))", baz.bazApply(a = 1))
+ assertEquals("applyDynamicNamed(bazApply)((a,1), (b,2))", baz.bazApply(a = 1, b = 2))
+ assertEquals("applyDynamicNamed(bazApply)((a,1), (,0))", baz.bazApply(a = 1, 0))
+ assertEquals("applyDynamicNamed(bazApply)((a,1), (a,5))", baz.bazApply(a = 1, a = 5))
+ assertEquals("applyDynamicNamed(bazApply)((,4), (a,1), (,5), (a,9))", baz.bazApply(4, a = 1, 5, a = 9))
+
+ assertEquals("updateDynamic(bazUpdate)(10)", baz.bazUpdate = 10)
+
+ assertEquals("selectDynamic(bazSelectUpdate).update(key, 10)", baz.bazSelectUpdate("key") = 10)
+ assertEquals("selectDynamic(bazSelectUpdate).update(7, value)", baz.bazSelectUpdate(7) = "value")
+ assertEquals("selectDynamic(bazSelectUpdate).update(7, 10)", baz.bazSelectUpdate(7) = 10)
+ }
+}