Skip to content

Commit f93a975

Browse files
committed
Refactor Typevar instantiation
It seemed more cohesive to keep the concepts of "instanceType" and "approximation" side-by-side.
1 parent dd1f92a commit f93a975

File tree

2 files changed

+52
-43
lines changed

2 files changed

+52
-43
lines changed

src/dotty/tools/dotc/core/ConstraintHandling.scala

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,57 @@ trait ConstraintHandling {
171171
inst
172172
}
173173

174+
/** The instance type of `param` in the current constraint (which contains `param`).
175+
* If `fromBelow` is true, the instance type is the lub of the parameter's
176+
* lower bounds; otherwise it is the glb of its upper bounds. However,
177+
* a lower bound instantiation can be a singleton type only if the upper bound
178+
* is also a singleton type.
179+
*/
180+
def instanceType(param: PolyParam, fromBelow: Boolean): Type = {
181+
def upperBound = constraint.fullUpperBound(param)
182+
def isSingleton(tp: Type): Boolean = tp match {
183+
case tp: SingletonType => true
184+
case AndType(tp1, tp2) => isSingleton(tp1) | isSingleton(tp2)
185+
case OrType(tp1, tp2) => isSingleton(tp1) & isSingleton(tp2)
186+
case _ => false
187+
}
188+
def isFullyDefined(tp: Type): Boolean = tp match {
189+
case tp: TypeVar => tp.isInstantiated && isFullyDefined(tp.instanceOpt)
190+
case tp: TypeProxy => isFullyDefined(tp.underlying)
191+
case tp: AndOrType => isFullyDefined(tp.tp1) && isFullyDefined(tp.tp2)
192+
case _ => true
193+
}
194+
def isOrType(tp: Type): Boolean = tp.stripTypeVar.dealias match {
195+
case tp: OrType => true
196+
case tp: RefinedOrRecType => isOrType(tp.parent)
197+
case AndType(tp1, tp2) => isOrType(tp1) | isOrType(tp2)
198+
case WildcardType(bounds: TypeBounds) => isOrType(bounds.hi)
199+
case _ => false
200+
}
201+
202+
// First, solve the constraint.
203+
var inst = approximation(param, fromBelow)
204+
205+
// Then, approximate by (1.) - (3.) and simplify as follows.
206+
// 1. If instance is from below and is a singleton type, yet
207+
// upper bound is not a singleton type, widen the instance.
208+
if (fromBelow && isSingleton(inst) && !isSingleton(upperBound))
209+
inst = inst.widen
210+
211+
inst = inst.simplified
212+
213+
// 2. If instance is from below and is a fully-defined union type, yet upper bound
214+
// is not a union type, approximate the union type from above by an intersection
215+
// of all common base types.
216+
if (fromBelow && isOrType(inst) && isFullyDefined(inst) && !isOrType(upperBound))
217+
inst = inst.approximateUnion
218+
219+
// 3. If instance is from below, and upper bound has open named parameters
220+
// make sure the instance has all named parameters of the bound.
221+
if (fromBelow) inst = inst.widenToNamedTypeParams(param.namedTypeParams)
222+
inst
223+
}
224+
174225
/** Constraint `c1` subsumes constraint `c2`, if under `c2` as constraint we have
175226
* for all poly params `p` defined in `c2` as `p >: L2 <: U2`:
176227
*

src/dotty/tools/dotc/core/Types.scala

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2878,53 +2878,11 @@ object Types {
28782878
* is also a singleton type.
28792879
*/
28802880
def instantiate(fromBelow: Boolean)(implicit ctx: Context): Type = {
2881-
def upperBound = ctx.typerState.constraint.fullUpperBound(origin)
2882-
def isSingleton(tp: Type): Boolean = tp match {
2883-
case tp: SingletonType => true
2884-
case AndType(tp1, tp2) => isSingleton(tp1) | isSingleton(tp2)
2885-
case OrType(tp1, tp2) => isSingleton(tp1) & isSingleton(tp2)
2886-
case _ => false
2887-
}
2888-
def isFullyDefined(tp: Type): Boolean = tp match {
2889-
case tp: TypeVar => tp.isInstantiated && isFullyDefined(tp.instanceOpt)
2890-
case tp: TypeProxy => isFullyDefined(tp.underlying)
2891-
case tp: AndOrType => isFullyDefined(tp.tp1) && isFullyDefined(tp.tp2)
2892-
case _ => true
2893-
}
2894-
def isOrType(tp: Type): Boolean = tp.stripTypeVar.dealias match {
2895-
case tp: OrType => true
2896-
case tp: RefinedOrRecType => isOrType(tp.parent)
2897-
case AndType(tp1, tp2) => isOrType(tp1) | isOrType(tp2)
2898-
case WildcardType(bounds: TypeBounds) => isOrType(bounds.hi)
2899-
case _ => false
2900-
}
2901-
2902-
// First, solve the constraint.
2903-
var inst = ctx.typeComparer.approximation(origin, fromBelow)
2904-
2905-
// Then, approximate by (1.) - (3.) and simplify as follows.
2906-
// 1. If instance is from below and is a singleton type, yet
2907-
// upper bound is not a singleton type, widen the instance.
2908-
if (fromBelow && isSingleton(inst) && !isSingleton(upperBound))
2909-
inst = inst.widen
2910-
2911-
inst = inst.simplified
2912-
2913-
// 2. If instance is from below and is a fully-defined union type, yet upper bound
2914-
// is not a union type, approximate the union type from above by an intersection
2915-
// of all common base types.
2916-
if (fromBelow && isOrType(inst) && isFullyDefined(inst) && !isOrType(upperBound))
2917-
inst = inst.approximateUnion
2918-
2919-
// 3. If instance is from below, and upper bound has open named parameters
2920-
// make sure the instance has all named parameters of the bound.
2921-
if (fromBelow) inst = inst.widenToNamedTypeParams(this.namedTypeParams)
2922-
2881+
val inst = ctx.typeComparer.instanceType(origin, fromBelow)
29232882
if (ctx.typerState.isGlobalCommittable)
29242883
assert(!inst.isInstanceOf[PolyParam], i"bad inst $this := $inst, constr = ${ctx.typerState.constraint}")
29252884
// If this fails, you might want to turn on Config.debugCheckConstraintsClosed
29262885
// to help find the root of the problem.
2927-
29282886
instantiateWith(inst)
29292887
}
29302888

0 commit comments

Comments
 (0)