Skip to content

Commit d32635a

Browse files
committed
Use jl.Class'es instead of ClassTag's in structural calls.
This simplifies the life of everyone: the expansion of structural calls, the implementation of `scala.reflect.Selectable`, and the reverse engineering performed by the Scala.js back-end. As a side benefit, the new transformation supports bottom types in the arguments. It was previously limited because we do not allow the generation of `ClassTag`s for `Null` and `Nothing`: they are unsound in arrays, which is what `ClassTag`s are designed for, but on their own they have perfectly valid `Class`'es.
1 parent ac806b8 commit d32635a

File tree

12 files changed

+102
-102
lines changed

12 files changed

+102
-102
lines changed

compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala

Lines changed: 31 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3199,26 +3199,24 @@ class JSCodeGen()(using genCtx: Context) {
31993199
}
32003200

32013201
/** Gen JS code for an asInstanceOf cast (for reference types only) */
3202-
private def genAsInstanceOf(value: js.Tree, to: Type)(
3203-
implicit pos: Position): js.Tree = {
3204-
3205-
val sym = to.typeSymbol
3202+
private def genAsInstanceOf(value: js.Tree, to: Type)(implicit pos: Position): js.Tree =
3203+
genAsInstanceOf(value, toIRType(to))
32063204

3207-
if (sym == defn.ObjectClass || sym.isJSType) {
3208-
/* asInstanceOf[Object] always succeeds, and
3209-
* asInstanceOf to a raw JS type is completely erased.
3210-
*/
3211-
value
3212-
} else if (sym == defn.NullClass) {
3213-
js.If(
3214-
js.BinaryOp(js.BinaryOp.===, value, js.Null()),
3215-
js.Null(),
3216-
genThrowClassCastException())(
3217-
jstpe.NullType)
3218-
} else if (sym == defn.NothingClass) {
3219-
js.Block(value, genThrowClassCastException())
3220-
} else {
3221-
js.AsInstanceOf(value, toIRType(to))
3205+
/** Gen JS code for an asInstanceOf cast (for reference types only) */
3206+
private def genAsInstanceOf(value: js.Tree, to: jstpe.Type)(implicit pos: Position): js.Tree = {
3207+
to match {
3208+
case jstpe.AnyType =>
3209+
value
3210+
case jstpe.NullType =>
3211+
js.If(
3212+
js.BinaryOp(js.BinaryOp.===, value, js.Null()),
3213+
js.Null(),
3214+
genThrowClassCastException())(
3215+
jstpe.NullType)
3216+
case jstpe.NothingType =>
3217+
js.Block(value, genThrowClassCastException())
3218+
case _ =>
3219+
js.AsInstanceOf(value, to)
32223220
}
32233221
}
32243222

@@ -3558,7 +3556,7 @@ class JSCodeGen()(using genCtx: Context) {
35583556
* {{{
35593557
* reflectiveSelectable(structural).selectDynamic("foo")
35603558
* reflectiveSelectable(structural).applyDynamic("bar",
3561-
* ClassTag(classOf[Int]), ClassTag(classOf[String])
3559+
* classOf[Int], classOf[String]
35623560
* )(
35633561
* 6, "hello"
35643562
* )
@@ -3569,7 +3567,7 @@ class JSCodeGen()(using genCtx: Context) {
35693567
* {{{
35703568
* reflectiveSelectable(structural).selectDynamic("foo") // same as above
35713569
* reflectiveSelectable(structural).applyDynamic("bar",
3572-
* wrapRefArray([ ClassTag(classOf[Int]), ClassTag(classOf[String]) : ClassTag ]
3570+
* wrapRefArray([ classOf[Int], classOf[String] : jl.Class ]
35733571
* )(
35743572
* genericWrapArray([ Int.box(6), "hello" : Object ])
35753573
* )
@@ -3599,7 +3597,7 @@ class JSCodeGen()(using genCtx: Context) {
35993597
*
36003598
* - the original receiver `structural`
36013599
* - the method name as a compile-time string `foo` or `bar`
3602-
* - the `tp: Type`s that have been wrapped in `ClassTag(classOf[tp])`, as a
3600+
* - the `tp: Type`s that have been wrapped in `classOf[tp]`, as a
36033601
* compile-time List[Type], from which we'll derive `jstpe.Type`s for the
36043602
* `asInstanceOf`s and `jstpe.TypeRef`s for the `MethodName.reflectiveProxy`
36053603
* - the actual arguments as a compile-time `List[Tree]`
@@ -3648,38 +3646,30 @@ class JSCodeGen()(using genCtx: Context) {
36483646
} else {
36493647
// Extract the param type refs and actual args from the 2nd and 3rd argument to applyDynamic
36503648
args.tail match {
3651-
case WrapArray(classTagsArray: JavaSeqLiteral) :: WrapArray(actualArgsAnyArray: JavaSeqLiteral) :: Nil =>
3652-
// Extract jstpe.Type's and jstpe.TypeRef's from the ClassTag.apply(_) trees
3653-
val formalParamTypesAndTypeRefs = classTagsArray.elems.map {
3654-
// ClassTag.apply(classOf[tp]) -> tp
3655-
case Apply(fun, Literal(const) :: Nil)
3656-
if fun.symbol == defn.ClassTagModule_apply && const.tag == Constants.ClazzTag =>
3649+
case WrapArray(classOfsArray: JavaSeqLiteral) :: WrapArray(actualArgsAnyArray: JavaSeqLiteral) :: Nil =>
3650+
// Extract jstpe.Type's and jstpe.TypeRef's from the classOf[_] trees
3651+
val formalParamTypesAndTypeRefs = classOfsArray.elems.map {
3652+
// classOf[tp] -> tp
3653+
case Literal(const) if const.tag == Constants.ClazzTag =>
36573654
toIRTypeAndTypeRef(const.typeValue)
3658-
// ClassTag.SpecialType -> erasure(SepecialType.typeRef) (e.g., ClassTag.Any -> Object)
3659-
case Apply(Select(classTagModule, name), Nil)
3660-
if classTagModule.symbol == defn.ClassTagModule &&
3661-
defn.SpecialClassTagClasses.exists(_.name == name.toTypeName) =>
3662-
toIRTypeAndTypeRef(TypeErasure.erasure(
3663-
defn.SpecialClassTagClasses.find(_.name == name.toTypeName).get.typeRef))
36643655
// Anything else is invalid
3665-
case classTag =>
3656+
case otherTree =>
36663657
report.error(
3667-
"The ClassTags passed to Selectable.applyDynamic must be " +
3668-
"literal ClassTag(classOf[T]) expressions " +
3669-
"(typically compiler-generated). " +
3658+
"The java.lang.Class[_] arguments passed to Selectable.applyDynamic must be " +
3659+
"literal classOf[T] expressions (typically compiler-generated). " +
36703660
"Other uses are not supported in Scala.js.",
3671-
classTag.sourcePos)
3661+
otherTree.sourcePos)
36723662
(jstpe.AnyType, jstpe.ClassRef(jsNames.ObjectClass))
36733663
}
36743664

36753665
// Gen the actual args, downcasting them to the formal param types
36763666
val actualArgs = actualArgsAnyArray.elems.zip(formalParamTypesAndTypeRefs).map {
36773667
(actualArgAny, formalParamTypeAndTypeRef) =>
36783668
val genActualArgAny = genExpr(actualArgAny)
3679-
js.AsInstanceOf(genActualArgAny, formalParamTypeAndTypeRef._1)(genActualArgAny.pos)
3669+
genAsInstanceOf(genActualArgAny, formalParamTypeAndTypeRef._1)(genActualArgAny.pos)
36803670
}
36813671

3682-
(formalParamTypesAndTypeRefs.map(_._2), actualArgs)
3672+
(formalParamTypesAndTypeRefs.map(pair => toParamOrResultTypeRef(pair._2)), actualArgs)
36833673

36843674
case _ =>
36853675
report.error(

compiler/src/dotty/tools/backend/sjs/JSEncoding.scala

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -231,13 +231,8 @@ object JSEncoding {
231231
}
232232

233233
/** Computes the type ref for a type, to be used in a method signature. */
234-
private def paramOrResultTypeRef(tpe: Type)(using Context): jstpe.TypeRef = {
235-
toTypeRef(tpe) match {
236-
case jstpe.ClassRef(ScalaRuntimeNullClassName) => jstpe.NullRef
237-
case jstpe.ClassRef(ScalaRuntimeNothingClassName) => jstpe.NothingRef
238-
case otherTypeRef => otherTypeRef
239-
}
240-
}
234+
private def paramOrResultTypeRef(tpe: Type)(using Context): jstpe.TypeRef =
235+
toParamOrResultTypeRef(toTypeRef(tpe))
241236

242237
def encodeLocalSym(sym: Symbol)(
243238
implicit ctx: Context, pos: ir.Position, localNames: LocalNameGenerator): js.LocalIdent = {
@@ -284,6 +279,15 @@ object JSEncoding {
284279
ClassName(sym1.javaClassName)
285280
}
286281

282+
/** Converts a general TypeRef to a TypeRef to be used in a method signature. */
283+
def toParamOrResultTypeRef(typeRef: jstpe.TypeRef): jstpe.TypeRef = {
284+
typeRef match {
285+
case jstpe.ClassRef(ScalaRuntimeNullClassName) => jstpe.NullRef
286+
case jstpe.ClassRef(ScalaRuntimeNothingClassName) => jstpe.NothingRef
287+
case _ => typeRef
288+
}
289+
}
290+
287291
def toIRTypeAndTypeRef(tp: Type)(using Context): (jstpe.Type, jstpe.TypeRef) = {
288292
val typeRefInternal = toTypeRefInternal(tp)
289293
(toIRTypeInternal(typeRefInternal), typeRefInternal._1)

compiler/src/dotty/tools/dotc/typer/Dynamic.scala

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import dotty.tools.dotc.core.Names.{Name, TermName}
1111
import dotty.tools.dotc.core.StdNames._
1212
import dotty.tools.dotc.core.Types._
1313
import dotty.tools.dotc.core.Decorators._
14+
import dotty.tools.dotc.core.TypeErasure
1415
import util.Spans._
1516
import core.Symbols._
1617
import core.Definitions
@@ -149,22 +150,22 @@ trait Dynamic {
149150
* x1.applyDynamic("a")(a11, ..., a1n, ..., aN1, ..., aNn)
150151
* .asInstanceOf[R]
151152
* ```
152-
* If this call resolves to an `applyDynamic` method that takes a `ClassTag[?]*` as second
153+
* If this call resolves to an `applyDynamic` method that takes a `Class[?]*` as second
153154
* parameter, we further rewrite this call to
154155
* scala```
155-
* x1.applyDynamic("a", CT11, ..., CT1n, ..., CTN1, ... CTNn)
156+
* x1.applyDynamic("a", c11, ..., c1n, ..., cN1, ... cNn)
156157
* (a11, ..., a1n, ..., aN1, ..., aNn)
157158
* .asInstanceOf[R]
158159
* ```
159-
* where CT11, ..., CTNn are the class tags representing the erasure of T11, ..., TNn.
160+
* where c11, ..., cNn are the classOf constants representing the erasures of T11, ..., TNn.
160161
*
161162
* It's an error if U is neither a value nor a method type, or a dependent method
162163
* type.
163164
*/
164165
def handleStructural(tree: Tree)(using Context): Tree = {
165166
val (fun @ Select(qual, name), targs, vargss) = decomposeCall(tree)
166167

167-
def structuralCall(selectorName: TermName, ctags: => List[Tree]) = {
168+
def structuralCall(selectorName: TermName, classOfs: => List[Tree]) = {
168169
val selectable = adapt(qual, defn.SelectableClass.typeRef)
169170

170171
// ($qual: Selectable).$selectorName("$name")
@@ -177,27 +178,27 @@ trait Dynamic {
177178
if (vargss.isEmpty) base
178179
else untpd.Apply(base, vargss.flatten.map(untpd.TypedSplice(_)))
179180

180-
// If function is an `applyDynamic` that takes a ClassTag* parameter,
181-
// add `ctags`.
182-
def addClassTags(tree: Tree): Tree = tree match
181+
// If function is an `applyDynamic` that takes a Class* parameter,
182+
// add `classOfs`.
183+
def addClassOfs(tree: Tree): Tree = tree match
183184
case Apply(fn: Apply, args) =>
184-
cpy.Apply(tree)(addClassTags(fn), args)
185+
cpy.Apply(tree)(addClassOfs(fn), args)
185186
case Apply(fn @ Select(_, nme.applyDynamic), nameArg :: _ :: Nil) =>
186187
fn.tpe.widen match
187188
case mt: MethodType => mt.paramInfos match
188-
case _ :: ctagsParam :: Nil
189-
if ctagsParam.isRepeatedParam
190-
&& ctagsParam.argInfos.head.isRef(defn.ClassTagClass) =>
191-
val ctagType = defn.ClassTagClass.typeRef.appliedTo(TypeBounds.empty)
189+
case _ :: classOfsParam :: Nil
190+
if classOfsParam.isRepeatedParam
191+
&& classOfsParam.argInfos.head.isRef(defn.ClassClass) =>
192+
val jlClassType = defn.ClassClass.typeRef.appliedTo(TypeBounds.empty)
192193
cpy.Apply(tree)(fn,
193-
nameArg :: seqToRepeated(SeqLiteral(ctags, TypeTree(ctagType))) :: Nil)
194+
nameArg :: seqToRepeated(SeqLiteral(classOfs, TypeTree(jlClassType))) :: Nil)
194195
case _ => tree
195196
case other => tree
196197
case _ => tree
197-
addClassTags(typed(scall))
198+
addClassOfs(typed(scall))
198199
}
199200

200-
def fail(name: Name, reason: String) =
201+
def fail(reason: String): Tree =
201202
errorTree(tree, em"Structural access not allowed on method $name because it $reason")
202203

203204
fun.tpe.widen match {
@@ -215,18 +216,21 @@ trait Dynamic {
215216
}
216217

217218
if (isDependentMethod(tpe))
218-
fail(name, i"has a method type with inter-parameter dependencies")
219+
fail(i"has a method type with inter-parameter dependencies")
219220
else {
220-
def ctags = tpe.paramInfoss.flatten.map(pt =>
221-
implicitArgTree(defn.ClassTagClass.typeRef.appliedTo(pt.widenDealias :: Nil), fun.span.endPos))
222-
structuralCall(nme.applyDynamic, ctags).cast(tpe.finalResultType)
221+
def classOfs =
222+
if tpe.paramInfoss.nestedExists(!TypeErasure.hasStableErasure(_)) then
223+
fail(i"has a parameter type with an unstable erasure") :: Nil
224+
else
225+
TypeErasure.erasure(tpe).asInstanceOf[MethodType].paramInfos.map(clsOf(_))
226+
structuralCall(nme.applyDynamic, classOfs).cast(tpe.finalResultType)
223227
}
224228

225229
// (@allanrenucci) I think everything below is dead code
226230
case _: PolyType =>
227-
fail(name, "is polymorphic")
231+
fail("is polymorphic")
228232
case tpe =>
229-
fail(name, i"has an unsupported type: $tpe")
233+
fail(i"has an unsupported type: $tpe")
230234
}
231235
}
232236
}

docs/docs/reference/changed-features/structural-types-spec.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,17 @@ def selectDynamic(name: String): T
3535
```
3636
Often, the return type `T` is `Any`.
3737

38-
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.
38+
Unlike `scala.Dynamic`, there is no special meaning for an `updateDynamic` method.
39+
However, we reserve the right to give it meaning in the future.
40+
Consequently, it is recommended not define any member called `updateDynamic` in `Selectable`s.
41+
42+
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.
3943
Its signature should be of one of the two following forms:
4044
```scala
4145
def applyDynamic(name: String)(args: Any*): T
42-
def applyDynamic(name: String, ctags: ClassTag[?]*)(args: Any*): T
46+
def applyDynamic(name: String, ctags: Class[?]*)(args: Any*): T
4347
```
44-
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
48+
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
4549
if `applyDynamic` is implemented using Java reflection, but it could be
4650
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.
4751

@@ -58,13 +62,13 @@ and `Rs` are structural refinement declarations, and given `v.a` of type `U`, we
5862
v.applyDynamic("a")(a11, ..., a1n, ..., aN1, ..., aNn)
5963
.asInstanceOf[R]
6064
```
61-
If this call resolves to an `applyDynamic` method of the second form that takes a `ClassTag[?]*` argument, we further rewrite this call to
65+
If this call resolves to an `applyDynamic` method of the second form that takes a `Class[?]*` argument, we further rewrite this call to
6266
```scala
63-
v.applyDynamic("a", CT11, ..., CT1n, ..., CTN1, ... CTNn)(
67+
v.applyDynamic("a", c11, ..., c1n, ..., cN1, ... cNn)(
6468
a11, ..., a1n, ..., aN1, ..., aNn)
6569
.asInstanceOf[R]
6670
```
67-
where each `CT_ij` is the class tag of the type of the formal parameter `Tij`
71+
where each `c_ij` is the literal `java.lang.Class[?]` of the type of the formal parameter `Tij`, i.e., `classOf[Tij]`.
6872

6973
- If `U` is neither a value nor a method type, or a dependent method
7074
type, an error is emitted.

docs/docs/reference/changed-features/structural-types.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,8 @@ differences.
175175
`Selectable` is a trait which declares the access operations.
176176

177177
- Two access operations, `selectDynamic` and `applyDynamic` are shared
178-
between both approaches. In `Selectable`, `applyDynamic` also takes
179-
`ClassTag` indicating the method's formal parameter types. `Dynamic`
180-
comes with `updateDynamic`.
178+
between both approaches. In `Selectable`, `applyDynamic` also may also take
179+
`java.lang.Class` arguments indicating the method's formal parameter types.
180+
`Dynamic` comes with `updateDynamic`.
181181

182182
[More details](structural-types-spec.md)

library/src/scala/Selectable.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@ package scala
1313
* `applyDynamic` is invoked for selections with arguments `v.m(...)`.
1414
* If there's only one kind of selection, the method supporting the
1515
* other may be omitted. The `applyDynamic` can also have a second parameter
16-
* list of class tag arguments, i.e. it may alternatively have the signature
16+
* list of `java.lang.Class` arguments, i.e. it may alternatively have the
17+
* signature
1718
*
18-
* def applyDynamic(name: String, paramClasses: ClassTag[_]*)(args: Any*): Any
19+
* def applyDynamic(name: String, paramClasses: Class[_]*)(args: Any*): Any
1920
*
20-
* In this case the call will synthesize `ClassTag` arguments for all formal parameter
21-
* types of the method in the structural type.
21+
* In this case the call will synthesize `Class` arguments for the erasure of
22+
* all formal parameter types of the method in the structural type.
2223
*/
2324
trait Selectable extends Any
2425

library/src/scala/reflect/Selectable.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,9 @@ trait Selectable extends scala.Selectable:
2727
* @param paramTypes The class tags of the selected method's formal parameter types
2828
* @param args The arguments to pass to the selected method
2929
*/
30-
def applyDynamic(name: String, paramTypes: ClassTag[_]*)(args: Any*): Any =
30+
def applyDynamic(name: String, paramTypes: Class[_]*)(args: Any*): Any =
3131
val rcls = selectedValue.getClass
32-
val paramClasses = paramTypes.map(_.runtimeClass)
33-
val mth = rcls.getMethod(name, paramClasses: _*)
32+
val mth = rcls.getMethod(name, paramTypes: _*)
3433
ensureAccessible(mth)
3534
mth.invoke(selectedValue, args.asInstanceOf[Seq[AnyRef]]: _*)
3635

tests/neg-scalajs/reflective-calls.scala

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ object Test {
99
val receiver: Any = ???
1010
reflectiveSelectable(receiver).selectDynamic("foo") // OK
1111
reflectiveSelectable(receiver).applyDynamic("foo")() // OK
12-
reflectiveSelectable(receiver).applyDynamic("foo", ClassTag(classOf[String]), ClassTag(classOf[List[_]]))("bar", Nil) // OK
12+
reflectiveSelectable(receiver).applyDynamic("foo", classOf[String], classOf[List[_]])("bar", Nil) // OK
1313
}
1414

1515
def badReceider(): Unit = {
@@ -25,21 +25,21 @@ object Test {
2525
reflectiveSelectable(receiver).applyDynamic(methodName)() // error
2626
}
2727

28-
def nonLiteralClassTag(): Unit = {
28+
def nonLiteralClassOf(): Unit = {
2929
val receiver: Any = ???
30-
val myClassTag: ClassTag[String] = ClassTag(classOf[String])
31-
reflectiveSelectable(receiver).applyDynamic("foo", myClassTag, ClassTag(classOf[List[_]]))("bar", Nil) // error
30+
val myClassOf: Class[String] = classOf[String]
31+
reflectiveSelectable(receiver).applyDynamic("foo", myClassOf, classOf[List[_]])("bar", Nil) // error
3232
}
3333

34-
def classTagVarArgs(): Unit = {
34+
def classOfVarArgs(): Unit = {
3535
val receiver: Any = ???
36-
val classTags: List[ClassTag[_]] = List(ClassTag(classOf[String]), ClassTag(classOf[List[_]]))
37-
reflectiveSelectable(receiver).applyDynamic("foo", classTags: _*)("bar", Nil) // error
36+
val classOfs: List[Class[_]] = List(classOf[String], classOf[List[_]])
37+
reflectiveSelectable(receiver).applyDynamic("foo", classOfs: _*)("bar", Nil) // error
3838
}
3939

4040
def argsVarArgs(): Unit = {
4141
val receiver: Any = ???
4242
val args: List[Any] = List("bar", Nil)
43-
reflectiveSelectable(receiver).applyDynamic("foo", ClassTag(classOf[String]), ClassTag(classOf[List[_]]))(args: _*) // error
43+
reflectiveSelectable(receiver).applyDynamic("foo", classOf[String], classOf[List[_]])(args: _*) // error
4444
}
4545
}

tests/neg/structural.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
object Test3 {
22
import scala.reflect.Selectable.reflectiveSelectable
3-
def g(x: { type T ; def t: T ; def f(a: T): Boolean }) = x.f(x.t) // error: no ClassTag for x.T
3+
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
44
g(new { type T = Int; def t = 4; def f(a:T) = true })
55
g(new { type T = Any; def t = 4; def f(a:T) = true })
66
val y: { type T = Int; def t = 4; def f(a:T) = true } // error: illegal refinement // error: illegal refinement

tests/semanticdb/expect/Advanced.expect.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ object Test/*<-advanced::Test.*/ {
2626
val s2/*<-advanced::Test.s2.*/ = s/*->advanced::Test.s.*/.s2/*->advanced::Structural#s2().*/
2727
val s2x/*<-advanced::Test.s2x.*/ = /*->scala::reflect::Selectable.reflectiveSelectable().*/s/*->advanced::Test.s.*/.s2/*->advanced::Structural#s2().*//*->scala::reflect::Selectable#selectDynamic().*/.x
2828
val s3/*<-advanced::Test.s3.*/ = s/*->advanced::Test.s.*/.s3/*->advanced::Structural#s3().*/
29-
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.`???`().*/)
29+
val s3x/*<-advanced::Test.s3x.*/ = /*->scala::reflect::Selectable.reflectiveSelectable().*/s/*->advanced::Test.s.*/.s3/*->advanced::Structural#s3().*//*->scala::reflect::Selectable#applyDynamic().*/.m(???/*->scala::Predef.`???`().*/)
3030

3131
val e/*<-advanced::Test.e.*/ = new Wildcards/*->advanced::Wildcards#*/
3232
val e1/*<-advanced::Test.e1.*/ = e/*->advanced::Test.e.*/.e1/*->advanced::Wildcards#e1().*/

0 commit comments

Comments
 (0)