diff --git a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala index 1a4f3e6d7ec2..db91dc02fd18 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala @@ -3199,26 +3199,24 @@ class JSCodeGen()(using genCtx: Context) { } /** Gen JS code for an asInstanceOf cast (for reference types only) */ - private def genAsInstanceOf(value: js.Tree, to: Type)( - implicit pos: Position): js.Tree = { - - val sym = to.typeSymbol + private def genAsInstanceOf(value: js.Tree, to: Type)(implicit pos: Position): js.Tree = + genAsInstanceOf(value, toIRType(to)) - if (sym == defn.ObjectClass || sym.isJSType) { - /* asInstanceOf[Object] always succeeds, and - * asInstanceOf to a raw JS type is completely erased. - */ - value - } else if (sym == defn.NullClass) { - js.If( - js.BinaryOp(js.BinaryOp.===, value, js.Null()), - js.Null(), - genThrowClassCastException())( - jstpe.NullType) - } else if (sym == defn.NothingClass) { - js.Block(value, genThrowClassCastException()) - } else { - js.AsInstanceOf(value, toIRType(to)) + /** Gen JS code for an asInstanceOf cast (for reference types only) */ + private def genAsInstanceOf(value: js.Tree, to: jstpe.Type)(implicit pos: Position): js.Tree = { + to match { + case jstpe.AnyType => + value + case jstpe.NullType => + js.If( + js.BinaryOp(js.BinaryOp.===, value, js.Null()), + js.Null(), + genThrowClassCastException())( + jstpe.NullType) + case jstpe.NothingType => + js.Block(value, genThrowClassCastException()) + case _ => + js.AsInstanceOf(value, to) } } @@ -3558,48 +3556,56 @@ class JSCodeGen()(using genCtx: Context) { * {{{ * reflectiveSelectable(structural).selectDynamic("foo") * reflectiveSelectable(structural).applyDynamic("bar", - * ClassTag(classOf[Int]), ClassTag(classOf[String]) + * classOf[Int], classOf[String] * )( * 6, "hello" * ) * }}} * - * and eventually reach the back-end as + * When the original `structural` value is already of a subtype of + * `scala.reflect.Selectable`, there is no conversion involved. There could + * also be any other arbitrary conversion, such as the deprecated bridge for + * Scala 2's `import scala.language.reflectiveCalls`. In general, the shape + * is therefore the following, for some `selectable: reflect.Selectable`: * * {{{ - * reflectiveSelectable(structural).selectDynamic("foo") // same as above - * reflectiveSelectable(structural).applyDynamic("bar", - * wrapRefArray([ ClassTag(classOf[Int]), ClassTag(classOf[String]) : ClassTag ] + * selectable.selectDynamic("foo") + * selectable.applyDynamic("bar", + * classOf[Int], classOf[String] * )( - * genericWrapArray([ Int.box(6), "hello" : Object ]) + * 6, "hello" * ) * }}} * - * If we use the deprecated `import scala.language.reflectiveCalls`, the - * wrapper for the receiver `structural` are the following instead: + * and eventually reaches the back-end as * * {{{ - * reflectiveSelectableFromLangReflectiveCalls(structural)( - * using scala.languageFeature.reflectiveCalls) + * selectable.selectDynamic("foo") // same as above + * selectable.applyDynamic("bar", + * wrapRefArray([ classOf[Int], classOf[String] : jl.Class ] + * )( + * genericWrapArray([ Int.box(6), "hello" : Object ]) + * ) * }}} * - * (in which case we don't care about the contextual argument). - * * In SJSIR, they must be encoded as follows: * * {{{ - * structural.foo;R() - * structural.bar;I;Ljava.lang.String;R( + * selectable.selectedValue;O().foo;R() + * selectable.selectedValue;O().bar;I;Ljava.lang.String;R( * Int.box(6).asInstanceOf[int], * "hello".asInstanceOf[java.lang.String] * ) * }}} * + * where `selectedValue;O()` is declared in `scala.reflect.Selectable` and + * holds the actual instance on which to perform the reflective operations. + * For the typical use case from the first snippet, it returns `structural`. + * * This means that we must deconstruct the elaborated calls to recover: * - * - the original receiver `structural` * - the method name as a compile-time string `foo` or `bar` - * - the `tp: Type`s that have been wrapped in `ClassTag(classOf[tp])`, as a + * - the `tp: Type`s that have been wrapped in `classOf[tp]`, as a * compile-time List[Type], from which we'll derive `jstpe.Type`s for the * `asInstanceOf`s and `jstpe.TypeRef`s for the `MethodName.reflectiveProxy` * - the actual arguments as a compile-time `List[Tree]` @@ -3609,26 +3615,10 @@ class JSCodeGen()(using genCtx: Context) { */ private def genReflectiveCall(tree: Apply, isSelectDynamic: Boolean): js.Tree = { implicit val pos = tree.span - val Apply(fun @ Select(receiver0, _), args) = tree + val Apply(fun @ Select(receiver, _), args) = tree - /* Extract the real receiver, which is the first argument to one of the - * implicit conversions scala.reflect.Selectable.reflectiveSelectable or - * scala.Selectable.reflectiveSelectableFromLangReflectiveCalls. - */ - val receiver = receiver0 match { - case Apply(fun1, receiver :: _) - if fun1.symbol == jsdefn.ReflectSelectable_reflectiveSelectable || - fun1.symbol == jsdefn.Selectable_reflectiveSelectableFromLangReflectiveCalls => - genExpr(receiver) - - case _ => - report.error( - "The receiver of Selectable.selectDynamic or Selectable.applyDynamic " + - "must be a call to the (implicit) method scala.reflect.Selectable.reflectiveSelectable. " + - "Other uses are not supported in Scala.js.", - tree.sourcePos) - js.Undefined() - } + val selectedValueTree = js.Apply(js.ApplyFlags.empty, genExpr(receiver), + js.MethodIdent(selectedValueMethodName), Nil)(jstpe.AnyType) // Extract the method name as a String val methodNameStr = args.head match { @@ -3648,27 +3638,19 @@ class JSCodeGen()(using genCtx: Context) { } else { // Extract the param type refs and actual args from the 2nd and 3rd argument to applyDynamic args.tail match { - case WrapArray(classTagsArray: JavaSeqLiteral) :: WrapArray(actualArgsAnyArray: JavaSeqLiteral) :: Nil => - // Extract jstpe.Type's and jstpe.TypeRef's from the ClassTag.apply(_) trees - val formalParamTypesAndTypeRefs = classTagsArray.elems.map { - // ClassTag.apply(classOf[tp]) -> tp - case Apply(fun, Literal(const) :: Nil) - if fun.symbol == defn.ClassTagModule_apply && const.tag == Constants.ClazzTag => + case WrapArray(classOfsArray: JavaSeqLiteral) :: WrapArray(actualArgsAnyArray: JavaSeqLiteral) :: Nil => + // Extract jstpe.Type's and jstpe.TypeRef's from the classOf[_] trees + val formalParamTypesAndTypeRefs = classOfsArray.elems.map { + // classOf[tp] -> tp + case Literal(const) if const.tag == Constants.ClazzTag => toIRTypeAndTypeRef(const.typeValue) - // ClassTag.SpecialType -> erasure(SepecialType.typeRef) (e.g., ClassTag.Any -> Object) - case Apply(Select(classTagModule, name), Nil) - if classTagModule.symbol == defn.ClassTagModule && - defn.SpecialClassTagClasses.exists(_.name == name.toTypeName) => - toIRTypeAndTypeRef(TypeErasure.erasure( - defn.SpecialClassTagClasses.find(_.name == name.toTypeName).get.typeRef)) // Anything else is invalid - case classTag => + case otherTree => report.error( - "The ClassTags passed to Selectable.applyDynamic must be " + - "literal ClassTag(classOf[T]) expressions " + - "(typically compiler-generated). " + + "The java.lang.Class[_] arguments passed to Selectable.applyDynamic must be " + + "literal classOf[T] expressions (typically compiler-generated). " + "Other uses are not supported in Scala.js.", - classTag.sourcePos) + otherTree.sourcePos) (jstpe.AnyType, jstpe.ClassRef(jsNames.ObjectClass)) } @@ -3676,10 +3658,10 @@ class JSCodeGen()(using genCtx: Context) { val actualArgs = actualArgsAnyArray.elems.zip(formalParamTypesAndTypeRefs).map { (actualArgAny, formalParamTypeAndTypeRef) => val genActualArgAny = genExpr(actualArgAny) - js.AsInstanceOf(genActualArgAny, formalParamTypeAndTypeRef._1)(genActualArgAny.pos) + genAsInstanceOf(genActualArgAny, formalParamTypeAndTypeRef._1)(genActualArgAny.pos) } - (formalParamTypesAndTypeRefs.map(_._2), actualArgs) + (formalParamTypesAndTypeRefs.map(pair => toParamOrResultTypeRef(pair._2)), actualArgs) case _ => report.error( @@ -3692,7 +3674,7 @@ class JSCodeGen()(using genCtx: Context) { val methodName = MethodName.reflectiveProxy(methodNameStr, formalParamTypeRefs) - js.Apply(js.ApplyFlags.empty, receiver, js.MethodIdent(methodName), actualArgs)(jstpe.AnyType) + js.Apply(js.ApplyFlags.empty, selectedValueTree, js.MethodIdent(methodName), actualArgs)(jstpe.AnyType) } /** Gen actual actual arguments to Scala method call. @@ -4306,10 +4288,13 @@ object JSCodeGen { private val NullPointerExceptionClass = ClassName("java.lang.NullPointerException") private val JSObjectClassName = ClassName("scala.scalajs.js.Object") + private val ObjectClassRef = jstpe.ClassRef(ir.Names.ObjectClass) + private val newSimpleMethodName = SimpleMethodName("new") - private val ObjectArgConstructorName = - MethodName.constructor(List(jstpe.ClassRef(ir.Names.ObjectClass))) + private val selectedValueMethodName = MethodName("selectedValue", Nil, ObjectClassRef) + + private val ObjectArgConstructorName = MethodName.constructor(List(ObjectClassRef)) private val thisOriginalName = OriginalName("this") diff --git a/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala b/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala index 02e660bbdd8b..0bc9bb9b4c8c 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala @@ -231,13 +231,8 @@ object JSEncoding { } /** Computes the type ref for a type, to be used in a method signature. */ - private def paramOrResultTypeRef(tpe: Type)(using Context): jstpe.TypeRef = { - toTypeRef(tpe) match { - case jstpe.ClassRef(ScalaRuntimeNullClassName) => jstpe.NullRef - case jstpe.ClassRef(ScalaRuntimeNothingClassName) => jstpe.NothingRef - case otherTypeRef => otherTypeRef - } - } + private def paramOrResultTypeRef(tpe: Type)(using Context): jstpe.TypeRef = + toParamOrResultTypeRef(toTypeRef(tpe)) def encodeLocalSym(sym: Symbol)( implicit ctx: Context, pos: ir.Position, localNames: LocalNameGenerator): js.LocalIdent = { @@ -284,6 +279,15 @@ object JSEncoding { ClassName(sym1.javaClassName) } + /** Converts a general TypeRef to a TypeRef to be used in a method signature. */ + def toParamOrResultTypeRef(typeRef: jstpe.TypeRef): jstpe.TypeRef = { + typeRef match { + case jstpe.ClassRef(ScalaRuntimeNullClassName) => jstpe.NullRef + case jstpe.ClassRef(ScalaRuntimeNothingClassName) => jstpe.NothingRef + case _ => typeRef + } + } + def toIRTypeAndTypeRef(tp: Type)(using Context): (jstpe.Type, jstpe.TypeRef) = { val typeRefInternal = toTypeRefInternal(tp) (toIRTypeInternal(typeRefInternal), typeRefInternal._1) diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index 2af321fa3f61..80c41bbe772f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -11,6 +11,7 @@ import dotty.tools.dotc.core.Names.{Name, TermName} import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.TypeErasure import util.Spans._ import core.Symbols._ import core.Definitions @@ -149,14 +150,14 @@ trait Dynamic { * x1.applyDynamic("a")(a11, ..., a1n, ..., aN1, ..., aNn) * .asInstanceOf[R] * ``` - * If this call resolves to an `applyDynamic` method that takes a `ClassTag[?]*` as second + * If this call resolves to an `applyDynamic` method that takes a `Class[?]*` as second * parameter, we further rewrite this call to * scala``` - * x1.applyDynamic("a", CT11, ..., CT1n, ..., CTN1, ... CTNn) + * x1.applyDynamic("a", c11, ..., c1n, ..., cN1, ... cNn) * (a11, ..., a1n, ..., aN1, ..., aNn) * .asInstanceOf[R] * ``` - * where CT11, ..., CTNn are the class tags representing the erasure of T11, ..., TNn. + * where c11, ..., cNn are the classOf constants representing the erasures of T11, ..., TNn. * * It's an error if U is neither a value nor a method type, or a dependent method * type. @@ -164,7 +165,7 @@ trait Dynamic { def handleStructural(tree: Tree)(using Context): Tree = { val (fun @ Select(qual, name), targs, vargss) = decomposeCall(tree) - def structuralCall(selectorName: TermName, ctags: => List[Tree]) = { + def structuralCall(selectorName: TermName, classOfs: => List[Tree]) = { val selectable = adapt(qual, defn.SelectableClass.typeRef) // ($qual: Selectable).$selectorName("$name") @@ -177,27 +178,27 @@ trait Dynamic { if (vargss.isEmpty) base else untpd.Apply(base, vargss.flatten.map(untpd.TypedSplice(_))) - // If function is an `applyDynamic` that takes a ClassTag* parameter, - // add `ctags`. - def addClassTags(tree: Tree): Tree = tree match + // If function is an `applyDynamic` that takes a Class* parameter, + // add `classOfs`. + def addClassOfs(tree: Tree): Tree = tree match case Apply(fn: Apply, args) => - cpy.Apply(tree)(addClassTags(fn), args) + cpy.Apply(tree)(addClassOfs(fn), args) case Apply(fn @ Select(_, nme.applyDynamic), nameArg :: _ :: Nil) => fn.tpe.widen match case mt: MethodType => mt.paramInfos match - case _ :: ctagsParam :: Nil - if ctagsParam.isRepeatedParam - && ctagsParam.argInfos.head.isRef(defn.ClassTagClass) => - val ctagType = defn.ClassTagClass.typeRef.appliedTo(TypeBounds.empty) + case _ :: classOfsParam :: Nil + if classOfsParam.isRepeatedParam + && classOfsParam.argInfos.head.isRef(defn.ClassClass) => + val jlClassType = defn.ClassClass.typeRef.appliedTo(TypeBounds.empty) cpy.Apply(tree)(fn, - nameArg :: seqToRepeated(SeqLiteral(ctags, TypeTree(ctagType))) :: Nil) + nameArg :: seqToRepeated(SeqLiteral(classOfs, TypeTree(jlClassType))) :: Nil) case _ => tree case other => tree case _ => tree - addClassTags(typed(scall)) + addClassOfs(typed(scall)) } - def fail(name: Name, reason: String) = + def fail(reason: String): Tree = errorTree(tree, em"Structural access not allowed on method $name because it $reason") fun.tpe.widen match { @@ -215,18 +216,21 @@ trait Dynamic { } if (isDependentMethod(tpe)) - fail(name, i"has a method type with inter-parameter dependencies") + fail(i"has a method type with inter-parameter dependencies") else { - def ctags = tpe.paramInfoss.flatten.map(pt => - implicitArgTree(defn.ClassTagClass.typeRef.appliedTo(pt.widenDealias :: Nil), fun.span.endPos)) - structuralCall(nme.applyDynamic, ctags).cast(tpe.finalResultType) + def classOfs = + if tpe.paramInfoss.nestedExists(!TypeErasure.hasStableErasure(_)) then + fail(i"has a parameter type with an unstable erasure") :: Nil + else + TypeErasure.erasure(tpe).asInstanceOf[MethodType].paramInfos.map(clsOf(_)) + structuralCall(nme.applyDynamic, classOfs).cast(tpe.finalResultType) } // (@allanrenucci) I think everything below is dead code case _: PolyType => - fail(name, "is polymorphic") + fail("is polymorphic") case tpe => - fail(name, i"has an unsupported type: $tpe") + fail(i"has an unsupported type: $tpe") } } } diff --git a/docs/docs/reference/changed-features/structural-types-spec.md b/docs/docs/reference/changed-features/structural-types-spec.md index f9951df9368a..248675c4c372 100644 --- a/docs/docs/reference/changed-features/structural-types-spec.md +++ b/docs/docs/reference/changed-features/structural-types-spec.md @@ -35,13 +35,17 @@ def selectDynamic(name: String): T ``` Often, the return type `T` is `Any`. -The `applyDynamic` method is used for selections that are applied to arguments. It takes a method name and possibly `ClassTag`s representing its parameters types as well as the arguments to pass to the function. +Unlike `scala.Dynamic`, there is no special meaning for an `updateDynamic` method. +However, we reserve the right to give it meaning in the future. +Consequently, it is recommended not to define any member called `updateDynamic` in `Selectable`s. + +The `applyDynamic` method is used for selections that are applied to arguments. It takes a method name and possibly `Class`es representing its parameters types as well as the arguments to pass to the function. Its signature should be of one of the two following forms: ```scala def applyDynamic(name: String)(args: Any*): T -def applyDynamic(name: String, ctags: ClassTag[?]*)(args: Any*): T +def applyDynamic(name: String, ctags: Class[?]*)(args: Any*): T ``` -Both versions are passed the actual arguments in the `args` parameter. The second version takes in addition a vararg argument of class tags that identify the method's parameter classes. Such an argument is needed +Both versions are passed the actual arguments in the `args` parameter. The second version takes in addition a vararg argument of `java.lang.Class`es that identify the method's parameter classes. Such an argument is needed if `applyDynamic` is implemented using Java reflection, but it could be useful in other cases as well. `selectDynamic` and `applyDynamic` can also take additional context parameters in using clauses. These are resolved in the normal way at the callsite. @@ -58,13 +62,13 @@ and `Rs` are structural refinement declarations, and given `v.a` of type `U`, we v.applyDynamic("a")(a11, ..., a1n, ..., aN1, ..., aNn) .asInstanceOf[R] ``` - If this call resolves to an `applyDynamic` method of the second form that takes a `ClassTag[?]*` argument, we further rewrite this call to + If this call resolves to an `applyDynamic` method of the second form that takes a `Class[?]*` argument, we further rewrite this call to ```scala - v.applyDynamic("a", CT11, ..., CT1n, ..., CTN1, ... CTNn)( + v.applyDynamic("a", c11, ..., c1n, ..., cN1, ... cNn)( a11, ..., a1n, ..., aN1, ..., aNn) .asInstanceOf[R] ``` - where each `CT_ij` is the class tag of the type of the formal parameter `Tij` + where each `c_ij` is the literal `java.lang.Class[?]` of the type of the formal parameter `Tij`, i.e., `classOf[Tij]`. - If `U` is neither a value nor a method type, or a dependent method type, an error is emitted. diff --git a/docs/docs/reference/changed-features/structural-types.md b/docs/docs/reference/changed-features/structural-types.md index b7879abe703f..088297615059 100644 --- a/docs/docs/reference/changed-features/structural-types.md +++ b/docs/docs/reference/changed-features/structural-types.md @@ -175,8 +175,8 @@ differences. `Selectable` is a trait which declares the access operations. - Two access operations, `selectDynamic` and `applyDynamic` are shared - between both approaches. In `Selectable`, `applyDynamic` also takes - `ClassTag` indicating the method's formal parameter types. `Dynamic` - comes with `updateDynamic`. + between both approaches. In `Selectable`, `applyDynamic` also may also take + `java.lang.Class` arguments indicating the method's formal parameter types. + `Dynamic` comes with `updateDynamic`. [More details](structural-types-spec.md) diff --git a/library/src/scala/Selectable.scala b/library/src/scala/Selectable.scala index 01f8dd902549..f89102ae4802 100644 --- a/library/src/scala/Selectable.scala +++ b/library/src/scala/Selectable.scala @@ -13,12 +13,13 @@ package scala * `applyDynamic` is invoked for selections with arguments `v.m(...)`. * If there's only one kind of selection, the method supporting the * other may be omitted. The `applyDynamic` can also have a second parameter - * list of class tag arguments, i.e. it may alternatively have the signature + * list of `java.lang.Class` arguments, i.e. it may alternatively have the + * signature * - * def applyDynamic(name: String, paramClasses: ClassTag[_]*)(args: Any*): Any + * def applyDynamic(name: String, paramClasses: Class[_]*)(args: Any*): Any * - * In this case the call will synthesize `ClassTag` arguments for all formal parameter - * types of the method in the structural type. + * In this case the call will synthesize `Class` arguments for the erasure of + * all formal parameter types of the method in the structural type. */ trait Selectable extends Any diff --git a/library/src/scala/reflect/Selectable.scala b/library/src/scala/reflect/Selectable.scala index c4069c6abcb0..a9606fd7b45b 100644 --- a/library/src/scala/reflect/Selectable.scala +++ b/library/src/scala/reflect/Selectable.scala @@ -1,8 +1,12 @@ package scala.reflect /** A class that implements structural selections using Java reflection. + * * It can be used as a supertrait of a class or be made available * as an implicit conversion via `reflectiveSelectable`. + * + * In Scala.js, it is implemented using a separate Scala.js-specific + * mechanism, since Java reflection is not available. */ trait Selectable extends scala.Selectable: @@ -12,8 +16,9 @@ trait Selectable extends scala.Selectable: */ protected def selectedValue: Any = this + // The Scala.js codegen relies on this method being final for correctness /** Select member with given name */ - def selectDynamic(name: String): Any = + final def selectDynamic(name: String): Any = val rcls = selectedValue.getClass try val fld = rcls.getField(name) @@ -22,15 +27,15 @@ trait Selectable extends scala.Selectable: catch case ex: NoSuchFieldException => applyDynamic(name)() + // The Scala.js codegen relies on this method being final for correctness /** Select method and apply to arguments. * @param name The name of the selected method * @param paramTypes The class tags of the selected method's formal parameter types * @param args The arguments to pass to the selected method */ - def applyDynamic(name: String, paramTypes: ClassTag[_]*)(args: Any*): Any = + final def applyDynamic(name: String, paramTypes: Class[_]*)(args: Any*): Any = val rcls = selectedValue.getClass - val paramClasses = paramTypes.map(_.runtimeClass) - val mth = rcls.getMethod(name, paramClasses: _*) + val mth = rcls.getMethod(name, paramTypes: _*) ensureAccessible(mth) mth.invoke(selectedValue, args.asInstanceOf[Seq[AnyRef]]: _*) @@ -40,4 +45,7 @@ object Selectable: * such that structural selections are performed on that value. */ implicit def reflectiveSelectable(x: Any): Selectable = - new Selectable { override val selectedValue = x } + new DefaultSelectable(x) + + @inline // important for Scala.js + private final class DefaultSelectable(override protected val selectedValue: Any) extends Selectable diff --git a/tests/neg-scalajs/reflective-calls.scala b/tests/neg-scalajs/reflective-calls.scala index b5beeac0c302..444ee364e8ff 100644 --- a/tests/neg-scalajs/reflective-calls.scala +++ b/tests/neg-scalajs/reflective-calls.scala @@ -6,40 +6,34 @@ object Test { * ensuring that the error cases we test are actually testing the right things. */ def sanityCheck(): Unit = { - val receiver: Any = ??? - reflectiveSelectable(receiver).selectDynamic("foo") // OK - reflectiveSelectable(receiver).applyDynamic("foo")() // OK - reflectiveSelectable(receiver).applyDynamic("foo", ClassTag(classOf[String]), ClassTag(classOf[List[_]]))("bar", Nil) // OK - } - - def badReceider(): Unit = { val receiver: ReflectSel = ??? - receiver.selectDynamic("foo") // error - receiver.applyDynamic("foo")() // error + receiver.selectDynamic("foo") // OK + receiver.applyDynamic("foo")() // OK + receiver.applyDynamic("foo", classOf[String], classOf[List[_]])("bar", Nil) // OK } def nonLiteralMethodName(): Unit = { - val receiver: Any = ??? + val receiver: ReflectSel = ??? val methodName: String = "foo" - reflectiveSelectable(receiver).selectDynamic(methodName) // error - reflectiveSelectable(receiver).applyDynamic(methodName)() // error + receiver.selectDynamic(methodName) // error + receiver.applyDynamic(methodName)() // error } - def nonLiteralClassTag(): Unit = { - val receiver: Any = ??? - val myClassTag: ClassTag[String] = ClassTag(classOf[String]) - reflectiveSelectable(receiver).applyDynamic("foo", myClassTag, ClassTag(classOf[List[_]]))("bar", Nil) // error + def nonLiteralClassOf(): Unit = { + val receiver: ReflectSel = ??? + val myClassOf: Class[String] = classOf[String] + receiver.applyDynamic("foo", myClassOf, classOf[List[_]])("bar", Nil) // error } - def classTagVarArgs(): Unit = { - val receiver: Any = ??? - val classTags: List[ClassTag[_]] = List(ClassTag(classOf[String]), ClassTag(classOf[List[_]])) - reflectiveSelectable(receiver).applyDynamic("foo", classTags: _*)("bar", Nil) // error + def classOfVarArgs(): Unit = { + val receiver: ReflectSel = ??? + val classOfs: List[Class[_]] = List(classOf[String], classOf[List[_]]) + receiver.applyDynamic("foo", classOfs: _*)("bar", Nil) // error } def argsVarArgs(): Unit = { - val receiver: Any = ??? + val receiver: ReflectSel = ??? val args: List[Any] = List("bar", Nil) - reflectiveSelectable(receiver).applyDynamic("foo", ClassTag(classOf[String]), ClassTag(classOf[List[_]]))(args: _*) // error + receiver.applyDynamic("foo", classOf[String], classOf[List[_]])(args: _*) // error } } diff --git a/tests/neg/structural.scala b/tests/neg/structural.scala index be28e4d09d17..c9892abe03ae 100644 --- a/tests/neg/structural.scala +++ b/tests/neg/structural.scala @@ -1,6 +1,6 @@ object Test3 { import scala.reflect.Selectable.reflectiveSelectable - def g(x: { type T ; def t: T ; def f(a: T): Boolean }) = x.f(x.t) // error: no ClassTag for x.T + def g(x: { type T ; def t: T ; def f(a: T): Boolean }) = x.f(x.t) // error: it has a parameter type with an unstable erasure g(new { type T = Int; def t = 4; def f(a:T) = true }) g(new { type T = Any; def t = 4; def f(a:T) = true }) val y: { type T = Int; def t = 4; def f(a:T) = true } // error: illegal refinement // error: illegal refinement diff --git a/tests/semanticdb/expect/Advanced.expect.scala b/tests/semanticdb/expect/Advanced.expect.scala index 6c5538204294..a3b8cfd1bc1a 100644 --- a/tests/semanticdb/expect/Advanced.expect.scala +++ b/tests/semanticdb/expect/Advanced.expect.scala @@ -26,7 +26,7 @@ object Test/*<-advanced::Test.*/ { val s2/*<-advanced::Test.s2.*/ = s/*->advanced::Test.s.*/.s2/*->advanced::Structural#s2().*/ val s2x/*<-advanced::Test.s2x.*/ = /*->scala::reflect::Selectable.reflectiveSelectable().*/s/*->advanced::Test.s.*/.s2/*->advanced::Structural#s2().*//*->scala::reflect::Selectable#selectDynamic().*/.x val s3/*<-advanced::Test.s3.*/ = s/*->advanced::Test.s.*/.s3/*->advanced::Structural#s3().*/ - val s3x/*<-advanced::Test.s3x.*/ = /*->scala::reflect::Selectable.reflectiveSelectable().*/s/*->advanced::Test.s.*/.s3/*->advanced::Structural#s3().*//*->scala::reflect::Selectable#applyDynamic().*/.m/*->scala::reflect::ClassTag.apply().*/(???/*->scala::Predef.`???`().*/) + val s3x/*<-advanced::Test.s3x.*/ = /*->scala::reflect::Selectable.reflectiveSelectable().*/s/*->advanced::Test.s.*/.s3/*->advanced::Structural#s3().*//*->scala::reflect::Selectable#applyDynamic().*/.m(???/*->scala::Predef.`???`().*/) val e/*<-advanced::Test.e.*/ = new Wildcards/*->advanced::Wildcards#*/ val e1/*<-advanced::Test.e1.*/ = e/*->advanced::Test.e.*/.e1/*->advanced::Wildcards#e1().*/ diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index 43cbc4772ea6..7d52afa6f6e3 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -50,7 +50,7 @@ Uri => Advanced.scala Text => empty Language => Scala Symbols => 34 entries -Occurrences => 91 entries +Occurrences => 90 entries Symbols: advanced/C# => class C @@ -161,7 +161,6 @@ Occurrences: [28:12..28:13): s -> advanced/Test.s. [28:14..28:16): s3 -> advanced/Structural#s3(). [28:16..28:16): -> scala/reflect/Selectable#applyDynamic(). -[28:18..28:18): -> scala/reflect/ClassTag.apply(). [28:19..28:22): ??? -> scala/Predef.`???`(). [30:6..30:7): e <- advanced/Test.e. [30:14..30:23): Wildcards -> advanced/Wildcards# diff --git a/tests/sjs-junit/test/org/scalajs/testsuite/compiler/CustomReflectSelectableTestScala3.scala b/tests/sjs-junit/test/org/scalajs/testsuite/compiler/CustomReflectSelectableTestScala3.scala new file mode 100644 index 000000000000..716d5b9c5c4a --- /dev/null +++ b/tests/sjs-junit/test/org/scalajs/testsuite/compiler/CustomReflectSelectableTestScala3.scala @@ -0,0 +1,26 @@ +package org.scalajs.testsuite.compiler + +import org.junit.Assert._ +import org.junit.Test + +class CustomReflectSelectableTestScala3 { + import CustomReflectSelectableTestScala3._ + + @Test def selectField(): Unit = { + val obj: reflect.Selectable { val x: Int } = new CustomReflectSelectable(42) + assertEquals(47, obj.x) + } + + @Test def callMethod(): Unit = { + val obj: reflect.Selectable { def foo(x: Int, y: String): String } = new CustomReflectSelectable(42) + assertEquals("3 bar 42", obj.foo(3, "bar")) + } +} + +object CustomReflectSelectableTestScala3 { + class CustomReflectSelectable(param: Int) extends reflect.Selectable { + val x: Int = 5 + param + + def foo(x: Int, y: String): String = s"$x $y $param" + } +} diff --git a/tests/sjs-junit/test/org/scalajs/testsuite/compiler/ReflectiveCallTestScala3.scala b/tests/sjs-junit/test/org/scalajs/testsuite/compiler/ReflectiveCallTestScala3.scala index ddf46d4ce971..904f69cfdfea 100644 --- a/tests/sjs-junit/test/org/scalajs/testsuite/compiler/ReflectiveCallTestScala3.scala +++ b/tests/sjs-junit/test/org/scalajs/testsuite/compiler/ReflectiveCallTestScala3.scala @@ -163,13 +163,11 @@ class ReflectiveCallTestScala3 { assertEquals("undefined any-string 5 Some(5) None", foo.foo((), "any-string", 5, Some[Int](5), None)) - // The following calls do not compile in Dotty because - // No ClassTag available for Null/Nothing + assertEquals("null", foo.nullMeth(null)) - //assertEquals("null", foo.nullMeth(null)) - //// Make sure that a call to nothingMeth can link - //if (Math.random() > 2) // always false - // foo.nothingMeth(???) + // Make sure that a call to nothingMeth can link + if (Math.random() > 2) // always false + foo.nothingMeth(???) } }