@@ -13,6 +13,11 @@ import collection.mutable
1313import ast .tpd ._
1414import reporting .trace
1515import reporting .diagnostic .Message
16+ import config .Printers .{gadts , typr }
17+ import typer .Applications ._
18+ import typer .ProtoTypes ._
19+ import typer .ForceDegree
20+ import typer .Inferencing .isFullyDefined
1621
1722import scala .annotation .internal .sharable
1823
@@ -334,6 +339,154 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
334339 * This test is used when we are too early in the pipeline to consider imports.
335340 */
336341 def scala2Setting = ctx.settings.language.value.contains(nme.Scala2 .toString)
342+
343+ /** Refine child based on parent
344+ *
345+ * In child class definition, we have:
346+ *
347+ * class Child[Ts] extends path.Parent[Us] with Es
348+ * object Child extends path.Parent[Us] with Es
349+ * val child = new path.Parent[Us] with Es // enum values
350+ *
351+ * Given a parent type `parent` and a child symbol `child`, we infer the prefix
352+ * and type parameters for the child:
353+ *
354+ * prefix.child[Vs] <:< parent
355+ *
356+ * where `Vs` are fresh type variables and `prefix` is the symbol prefix with all
357+ * non-module and non-package `ThisType` replaced by fresh type variables.
358+ *
359+ * If the subtyping is true, the instantiated type `p.child[Vs]` is
360+ * returned. Otherwise, `NoType` is returned.
361+ */
362+ def refineUsingParent (parent : Type , child : Symbol )(implicit ctx : Context ): Type = {
363+ if (child.isTerm && child.is(Case , butNot = Module )) return child.termRef // enum vals always match
364+
365+ // <local child> is a place holder from Scalac, it is hopeless to instantiate it.
366+ //
367+ // Quote from scalac (from nsc/symtab/classfile/Pickler.scala):
368+ //
369+ // ...When a sealed class/trait has local subclasses, a single
370+ // <local child> class symbol is added as pickled child
371+ // (instead of a reference to the anonymous class; that was done
372+ // initially, but seems not to work, ...).
373+ //
374+ if (child.name == tpnme.LOCAL_CHILD ) return child.typeRef
375+
376+ val childTp = if (child.isTerm) child.termRef else child.typeRef
377+
378+ instantiate(childTp, parent)(ctx.fresh.setNewTyperState()).dealias
379+ }
380+
381+ /** Instantiate type `tp1` to be a subtype of `tp2`
382+ *
383+ * Return the instantiated type if type parameters and this type
384+ * in `tp1` can be instantiated such that `tp1 <:< tp2`.
385+ *
386+ * Otherwise, return NoType.
387+ */
388+ private def instantiate (tp1 : NamedType , tp2 : Type )(implicit ctx : Context ): Type = {
389+ /** expose abstract type references to their bounds or tvars according to variance */
390+ class AbstractTypeMap (maximize : Boolean )(implicit ctx : Context ) extends TypeMap {
391+ def expose (lo : Type , hi : Type ): Type =
392+ if (variance == 0 )
393+ newTypeVar(TypeBounds (lo, hi))
394+ else if (variance == 1 )
395+ if (maximize) hi else lo
396+ else
397+ if (maximize) lo else hi
398+
399+ def apply (tp : Type ): Type = tp match {
400+ case tp : TypeRef if isBounds(tp.underlying) =>
401+ val lo = this (tp.info.loBound)
402+ val hi = this (tp.info.hiBound)
403+ // See tests/patmat/gadt.scala tests/patmat/exhausting.scala tests/patmat/t9657.scala
404+ val exposed = expose(lo, hi)
405+ typr.println(s " $tp exposed to =====> $exposed" )
406+ exposed
407+
408+ case AppliedType (tycon : TypeRef , args) if isBounds(tycon.underlying) =>
409+ val args2 = args.map(this )
410+ val lo = this (tycon.info.loBound).applyIfParameterized(args2)
411+ val hi = this (tycon.info.hiBound).applyIfParameterized(args2)
412+ val exposed = expose(lo, hi)
413+ typr.println(s " $tp exposed to =====> $exposed" )
414+ exposed
415+
416+ case _ =>
417+ mapOver(tp)
418+ }
419+ }
420+
421+ def minTypeMap (implicit ctx : Context ) = new AbstractTypeMap (maximize = false )
422+ def maxTypeMap (implicit ctx : Context ) = new AbstractTypeMap (maximize = true )
423+
424+ // Fix subtype checking for child instantiation,
425+ // such that `Foo(Test.this.foo) <:< Foo(Foo.this)`
426+ // See tests/patmat/i3938.scala
427+ class RemoveThisMap extends TypeMap {
428+ var prefixTVar : Type = null
429+ def apply (tp : Type ): Type = tp match {
430+ case ThisType (tref : TypeRef ) if ! tref.symbol.isStaticOwner =>
431+ if (tref.symbol.is(Module ))
432+ TermRef (this (tref.prefix), tref.symbol.sourceModule)
433+ else if (prefixTVar != null )
434+ this (tref)
435+ else {
436+ prefixTVar = WildcardType // prevent recursive call from assigning it
437+ prefixTVar = newTypeVar(TypeBounds .upper(this (tref)))
438+ prefixTVar
439+ }
440+ case tp => mapOver(tp)
441+ }
442+ }
443+
444+ // replace uninstantiated type vars with WildcardType, check tests/patmat/3333.scala
445+ def instUndetMap (implicit ctx : Context ) = new TypeMap {
446+ def apply (t : Type ): Type = t match {
447+ case tvar : TypeVar if ! tvar.isInstantiated => WildcardType (tvar.origin.underlying.bounds)
448+ case _ => mapOver(t)
449+ }
450+ }
451+
452+ val removeThisType = new RemoveThisMap
453+ val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) }
454+ val protoTp1 = removeThisType.apply(tp1).appliedTo(tvars)
455+
456+ val force = new ForceDegree .Value (
457+ tvar =>
458+ ! (ctx.typerState.constraint.entry(tvar.origin) `eq` tvar.origin.underlying) ||
459+ (tvar `eq` removeThisType.prefixTVar),
460+ minimizeAll = false ,
461+ allowBottom = false
462+ )
463+
464+ // If parent contains a reference to an abstract type, then we should
465+ // refine subtype checking to eliminate abstract types according to
466+ // variance. As this logic is only needed in exhaustivity check,
467+ // we manually patch subtyping check instead of changing TypeComparer.
468+ // See tests/patmat/i3645b.scala
469+ def parentQualify = tp1.widen.classSymbol.info.parents.exists { parent =>
470+ implicit val ictx = ctx.fresh.setNewTyperState()
471+ parent.argInfos.nonEmpty && minTypeMap.apply(parent) <:< maxTypeMap.apply(tp2)
472+ }
473+
474+ if (protoTp1 <:< tp2) {
475+ if (isFullyDefined(protoTp1, force)) protoTp1
476+ else instUndetMap.apply(protoTp1)
477+ }
478+ else {
479+ val protoTp2 = maxTypeMap.apply(tp2)
480+ if (protoTp1 <:< protoTp2 || parentQualify) {
481+ if (isFullyDefined(AndType (protoTp1, protoTp2), force)) protoTp1
482+ else instUndetMap.apply(protoTp1)
483+ }
484+ else {
485+ typr.println(s " $protoTp1 <:< $protoTp2 = false " )
486+ NoType
487+ }
488+ }
489+ }
337490}
338491
339492object TypeOps {
0 commit comments