diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 38f5dbaf4e00..ad11d90a26ac 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -913,7 +913,23 @@ trait Applications extends Compatibility { fun1.tpe match { case err: ErrorType => cpy.Apply(tree)(fun1, proto.typedArgs()).withType(err) - case TryDynamicCallType => typedDynamicApply(tree, pt) + case TryDynamicCallType => + val isInsertedApply = fun1 match { + case Select(_, nme.apply) => fun1.span.isSynthetic + case TypeApply(sel @ Select(_, nme.apply), _) => sel.span.isSynthetic + /* TODO Get rid of this case. It is still syntax-based, therefore unreliable. + * It is necessary for things like `someDynamic[T](...)`, because in that case, + * somehow typedFunPart returns a tree that was typed as `TryDynamicCallType`, + * so clearly with the view that an apply insertion was necessary, but doesn't + * actually insert the apply! + * This is probably something wrong in apply insertion, but I (@sjrd) am out of + * my depth there. + * In the meantime, this makes tests pass. + */ + case TypeApply(fun, _) => !fun.isInstanceOf[Select] + case _ => false + } + typedDynamicApply(tree, isInsertedApply, pt) case _ => if (originalProto.isDropped) fun1 else if (fun1.symbol == defn.Compiletime_summonFrom) diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index 0d309c42cad1..2af321fa3f61 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -61,7 +61,7 @@ trait Dynamic { * foo.bar(x = bazX, y = bazY, baz, ...) ~~> foo.applyDynamicNamed("bar")(("x", bazX), ("y", bazY), ("", baz), ...) * foo.bar[T0, ...](x = bazX, y = bazY, baz, ...) ~~> foo.applyDynamicNamed[T0, ...]("bar")(("x", bazX), ("y", bazY), ("", baz), ...) */ - def typedDynamicApply(tree: untpd.Apply, pt: Type)(using Context): Tree = { + def typedDynamicApply(tree: untpd.Apply, isInsertedApply: Boolean, pt: Type)(using Context): Tree = { def typedDynamicApply(qual: untpd.Tree, name: Name, selSpan: Span, targs: List[untpd.Tree]): Tree = { def isNamedArg(arg: untpd.Tree): Boolean = arg match { case NamedArg(_, _) => true; case _ => false } val args = tree.args @@ -79,15 +79,22 @@ trait Dynamic { } } - tree.fun match { - case sel @ Select(qual, name) if !isDynamicMethod(name) => - typedDynamicApply(qual, name, sel.span, Nil) - case TypeApply(sel @ Select(qual, name), targs) if !isDynamicMethod(name) => - typedDynamicApply(qual, name, sel.span, targs) - case TypeApply(fun, targs) => - typedDynamicApply(fun, nme.apply, fun.span, targs) - case fun => - typedDynamicApply(fun, nme.apply, fun.span, Nil) + if (isInsertedApply) { + tree.fun match { + case TypeApply(fun, targs) => + typedDynamicApply(fun, nme.apply, fun.span, targs) + case fun => + typedDynamicApply(fun, nme.apply, fun.span, Nil) + } + } else { + tree.fun match { + case sel @ Select(qual, name) if !isDynamicMethod(name) => + typedDynamicApply(qual, name, sel.span, Nil) + case TypeApply(sel @ Select(qual, name), targs) if !isDynamicMethod(name) => + typedDynamicApply(qual, name, sel.span, targs) + case _ => + errorTree(tree, em"Dynamic insertion not applicable") + } } } diff --git a/project/Build.scala b/project/Build.scala index b94e8f57976b..a23eece75c75 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1068,7 +1068,7 @@ object Build { -- "InteroperabilityTest.scala" // various compile errors -- "OptimizerTest.scala" // compile errors: false + string and () + string -- "ReflectionTest.scala" // tests fail - -- "RegressionJSTest.scala" // compile error with js.Dynamic.literal + -- "RegressionJSTest.scala" // non-native JS classes -- "RuntimeTypesTest.scala" // compile errors: no ClassTag for Null and Nothing )).get @@ -1081,23 +1081,22 @@ object Build { ++ (dir / "js/src/test/scala/org/scalajs/testsuite/jsinterop" ** (("*.scala": FileFilter) -- "AsyncTest.scala" // needs PromiseMock.scala - -- "DynamicTest.scala" // compile error with js.Dynamic.literal + -- "DynamicTest.scala" // one test requires JS exports, all other tests pass -- "ExportsTest.scala" // JS exports -- "FunctionTest.scala" // IR checking errors -- "IterableTest.scala" // non-native JS classes -- "JSExportStaticTest.scala" // JS exports - -- "JSNameTest.scala" // compile error with js.Dynamic.literal -- "JSNativeInPackage.scala" // IR checking errors -- "JSOptionalTest.scala" // non-native JS classes - -- "JSSymbolTest.scala" // compile error with js.Dynamic.literal - -- "MiscInteropTest.scala" // compile error with js.Dynamic.literal + -- "JSSymbolTest.scala" // non-native JS classes + -- "MiscInteropTest.scala" // non-native JS classes -- "ModulesWithGlobalFallbackTest.scala" // non-native JS classes -- "NestedJSClassTest.scala" // non-native JS classes -- "NonNativeJSTypeTest.scala" // non-native JS classes -- "PromiseMock.scala" // non-native JS classes - -- "SpecialTest.scala" // compile error with js.Dynamic.literal + -- "SpecialTest.scala" // assertion error in ExpandSAMs -- "SymbolTest.scala" // IR checking errors - -- "ThisFunctionTest.scala" // compile error with js.Dynamic.literal + -- "ThisFunctionTest.scala" // assertion error in ExpandSAMs -- "UndefOrTest.scala" // StackOverflow in the compiler )).get @@ -1118,10 +1117,7 @@ object Build { )).get ++ (dir / "js/src/test/scala/org/scalajs/testsuite/niobuffer" ** "*.scala").get - - ++ (dir / "js/src/test/scala/org/scalajs/testsuite/scalalib" ** (("*.scala": FileFilter) - -- "ScalaRunTimeJSTest.scala" // compile error with js.Dynamic.literal - )).get + ++ (dir / "js/src/test/scala/org/scalajs/testsuite/scalalib" ** "*.scala").get ++ (dir / "js/src/test/scala/org/scalajs/testsuite/typedarray" ** (("*.scala": FileFilter) -- "TypedArrayTest.scala" // assertion error in ExpandSAMs diff --git a/tests/run/dynamicDynamicTests.scala b/tests/run/dynamicDynamicTests.scala index 51abade80856..b2bbb529937b 100644 --- a/tests/run/dynamicDynamicTests.scala +++ b/tests/run/dynamicDynamicTests.scala @@ -49,6 +49,7 @@ object Test { def main(args: Array[String]): Unit = { runFooTests1() runFooTests2() + runFooTests3() runBarTests() runBazTests() runQuxTests() @@ -102,6 +103,63 @@ object Test { assertEquals("selectDynamic(bazSelectUpdate).update(key, value)", foo.bazSelectUpdate("key") = "value") } + /** Test apply insertions kick in before dynamic calls. */ + def runFooTests3() = { + val foo = new Foo + + assertEquals("applyDynamic(apply)()", foo()) + assertEquals("applyDynamic(apply)(1)", foo(1)) + assertEquals("applyDynamic(apply)(1, 2, 3)", foo(1, 2, 3)) + assertEquals("applyDynamic(apply)(1, 2, a)", foo(1, 2, "a")) + assertEquals("applyDynamic(apply)(1, 2, a)", foo(List(1, 2, "a"): _*)) + + assertEquals("applyDynamicNamed(apply)((a,1))", foo(a = 1)) + assertEquals("applyDynamicNamed(apply)((a,1), (b,2))", foo(a = 1, b = 2)) + assertEquals("applyDynamicNamed(apply)((a,1), (,0))", foo(a = 1, 0)) + assertEquals("applyDynamicNamed(apply)((a,1), (a,5))", foo(a = 1, a = 5)) + assertEquals("applyDynamicNamed(apply)((,d), (a,1), (,5), (a,c))", foo("d", a = 1, 5, a = 'c')) + + assertEquals("applyDynamic(apply)()", foo.apply()) + assertEquals("applyDynamic(apply)(1)", foo.apply(1)) + assertEquals("applyDynamic(apply)(1, 2, 3)", foo.apply(1, 2, 3)) + assertEquals("applyDynamic(apply)(1, 2, a)", foo.apply(1, 2, "a")) + assertEquals("applyDynamic(apply)(1, 2, a)", foo.apply(List(1, 2, "a"): _*)) + + assertEquals("applyDynamicNamed(apply)((a,1))", foo.apply(a = 1)) + assertEquals("applyDynamicNamed(apply)((a,1), (b,2))", foo.apply(a = 1, b = 2)) + assertEquals("applyDynamicNamed(apply)((a,1), (,0))", foo.apply(a = 1, 0)) + assertEquals("applyDynamicNamed(apply)((a,1), (a,5))", foo.apply(a = 1, a = 5)) + assertEquals("applyDynamicNamed(apply)((,d), (a,1), (,5), (a,c))", foo.apply("d", a = 1, 5, a = 'c')) + + object bar { + val foo: Foo = new Foo + } + + assertEquals("applyDynamic(apply)()", bar.foo()) + assertEquals("applyDynamic(apply)(1)", bar.foo(1)) + assertEquals("applyDynamic(apply)(1, 2, 3)", bar.foo(1, 2, 3)) + assertEquals("applyDynamic(apply)(1, 2, a)", bar.foo(1, 2, "a")) + assertEquals("applyDynamic(apply)(1, 2, a)", bar.foo(List(1, 2, "a"): _*)) + + assertEquals("applyDynamicNamed(apply)((a,1))", bar.foo(a = 1)) + assertEquals("applyDynamicNamed(apply)((a,1), (b,2))", bar.foo(a = 1, b = 2)) + assertEquals("applyDynamicNamed(apply)((a,1), (,0))", bar.foo(a = 1, 0)) + assertEquals("applyDynamicNamed(apply)((a,1), (a,5))", bar.foo(a = 1, a = 5)) + assertEquals("applyDynamicNamed(apply)((,d), (a,1), (,5), (a,c))", bar.foo("d", a = 1, 5, a = 'c')) + + assertEquals("applyDynamic(apply)()", bar.foo.apply()) + assertEquals("applyDynamic(apply)(1)", bar.foo.apply(1)) + assertEquals("applyDynamic(apply)(1, 2, 3)", bar.foo.apply(1, 2, 3)) + assertEquals("applyDynamic(apply)(1, 2, a)", bar.foo.apply(1, 2, "a")) + assertEquals("applyDynamic(apply)(1, 2, a)", bar.foo.apply(List(1, 2, "a"): _*)) + + assertEquals("applyDynamicNamed(apply)((a,1))", bar.foo.apply(a = 1)) + assertEquals("applyDynamicNamed(apply)((a,1), (b,2))", bar.foo.apply(a = 1, b = 2)) + assertEquals("applyDynamicNamed(apply)((a,1), (,0))", bar.foo.apply(a = 1, 0)) + assertEquals("applyDynamicNamed(apply)((a,1), (a,5))", bar.foo.apply(a = 1, a = 5)) + assertEquals("applyDynamicNamed(apply)((,d), (a,1), (,5), (a,c))", bar.foo.apply("d", a = 1, 5, a = 'c')) + } + /** Test cains of dynamic calls. */ def runBarTests() = { val bar = new Bar("bar")