Skip to content

Commit d2f97a3

Browse files
authored
Merge pull request scala#10157 from joroKr21/sammy-refined
Enable SAM syntax to refine type members
2 parents 343b85b + a92cc22 commit d2f97a3

File tree

5 files changed

+77
-24
lines changed

5 files changed

+77
-24
lines changed

src/compiler/scala/tools/nsc/ast/TreeGen.scala

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import scala.collection.mutable.ListBuffer
1818
import symtab.Flags._
1919
import scala.reflect.internal.util.FreshNameCreator
2020
import scala.reflect.internal.util.ListOfNil
21+
import scala.util.chaining._
2122

2223
/** XXX to resolve: TreeGen only assumes global is a SymbolTable, but
2324
* TreeDSL at the moment expects a Global. Can we get by with SymbolTable?
@@ -366,15 +367,21 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
366367

367368
def expandFunction(localTyper: analyzer.Typer)(fun: Function, inConstructorFlag: Long): Tree = {
368369
val anonClass = fun.symbol.owner.newAnonymousFunctionClass(fun.pos, inConstructorFlag)
369-
val parents = if (isFunctionType(fun.tpe)) {
370-
anonClass addAnnotation SerialVersionUIDAnnotation
371-
addSerializable(abstractFunctionType(fun.vparams.map(_.symbol.tpe), fun.body.tpe.deconst))
372-
} else {
373-
if (fun.tpe.typeSymbol.isSubClass(SerializableClass))
374-
anonClass addAnnotation SerialVersionUIDAnnotation
375-
fun.tpe :: Nil
370+
val typeSym = fun.tpe.typeSymbol
371+
val isFunction = isFunctionSymbol(typeSym)
372+
if (isFunction || typeSym.isSubClass(SerializableClass))
373+
anonClass.addAnnotation(SerialVersionUIDAnnotation)
374+
375+
val rScope = newScope
376+
def parents(tp: Type): List[Type] = tp match {
377+
case RefinedType(ps, scope) =>
378+
assert(scope.forall(_.isType), s"Cannot expand function of type $tp")
379+
ps.flatMap(parents).tap(_ => scope.foreach(rScope.enter))
380+
case _ =>
381+
if (!isFunction) tp :: Nil
382+
else addSerializable(abstractFunctionType(fun.vparams.map(_.symbol.tpe), fun.body.tpe.deconst))
376383
}
377-
anonClass setInfo ClassInfoType(parents, newScope, anonClass)
384+
anonClass.setInfo(ClassInfoType(parents(fun.tpe), rScope, anonClass))
378385

379386
// The original owner is used in the backend for the EnclosingMethod attribute. If fun is
380387
// nested in a value-class method, its owner was already changed to the extension method.
@@ -387,7 +394,8 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
387394
localTyper.typedPos(fun.pos) {
388395
Block(
389396
ClassDef(anonClass, NoMods, ListOfNil, List(samDef), fun.pos),
390-
Typed(New(anonClass.tpe), TypeTree(fun.tpe)))
397+
Typed(New(anonClass.tpe), TypeTree(fun.tpe)),
398+
)
391399
}
392400
}
393401

src/reflect/scala/reflect/internal/Definitions.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1019,8 +1019,14 @@ trait Definitions extends api.StandardDefinitions {
10191019
* and the caching means that samOf is effectively computed during typer (assuming the same inputs were presented to samOf during that phase).
10201020
* It's kind of strange that erasure sees deferredMembers that typer does not (see commented out assert below)
10211021
*/
1022-
def samOf(tp: Type): Symbol =
1023-
if (isNonRefinementClassType(unwrapToClass(tp))) { // TODO: is this really faster than computing tpSym below? how about just `tp.typeSymbol.isClass` (and !tpSym.isRefinementClass)?
1022+
def samOf(tp: Type): Symbol = {
1023+
@tailrec def isEligible(tp: Type): Boolean = unwrapToClass(tp) match {
1024+
case TypeRef(_, sym, _) => sym.isClass && !sym.isRefinementClass
1025+
case RefinedType(parent :: Nil, decls) => decls.forall(_.isType) && isEligible(parent)
1026+
case _ => false
1027+
}
1028+
1029+
if (isEligible(tp)) {
10241030
// look at erased type because we (only) care about what ends up in bytecode
10251031
// (e.g., an alias type is fine as long as is compiles to a single-abstract-method)
10261032
val tpSym: Symbol = erasure.javaErasure(tp).typeSymbol
@@ -1065,6 +1071,7 @@ trait Definitions extends api.StandardDefinitions {
10651071

10661072
samCache.getOrElseUpdate(tpSym, compute)
10671073
} else NoSymbol
1074+
}
10681075

10691076
def samOfProto(pt: Type): Symbol =
10701077
pt match {
Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,74 @@
1-
sammy_restrictions.scala:38: error: type mismatch;
1+
sammy_restrictions.scala:40: error: type mismatch;
22
found : () => Int
33
required: NoAbstract
44
def f0 = (() => 0) : NoAbstract
55
^
6-
sammy_restrictions.scala:39: error: type mismatch;
6+
sammy_restrictions.scala:41: error: type mismatch;
77
found : Int => Int
88
required: TwoAbstract
99
def f1 = ((x: Int) => 0): TwoAbstract
1010
^
11-
sammy_restrictions.scala:40: error: type mismatch;
11+
sammy_restrictions.scala:42: error: type mismatch;
1212
found : Int => Int
1313
required: NoEmptyConstructor
1414
def f2 = ((x: Int) => 0): NoEmptyConstructor
1515
^
16-
sammy_restrictions.scala:41: error: type mismatch;
16+
sammy_restrictions.scala:43: error: type mismatch;
1717
found : Int => Int
1818
required: MultipleConstructorLists
1919
def f3 = ((x: Int) => 0): MultipleConstructorLists
2020
^
21-
sammy_restrictions.scala:42: error: type mismatch;
21+
sammy_restrictions.scala:44: error: type mismatch;
2222
found : Int => Int
2323
required: OneEmptySecondaryConstructor
2424
def f4 = ((x: Int) => 0): OneEmptySecondaryConstructor // derived class must have an empty *primary* to call.
2525
^
26-
sammy_restrictions.scala:43: error: type mismatch;
26+
sammy_restrictions.scala:45: error: type mismatch;
2727
found : Int => Int
2828
required: MultipleMethodLists
2929
def f5 = ((x: Int) => 0): MultipleMethodLists
3030
^
31-
sammy_restrictions.scala:44: error: type mismatch;
31+
sammy_restrictions.scala:46: error: type mismatch;
3232
found : Int => Int
3333
required: ImplicitConstructorParam
3434
def f6 = ((x: Int) => 0): ImplicitConstructorParam
3535
^
36-
sammy_restrictions.scala:45: error: type mismatch;
36+
sammy_restrictions.scala:47: error: type mismatch;
3737
found : Int => Int
3838
required: ImplicitMethodParam
3939
def f7 = ((x: Int) => 0): ImplicitMethodParam
4040
^
41-
sammy_restrictions.scala:46: error: type mismatch;
41+
sammy_restrictions.scala:48: error: type mismatch;
4242
found : Int => Int
4343
required: PolyMethod
4444
def f8 = ((x: Int) => 0): PolyMethod
4545
^
46-
sammy_restrictions.scala:47: error: type mismatch;
46+
sammy_restrictions.scala:49: error: type mismatch;
4747
found : Int => Int
4848
required: SelfTp
4949
def f9 = ((x: Int) => 0): SelfTp
5050
^
51-
sammy_restrictions.scala:48: error: type mismatch;
51+
sammy_restrictions.scala:50: error: type mismatch;
5252
found : Int => Int
5353
required: T1 with U1
5454
def g0 = ((x: Int) => 0): T1 with U1
5555
^
56-
sammy_restrictions.scala:49: error: type mismatch;
56+
sammy_restrictions.scala:51: error: type mismatch;
5757
found : Int => Int
5858
required: Test.NonClassTypeRefinement
5959
(which expands to) DerivedOneAbstract with OneAbstract
6060
def g1 = ((x: Int) => 0): NonClassTypeRefinement
6161
^
62-
12 errors
62+
sammy_restrictions.scala:52: error: type mismatch;
63+
found : Int => Int
64+
required: Test.NonOverridingMethodRefinement
65+
(which expands to) OneAbstract{def apples(): Int}
66+
def h1 = ((x: Int) => 0): NonOverridingMethodRefinement
67+
^
68+
sammy_restrictions.scala:53: error: type mismatch;
69+
found : Int => Int
70+
required: Test.OverridingMethodRefinement
71+
(which expands to) OneAbstract{def ap(a: Int): Int}
72+
def h2 = ((x: Int) => 0): OverridingMethodRefinement
73+
^
74+
14 errors

test/files/neg/sammy_restrictions.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ trait T1 { def t(a: Int): Int }; trait U1
3232
object Test {
3333
implicit val s: String = ""
3434
type NonClassTypeRefinement = DerivedOneAbstract with OneAbstract
35+
type NonOverridingMethodRefinement = OneAbstract { def apples(): Int }
36+
type OverridingMethodRefinement = OneAbstract { def ap(a: Int): Int } // allowed in Scala 3
3537
type NonClassType = DerivedOneAbstract
3638

3739
// errors:
@@ -47,6 +49,8 @@ object Test {
4749
def f9 = ((x: Int) => 0): SelfTp
4850
def g0 = ((x: Int) => 0): T1 with U1
4951
def g1 = ((x: Int) => 0): NonClassTypeRefinement
52+
def h1 = ((x: Int) => 0): NonOverridingMethodRefinement
53+
def h2 = ((x: Int) => 0): OverridingMethodRefinement
5054

5155
// allowed:
5256
def g2 = ((x: Int) => 0): OneEmptyConstructor

test/files/pos/sammy_refined.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
trait DepFn[-A] {
2+
type Out
3+
def apply(in: A): Out
4+
}
5+
6+
object DepFn {
7+
type Aux[-A, B] = DepFn[A] { type Out = B }
8+
type AuxF[F[_], A] = Aux[F[A], F[A]] { type B >: A }
9+
val length: DepFn[String] { type Out = Int } = _.length
10+
val upper: Aux[String, String] = _.toUpperCase
11+
val reverse: AuxF[List, Int] = _.reverse
12+
}
13+
14+
class Outer {
15+
// T here does not compile to a SAM in bytecode,
16+
// because of the outer reference to the enclosing class.
17+
trait T { def f(x: Int): Int }
18+
val t1: T = x => x
19+
val t2: T { type U = String } = x => x
20+
val t3: T { type U } = x => x
21+
val t4: (T { type U }) { type V } = x => x
22+
}

0 commit comments

Comments
 (0)