Skip to content
This repository was archived by the owner on Sep 1, 2020. It is now read-only.

Commit 7ff86fc

Browse files
mandubianmilessabin
authored andcommitted
providing minimalistic & controlled kind-polymorphism
1 parent 916320e commit 7ff86fc

Some content is hidden

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

54 files changed

+2294
-33
lines changed

bincompat-forward.whitelist.conf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ filter {
110110
{
111111
matchName="scala.reflect.runtime.Settings.YinductionHeuristics"
112112
problemName=MissingMethodProblem
113+
},
114+
{
115+
matchName="scala.reflect.runtime.Settings.YkindPolymorphism"
116+
problemName=DirectMissingMethodProblem
113117
}
114118
]
115119
}

src/compiler/scala/reflect/reify/Reifier.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ abstract class Reifier extends States
110110
// needs to be solved some day
111111
// upd. a new hope: https://groups.google.com/forum/#!topic/scala-internals/TtCTPlj_qcQ
112112
var importantSymbols = Set[Symbol](
113-
NothingClass, AnyClass, SingletonClass, PredefModule, ScalaRunTimeModule, TypeCreatorClass, TreeCreatorClass, MirrorClass,
113+
NothingClass, AnyClass, SingletonClass, AnyKindClass, PredefModule, ScalaRunTimeModule, TypeCreatorClass, TreeCreatorClass, MirrorClass,
114114
ApiUniverseClass, JavaUniverseClass, ReflectRuntimePackage, runDefinitions.ReflectRuntimeCurrentMirror)
115115
importantSymbols ++= importantSymbols map (_.companionSymbol)
116116
importantSymbols ++= importantSymbols map (_.moduleClass)

src/compiler/scala/tools/nsc/settings/ScalaSettings.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ trait ScalaSettings extends AbsScalaSettings
3030
protected def defaultClasspath = sys.env.getOrElse("CLASSPATH", ".")
3131

3232
/** Enabled under -Xexperimental. */
33-
protected def experimentalSettings = List[BooleanSetting](YpartialUnification, YinductionHeuristics)
33+
protected def experimentalSettings = List[BooleanSetting](YpartialUnification, YinductionHeuristics, YkindPolymorphism)
3434

3535
/** Enabled under -Xfuture. */
3636
protected def futureSettings = List[BooleanSetting]()
@@ -222,6 +222,7 @@ trait ScalaSettings extends AbsScalaSettings
222222
val YpartialUnification = BooleanSetting ("-Ypartial-unification", "Enable partial unification in type constructor inference")
223223
val Yvirtpatmat = BooleanSetting ("-Yvirtpatmat", "Enable pattern matcher virtualization")
224224
val YinductionHeuristics = BooleanSetting ("-Yinduction-heuristics", "Enable induction heuristics in implicit resolution")
225+
val YkindPolymorphism = BooleanSetting ("-Ykind-polymorphism", "Enable kind polymorphism")
225226

226227
val exposeEmptyPackage = BooleanSetting ("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly()
227228
val Ydelambdafy = ChoiceSetting ("-Ydelambdafy", "strategy", "Strategy used for translating lambdas into JVM code.", List("inline", "method"), "method")

src/compiler/scala/tools/nsc/transform/Erasure.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ abstract class Erasure extends InfoTransform
146146
if (sym == ArrayClass && args.nonEmpty)
147147
if (unboundedGenericArrayLevel(tp1) == 1) ObjectTpe
148148
else mapOver(tp1)
149-
else if (sym == AnyClass || sym == AnyValClass || sym == SingletonClass)
149+
else if (sym == AnyClass || sym == AnyValClass || sym == SingletonClass || sym == AnyKindClass)
150150
ObjectTpe
151151
else if (sym == UnitClass)
152152
BoxedUnitTpe
@@ -308,7 +308,7 @@ abstract class Erasure extends InfoTransform
308308
assert(!sym.isAliasType, "Unexpected alias type: " + sym)
309309
"" + TVAR_TAG + sym.name + ";"
310310
}
311-
else if (sym == AnyClass || sym == AnyValClass || sym == SingletonClass)
311+
else if (sym == AnyClass || sym == AnyValClass || sym == SingletonClass || sym == AnyKindClass)
312312
jsig(ObjectTpe)
313313
else if (sym == UnitClass)
314314
jsig(BoxedUnitTpe)

