Skip to content

Commit 9c83c67

Browse files
committed
Add support for functions with phantom parameters
1 parent 04bbfe7 commit 9c83c67

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

Lines changed: 99 additions & 42 deletions
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)
@@ -635,7 +650,7 @@ class Definitions {
635650

636651
object FunctionOf {
637652
def apply(args: List[Type], resultType: Type, isImplicit: Boolean = false)(implicit ctx: Context) =
638-
FunctionType(args.length, isImplicit).appliedTo(args ::: resultType :: Nil)
653+
FunctionType(Phantomicity(args :+ resultType), isImplicit).appliedTo(args ::: resultType :: Nil)
639654
def unapply(ft: Type)(implicit ctx: Context) = {
640655
val tsym = ft.typeSymbol
641656
if (isFunctionClass(tsym)) {
@@ -693,18 +708,30 @@ class Definitions {
693708
lazy val TupleType = mkArityArray("scala.Tuple", MaxTupleArity, 2)
694709
lazy val ProductNType = mkArityArray("scala.Product", MaxTupleArity, 0)
695710

696-
def FunctionClass(n: Int, isImplicit: Boolean = false)(implicit ctx: Context) =
711+
def FunctionClass(n: Int, isImplicit: Boolean = false)(implicit ctx: Context): Symbol =
697712
if (isImplicit) ctx.requiredClass("scala.ImplicitFunction" + n.toString)
698713
else if (n <= MaxImplementedFunctionArity) FunctionClassPerRun()(ctx)(n)
699714
else ctx.requiredClass("scala.Function" + n.toString)
700715

716+
def FunctionClass(phantomicity: Phantomicity, isImplicit: Boolean)(implicit ctx: Context): Symbol = {
717+
if (phantomicity.hasPhantoms) {
718+
val prefix = if (isImplicit) "scala.ImplicitPhantomFunction" else "scala.PhantomFunction"
719+
ctx.requiredClass(prefix + phantomicity.encodedString)
720+
} else FunctionClass(phantomicity.arity, isImplicit)
721+
}
722+
701723
lazy val Function0_applyR = ImplementedFunctionType(0).symbol.requiredMethodRef(nme.apply)
702724
def Function0_apply(implicit ctx: Context) = Function0_applyR.symbol
703725

704726
def FunctionType(n: Int, isImplicit: Boolean = false)(implicit ctx: Context): TypeRef =
705727
if (n <= MaxImplementedFunctionArity && (!isImplicit || ctx.erasedTypes)) ImplementedFunctionType(n)
706728
else FunctionClass(n, isImplicit).typeRef
707729

730+
def FunctionType(phantomicity: Phantomicity, isImplicit: Boolean)(implicit ctx: Context): TypeRef = {
731+
if (phantomicity.hasPhantoms) FunctionClass(phantomicity, isImplicit).typeRef
732+
else FunctionType(phantomicity.arity, isImplicit)
733+
}
734+
708735
private lazy val TupleTypes: Set[TypeRef] = TupleType.toSet
709736
private lazy val ProductTypes: Set[TypeRef] = ProductNType.toSet
710737

@@ -730,23 +757,37 @@ class Definitions {
730757
/** Is a function class.
731758
* - FunctionN for N >= 0
732759
* - ImplicitFunctionN for N >= 0
760+
* - PhantomFunctionM for a valid M
761+
* - ImplicitPhantomFunctionM for a valid M
733762
*/
734763
def isFunctionClass(cls: Symbol) = scalaClassName(cls).isFunction
735764

736765
/** Is an implicit function class.
737766
* - ImplicitFunctionN for N >= 0
767+
* - ImplicitPhantomFunctionN for a valid M
738768
*/
739769
def isImplicitFunctionClass(cls: Symbol) = scalaClassName(cls).isImplicitFunction
740770

771+
/** Is a phantom function class.
772+
* - PhantomFunctionM for a valid M
773+
* - ImplicitPhantomFunctionM for a valid M
774+
*/
775+
def isPhantomFunctionClass(cls: Symbol) = scalaClassName(cls).isPhantomFunction
776+
741777
/** Is a class that will be erased to FunctionXXL
742778
* - FunctionN for N >= 22
743779
* - ImplicitFunctionN for N >= 22
780+
* - PhantomFunctionM for N >= 22, where N is the number of non phantoms in M
781+
* - ImplicitPhantomFunctionM for N >= 22, where N is the number of non phantoms in M
744782
*/
745-
def isXXLFunctionClass(cls: Symbol) = scalaClassName(cls).functionArity > MaxImplementedFunctionArity
783+
def isXXLFunctionClass(cls: Symbol) =
784+
scalaClassName(cls).phantomicity.erasedArity > MaxImplementedFunctionArity
746785

747786
/** Is a synthetic function class
748787
* - FunctionN for N > 22
749788
* - ImplicitFunctionN for N >= 0
789+
* - PhantomFunctionM for a valid M
790+
* - ImplicitPhantomFunctionM for a valid M
750791
*/
751792
def isSyntheticFunctionClass(cls: Symbol) = scalaClassName(cls).isSyntheticFunction
752793

@@ -755,31 +796,42 @@ class Definitions {
755796
def isProductClass(cls: Symbol) = isVarArityClass(cls, tpnme.Product)
756797

757798
/** Returns the erased class of the function class `cls`
758-
* - FunctionN for N > 22 becomes FunctionXXL
759799
* - FunctionN for 22 > N >= 0 remains as FunctionN
760-
* - ImplicitFunctionN for N > 22 becomes FunctionXXL
800+
* - FunctionN for N > 22 becomes FunctionXXL
761801
* - ImplicitFunctionN for 22 > N >= 0 becomes FunctionN
802+
* - ImplicitFunctionN for N > 22 becomes FunctionXXL
762803
* - anything else becomes a NoSymbol
763804
*/
764805
def erasedFunctionClass(cls: Symbol): Symbol = {
765-
val arity = scalaClassName(cls).functionArity
766-
if (arity > 22) defn.FunctionXXLClass
767-
else if (arity >= 0) defn.FunctionClass(arity)
768-
else NoSymbol
806+
val phantomicity = scalaClassName(cls).phantomicity
807+
if (!phantomicity.isValid) NoSymbol
808+
else if (phantomicity.erasedArity > 22) defn.FunctionXXLClass
809+
else defn.FunctionClass(phantomicity.erasedArity)
810+
}
811+
812+
/** Returns the erased class of the function class `cls`
813+
* - PhantomFunctionM becomes FunctionN where N is the number of non phantoms in M
814+
* - ImplicitPhantomFunctionM becomes ImplicitFunctionN where N is the number of non phantoms in M
815+
* - cls otherwise
816+
*/
817+
def erasedPhantomsFunctionClass(cls: Symbol): Symbol = {
818+
val phantomicity = scalaClassName(cls).phantomicity
819+
if (!phantomicity.isValid) cls
820+
else defn.FunctionClass(phantomicity.erasedArity, cls.name.isImplicitFunction)
769821
}
770822

771823
/** Returns the erased type of the function class `cls`
772-
* - FunctionN for N > 22 becomes FunctionXXL
773824
* - FunctionN for 22 > N >= 0 remains as FunctionN
774-
* - ImplicitFunctionN for N > 22 becomes FunctionXXL
825+
* - FunctionN for N > 22 becomes FunctionXXL
775826
* - ImplicitFunctionN for 22 > N >= 0 becomes FunctionN
827+
* - ImplicitFunctionN for N > 22 becomes FunctionXXL
776828
* - anything else becomes a NoType
777829
*/
778830
def erasedFunctionType(cls: Symbol): Type = {
779-
val arity = scalaClassName(cls).functionArity
780-
if (arity > 22) defn.FunctionXXLType
781-
else if (arity >= 0) defn.FunctionType(arity)
782-
else NoType
831+
val phantomicity = scalaClassName(cls).phantomicity
832+
if (!phantomicity.isValid) NoType
833+
else if (phantomicity.erasedArity > 22) defn.FunctionXXLType
834+
else defn.FunctionType(phantomicity.erasedArity)
783835
}
784836

785837
val predefClassNames: Set[Name] =
@@ -823,7 +875,10 @@ class Definitions {
823875
* trait gets screwed up. Therefore, it is mandatory that FunctionXXL
824876
* is treated as a NoInit trait.
825877
*/
826-
lazy val NoInitClasses = NotRuntimeClasses + FunctionXXLClass
878+
private lazy val NoInitClasses = NotRuntimeClasses + FunctionXXLClass
879+
880+
def isNoInitClass(cls: Symbol): Boolean =
881+
cls.is(NoInitsTrait) || NoInitClasses.contains(cls) || isFunctionClass(cls)
827882

828883
def isPolymorphicAfterErasure(sym: Symbol) =
829884
(sym eq Any_isInstanceOf) || (sym eq Any_asInstanceOf)
@@ -852,7 +907,7 @@ class Definitions {
852907
def isFunctionType(tp: Type)(implicit ctx: Context) = {
853908
val arity = functionArity(tp)
854909
val sym = tp.dealias.typeSymbol
855-
arity >= 0 && isFunctionClass(sym) && tp.isRef(FunctionType(arity, sym.name.isImplicitFunction).typeSymbol)
910+
arity >= 0 && isFunctionClass(sym) && tp.isRef(FunctionType(sym.name.phantomicity, sym.name.isImplicitFunction).typeSymbol)
856911
}
857912

858913
def functionArity(tp: Type)(implicit ctx: Context) = tp.dealias.argInfos.length - 1
@@ -1008,14 +1063,16 @@ class Definitions {
10081063
def isPhantomAssume(sym: Symbol)(implicit ctx: Context): Boolean =
10091064
sym.exists && (sym.owner eq PhantomClass) && sym.name == nme.assume_
10101065

1011-
def topOf(tp: Type)(implicit ctx: Context): Type = tp.phantomTopClass match {
1012-
case top: ClassInfo => top.prefix.select(tpnme.Any)
1013-
case _ => defn.AnyType
1066+
def topOf(tp: Type)(implicit ctx: Context): Type = {
1067+
val lattice = tp.phantomLatticeClass
1068+
if (lattice.exists) lattice.select(tpnme.Any)
1069+
else defn.AnyType
10141070
}
10151071

1016-
def bottomOf(tp: Type)(implicit ctx: Context): Type = tp.phantomTopClass match {
1017-
case top: ClassInfo => top.prefix.select(tpnme.Nothing)
1018-
case _ => defn.NothingType
1072+
def bottomOf(tp: Type)(implicit ctx: Context): Type = {
1073+
val lattice = tp.phantomLatticeClass
1074+
if (lattice.exists) lattice.select(tpnme.Nothing)
1075+
else defn.NothingType
10191076
}
10201077

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

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

Lines changed: 43 additions & 9 deletions
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)