Skip to content

Commit ae0560d

Browse files
committed
Add support for functions with phantom parameters
1 parent c040eb3 commit ae0560d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+899
-66
lines changed

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

+99-42
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), ExpandedName | paramFlags, scope)
70+
enterTypeParam(cls, suffix.toTypeName.expandedName(cls), ExpandedName | 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.
@@ -89,7 +89,7 @@ class Definitions {
8989
newClassSymbol(ScalaPackageClass, name, EmptyFlags, completer).entered
9090
}
9191

92-
/** The trait FunctionN or ImplicitFunctionN, for some N
92+
/** The trait FunctionN, ImplicitFunctionN, PhantomFunctionM, ImplicitPhantomFunctionM, for some N
9393
* @param name The name of the trait to be created
9494
*
9595
* FunctionN traits follow this template:
@@ -107,30 +107,45 @@ class Definitions {
107107
* trait ImplicitFunctionN[T0,...,T{N-1}, R] extends Object with FunctionN[T0,...,T{N-1}, R] {
108108
* def apply(implicit $x0: T0, ..., $x{N_1}: T{N-1}): R
109109
* }
110+
*
111+
* PhantomFunctionM traits follow this template:
112+
*
113+
* trait PhantomFunctionM[T0,...T{N-1}, R] extends Object {
114+
* def apply($x0: T0, ..., $x{N_1}: T{N-1}): R
115+
* }
116+
*
117+
* where M represents the phantomicity of all Ti.
118+
*
119+
* ImplicitPhantomFunctionM traits follow this template:
120+
*
121+
* trait ImplicitPhantomFunctionM[T0,...,T{N-1}, R] extends Object with PhantomFunctionM[T0,...,T{N-1}, R] {
122+
* def apply(implicit $x0: T0, ..., $x{N_1}: T{N-1}): R
123+
* }
124+
*
125+
* where M represents the phantomicity of all Ti.
126+
*
110127
*/
111128
private def newFunctionNTrait(name: TypeName) = {
112129
val completer = new LazyType {
113130
def complete(denot: SymDenotation)(implicit ctx: Context): Unit = {
114131
val cls = denot.asClass.classSymbol
115132
val decls = newScope
116-
val arity = name.functionArity
133+
val phantomicity = name.phantomicity
134+
val arity = phantomicity.arity
117135
val argParams =
118136
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)
137+
enterTypeParam(cls, name ++ "$T" ++ i.toString, Contravariant, decls, phantomicity.tParamBounds(i)).typeRef
138+
val resParam = enterTypeParam(cls, name ++ "$R", Covariant, decls, phantomicity.tParamBounds(arity)).typeRef
121139
val (methodType, parentTraits) =
122-
if (name.startsWith(tpnme.ImplicitFunction)) {
123-
val superTrait =
124-
FunctionType(arity).appliedTo(argParams.map(_.typeRef) ::: resParam.typeRef :: Nil)
125-
(ImplicitMethodType, ctx.normalizeToClassRefs(superTrait :: Nil, cls, decls))
140+
if (name.isImplicitFunction) {
141+
val superTrait = FunctionType(phantomicity, isImplicit = false)
142+
val appliedSuperTrait = superTrait.appliedTo(argParams ::: resParam :: Nil)
143+
(ImplicitMethodType, ctx.normalizeToClassRefs(appliedSuperTrait :: Nil, cls, decls))
126144
}
127145
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)
146+
147+
decls.enter(newMethod(cls, nme.apply, methodType(argParams, resParam), Deferred))
148+
denot.info = ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: parentTraits, decls)
134149
}
135150
}
136151
newClassSymbol(ScalaPackageClass, name, Trait | NoInits, completer)
@@ -640,7 +655,7 @@ class Definitions {
640655

641656
object FunctionOf {
642657
def apply(args: List[Type], resultType: Type, isImplicit: Boolean = false)(implicit ctx: Context) =
643-
FunctionType(args.length, isImplicit).appliedTo(args ::: resultType :: Nil)
658+
FunctionType(Phantomicity(args :+ resultType), isImplicit).appliedTo(args ::: resultType :: Nil)
644659
def unapply(ft: Type)(implicit ctx: Context) = {
645660
val tsym = ft.typeSymbol
646661
if (isFunctionClass(tsym)) {
@@ -698,18 +713,30 @@ class Definitions {
698713
lazy val TupleType = mkArityArray("scala.Tuple", MaxTupleArity, 2)
699714
lazy val ProductNType = mkArityArray("scala.Product", MaxTupleArity, 0)
700715

701-
def FunctionClass(n: Int, isImplicit: Boolean = false)(implicit ctx: Context) =
716+
def FunctionClass(n: Int, isImplicit: Boolean = false)(implicit ctx: Context): Symbol =
702717
if (isImplicit) ctx.requiredClass("scala.ImplicitFunction" + n.toString)
703718
else if (n <= MaxImplementedFunctionArity) FunctionClassPerRun()(ctx)(n)
704719
else ctx.requiredClass("scala.Function" + n.toString)
705720

721+
def FunctionClass(phantomicity: Phantomicity, isImplicit: Boolean)(implicit ctx: Context): Symbol = {
722+
if (phantomicity.hasPhantoms) {
723+
val prefix = if (isImplicit) "scala.ImplicitPhantomFunction" else "scala.PhantomFunction"
724+
ctx.requiredClass(prefix + phantomicity.encodedString)
725+
} else FunctionClass(phantomicity.arity, isImplicit)
726+
}
727+
706728
lazy val Function0_applyR = ImplementedFunctionType(0).symbol.requiredMethodRef(nme.apply)
707729
def Function0_apply(implicit ctx: Context) = Function0_applyR.symbol
708730

709731
def FunctionType(n: Int, isImplicit: Boolean = false)(implicit ctx: Context): TypeRef =
710732
if (n <= MaxImplementedFunctionArity && (!isImplicit || ctx.erasedTypes)) ImplementedFunctionType(n)
711733
else FunctionClass(n, isImplicit).typeRef
712734

735+
def FunctionType(phantomicity: Phantomicity, isImplicit: Boolean)(implicit ctx: Context): TypeRef = {
736+
if (phantomicity.hasPhantoms) FunctionClass(phantomicity, isImplicit).typeRef
737+
else FunctionType(phantomicity.arity, isImplicit)
738+
}
739+
713740
private lazy val TupleTypes: Set[TypeRef] = TupleType.toSet
714741
private lazy val ProductTypes: Set[TypeRef] = ProductNType.toSet
715742

@@ -735,23 +762,37 @@ class Definitions {
735762
/** Is a function class.
736763
* - FunctionN for N >= 0
737764
* - ImplicitFunctionN for N >= 0
765+
* - PhantomFunctionM for a valid M
766+
* - ImplicitPhantomFunctionM for a valid M
738767
*/
739768
def isFunctionClass(cls: Symbol) = scalaClassName(cls).isFunction
740769

741770
/** Is an implicit function class.
742771
* - ImplicitFunctionN for N >= 0
772+
* - ImplicitPhantomFunctionN for a valid M
743773
*/
744774
def isImplicitFunctionClass(cls: Symbol) = scalaClassName(cls).isImplicitFunction
745775

776+
/** Is a phantom function class.
777+
* - PhantomFunctionM for a valid M
778+
* - ImplicitPhantomFunctionM for a valid M
779+
*/
780+
def isPhantomFunctionClass(cls: Symbol) = scalaClassName(cls).isPhantomFunction
781+
746782
/** Is a class that will be erased to FunctionXXL
747783
* - FunctionN for N >= 22
748784
* - ImplicitFunctionN for N >= 22
785+
* - PhantomFunctionM for N >= 22, where N is the number of non phantoms in M
786+
* - ImplicitPhantomFunctionM for N >= 22, where N is the number of non phantoms in M
749787
*/
750-
def isXXLFunctionClass(cls: Symbol) = scalaClassName(cls).functionArity > MaxImplementedFunctionArity
788+
def isXXLFunctionClass(cls: Symbol) =
789+
scalaClassName(cls).phantomicity.erasedArity > MaxImplementedFunctionArity
751790

752791
/** Is a synthetic function class
753792
* - FunctionN for N > 22
754793
* - ImplicitFunctionN for N >= 0
794+
* - PhantomFunctionM for a valid M
795+
* - ImplicitPhantomFunctionM for a valid M
755796
*/
756797
def isSyntheticFunctionClass(cls: Symbol) = scalaClassName(cls).isSyntheticFunction
757798

@@ -760,31 +801,42 @@ class Definitions {
760801
def isProductClass(cls: Symbol) = isVarArityClass(cls, tpnme.Product)
761802

762803
/** Returns the erased class of the function class `cls`
763-
* - FunctionN for N > 22 becomes FunctionXXL
764804
* - FunctionN for 22 > N >= 0 remains as FunctionN
765-
* - ImplicitFunctionN for N > 22 becomes FunctionXXL
805+
* - FunctionN for N > 22 becomes FunctionXXL
766806
* - ImplicitFunctionN for 22 > N >= 0 becomes FunctionN
807+
* - ImplicitFunctionN for N > 22 becomes FunctionXXL
767808
* - anything else becomes a NoSymbol
768809
*/
769810
def erasedFunctionClass(cls: Symbol): Symbol = {
770-
val arity = scalaClassName(cls).functionArity
771-
if (arity > 22) defn.FunctionXXLClass
772-
else if (arity >= 0) defn.FunctionClass(arity)
773-
else NoSymbol
811+
val phantomicity = scalaClassName(cls).phantomicity
812+
if (!phantomicity.isValid) NoSymbol
813+
else if (phantomicity.erasedArity > 22) defn.FunctionXXLClass
814+
else defn.FunctionClass(phantomicity.erasedArity)
815+
}
816+
817+
/** Returns the erased class of the function class `cls`
818+
* - PhantomFunctionM becomes FunctionN where N is the number of non phantoms in M
819+
* - ImplicitPhantomFunctionM becomes ImplicitFunctionN where N is the number of non phantoms in M
820+
* - cls otherwise
821+
*/
822+
def erasedPhantomsFunctionClass(cls: Symbol): Symbol = {
823+
val phantomicity = scalaClassName(cls).phantomicity
824+
if (!phantomicity.isValid) cls
825+
else defn.FunctionClass(phantomicity.erasedArity, cls.name.isImplicitFunction)
774826
}
775827

776828
/** Returns the erased type of the function class `cls`
777-
* - FunctionN for N > 22 becomes FunctionXXL
778829
* - FunctionN for 22 > N >= 0 remains as FunctionN
779-
* - ImplicitFunctionN for N > 22 becomes FunctionXXL
830+
* - FunctionN for N > 22 becomes FunctionXXL
780831
* - ImplicitFunctionN for 22 > N >= 0 becomes FunctionN
832+
* - ImplicitFunctionN for N > 22 becomes FunctionXXL
781833
* - anything else becomes a NoType
782834
*/
783835
def erasedFunctionType(cls: Symbol): Type = {
784-
val arity = scalaClassName(cls).functionArity
785-
if (arity > 22) defn.FunctionXXLType
786-
else if (arity >= 0) defn.FunctionType(arity)
787-
else NoType
836+
val phantomicity = scalaClassName(cls).phantomicity
837+
if (!phantomicity.isValid) NoType
838+
else if (phantomicity.erasedArity > 22) defn.FunctionXXLType
839+
else defn.FunctionType(phantomicity.erasedArity)
788840
}
789841

790842
val predefClassNames: Set[Name] =
@@ -828,7 +880,10 @@ class Definitions {
828880
* trait gets screwed up. Therefore, it is mandatory that FunctionXXL
829881
* is treated as a NoInit trait.
830882
*/
831-
lazy val NoInitClasses = NotRuntimeClasses + FunctionXXLClass
883+
private lazy val NoInitClasses = NotRuntimeClasses + FunctionXXLClass
884+
885+
def isNoInitClass(cls: Symbol): Boolean =
886+
cls.is(NoInitsTrait) || NoInitClasses.contains(cls) || isFunctionClass(cls)
832887

833888
def isPolymorphicAfterErasure(sym: Symbol) =
834889
(sym eq Any_isInstanceOf) || (sym eq Any_asInstanceOf)
@@ -857,7 +912,7 @@ class Definitions {
857912
def isFunctionType(tp: Type)(implicit ctx: Context) = {
858913
val arity = functionArity(tp)
859914
val sym = tp.dealias.typeSymbol
860-
arity >= 0 && isFunctionClass(sym) && tp.isRef(FunctionType(arity, sym.name.isImplicitFunction).typeSymbol)
915+
arity >= 0 && isFunctionClass(sym) && tp.isRef(FunctionType(sym.name.phantomicity, sym.name.isImplicitFunction).typeSymbol)
861916
}
862917

863918
def functionArity(tp: Type)(implicit ctx: Context) = tp.dealias.argInfos.length - 1
@@ -1013,14 +1068,16 @@ class Definitions {
10131068
def isPhantomAssume(sym: Symbol)(implicit ctx: Context): Boolean =
10141069
sym.exists && (sym.owner eq PhantomClass) && sym.name == nme.assume_
10151070

1016-
def topOf(tp: Type)(implicit ctx: Context): Type = tp.phantomTopClass match {
1017-
case top: ClassInfo => top.prefix.select(tpnme.Any)
1018-
case _ => defn.AnyType
1071+
def topOf(tp: Type)(implicit ctx: Context): Type = {
1072+
val lattice = tp.phantomLatticeClass
1073+
if (lattice.exists) lattice.select(tpnme.Any)
1074+
else defn.AnyType
10191075
}
10201076

1021-
def bottomOf(tp: Type)(implicit ctx: Context): Type = tp.phantomTopClass match {
1022-
case top: ClassInfo => top.prefix.select(tpnme.Nothing)
1023-
case _ => defn.NothingType
1077+
def bottomOf(tp: Type)(implicit ctx: Context): Type = {
1078+
val lattice = tp.phantomLatticeClass
1079+
if (lattice.exists) lattice.select(tpnme.Nothing)
1080+
else defn.NothingType
10241081
}
10251082

10261083
lazy val ErasedPhantomClass = ctx.requiredClass("dotty.runtime.ErasedPhantom")

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

+43-9
Original file line numberDiff line numberDiff line change
@@ -232,35 +232,63 @@ object NameOps {
232232
}
233233
}
234234

235-
/** Is a synthetic function name
235+
/** Return the function arity
236236
* - N for FunctionN
237237
* - N for ImplicitFunctionN
238-
* - (-1) otherwise
238+
* - N for PhantomFunctionM where N is the length of M
239+
* - N for ImplicitPhantomFunctionM where N is the length of M
240+
* - (-1) otherwise
239241
*/
240-
def functionArity: Int =
241-
functionArityFor(tpnme.Function) max functionArityFor(tpnme.ImplicitFunction)
242+
def functionArity(implicit ctx: Context): Int = phantomicity.arity
243+
244+
/** Checks and returns the phantomicity of the function */
245+
def phantomicity(implicit ctx: Context): Phantomicity = {
246+
val arity = functionArityFor(tpnme.Function) max functionArityFor(tpnme.ImplicitFunction)
247+
if (arity >= 0) Phantomicity.noPhantoms(arity)
248+
else {
249+
val phantomicity = phantomFunctionPhantomicity(tpnme.PhantomFunction)
250+
if (phantomicity.isValid) phantomicity
251+
else phantomFunctionPhantomicity(tpnme.ImplicitPhantomFunction)
252+
}
253+
}
242254

243255
/** Is a function name
244256
* - FunctionN for N >= 0
245257
* - ImplicitFunctionN for N >= 0
258+
* - PhantomFunctionM for a valid M
259+
* - ImplicitPhantomFunctionM for a valid M
246260
* - false otherwise
247261
*/
248-
def isFunction: Boolean = functionArity >= 0
262+
def isFunction(implicit ctx: Context): Boolean = functionArity >= 0
249263

250264
/** Is a implicit function name
251265
* - ImplicitFunctionN for N >= 0
266+
* - ImplicitPhantomFunctionM for a valid M
267+
* - false otherwise
268+
*/
269+
def isImplicitFunction: Boolean = {
270+
functionArityFor(tpnme.ImplicitFunction) >= 0 ||
271+
phantomFunctionPhantomicity(tpnme.ImplicitPhantomFunction).isValid
272+
}
273+
274+
/** Is a phantom function name
275+
* - PhantomFunctionM for a valid M
276+
* - ImplicitPhantomFunctionM for a valid M
252277
* - false otherwise
253278
*/
254-
def isImplicitFunction: Boolean = functionArityFor(tpnme.ImplicitFunction) >= 0
279+
def isPhantomFunction(implicit ctx: Context): Boolean = phantomicity.hasPhantoms
255280

256281
/** Is a synthetic function name
257282
* - FunctionN for N > 22
258283
* - ImplicitFunctionN for N >= 0
284+
* - PhantomFunctionM for a valid M
285+
* - ImplicitPhantomFunctionM for a valid M
259286
* - false otherwise
260287
*/
261-
def isSyntheticFunction: Boolean = {
262-
functionArityFor(tpnme.Function) > MaxImplementedFunctionArity ||
263-
functionArityFor(tpnme.ImplicitFunction) >= 0
288+
def isSyntheticFunction(implicit ctx: Context): Boolean = {
289+
val p = phantomicity
290+
if (name.startsWith(tpnme.Function)) p.arity > MaxImplementedFunctionArity
291+
else p.isValid
264292
}
265293

266294
/** Parsed function arity for function with some specific prefix */
@@ -271,6 +299,12 @@ object NameOps {
271299
else -1
272300
}
273301

302+
/** Parsed function phantomicity for function with some specific prefix */
303+
private def phantomFunctionPhantomicity(prefix: Name): Phantomicity = {
304+
lazy val p = Phantomicity.from(name.toString.substring(prefix.length))
305+
if (name.startsWith(prefix) && p.isValid) p
306+
else Phantomicity.invalid
307+
}
274308

275309
/** The number of hops specified in an outer-select name */
276310
def outerSelectHops: Int = {

0 commit comments

Comments
 (0)