src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,6 @@ trait ContextErrors {
226226
}
227227
assert(!foundType.isErroneous, s"AdaptTypeError - foundType is Erroneous: $foundType")
228228
assert(!req.isErroneous, s"AdaptTypeError - req is Erroneous: $req")
229-
230229
issueNormalTypeError(callee, withAddendum(callee.pos)(typeErrorMsg(foundType, req)))
231230
infer.explainTypes(foundType, req)
232231
}
@@ -695,6 +694,38 @@ trait ContextErrors {
695694
setError(tree)
696695
}
697696

697+
def InferredReturnTypeError(tree: Tree, pt: Type) = {
698+
issueNormalTypeError(tree, s"inferred result type ($pt) takes type parameters")
699+
setError(tree)
700+
}
701+
702+
def AnyKindTypeError(tree: Tree) = {
703+
issueNormalTypeError(tree, s"AnyKind can't be used as a real type")
704+
setError(tree)
705+
}
706+
707+
def KindPolymorphicKindArityMismatchError(tree: Tree, pt: Type, targs: List[Type], badTParamArgs: List[(Symbol, Type)], appliedType: Type) = {
708+
def typeKindStr(tp: Type): String = {
709+
if(tp.typeParams.isEmpty) "*"
710+
else tp.typeParams.map(p => symKindStr(p)+"->").mkString("(", "", "*)")
711+
}
712+
713+
def symKindStr(tp: Symbol): String = {
714+
if(tp.typeParams.isEmpty) "*"
715+
else tp.typeParams.map(p => symKindStr(p)+"->").mkString("(", "", "*)")
716+
}
717+
718+
issueNormalTypeError(tree, {
719+
val kindStrs = badTParamArgs.map { case (tparam, targ) =>
720+
val kind1 = symKindStr(tparam)
721+
val kind2 = typeKindStr(targ)
722+
s"${tparam}[$kind1]<=>$targ[$kind2]"
723+
}
724+
s"[Kind-Polymorphic Error] $tree inferred to type ${pt} with illegally kinded type arguments ${targs.mkString(", ")} applied to type ${appliedType} ${if(kindStrs.nonEmpty) kindStrs.mkString("(kind errors: ", ", ", ")") else ""}"
725+
})
726+
setError(tree)
727+
}
728+
698729
def KindArityMismatchError(tree: Tree, pt: Type) = {
699730
issueNormalTypeError(tree,
700731
tree.tpe+" takes "+countElementsAsString(tree.tpe.typeParams.length, "type parameter")+

src/compiler/scala/tools/nsc/typechecker/RefChecks.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,10 @@ abstract class RefChecks extends Transform {
466466
overrideError("cannot be used here - term macros cannot override abstract methods")
467467
} else if (other.isTermMacro && !member.isTermMacro) { // (1.10)
468468
overrideError("cannot be used here - only term macros can override term macros")
469-
} else {
469+
} else if (other.tpe.isComplete && other.tpe.typeSymbol.isNonBottomSubClass(definitions.AnyKindClass)) {
470+
// a AnyKind bounded type can be overriden by a type not bounded by AnyKind (this is the purpose of it)
471+
}
472+
else {
470473
checkOverrideTypes()
471474
checkOverrideDeprecated()
472475
if (settings.warnNullaryOverride) {

src/compiler/scala/tools/nsc/typechecker/Typers.scala

Lines changed: 74 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
170170

171171
val res =
172172
if (paramFailed || (paramTp.isErroneous && {paramFailed = true; true})) SearchFailure
173-
else inferImplicitFor(paramTp, fun, context, reportAmbiguous = context.reportErrors)
173+
else {
174+
inferImplicitFor(paramTp, fun, context, reportAmbiguous = context.reportErrors)
175+
}
176+
174177
argResultsBuff += res
175178

176179
if (res.isSuccess) {
@@ -590,7 +593,6 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
590593
* 4. Give getClass calls a more precise type based on the type of the target of the call.
591594
*/
592595
protected def stabilize(tree: Tree, pre: Type, mode: Mode, pt: Type): Tree = {
593-
594596
// Side effect time! Don't be an idiot like me and think you
595597
// can move "val sym = tree.symbol" before this line, because
596598
// inferExprAlternative side-effects the tree's symbol.
@@ -828,7 +830,6 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
828830
protected def adapt(tree: Tree, mode: Mode, pt: Type, original: Tree = EmptyTree): Tree = {
829831
def hasUndets = context.undetparams.nonEmpty
830832
def hasUndetsInMonoMode = hasUndets && !mode.inPolyMode
831-
832833
def adaptToImplicitMethod(mt: MethodType): Tree = {
833834
if (hasUndets) { // (9) -- should revisit dropped condition `hasUndetsInMonoMode`
834835
// dropped so that type args of implicit method are inferred even if polymorphic expressions are allowed
@@ -965,20 +966,51 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
965966
case _ => pt == WildcardType
966967
}
967968

969+
// should be recursive for checking kind of all subtypes
970+
def kpBadKindArityMatches(tparams: List[Symbol], targs: List[Type]): List[(Symbol, Type)] = {
971+
tparams.zip(targs).flatMap { case (tparam, targ) =>
972+
if (isAnyKind(tparam.tpe)) List()
973+
else if (!sameLength(targ.typeParams, tparam.typeParams)) List(tparam -> targ)
974+
else kpBadKindArityMatches(tparam.typeParams, targ.typeParams.map(_.tpe))
975+
}
976+
}
977+
968978
// todo. It would make sense when mode.inFunMode to instead use
969979
// tree setType tree.tpe.normalize
970980
// when typechecking, say, TypeApply(Ident(`some abstract type symbol`), List(...))
971981
// because otherwise Ident will have its tpe set to a TypeRef, not to a PolyType, and `typedTypeApply` will fail
972982
// but this needs additional investigation, because it crashes t5228, gadts1 and maybe something else
973983
if (mode.inFunMode)
974984
tree
975-
else if (properTypeRequired && tree.symbol.typeParams.nonEmpty) // (7)
985+
else if (properTypeRequired && tree.symbol.typeParams.nonEmpty) // (7)
976986
MissingTypeParametersError(tree)
977-
else if (kindArityMismatch && !kindArityMismatchOk) // (7.1) @M: check kind-arity
987+
else if (kindArityMismatch && !kindArityMismatchOk) // (7.1) @M: check kind-arity
978988
KindArityMismatchError(tree, pt)
979989
else tree match { // (6)
980990
case TypeTree() => tree
981-
case _ => TypeTree(tree.tpe) setOriginal tree
991+
case _ =>
992+
if(settings.YkindPolymorphism) {
993+
// we exclude RefinedClasses because in kind-polymorphic cases, it can happen
994+
// type Kinder.Aux[H, M, HL0] = Kinder[H]{type M = M; type Args = HL0} gives
995+
// isRefinementClass:true tree.tpe.typeSymbol:<refinement of Test.Kinder[H]>
996+
// tree.tpe.typeSymbol.typeParams:List()
997+
// tree.tpe.typeArgs:List(H, M, HL0)
998+
//
999+
if(!tree.tpe.typeSymbol.isRefinementClass && tree.tpe.typeSymbol.typeParams.length == 0 && tree.tpe.typeArgs.length > 0) {
1000+
KindPolymorphicKindArityMismatchError(original, tree.tpe, tree.tpe.typeArgs, List(), tree.tpe.typeSymbol.tpe)
1001+
}
1002+
else if(tree.tpe.typeSymbol.typeParams.length > tree.tpe.typeArgs.length && tree.tpe.typeArgs.length > 0) {
1003+
// List() to have a nicer error msg
1004+
KindPolymorphicKindArityMismatchError(original, tree.tpe, tree.tpe.typeArgs, List(), tree.tpe.typeSymbol.tpe)
1005+
}
1006+
else {
1007+
val badKinds = kpBadKindArityMatches(tree.tpe.typeSymbol.typeParams, tree.tpe.typeArgs)
1008+
if(badKinds.nonEmpty) {
1009+
KindPolymorphicKindArityMismatchError(original, tree.tpe, tree.tpe.typeArgs, badKinds, tree.tpe.typeSymbol.tpe)
1010+
} else TypeTree(tree.tpe) setOriginal tree
1011+
}
1012+
}
1013+
else TypeTree(tree.tpe) setOriginal tree
9821014
}
9831015
}
9841016

@@ -1103,11 +1135,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
11031135
// all in one fell swoop at the end of typedFunction?
11041136
val samAttach = inferSamType(tree, pt, mode)
11051137

1138+
11061139
if (samAttach.samTp ne NoType) tree.setType(samAttach.samTp).updateAttachment(samAttach)
11071140
else { // (15) implicit view application
11081141
val coercion =
11091142
if (context.implicitsEnabled) inferView(tree, tree.tpe, pt)
11101143
else EmptyTree
1144+
11111145
if (coercion ne EmptyTree) {
11121146
def msg = s"inferred view from ${tree.tpe} to $pt via $coercion: ${coercion.tpe}"
11131147
if (settings.logImplicitConv) context.echo(tree.pos, msg)
@@ -2270,6 +2304,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
22702304
val tpt1 = checkNoEscaping.privates(meth, typedType(ddef.tpt))
22712305
checkNonCyclic(ddef, tpt1)
22722306
ddef.tpt.setType(tpt1.tpe)
2307+
2308+
// AnyKind shouldn't be usable as a real type
2309+
if(ddef.tpt.tpe eq definitions.AnyKindClass.tpe) {
2310+
AnyKindTypeError(ddef.tpt)
2311+
}
22732312
val typedMods = typedModifiers(ddef.mods)
22742313
var rhs1 =
22752314
if (ddef.name == nme.CONSTRUCTOR && !ddef.symbol.hasStaticFlag) { // need this to make it possible to generate static ctors
@@ -4054,6 +4093,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
40544093
checkCheckable(tree, targs.head, scrutineeType, inPattern = false)
40554094
}
40564095
val resultpe = restpe.instantiateTypeParams(tparams, targs)
4096+
4097+
// when using YkindPolymorphism, in case resulType has been inferred to a Type Contructor or AnyKind,
4098+
// let's inform with a nice error
4099+
if(settings.YkindPolymorphism && (resultpe.resultType.typeParams.nonEmpty || (resultpe.resultType eq definitions.AnyKindClass))) {
4100+
InferredReturnTypeError(fun, resultpe)
4101+
} else
40574102
//@M substitution in instantiateParams needs to be careful!
40584103
//@M example: class Foo[a] { def foo[m[x]]: m[a] = error("") } (new Foo[Int]).foo[List] : List[Int]
40594104
//@M --> first, m[a] gets changed to m[Int], then m gets substituted for List,
@@ -5106,17 +5151,24 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
51065151
if (sameLength(tparams, args)) {
51075152
// @M: kind-arity checking is done here and in adapt, full kind-checking is in checkKindBounds (in Infer)
51085153
val args1 = map2Conserve(args, tparams) { (arg, tparam) =>
5109-
def ptParams = Kind.FromParams(tparam.typeParams)
5110-
5111-
// if symbol hasn't been fully loaded, can't check kind-arity except when we're in a pattern,
5112-
// where we can (we can't take part in F-Bounds) and must (SI-8023)
5113-
val pt = if (mode.typingPatternOrTypePat) {
5114-
tparam.initialize; ptParams
5154+
// if in KindPolymorphism, let's check type is complete, that it is AnyKind and use the inTypeConstructorAllowed mode
5155+
// in order to avoid ProperTypeRequired and let inference happen later
5156+
if(settings.YkindPolymorphism && tparam.rawInfo.isComplete && isAnyKind(tparam.tpe)) {
5157+
typedHigherKindedType(arg, mode)
51155158
}
5116-
else if (isComplete) ptParams
5117-
else Kind.Wildcard
5159+
else {
5160+
def ptParams = Kind.FromParams(tparam.typeParams)
5161+
5162+
// if symbol hasn't been fully loaded, can't check kind-arity except when we're in a pattern,
5163+
// where we can (we can't take part in F-Bounds) and must (SI-8023)
5164+
val pt = if (mode.typingPatternOrTypePat) {
5165+
tparam.initialize; ptParams
5166+
}
5167+
else if (isComplete) ptParams
5168+
else Kind.Wildcard
51185169

5119-
typedHigherKindedType(arg, mode, pt)
5170+
typedHigherKindedType(arg, mode, pt)
5171+
}
51205172
}
51215173
val argtypes = mapList(args1)(treeTpe)
51225174

@@ -5297,7 +5349,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
52975349

52985350
// @M maybe the well-kindedness check should be done when checking the type arguments conform to the type parameters' bounds?
52995351
val args1 = if (sameLength(args, tparams)) map2Conserve(args, tparams) {
5300-
(arg, tparam) => typedHigherKindedType(arg, mode, Kind.FromParams(tparam.typeParams))
5352+
(arg, tparam) =>
5353+
// if in KindPolymorphism, let's check type is complete, that it is AnyKind and use the inTypeConstructorAllowed mode
5354+
// in order to avoid ProperTypeRequired and let inference happen later
5355+
if(settings.YkindPolymorphism && tparam.rawInfo.isComplete && isAnyKind(tparam.tpe)) {
5356+
typedHigherKindedType(arg, mode)
5357+
}
5358+
else typedHigherKindedType(arg, mode, Kind.FromParams(tparam.typeParams))
53015359
}
53025360
else {
53035361
//@M this branch is correctly hit for an overloaded polymorphic type. It also has to handle erroneous cases.

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,9 @@ trait Definitions extends api.StandardDefinitions {
385385
lazy val JavaRepeatedParamClass = specialPolyClass(tpnme.JAVA_REPEATED_PARAM_CLASS_NAME, COVARIANT)(tparam => arrayType(tparam.tpe))
386386
lazy val RepeatedParamClass = specialPolyClass(tpnme.REPEATED_PARAM_CLASS_NAME, COVARIANT)(tparam => seqType(tparam.tpe))
387387

388+
// TODO : requires ABSTRACT | TRAIT | FINAL ????
389+
lazy val AnyKindClass = enterNewClass(ScalaPackageClass, tpnme.AnyKind, AnyTpe :: Nil, ABSTRACT | TRAIT) markAllCompleted
390+
388391
def isByNameParamType(tp: Type) = tp.typeSymbol == ByNameParamClass
389392
def isScalaRepeatedParamType(tp: Type) = tp.typeSymbol == RepeatedParamClass
390393
def isJavaRepeatedParamType(tp: Type) = tp.typeSymbol == JavaRepeatedParamClass
@@ -756,7 +759,7 @@ trait Definitions extends api.StandardDefinitions {
756759
def isStable(tp: Type): Boolean = tp match {
757760
case _: SingletonType => true
758761
case NoPrefix => true
759-
case TypeRef(_, NothingClass | SingletonClass, _) => true
762+
case TypeRef(_, NothingClass | SingletonClass | AnyKindClass, _) => true
760763
case TypeRef(_, sym, _) if sym.isAbstractType => tp.bounds.hi.typeSymbol isSubClass SingletonClass
761764
case TypeRef(pre, sym, _) if sym.isModuleClass => isStable(pre)
762765
case TypeRef(_, _, _) if tp ne tp.dealias => isStable(tp.dealias)
@@ -1357,7 +1360,8 @@ trait Definitions extends api.StandardDefinitions {
13571360
AnyValClass,
13581361
NullClass,
13591362
NothingClass,
1360-
SingletonClass
1363+
SingletonClass,
1364+
AnyKindClass
13611365
)
13621366
/** Lists core methods that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */
13631367
lazy val syntheticCoreMethods = List(

src/reflect/scala/reflect/internal/Kinds.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,10 @@ trait Kinds {
151151
log("checkKindBoundsHK under params: "+ underHKParams +" with args "+ withHKArgs)
152152
}
153153

154-
if (!sameLength(hkargs, hkparams)) {
154+
def isAnyKind = settings.YkindPolymorphism && param.tpe.typeSymbol.isNonBottomSubClass(definitions.AnyKindClass)
155+
156+
// if using Kind Polymorphism, let it pass now and let inference happen a bit later
157+
if (!sameLength(hkargs, hkparams) && !isAnyKind) {
155158
// Any and Nothing are kind-overloaded
156159
if (arg == AnyClass || arg == NothingClass) NoKindErrors
157160
// shortcut: always set error, whether explainTypesOrNot

src/reflect/scala/reflect/internal/StdNames.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ trait StdNames {
256256
final val Singleton: NameType = "Singleton"
257257
final val Throwable: NameType = "Throwable"
258258
final val unchecked: NameType = "unchecked"
259+
final val AnyKind: NameType = "AnyKind"
259260

260261
final val api: NameType = "api"
261262
final val Annotation: NameType = "Annotation"

0 commit comments

Comments
 (0)