Skip to content

Commit 5b53cd8

Browse files
authored
Merge pull request #6084 from dotty-staging/add-creator-applications
Implement creator applications
2 parents 36774e6 + 52f2e63 commit 5b53cd8

31 files changed

+589
-151
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,13 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
106106
case _ => Nil
107107
}
108108

109+
/** Is tree a path? */
110+
def isPath(tree: Tree): Boolean = unsplice(tree) match {
111+
case Ident(_) | This(_) | Super(_, _) => true
112+
case Select(qual, _) => isPath(qual)
113+
case _ => false
114+
}
115+
109116
/** Is tree a self constructor call this(...)? I.e. a call to a constructor of the
110117
* same object?
111118
*/

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

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ abstract class Constraint extends Showable {
1717

1818
type This <: Constraint
1919

20-
/** Does the constraint's domain contain the type parameters of `pt`? */
21-
def contains(pt: TypeLambda): Boolean
20+
/** Does the constraint's domain contain the type parameters of `tl`? */
21+
def contains(tl: TypeLambda): Boolean
2222

2323
/** Does the constraint's domain contain the type parameter `param`? */
2424
def contains(param: TypeParamRef): Boolean
@@ -106,14 +106,22 @@ abstract class Constraint extends Showable {
106106
*/
107107
def replace(param: TypeParamRef, tp: Type)(implicit ctx: Context): This
108108

109-
/** Is entry associated with `pt` removable? This is the case if
109+
/** Is entry associated with `tl` removable? This is the case if
110110
* all type parameters of the entry are associated with type variables
111111
* which have their `inst` fields set.
112112
*/
113-
def isRemovable(pt: TypeLambda): Boolean
113+
def isRemovable(tl: TypeLambda): Boolean
114114

115-
/** A new constraint with all entries coming from `pt` removed. */
116-
def remove(pt: TypeLambda)(implicit ctx: Context): This
115+
/** A new constraint with all entries coming from `tl` removed. */
116+
def remove(tl: TypeLambda)(implicit ctx: Context): This
117+
118+
/** A new constraint with entry `tl` renamed to a fresh type lambda */
119+
def rename(tl: TypeLambda)(implicit ctx: Context): This
120+
121+
/** The given `tl` in case it is not contained in this constraint,
122+
* a fresh copy of `tl` otherwise.
123+
*/
124+
def ensureFresh(tl: TypeLambda)(implicit ctx: Context): TypeLambda
117125

118126
/** The type lambdas constrained by this constraint */
119127
def domainLambdas: List[TypeLambda]

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ object NameOps {
6969
def isSetterName: Boolean = name endsWith str.SETTER_SUFFIX
7070
def isScala2LocalSuffix: Boolean = testSimple(_.endsWith(" "))
7171
def isSelectorName: Boolean = testSimple(n => n.startsWith("_") && n.drop(1).forall(_.isDigit))
72+
def isAnonymousClassName: Boolean = name.startsWith(str.ANON_CLASS)
73+
def isAnonymousFunctionName: Boolean = name.startsWith(str.ANON_FUN)
7274

7375
/** Is name a variable name? */
7476
def isVariableName: Boolean = testSimple { n =>

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

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import collection.mutable
88
import printing.Printer
99
import printing.Texts._
1010
import config.Config
11+
import config.Printers.constr
1112
import reflect.ClassTag
1213
import annotation.tailrec
1314
import annotation.internal.sharable
@@ -503,6 +504,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
503504
}
504505

505506
def & (other: Constraint, otherHasErrors: Boolean)(implicit ctx: Context): OrderingConstraint = {
507+
506508
def merge[T](m1: ArrayValuedMap[T], m2: ArrayValuedMap[T], join: (T, T) => T): ArrayValuedMap[T] = {
507509
var merged = m1
508510
def mergeArrays(xs1: Array[T], xs2: Array[T]) = {
@@ -527,21 +529,71 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
527529
case (e1: TypeBounds, e2: TypeBounds) => e1 & e2
528530
case (e1: TypeBounds, _) if e1 contains e2 => e2
529531
case (_, e2: TypeBounds) if e2 contains e1 => e1
530-
case (tv1: TypeVar, tv2: TypeVar) if tv1.instanceOpt eq tv2.instanceOpt => e1
532+
case (tv1: TypeVar, tv2: TypeVar) if tv1 eq tv2 => e1
531533
case _ =>
532534
if (otherHasErrors)
533535
e1
534536
else
535537
throw new AssertionError(i"cannot merge $this with $other, mergeEntries($e1, $e2) failed")
536538
}
537539

538-
val that = other.asInstanceOf[OrderingConstraint]
540+
/** Ensure that constraint `c` does not associate different TypeVars for the
541+
* same type lambda than this constraint. Do this by renaming type lambdas
542+
* in `c` where necessary.
543+
*/
544+
def ensureNotConflicting(c: OrderingConstraint): OrderingConstraint = {
545+
def hasConflictingTypeVarsFor(tl: TypeLambda) =
546+
this.typeVarOfParam(tl.paramRefs(0)) ne c.typeVarOfParam(tl.paramRefs(0))
547+
// Note: Since TypeVars are allocated in bulk for each type lambda, we only
548+
// have to check the first one to find out if some of them are different.
549+
val conflicting = c.domainLambdas.find(tl =>
550+
this.contains(tl) && hasConflictingTypeVarsFor(tl))
551+
conflicting match {
552+
case Some(tl) => ensureNotConflicting(c.rename(tl))
553+
case None => c
554+
}
555+
}
556+
557+
val that = ensureNotConflicting(other.asInstanceOf[OrderingConstraint])
558+
539559
new OrderingConstraint(
540560
merge(this.boundsMap, that.boundsMap, mergeEntries),
541561
merge(this.lowerMap, that.lowerMap, mergeParams),
542562
merge(this.upperMap, that.upperMap, mergeParams))
563+
}.reporting(res => i"constraint merge $this with $other = $res", constr)
564+
565+
def rename(tl: TypeLambda)(implicit ctx: Context): OrderingConstraint = {
566+
assert(contains(tl))
567+
val tl1 = ensureFresh(tl)
568+
def swapKey[T](m: ArrayValuedMap[T]) = m.remove(tl).updated(tl1, m(tl))
569+
var current = newConstraint(swapKey(boundsMap), swapKey(lowerMap), swapKey(upperMap))
570+
def subst[T <: Type](x: T): T = x.subst(tl, tl1).asInstanceOf[T]
571+
current.foreachParam {(p, i) =>
572+
current = boundsLens.map(this, current, p, i, subst)
573+
current = lowerLens.map(this, current, p, i, _.map(subst))
574+
current = upperLens.map(this, current, p, i, _.map(subst))
575+
}
576+
current.foreachTypeVar { tvar =>
577+
val TypeParamRef(binder, n) = tvar.origin
578+
if (binder eq tl) tvar.setOrigin(tl1.paramRefs(n))
579+
}
580+
constr.println(i"renamd $this to $current")
581+
current
543582
}
544583

584+
def ensureFresh(tl: TypeLambda)(implicit ctx: Context): TypeLambda =
585+
if (contains(tl)) {
586+
var paramInfos = tl.paramInfos
587+
if (tl.isInstanceOf[HKLambda]) {
588+
// HKLambdas are hash-consed, need to create an artificial difference by adding
589+
// a LazyRef to a bound.
590+
val TypeBounds(lo, hi) :: pinfos1 = tl.paramInfos
591+
paramInfos = TypeBounds(lo, LazyRef(_ => hi)) :: pinfos1
592+
}
593+
ensureFresh(tl.newLikeThis(tl.paramNames, paramInfos, tl.resultType))
594+
}
595+
else tl
596+
545597
override def checkClosed()(implicit ctx: Context): Unit = {
546598
def isFreeTypeParamRef(tp: Type) = tp match {
547599
case TypeParamRef(binder: TypeLambda, _) => !contains(binder)

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -484,13 +484,13 @@ object SymDenotations {
484484

485485
/** Is this symbol an anonymous class? */
486486
final def isAnonymousClass(implicit ctx: Context): Boolean =
487-
isClass && (initial.name startsWith str.ANON_CLASS)
487+
isClass && initial.name.isAnonymousClassName
488488

489489
final def isAnonymousFunction(implicit ctx: Context): Boolean =
490-
this.symbol.is(Method) && (initial.name startsWith str.ANON_FUN)
490+
this.symbol.is(Method) && initial.name.isAnonymousFunctionName
491491

492492
final def isAnonymousModuleVal(implicit ctx: Context): Boolean =
493-
this.symbol.is(ModuleVal) && (initial.name startsWith str.ANON_CLASS)
493+
this.symbol.is(ModuleVal) && initial.name.isAnonymousClassName
494494

495495
/** Is this a synthetic method that represents conversions between representations of a value class
496496
* These methods are generated in ExtensionMethods

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

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,19 @@ import Contexts._
77
import util.{SimpleIdentityMap, SimpleIdentitySet}
88
import reporting._
99
import config.Config
10+
import config.Printers.constr
1011
import collection.mutable
1112
import java.lang.ref.WeakReference
1213
import util.Stats
14+
import Decorators._
1315

1416
import scala.annotation.internal.sharable
1517

1618
object TyperState {
1719
@sharable private var nextId: Int = 0
1820
}
1921

20-
class TyperState(previous: TyperState /* | Null */) {
22+
class TyperState(private val previous: TyperState /* | Null */) {
2123

2224
Stats.record("typerState")
2325

@@ -143,10 +145,11 @@ class TyperState(previous: TyperState /* | Null */) {
143145
def commit()(implicit ctx: Context): Unit = {
144146
Stats.record("typerState.commit")
145147
val targetState = ctx.typerState
148+
if (constraint ne targetState.constraint)
149+
constr.println(i"committing $this to $targetState, fromConstr = $constraint, toConstr = ${targetState.constraint}")
146150
assert(isCommittable)
147-
targetState.constraint =
148-
if (targetState.constraint eq previousConstraint) constraint
149-
else targetState.constraint & (constraint, otherHasErrors = reporter.errorsReported)
151+
if (targetState.constraint eq previousConstraint) targetState.constraint = constraint
152+
else targetState.mergeConstraintWith(this)
150153
constraint foreachTypeVar { tvar =>
151154
if (tvar.owningState.get eq this) tvar.owningState = new WeakReference(targetState)
152155
}
@@ -156,6 +159,9 @@ class TyperState(previous: TyperState /* | Null */) {
156159
isCommitted = true
157160
}
158161

162+
def mergeConstraintWith(that: TyperState)(implicit ctx: Context): Unit =
163+
constraint = constraint & (that.constraint, otherHasErrors = that.reporter.errorsReported)
164+
159165
/** Make type variable instances permanent by assigning to `inst` field if
160166
* type variable instantiation cannot be retracted anymore. Then, remove
161167
* no-longer needed constraint entries.
@@ -176,7 +182,12 @@ class TyperState(previous: TyperState /* | Null */) {
176182
constraint = constraint.remove(poly)
177183
}
178184

179-
override def toString: String = s"TS[$id]"
185+
override def toString: String = {
186+
def ids(state: TyperState): List[String] =
187+
s"${state.id}${if (state.isCommittable) "" else "X"}" ::
188+
(if (state.previous == null) Nil else ids(state.previous))
189+
s"TS[${ids(this).mkString(", ")}]"
190+
}
180191

181192
def stateChainStr: String = s"$this${if (previous == null) "" else previous.stateChainStr}"
182193
}

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3667,7 +3667,14 @@ object Types {
36673667
* `owningTree` and `owner` are used to determine whether a type-variable can be instantiated
36683668
* at some given point. See `Inferencing#interpolateUndetVars`.
36693669
*/
3670-
final class TypeVar(val origin: TypeParamRef, creatorState: TyperState) extends CachedProxyType with ValueType {
3670+
final class TypeVar(private var _origin: TypeParamRef, creatorState: TyperState) extends CachedProxyType with ValueType {
3671+
3672+
def origin: TypeParamRef = _origin
3673+
3674+
/** Set origin to new parameter. Called if we merge two conflicting constraints.
3675+
* See OrderingConstraint#merge, OrderingConstraint#rename
3676+
*/
3677+
def setOrigin(p: TypeParamRef) = _origin = p
36713678

36723679
/** The permanent instance type of the variable, or NoType is none is given yet */
36733680
private[this] var myInst: Type = NoType

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import Denotations._
1515
import SymDenotations._
1616
import StdNames.{nme, tpnme}
1717
import ast.{Trees, untpd}
18-
import typer.{Implicits, Namer}
18+
import typer.{Implicits, Namer, Applications}
1919
import typer.ProtoTypes._
2020
import Trees._
2121
import TypeApplications._
@@ -578,6 +578,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
578578
else keywordStr("'{") ~ toTextGlobal(dropBlock(tree)) ~ keywordStr("}")
579579
case Splice(tree) =>
580580
keywordStr("${") ~ toTextGlobal(dropBlock(tree)) ~ keywordStr("}")
581+
case tree: Applications.IntegratedTypeArgs =>
582+
toText(tree.app) ~ Str("(with integrated type args)").provided(ctx.settings.YprintDebug.value)
581583
case Thicket(trees) =>
582584
"Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}"
583585
case _ =>

compiler/src/dotty/tools/dotc/reporting/Reporter.scala

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -95,18 +95,19 @@ trait Reporting { this: Context =>
9595
def strictWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit = {
9696
val fullPos = addInlineds(pos)
9797
if (this.settings.strict.value) error(msg, fullPos)
98-
else reportWarning(new ExtendMessage(() => msg)(_ + "\n(This would be an error under strict mode)").warning(fullPos))
98+
else reportWarning(
99+
new ExtendMessage(() => msg)(_ + "\n(This would be an error under strict mode)")
100+
.warning(fullPos))
99101
}
100102

101-
def error(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit = {
102-
reporter.report(new Error(msg, addInlineds(pos)))
103-
}
104-
105-
def errorOrMigrationWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit = {
103+
def error(msg: => Message, pos: SourcePosition = NoSourcePosition, sticky: Boolean = false): Unit = {
106104
val fullPos = addInlineds(pos)
107-
if (ctx.scala2Mode) migrationWarning(msg, fullPos) else error(msg, fullPos)
105+
reporter.report(if (sticky) new StickyError(msg, fullPos) else new Error(msg, fullPos))
108106
}
109107

108+
def errorOrMigrationWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
109+
if (ctx.scala2Mode) migrationWarning(msg, pos) else error(msg, pos)
110+
110111
def restrictionError(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
111112
reporter.report {
112113
new ExtendMessage(() => msg)(m => s"Implementation restriction: $m").error(addInlineds(pos))
@@ -203,6 +204,9 @@ abstract class Reporter extends interfaces.ReporterResult {
203204
/** All errors reported by this reporter (ignoring outer reporters) */
204205
def allErrors: List[Error] = errors
205206

207+
/** Were sticky errors reported? Overridden in StoreReporter. */
208+
def hasStickyErrors: Boolean = false
209+
206210
/** Have errors been reported by this reporter, or in the
207211
* case where this is a StoreReporter, by an outer reporter?
208212
*/

compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ class StoreReporter(outer: Reporter) extends Reporter {
3131
override def hasUnreportedErrors: Boolean =
3232
outer != null && infos != null && infos.exists(_.isInstanceOf[Error])
3333

34+
override def hasStickyErrors: Boolean =
35+
infos != null && infos.exists(_.isInstanceOf[StickyError])
36+
3437
override def removeBufferedMessages(implicit ctx: Context): List[MessageContainer] =
3538
if (infos != null) try infos.toList finally infos = null
3639
else Nil

compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ object messages {
3535
pos: SourcePosition
3636
) extends MessageContainer(msgFn, pos, ERROR)
3737

38+
/** A sticky error is an error that should not be hidden by backtracking and
39+
* trying some alternative path. Typcially, errors issued after catching
40+
* a TypeError exception are sticky.
41+
*/
42+
class StickyError(
43+
msgFn: => Message,
44+
pos: SourcePosition
45+
) extends Error(msgFn, pos)
46+
3847
class Warning(
3948
msgFn: => Message,
4049
pos: SourcePosition

compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder
249249
case ex: TypeError =>
250250
// See neg/i1750a for an example where a cyclic error can arise.
251251
// The root cause in this example is an illegal "override" of an inner trait
252-
ctx.error(ex.toMessage, csym.sourcePos)
252+
ctx.error(ex.toMessage, csym.sourcePos, sticky = true)
253253
defn.ObjectType :: Nil
254254
}
255255
if (ValueClasses.isDerivedValueClass(csym)) {

compiler/src/dotty/tools/dotc/transform/MacroTransform.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ abstract class MacroTransform extends Phase {
6464
}
6565
catch {
6666
case ex: TypeError =>
67-
ctx.error(ex.toMessage, tree.sourcePos)
67+
ctx.error(ex.toMessage, tree.sourcePos, sticky = true)
6868
tree
6969
}
7070

compiler/src/dotty/tools/dotc/transform/MegaPhase.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase {
205205
}
206206
catch {
207207
case ex: TypeError =>
208-
ctx.error(ex.toMessage, tree.sourcePos)
208+
ctx.error(ex.toMessage, tree.sourcePos, sticky = true)
209209
tree
210210
}
211211
if (tree.isInstanceOf[NameTree]) goNamed(tree, start) else goUnnamed(tree, start)

compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ object OverridingPairs {
134134
case ex: TypeError =>
135135
// See neg/i1750a for an example where a cyclic error can arise.
136136
// The root cause in this example is an illegal "override" of an inner trait
137-
ctx.error(ex.toMessage, base.sourcePos)
137+
ctx.error(ex.toMessage, base.sourcePos, sticky = true)
138138
}
139139
} else {
140140
curEntry = curEntry.prev

0 commit comments

Comments
 (0)