Skip to content

Commit 0d0bd51

Browse files
committed
Implement simple phantom functions
Simple phantom functions are FunctionN/ImplicitFunctionN types where all parameters are of the same phantom universe. They are synthesized in the object defining the phantom universe. It main usage should be through `(MyPhantom) => Int` nothation as it does not require any imports.
1 parent 1d0e940 commit 0d0bd51

40 files changed

+573
-54
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

+87-27
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,14 @@ class Definitions {
6060
private def enterCompleteClassSymbol(owner: Symbol, name: TypeName, flags: FlagSet, parents: List[TypeRef], decls: Scope = newScope) =
6161
ctx.newCompleteClassSymbol(owner, name, flags | Permanent, parents, decls).entered
6262

63-
private def enterTypeField(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) =
64-
scope.enter(newSymbol(cls, name, flags, TypeBounds.empty))
63+
private def enterTypeField(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope, typeBounds: TypeBounds) =
64+
scope.enter(newSymbol(cls, name, flags, typeBounds))
6565

66-
private def enterTypeParam(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) =
67-
enterTypeField(cls, name, flags | ClassTypeParamCreationFlags, scope)
66+
private def enterTypeParam(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope, typeBounds: TypeBounds) =
67+
enterTypeField(cls, name, flags | ClassTypeParamCreationFlags, scope, typeBounds)
6868

6969
private def enterSyntheticTypeParam(cls: ClassSymbol, paramFlags: FlagSet, scope: MutableScope, suffix: String = "T0") =
70-
enterTypeParam(cls, suffix.toTypeName.expandedName(cls), paramFlags, scope)
70+
enterTypeParam(cls, suffix.toTypeName.expandedName(cls), paramFlags, scope, TypeBounds.empty)
7171

7272
// NOTE: Ideally we would write `parentConstrs: => Type*` but SIP-24 is only
7373
// implemented in Dotty and not in Scala 2.
@@ -108,32 +108,30 @@ class Definitions {
108108
* def apply(implicit $x0: T0, ..., $x{N_1}: T{N-1}): R
109109
* }
110110
*/
111-
def newFunctionNTrait(name: TypeName): ClassSymbol = {
111+
def newFunctionNTrait(name: TypeName, lattice: Symbol): ClassSymbol = {
112112
val completer = new LazyType {
113113
def complete(denot: SymDenotation)(implicit ctx: Context): Unit = {
114114
val cls = denot.asClass.classSymbol
115115
val decls = newScope
116116
val arity = name.functionArity
117+
val top = lattice.thisType.select(tpnme.Any)
118+
val bottom = lattice.thisType.select(tpnme.Nothing)
117119
val argParams =
118120
for (i <- List.range(0, arity)) yield
119-
enterTypeParam(cls, name ++ "$T" ++ i.toString, Contravariant, decls)
120-
val resParam = enterTypeParam(cls, name ++ "$R", Covariant, decls)
121+
enterTypeParam(cls, name ++ "$T" ++ i.toString, Contravariant, decls, TypeBounds(bottom, top)).typeRef
122+
val resParam = enterTypeParam(cls, name ++ "$R", Covariant, decls, TypeBounds.empty).typeRef
121123
val (methodType, parentTraits) =
122124
if (name.firstPart.startsWith(str.ImplicitFunction)) {
123125
val superTrait =
124-
FunctionType(arity).appliedTo(argParams.map(_.typeRef) ::: resParam.typeRef :: Nil)
126+
FunctionType(arity, isImplicit = false, top).appliedTo(argParams ::: resParam :: Nil)
125127
(ImplicitMethodType, ctx.normalizeToClassRefs(superTrait :: Nil, cls, decls))
126128
}
127129
else (MethodType, Nil)
128-
val applyMeth =
129-
decls.enter(
130-
newMethod(cls, nme.apply,
131-
methodType(argParams.map(_.typeRef), resParam.typeRef), Deferred))
132-
denot.info =
133-
ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: parentTraits, decls)
130+
decls.enter(newMethod(cls, nme.apply, methodType(argParams, resParam), Deferred))
131+
denot.info = ClassInfo(lattice.thisType, cls, ObjectType :: parentTraits, decls)
134132
}
135133
}
136-
newClassSymbol(ScalaPackageClass, name, Trait | NoInits, completer)
134+
newClassSymbol(lattice, name, Trait | NoInits, completer)
137135
}
138136

139137
private def newMethod(cls: ClassSymbol, name: TermName, info: Type, flags: FlagSet = EmptyFlags): TermSymbol =
@@ -193,7 +191,7 @@ class Definitions {
193191
val cls = ScalaPackageVal.moduleClass.asClass
194192
cls.info.decls.openForMutations.useSynthesizer(
195193
name => ctx =>
196-
if (name.isTypeName && name.isSyntheticFunction) newFunctionNTrait(name.asTypeName)
194+
if (name.isTypeName && name.isSyntheticScalaFunction) newFunctionNTrait(name.asTypeName, cls)
197195
else NoSymbol)
198196
cls
199197
}
@@ -654,7 +652,7 @@ class Definitions {
654652

655653
object FunctionOf {
656654
def apply(args: List[Type], resultType: Type, isImplicit: Boolean = false)(implicit ctx: Context) =
657-
FunctionType(args.length, isImplicit).appliedTo(args ::: resultType :: Nil)
655+
FunctionType(args, resultType, isImplicit).appliedTo(args ::: resultType :: Nil)
658656
def unapply(ft: Type)(implicit ctx: Context) = {
659657
val tsym = ft.typeSymbol
660658
if (isFunctionClass(tsym)) {
@@ -719,16 +717,38 @@ class Definitions {
719717
lazy val Function0_applyR = ImplementedFunctionType(0).symbol.requiredMethodRef(nme.apply)
720718
def Function0_apply(implicit ctx: Context) = Function0_applyR.symbol
721719

722-
def FunctionType(n: Int, isImplicit: Boolean = false)(implicit ctx: Context): TypeRef =
723-
if (n <= MaxImplementedFunctionArity && (!isImplicit || ctx.erasedTypes)) ImplementedFunctionType(n)
720+
def FunctionType(n: Int, isImplicit: Boolean = false, top: Type = AnyType)(implicit ctx: Context): TypeRef = {
721+
if (top.isPhantom) {
722+
val functionPrefix = if (isImplicit) str.ImplicitFunction else str.Function
723+
val functionName = (functionPrefix + n).toTypeName
724+
val functionType = top.normalizedPrefix.select(functionName)
725+
assert(functionType.classSymbol.exists)
726+
functionType.asInstanceOf[TypeRef]
727+
}
728+
else if (n <= MaxImplementedFunctionArity && (!isImplicit || ctx.erasedTypes)) ImplementedFunctionType(n)
724729
else FunctionClass(n, isImplicit).typeRef
730+
}
731+
732+
def FunctionType(args: List[Type], resultType: Type, isImplicit: Boolean)(implicit ctx: Context): TypeRef =
733+
FunctionType(args.length, isImplicit, topInSameUniverse(args, "function arguments."))
734+
735+
private def topInSameUniverse(types: List[Type], relationship: => String)(implicit ctx: Context): Type = {
736+
types match {
737+
case first :: rest => (first /: rest)(inSameUniverse((t1, _) => t1.topType, _, _, relationship, ctx.owner.pos))
738+
case Nil => defn.AnyType
739+
}
740+
}
725741

726742
private lazy val TupleTypes: Set[TypeRef] = TupleType.toSet
727743

728744
/** If `cls` is a class in the scala package, its name, otherwise EmptyTypeName */
729745
def scalaClassName(cls: Symbol)(implicit ctx: Context): TypeName =
730746
if (cls.isClass && cls.owner == ScalaPackageClass) cls.asClass.name else EmptyTypeName
731747

748+
/** If `cls` is a class in an object extending scala.Phantom, its name, otherwise EmptyTypeName */
749+
def phantomClassName(cls: Symbol)(implicit ctx: Context): TypeName =
750+
if (cls.isClass && cls.owner.derivesFrom(PhantomClass)) cls.asClass.name else EmptyTypeName
751+
732752
/** If type `ref` refers to a class in the scala package, its name, otherwise EmptyTypeName */
733753
def scalaClassName(ref: Type)(implicit ctx: Context): TypeName = scalaClassName(ref.classSymbol)
734754

@@ -747,24 +767,28 @@ class Definitions {
747767
* - FunctionN for N >= 0
748768
* - ImplicitFunctionN for N >= 0
749769
*/
750-
def isFunctionClass(cls: Symbol) = scalaClassName(cls).isFunction
770+
def isFunctionClass(cls: Symbol) =
771+
scalaClassName(cls).isFunction || phantomClassName(cls).isFunction
751772

752773
/** Is an implicit function class.
753774
* - ImplicitFunctionN for N >= 0
754775
*/
755-
def isImplicitFunctionClass(cls: Symbol) = scalaClassName(cls).isImplicitFunction
776+
def isImplicitFunctionClass(cls: Symbol) =
777+
scalaClassName(cls).isImplicitFunction || phantomClassName(cls).isImplicitFunction
756778

757779
/** Is a class that will be erased to FunctionXXL
758780
* - FunctionN for N >= 22
759781
* - ImplicitFunctionN for N >= 22
760782
*/
761-
def isXXLFunctionClass(cls: Symbol) = scalaClassName(cls).functionArity > MaxImplementedFunctionArity
783+
def isXXLFunctionClass(cls: Symbol) =
784+
scalaClassName(cls).functionArity > MaxImplementedFunctionArity
762785

763786
/** Is a synthetic function class
764787
* - FunctionN for N > 22
765788
* - ImplicitFunctionN for N >= 0
766789
*/
767-
def isSyntheticFunctionClass(cls: Symbol) = scalaClassName(cls).isSyntheticFunction
790+
def isSyntheticFunctionClass(cls: Symbol) =
791+
scalaClassName(cls).isSyntheticScalaFunction || phantomClassName(cls).isFunction
768792

769793
def isAbstractFunctionClass(cls: Symbol) = isVarArityClass(cls, str.AbstractFunction)
770794
def isTupleClass(cls: Symbol) = isVarArityClass(cls, str.Tuple)
@@ -781,6 +805,7 @@ class Definitions {
781805
val arity = scalaClassName(cls).functionArity
782806
if (arity > 22) defn.FunctionXXLClass
783807
else if (arity >= 0) defn.FunctionClass(arity)
808+
else if (phantomClassName(cls).isFunction) defn.FunctionClass(0)
784809
else NoSymbol
785810
}
786811

@@ -795,6 +820,7 @@ class Definitions {
795820
val arity = scalaClassName(cls).functionArity
796821
if (arity > 22) defn.FunctionXXLType
797822
else if (arity >= 0) defn.FunctionType(arity)
823+
else if (phantomClassName(cls).isFunction) defn.FunctionType(0)
798824
else NoType
799825
}
800826

@@ -839,7 +865,10 @@ class Definitions {
839865
* trait gets screwed up. Therefore, it is mandatory that FunctionXXL
840866
* is treated as a NoInit trait.
841867
*/
842-
lazy val NoInitClasses = NotRuntimeClasses + FunctionXXLClass
868+
private lazy val NoInitClasses = NotRuntimeClasses + FunctionXXLClass
869+
870+
def isNoInitClass(cls: Symbol): Boolean =
871+
cls.is(NoInitsTrait) || NoInitClasses.contains(cls) || isFunctionClass(cls)
843872

844873
def isPolymorphicAfterErasure(sym: Symbol) =
845874
(sym eq Any_isInstanceOf) || (sym eq Any_asInstanceOf)
@@ -860,7 +889,11 @@ class Definitions {
860889
def isFunctionType(tp: Type)(implicit ctx: Context) = {
861890
val arity = functionArity(tp)
862891
val sym = tp.dealias.typeSymbol
863-
arity >= 0 && isFunctionClass(sym) && tp.isRef(FunctionType(arity, sym.name.isImplicitFunction).typeSymbol)
892+
def top =
893+
if (!sym.owner.derivesFrom(defn.PhantomClass)) defn.AnyType
894+
else sym.owner.thisType.select(tpnme.Any)
895+
def funType = FunctionType(arity, sym.name.isImplicitFunction, top)
896+
arity >= 0 && isFunctionClass(sym) && tp.isRef(funType.typeSymbol)
864897
}
865898

866899
def functionArity(tp: Type)(implicit ctx: Context) = tp.dealias.argInfos.length - 1
@@ -975,12 +1008,27 @@ class Definitions {
9751008
// ----- Phantoms ---------------------------------------------------------
9761009

9771010
lazy val PhantomClass: ClassSymbol = {
978-
val cls = completeClass(enterCompleteClassSymbol(ScalaPackageClass, tpnme.Phantom, NoInitsTrait, List(AnyType)))
1011+
val clsScope = newScope.openForMutations
1012+
val cls = completeClass(enterCompleteClassSymbol(ScalaPackageClass, tpnme.Phantom, NoInitsTrait, List(AnyType), clsScope))
9791013

9801014
val any = enterCompleteClassSymbol(cls, tpnme.Any, Protected | Final | NoInitsTrait, Nil)
9811015
val nothing = enterCompleteClassSymbol(cls, tpnme.Nothing, Protected | Final | NoInitsTrait, List(any.typeRef))
9821016
enterMethod(cls, nme.assume_, MethodType(Nil, nothing.typeRef), Protected | Final | Method)
9831017

1018+
clsScope.useSynthesizer { name => ctx =>
1019+
if (name.isTypeName && name.isFunction) newFunctionNTrait(name.asTypeName, cls)
1020+
else NoSymbol
1021+
}
1022+
1023+
if (config.Config.useFingerPrints) {
1024+
// FIXME: this should not be required, must create them early to make sure they are
1025+
// found in the members of objects extending scala.Phantom
1026+
for (i <- 1 to MaxImplementedFunctionArity) {
1027+
clsScope.lookup((str.Function + i).toTypeName)
1028+
clsScope.lookup((str.ImplicitFunction + i).toTypeName)
1029+
}
1030+
}
1031+
9841032
cls
9851033
}
9861034
lazy val Phantom_AnyClass = PhantomClass.unforcedDecls.find(_.name eq tpnme.Any).asClass
@@ -990,4 +1038,16 @@ class Definitions {
9901038
/** If the symbol is of the class scala.Phantom.Any or scala.Phantom.Nothing */
9911039
def isPhantomTerminalClass(sym: Symbol) = (sym eq Phantom_AnyClass) || (sym eq Phantom_NothingClass)
9921040

1041+
/** Ensure that `tp2`' is in the same universe as `tp1`. If that's the case, return
1042+
* `op` applied to both types.
1043+
* If not, issue an error and return `tp1`'.
1044+
*/
1045+
def inSameUniverse(op: (Type, Type) => Type, tp1: Type, tp2: Type, relationship: => String, pos: Position)(implicit ctx: Context): Type =
1046+
if (tp1.topType == tp2.topType)
1047+
op(tp1, tp2)
1048+
else {
1049+
ctx.error(ex"$tp1 and $tp2 are in different universes. They cannot be combined in $relationship", pos)
1050+
tp1
1051+
}
1052+
9931053
}

compiler/src/dotty/tools/dotc/core/NameOps.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ object NameOps {
154154
}
155155
}
156156

157-
/** Is a synthetic function name
157+
/** Is a function name
158158
* - N for FunctionN
159159
* - N for ImplicitFunctionN
160160
* - (-1) otherwise
@@ -175,14 +175,14 @@ object NameOps {
175175
*/
176176
def isImplicitFunction: Boolean = functionArityFor(str.ImplicitFunction) >= 0
177177

178-
/** Is a synthetic function name
178+
/** Is a synthetic function name (in scala package)
179179
* - FunctionN for N > 22
180180
* - ImplicitFunctionN for N >= 0
181181
* - false otherwise
182182
*/
183-
def isSyntheticFunction: Boolean = {
183+
def isSyntheticScalaFunction: Boolean = {
184184
functionArityFor(str.Function) > MaxImplementedFunctionArity ||
185-
functionArityFor(str.ImplicitFunction) >= 0
185+
functionArityFor(str.ImplicitFunction) >= 0
186186
}
187187

188188
/** Parsed function arity for function with some specific prefix */

compiler/src/dotty/tools/dotc/core/TypeErasure.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,8 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
364364
else if (semiEraseVCs && isDerivedValueClass(sym)) eraseDerivedValueClassRef(tp)
365365
else if (sym == defn.ArrayClass) apply(tp.appliedTo(TypeBounds.empty)) // i966 shows that we can hit a raw Array type.
366366
else if (defn.isSyntheticFunctionClass(sym)) defn.erasedFunctionType(sym)
367-
else if (defn.isPhantomTerminalClass(tp.symbol)) PhantomErasure.erasedPhantomType
367+
else if (defn.isPhantomTerminalClass(sym)) PhantomErasure.erasedPhantomType
368+
else if (sym eq defn.PhantomClass) defn.ObjectType
368369
else eraseNormalClassRef(tp)
369370
case tp: RefinedType =>
370371
val parent = tp.parent
@@ -407,8 +408,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
407408
case tr :: trs1 =>
408409
assert(!tr.classSymbol.is(Trait), cls)
409410
val tr1 = if (cls is Trait) defn.ObjectType else tr
410-
// We remove the Phantom trait to erase the definitions of Phantom.{assume, Any, Nothing}
411-
tr1 :: trs1.filterNot(x => x.isRef(defn.ObjectClass) || x.isRef(defn.PhantomClass))
411+
tr1 :: trs1.filterNot(_ isRef defn.ObjectClass)
412412
case nil => nil
413413
}
414414
val erasedDecls = decls.filteredScope(sym => !sym.isType || sym.isClass)

compiler/src/dotty/tools/dotc/transform/Mixin.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform =>
176176
case Some(call) =>
177177
if (defn.NotRuntimeClasses.contains(baseCls)) Nil else call :: Nil
178178
case None =>
179-
if (baseCls.is(NoInitsTrait) || defn.NoInitClasses.contains(baseCls)) Nil
179+
if (defn.isNoInitClass(baseCls)) Nil
180180
else {
181181
//println(i"synth super call ${baseCls.primaryConstructor}: ${baseCls.primaryConstructor.info}")
182182
transformFollowingDeep(superRef(baseCls.primaryConstructor).appliedToNone) :: Nil

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

+4-16
Original file line numberDiff line numberDiff line change
@@ -453,10 +453,10 @@ trait TypeAssigner {
453453
tree.withType(ref.tpe)
454454

455455
def assignType(tree: untpd.AndTypeTree, left: Tree, right: Tree)(implicit ctx: Context) =
456-
tree.withType(inSameUniverse(_ & _, left.tpe, right, "an `&`"))
456+
tree.withType(defn.inSameUniverse(_ & _, left.tpe, right.tpe, "an `&`", right.pos))
457457

458458
def assignType(tree: untpd.OrTypeTree, left: Tree, right: Tree)(implicit ctx: Context) =
459-
tree.withType(inSameUniverse(_ | _, left.tpe, right, "an `|`"))
459+
tree.withType(defn.inSameUniverse(_ | _, left.tpe, right.tpe, "an `|`", right.pos))
460460

461461
/** Assign type of RefinedType.
462462
* Refinements are typed as if they were members of refinement class `refineCls`.
@@ -489,7 +489,7 @@ trait TypeAssigner {
489489
def assignType(tree: untpd.TypeBoundsTree, lo: Tree, hi: Tree)(implicit ctx: Context) =
490490
tree.withType(
491491
if (lo eq hi) TypeAlias(lo.tpe)
492-
else inSameUniverse(TypeBounds(_, _), lo.tpe, hi, "type bounds"))
492+
else defn.inSameUniverse(TypeBounds(_, _), lo.tpe, hi.tpe, "type bounds", hi.pos))
493493

494494
def assignType(tree: untpd.Bind, sym: Symbol)(implicit ctx: Context) =
495495
tree.withType(NamedType.withFixedSym(NoPrefix, sym))
@@ -535,21 +535,9 @@ trait TypeAssigner {
535535
def assignType(tree: untpd.PackageDef, pid: Tree)(implicit ctx: Context) =
536536
tree.withType(pid.symbol.valRef)
537537

538-
/** Ensure that `tree2`'s type is in the same universe as `tree1`. If that's the case, return
539-
* `op` applied to both types.
540-
* If not, issue an error and return `tree1`'s type.
541-
*/
542-
private def inSameUniverse(op: (Type, Type) => Type, tp1: Type, tree2: Tree, relationship: => String)(implicit ctx: Context): Type =
543-
if (tp1.topType == tree2.tpe.topType)
544-
op(tp1, tree2.tpe)
545-
else {
546-
ctx.error(ex"$tp1 and ${tree2.tpe} are in different universes. They cannot be combined in $relationship", tree2.pos)
547-
tp1
548-
}
549-
550538
private def lubInSameUniverse(trees: List[Tree], relationship: => String)(implicit ctx: Context): Type =
551539
trees match {
552-
case first :: rest => (first.tpe /: rest)(inSameUniverse(_ | _, _, _, relationship))
540+
case first :: rest => (first.tpe /: rest)((tp, tree) => defn.inSameUniverse(_ | _, tp, tree.tpe, relationship, tree.pos))
553541
case Nil => defn.NothingType
554542
}
555543
}

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -671,9 +671,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
671671
def typedFunction(tree: untpd.Function, pt: Type)(implicit ctx: Context) = track("typedFunction") {
672672
val untpd.Function(args, body) = tree
673673
if (ctx.mode is Mode.Type) {
674-
val funCls = defn.FunctionClass(args.length, tree.isInstanceOf[untpd.ImplicitFunction])
675-
typed(cpy.AppliedTypeTree(tree)(
676-
untpd.TypeTree(funCls.typeRef), args :+ body), pt)
674+
val argsTypes = args.map(tr => typed(tr).tpe)
675+
val funTpe = defn.FunctionType(argsTypes, typed(body).tpe, tree.isInstanceOf[untpd.ImplicitFunction])
676+
typed(cpy.AppliedTypeTree(tree)(untpd.TypeTree(funTpe), args :+ body), pt)
677677
}
678678
else {
679679
val params = args.asInstanceOf[List[untpd.ValDef]]

library/src/scala/Phantom.scala

+15
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,20 @@ trait Phantom {
88
protected final trait Nothing extends this.Any
99
1010
protected final def assume: this.Nothing
11+
12+
trait Function1[-T1 <: this.Any, +R] {
13+
def apply(x1: T1): R
14+
}
15+
trait ImplicitFunction1[-T1 <: this.Any, +R] extends Function1[T1, R] {
16+
/*implicit*/ def apply(x1: T1): R
17+
}
18+
...
19+
trait FunctionN[-T1 <: this.Any, ..., -Tn <: this.Any, +R] {
20+
def apply(x1: T1, ..., xn: Tn): R
21+
}
22+
trait ImplicitFunctionN[-T1 <: this.Any, ..., -Tn <: this.Any, +R] extends FunctionN[T1, ..., Tn, R] {
23+
/*implicit*/ def apply(x1: T1, ..., xn: Tn): R
24+
}
25+
1126
}
1227
*/

tests/neg/phantom-Functions-1.scala

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
class PhantomFun1NoApply extends Function1[Boo.Casper, Unit] // error: class PhantomFun1NoApply needs to be abstract, since def apply: (p0: Casper)Unit is not defined
3+
4+
object Boo extends Phantom {
5+
type Casper <: this.Any
6+
}

tests/neg/phantom-Functions-2.scala

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
class PhantomFun1 extends Boo.Function2[Boo.Casper, Int, Unit] { // error: Type argument Int does not conform to upper bound Boo.Any
3+
def apply(x1: Boo.Casper, x2: Int): Unit = ()
4+
}
5+
6+
class PhantomFun2 extends Boo.Function2[Int, Boo.Casper, Unit] { // error: Type argument Int does not conform to upper bound Boo.Any
7+
def apply(x1: Boo.Casper, x2: Int): Unit = ()
8+
}
9+
10+
class Fun extends Function2[Int, Int, Unit] {
11+
def apply(x1: Int, x2: Int): Unit = ()
12+
}
13+
14+
object Boo extends Phantom {
15+
type Casper <: this.Any
16+
}

0 commit comments

Comments
 (0)