Skip to content

Commit 487e143

Browse files
committed
WIP Add support for functions with phantom parameters
working prototype of phantom functions with multiple phantom lattices
1 parent 1146d31 commit 487e143

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
-63
lines changed

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

Lines changed: 99 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,14 @@ class Definitions {
6161
private def enterCompleteClassSymbol(owner: Symbol, name: TypeName, flags: FlagSet, parents: List[TypeRef], decls: Scope = newScope) =
6262
ctx.newCompleteClassSymbol(owner, name, flags | Permanent, parents, decls).entered
6363

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

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

7070
private def enterSyntheticTypeParam(cls: ClassSymbol, paramFlags: FlagSet, scope: MutableScope, suffix: String = "T0") =
71-
enterTypeParam(cls, suffix.toTypeName.expandedName(cls), ExpandedName | paramFlags, scope)
71+
enterTypeParam(cls, suffix.toTypeName.expandedName(cls), ExpandedName | paramFlags, scope, TypeBounds.empty)
7272

7373
// NOTE: Ideally we would write `parentConstrs: => Type*` but SIP-24 is only
7474
// implemented in Dotty and not in Scala 2.
@@ -90,7 +90,7 @@ class Definitions {
9090
newClassSymbol(ScalaPackageClass, name, EmptyFlags, completer).entered
9191
}
9292

93-
/** The trait FunctionN or ImplicitFunctionN, for some N
93+
/** The trait FunctionN, ImplicitFunctionN, PhantomFunctionM, ImplicitPhantomFunctionM, for some N
9494
* @param name The name of the trait to be created
9595
*
9696
* FunctionN traits follow this template:
@@ -108,30 +108,45 @@ class Definitions {
108108
* trait ImplicitFunctionN[T0,...,T{N-1}, R] extends Object with FunctionN[T0,...,T{N-1}, R] {
109109
* def apply(implicit $x0: T0, ..., $x{N_1}: T{N-1}): R
110110
* }
111+
*
112+
* PhantomFunctionM traits follow this template:
113+
*
114+
* trait PhantomFunctionM[T0,...T{N-1}, R] extends Object {
115+
* def apply($x0: T0, ..., $x{N_1}: T{N-1}): R
116+
* }
117+
*
118+
* where M represents the phantomicity of all Ti.
119+
*
120+
* ImplicitPhantomFunctionM traits follow this template:
121+
*
122+
* trait ImplicitPhantomFunctionM[T0,...,T{N-1}, R] extends Object with PhantomFunctionM[T0,...,T{N-1}, R] {
123+
* def apply(implicit $x0: T0, ..., $x{N_1}: T{N-1}): R
124+
* }
125+
*
126+
* where M represents the phantomicity of all Ti.
127+
*
111128
*/
112129
private def newFunctionNTrait(name: TypeName) = {
113130
val completer = new LazyType {
114131
def complete(denot: SymDenotation)(implicit ctx: Context): Unit = {
115132
val cls = denot.asClass.classSymbol
116133
val decls = newScope
117-
val arity = name.functionArity
134+
val phantomicity = name.phantomicity
135+
val arity = phantomicity.arity
118136
val argParams =
119137
for (i <- List.range(0, arity)) yield
120-
enterTypeParam(cls, name ++ "$T" ++ i.toString, Contravariant, decls)
121-
val resParam = enterTypeParam(cls, name ++ "$R", Covariant, decls)
138+
enterTypeParam(cls, name ++ "$T" ++ i.toString, Contravariant, decls, phantomicity.tParamBounds(i)).typeRef
139+
val resParam = enterTypeParam(cls, name ++ "$R", Covariant, decls, phantomicity.tParamBounds(arity)).typeRef
122140
val (methodType, parentTraits) =
123-
if (name.startsWith(tpnme.ImplicitFunction)) {
124-
val superTrait =
125-
FunctionType(arity).appliedTo(argParams.map(_.typeRef) ::: resParam.typeRef :: Nil)
126-
(ImplicitMethodType, ctx.normalizeToClassRefs(superTrait :: Nil, cls, decls))
141+
if (name.isImplicitFunction) {
142+
val superTrait = FunctionType(phantomicity, isImplicit = false)
143+
val appliedSuperTrait = superTrait.appliedTo(argParams ::: resParam :: Nil)
144+
(ImplicitMethodType, ctx.normalizeToClassRefs(appliedSuperTrait :: Nil, cls, decls))
127145
}
128146
else (MethodType, Nil)
129-
val applyMeth =
130-
decls.enter(
131-
newMethod(cls, nme.apply,
132-
methodType(argParams.map(_.typeRef), resParam.typeRef), Deferred))
133-
denot.info =
134-
ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: parentTraits, decls)
147+
148+
decls.enter(newMethod(cls, nme.apply, methodType(argParams, resParam), Deferred))
149+
denot.info = ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: parentTraits, decls)
135150
}
136151
}
137152
newClassSymbol(ScalaPackageClass, name, Trait | NoInits, completer)
@@ -636,7 +651,7 @@ class Definitions {
636651

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

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

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

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

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

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

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

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

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

@@ -756,31 +797,42 @@ class Definitions {
756797
def isProductClass(cls: Symbol) = isVarArityClass(cls, tpnme.Product)
757798

758799
/** Returns the erased class of the function class `cls`
759-
* - FunctionN for N > 22 becomes FunctionXXL
760800
* - FunctionN for 22 > N >= 0 remains as FunctionN
761-
* - ImplicitFunctionN for N > 22 becomes FunctionXXL
801+
* - FunctionN for N > 22 becomes FunctionXXL
762802
* - ImplicitFunctionN for 22 > N >= 0 becomes FunctionN
803+
* - ImplicitFunctionN for N > 22 becomes FunctionXXL
763804
* - anything else becomes a NoSymbol
764805
*/
765806
def erasedFunctionClass(cls: Symbol): Symbol = {
766-
val arity = scalaClassName(cls).functionArity
767-
if (arity > 22) defn.FunctionXXLClass
768-
else if (arity >= 0) defn.FunctionClass(arity)
769-
else NoSymbol
807+
val phantomicity = scalaClassName(cls).phantomicity
808+
if (!phantomicity.isValid) NoSymbol
809+
else if (phantomicity.erasedArity > 22) defn.FunctionXXLClass
810+
else defn.FunctionClass(phantomicity.erasedArity)
811+
}
812+
813+
/** Returns the erased class of the function class `cls`
814+
* - PhantomFunctionM becomes FunctionN where N is the number of non phantoms in M
815+
* - ImplicitPhantomFunctionM becomes ImplicitFunctionN where N is the number of non phantoms in M
816+
* - cls otherwise
817+
*/
818+
def erasedPhantomsFunctionClass(cls: Symbol): Symbol = {
819+
val phantomicity = scalaClassName(cls).phantomicity
820+
if (!phantomicity.isValid) cls
821+
else defn.FunctionClass(phantomicity.erasedArity, cls.name.isImplicitFunction)
770822
}
771823

772824
/** Returns the erased type of the function class `cls`
773-
* - FunctionN for N > 22 becomes FunctionXXL
774825
* - FunctionN for 22 > N >= 0 remains as FunctionN
775-
* - ImplicitFunctionN for N > 22 becomes FunctionXXL
826+
* - FunctionN for N > 22 becomes FunctionXXL
776827
* - ImplicitFunctionN for 22 > N >= 0 becomes FunctionN
828+
* - ImplicitFunctionN for N > 22 becomes FunctionXXL
777829
* - anything else becomes a NoType
778830
*/
779831
def erasedFunctionType(cls: Symbol): Type = {
780-
val arity = scalaClassName(cls).functionArity
781-
if (arity > 22) defn.FunctionXXLType
782-
else if (arity >= 0) defn.FunctionType(arity)
783-
else NoType
832+
val phantomicity = scalaClassName(cls).phantomicity
833+
if (!phantomicity.isValid) NoType
834+
else if (phantomicity.erasedArity > 22) defn.FunctionXXLType
835+
else defn.FunctionType(phantomicity.erasedArity)
784836
}
785837

786838
val predefClassNames: Set[Name] =
@@ -824,7 +876,10 @@ class Definitions {
824876
* trait gets screwed up. Therefore, it is mandatory that FunctionXXL
825877
* is treated as a NoInit trait.
826878
*/
827-
lazy val NoInitClasses = NotRuntimeClasses + FunctionXXLClass
879+
private lazy val NoInitClasses = NotRuntimeClasses + FunctionXXLClass
880+
881+
def isNoInitClass(cls: Symbol): Boolean =
882+
cls.is(NoInitsTrait) || NoInitClasses.contains(cls) || isFunctionClass(cls)
828883

829884
def isPolymorphicAfterErasure(sym: Symbol) =
830885
(sym eq Any_isInstanceOf) || (sym eq Any_asInstanceOf)
@@ -853,7 +908,7 @@ class Definitions {
853908
def isFunctionType(tp: Type)(implicit ctx: Context) = {
854909
val arity = functionArity(tp)
855910
val sym = tp.dealias.typeSymbol
856-
arity >= 0 && isFunctionClass(sym) && tp.isRef(FunctionType(arity, sym.name.isImplicitFunction).typeSymbol)
911+
arity >= 0 && isFunctionClass(sym) && tp.isRef(FunctionType(sym.name.phantomicity, sym.name.isImplicitFunction).typeSymbol)
857912
}
858913

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

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

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

10221079
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)