Skip to content

Commit 14fb071

Browse files
authored
Merge pull request #4080 from dotty-staging/change-interpolation-3
Rework type inference
2 parents bdfe740 + 855e0e9 commit 14fb071

20 files changed

+515
-352
lines changed

compiler/src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,11 @@ object Trees {
573573
s"TypeTree${if (hasType) s"[$typeOpt]" else ""}"
574574
}
575575

576+
/** A type tree that defines a new type variable. Its type is always a TypeVar.
577+
* Every TypeVar is created as the type of one TypeVarBinder.
578+
*/
579+
class TypeVarBinder[-T >: Untyped] extends TypeTree[T]
580+
576581
/** ref.type */
577582
case class SingletonTypeTree[-T >: Untyped] private[ast] (ref: Tree[T])
578583
extends DenotingTree[T] with TypTree[T] {

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ class ScalaSettings extends Settings.SettingGroup {
8787
val YdisableFlatCpCaching = BooleanSetting("-YdisableFlatCpCaching", "Do not cache flat classpath representation of classpath elements from jars across compiler instances.")
8888

8989
val YnoImports = BooleanSetting("-Yno-imports", "Compile without importing scala.*, java.lang.*, or Predef.")
90+
val YnoInline = BooleanSetting("-Yno-inline", "Suppress inlining.")
9091
val YnoGenericSig = BooleanSetting("-Yno-generic-signatures", "Suppress generation of generic signatures for Java.")
9192
val YnoPredef = BooleanSetting("-Yno-predef", "Compile without importing Predef.")
9293
val Yskip = PhasesSetting("-Yskip", "Skip")
@@ -126,7 +127,7 @@ class ScalaSettings extends Settings.SettingGroup {
126127
val YexplainLowlevel = BooleanSetting("-Yexplain-lowlevel", "When explaining type errors, show types at a lower level.")
127128
val YnoDoubleBindings = BooleanSetting("-Yno-double-bindings", "Assert no namedtype is bound twice (should be enabled only if program is error-free).")
128129
val YshowVarBounds = BooleanSetting("-Yshow-var-bounds", "Print type variables with their bounds")
129-
val YnoInline = BooleanSetting("-Yno-inline", "Suppress inlining.")
130+
val YshowNoInline = BooleanSetting("-Yshow-no-inline", "Show inlined code without the 'inlined from' info")
130131

131132
/** Linker specific flags */
132133
val optimise = BooleanSetting("-optimise", "Generates faster bytecode by applying local optimisations to the .program") withAbbreviation "-optimize"

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,19 +86,21 @@ trait ConstraintHandling {
8686
finally homogenizeArgs = saved
8787
}
8888

89+
private def location(implicit ctx: Context) = "" // i"in ${ctx.typerState.stateChainStr}" // use for debugging
90+
8991
protected def addUpperBound(param: TypeParamRef, bound: Type): Boolean = {
9092
def description = i"constraint $param <: $bound to\n$constraint"
9193
if (bound.isRef(defn.NothingClass) && ctx.typerState.isGlobalCommittable) {
9294
def msg = s"!!! instantiated to Nothing: $param, constraint = ${constraint.show}"
9395
if (Config.failOnInstantiationToNothing) assert(false, msg)
9496
else ctx.log(msg)
9597
}
96-
constr.println(i"adding $description in ${ctx.typerState.hashesStr}")
98+
constr.println(i"adding $description$location")
9799
val lower = constraint.lower(param)
98100
val res =
99101
addOneBound(param, bound, isUpper = true) &&
100102
lower.forall(addOneBound(_, bound, isUpper = true))
101-
constr.println(i"added $description = $res in ${ctx.typerState.hashesStr}")
103+
constr.println(i"added $description = $res$location")
102104
res
103105
}
104106

@@ -109,7 +111,7 @@ trait ConstraintHandling {
109111
val res =
110112
addOneBound(param, bound, isUpper = false) &&
111113
upper.forall(addOneBound(_, bound, isUpper = false))
112-
constr.println(i"added $description = $res in ${ctx.typerState.hashesStr}")
114+
constr.println(i"added $description = $res$location")
113115
res
114116
}
115117

@@ -122,12 +124,12 @@ trait ConstraintHandling {
122124
val up2 = p2 :: constraint.exclusiveUpper(p2, p1)
123125
val lo1 = constraint.nonParamBounds(p1).lo
124126
val hi2 = constraint.nonParamBounds(p2).hi
125-
constr.println(i"adding $description down1 = $down1, up2 = $up2 ${ctx.typerState.hashesStr}")
127+
constr.println(i"adding $description down1 = $down1, up2 = $up2$location")
126128
constraint = constraint.addLess(p1, p2)
127129
down1.forall(addOneBound(_, hi2, isUpper = true)) &&
128130
up2.forall(addOneBound(_, lo1, isUpper = false))
129131
}
130-
constr.println(i"added $description = $res ${ctx.typerState.hashesStr}")
132+
constr.println(i"added $description = $res$location")
131133
res
132134
}
133135

@@ -252,6 +254,9 @@ trait ConstraintHandling {
252254
case tp: SingletonType => true
253255
case AndType(tp1, tp2) => isMultiSingleton(tp1) | isMultiSingleton(tp2)
254256
case OrType(tp1, tp2) => isMultiSingleton(tp1) & isMultiSingleton(tp2)
257+
case tp: TypeRef => isMultiSingleton(tp.info.hiBound)
258+
case tp: TypeVar => isMultiSingleton(tp.underlying)
259+
case tp: TypeParamRef => isMultiSingleton(bounds(tp).hi)
255260
case _ => false
256261
}
257262
def isFullyDefined(tp: Type): Boolean = tp match {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ object Denotations {
130130
if ((cachedPrefix ne pre) || ctx.period != validAsSeenFrom) {
131131
cachedAsSeenFrom = computeAsSeenFrom(pre)
132132
cachedPrefix = pre
133-
validAsSeenFrom = ctx.period
133+
validAsSeenFrom = if (pre.isProvisional) Nowhere else ctx.period
134134
}
135135
cachedAsSeenFrom
136136
} else computeAsSeenFrom(pre)

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

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,6 +1191,26 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
11911191
isSubRef(tp1, tp2) && isSubRef(tp2, tp1)
11921192
}
11931193

1194+
/** If the range `tp1..tp2` consist of a single type, that type, otherwise NoType`.
1195+
* This is the case if `tp1 =:= tp2`, but also if `tp1 <:< tp2`, `tp1` is a singleton type,
1196+
* and `tp2` derives from `scala.Singleton` (or vice-versa). Examples of the latter case:
1197+
*
1198+
* "name".type .. Singleton
1199+
* "name".type .. String & Singleton
1200+
* Singleton .. "name".type
1201+
* String & Singleton .. "name".type
1202+
*
1203+
* All consist of the single type `"name".type`.
1204+
*/
1205+
def singletonInterval(tp1: Type, tp2: Type): Type = {
1206+
def isSingletonBounds(lo: Type, hi: Type) =
1207+
lo.isSingleton && hi.derivesFrom(defn.SingletonClass) && isSubTypeWhenFrozen(lo, hi)
1208+
if (isSameTypeWhenFrozen(tp1, tp2)) tp1
1209+
else if (isSingletonBounds(tp1, tp2)) tp1
1210+
else if (isSingletonBounds(tp2, tp1)) tp2
1211+
else NoType
1212+
}
1213+
11941214
/** The greatest lower bound of two types */
11951215
def glb(tp1: Type, tp2: Type): Type = /*>|>*/ trace(s"glb(${tp1.show}, ${tp2.show})", subtyping, show = true) /*<|<*/ {
11961216
if (tp1 eq tp2) tp1
@@ -1279,9 +1299,10 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
12791299
case tparam :: tparamsRest =>
12801300
val arg1 :: args1Rest = args1
12811301
val arg2 :: args2Rest = args2
1302+
val common = singletonInterval(arg1, arg2)
12821303
val v = tparam.paramVariance
12831304
val lubArg =
1284-
if (isSameTypeWhenFrozen(arg1, arg2)) arg1
1305+
if (common.exists) common
12851306
else if (v > 0) lub(arg1.hiBound, arg2.hiBound, canConstrain)
12861307
else if (v < 0) glb(arg1.loBound, arg2.loBound)
12871308
else TypeBounds(glb(arg1.loBound, arg2.loBound),
@@ -1310,9 +1331,10 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
13101331
case tparam :: tparamsRest =>
13111332
val arg1 :: args1Rest = args1
13121333
val arg2 :: args2Rest = args2
1334+
val common = singletonInterval(arg1, arg2)
13131335
val v = tparam.paramVariance
13141336
val glbArg =
1315-
if (isSameTypeWhenFrozen(arg1, arg2)) arg1
1337+
if (common.exists) common
13161338
else if (v > 0) glb(arg1.hiBound, arg2.hiBound)
13171339
else if (v < 0) lub(arg1.loBound, arg2.loBound)
13181340
else if (arg1.isInstanceOf[TypeBounds] || arg2.isInstanceOf[TypeBounds])

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

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,23 @@ package core
55
import Types._
66
import Flags._
77
import Contexts._
8-
import util.{SimpleIdentityMap, DotClass}
8+
import util.{SimpleIdentityMap, SimpleIdentitySet, DotClass}
99
import reporting._
1010
import printing.{Showable, Printer}
1111
import printing.Texts._
1212
import config.Config
1313
import collection.mutable
1414
import java.lang.ref.WeakReference
15+
import Decorators._
1516

16-
class TyperState(previous: TyperState /* | Null */) extends DotClass with Showable {
17+
object TyperState {
18+
@sharable private var nextId: Int = 0
19+
}
20+
21+
class TyperState(previous: TyperState /* | Null */) {
22+
23+
val id = TyperState.nextId
24+
TyperState.nextId += 1
1725

1826
private[this] var myReporter =
1927
if (previous == null) new ConsoleReporter() else previous.reporter
@@ -42,19 +50,6 @@ class TyperState(previous: TyperState /* | Null */) extends DotClass with Showab
4250
private val previousConstraint =
4351
if (previous == null) constraint else previous.constraint
4452

45-
private[this] var myEphemeral: Boolean =
46-
if (previous == null) false else previous.ephemeral
47-
48-
/** The ephemeral flag is set as a side effect if an operation accesses
49-
* the underlying type of a type variable. The reason we need this flag is
50-
* that any such operation is not referentially transparent; it might logically change
51-
* its value at the moment the type variable is instantiated. Caching code needs to
52-
* check the ephemeral flag; If the flag is set during an operation, the result
53-
* of that operation should not be cached.
54-
*/
55-
def ephemeral = myEphemeral
56-
def ephemeral_=(x: Boolean): Unit = { myEphemeral = x }
57-
5853
private[this] var myIsCommittable = true
5954

6055
def isCommittable = myIsCommittable
@@ -81,6 +76,11 @@ class TyperState(previous: TyperState /* | Null */) extends DotClass with Showab
8176
/** The uninstantiated variables */
8277
def uninstVars = constraint.uninstVars
8378

79+
/** The set of uninstantiated type variables which have this state as their owning state */
80+
private[this] var myOwnedVars: TypeVars = SimpleIdentitySet.empty
81+
def ownedVars = myOwnedVars
82+
def ownedVars_=(vs: TypeVars): Unit = myOwnedVars = vs
83+
8484
/** Gives for each instantiated type var that does not yet have its `inst` field
8585
* set, the instance value stored in the constraint. Storing instances in constraints
8686
* is done only in a temporary way for contexts that may be retracted
@@ -159,7 +159,7 @@ class TyperState(previous: TyperState /* | Null */) extends DotClass with Showab
159159
constraint foreachTypeVar { tvar =>
160160
if (tvar.owningState.get eq this) tvar.owningState = new WeakReference(targetState)
161161
}
162-
targetState.ephemeral |= ephemeral
162+
targetState.ownedVars ++= ownedVars
163163
targetState.gc()
164164
reporter.flush()
165165
isCommitted = true
@@ -185,8 +185,7 @@ class TyperState(previous: TyperState /* | Null */) extends DotClass with Showab
185185
constraint = constraint.remove(poly)
186186
}
187187

188-
override def toText(printer: Printer): Text = constraint.toText(printer)
188+
override def toString: String = s"TS[$id]"
189189

190-
def hashesStr: String =
191-
if (previous == null) "" else hashCode.toString + " -> " + previous.hashesStr
190+
def stateChainStr: String = s"$this${if (previous == null) "" else previous.stateChainStr}"
192191
}

0 commit comments

Comments
 (0)