Skip to content

Commit d35d4dc

Browse files
authored
Merge branch 'master' into check-ordinal-match
2 parents 760f27a + 2b74f88 commit d35d4dc

File tree

20 files changed

+525
-220
lines changed

20 files changed

+525
-220
lines changed

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -702,13 +702,18 @@ object TypeOps:
702702
//
703703
// 1. Replace type parameters in T with tvars
704704
// 2. Replace non-class applied types with a tvar, bounded by its type constructor's underlying type
705+
// 3. Replace non-reducing MatchType with its bound
705706
//
706707
val approximateParent = new TypeMap {
707708
val boundTypeParams = util.HashMap[TypeRef, TypeVar]()
708709

709710
def apply(tp: Type): Type = tp.dealias match {
710-
case _: MatchType =>
711-
tp // break cycles
711+
case tp: MatchType =>
712+
val reduced = tp.reduced
713+
if reduced.exists then tp // break cycles
714+
else mapOver(tp.bound) // if the match type doesn't statically reduce
715+
// then to avoid it failing the <:<
716+
// we'll approximate by widening to its bounds
712717

713718
case tp: TypeRef if !tp.symbol.isClass =>
714719
def lo = LazyRef.of(apply(tp.underlying.loBound))
@@ -726,7 +731,7 @@ object TypeOps:
726731
tv
727732
end if
728733

729-
case AppliedType(tycon: TypeRef, _) if !tycon.dealias.typeSymbol.isClass =>
734+
case tp @ AppliedType(tycon: TypeRef, _) if !tycon.dealias.typeSymbol.isClass && !tp.isMatchAlias =>
730735

731736
// In tests/patmat/i3645g.scala, we need to tell whether it's possible
732737
// that K1 <: K[Foo]. If yes, we issue a warning; otherwise, no

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -427,10 +427,7 @@ object Types {
427427
def isMatch(using Context): Boolean = stripped match {
428428
case _: MatchType => true
429429
case tp: HKTypeLambda => tp.resType.isMatch
430-
case tp: AppliedType =>
431-
tp.tycon match
432-
case tycon: TypeRef => tycon.info.isInstanceOf[MatchAlias]
433-
case _ => false
430+
case tp: AppliedType => tp.isMatchAlias
434431
case _ => false
435432
}
436433

compiler/src/dotty/tools/dotc/typer/Inliner.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,8 +304,9 @@ object Inliner {
304304
*/
305305
def inlineCallTrace(callSym: Symbol, pos: SourcePosition)(using Context): Tree = {
306306
assert(ctx.source == pos.source)
307-
if (callSym.is(Macro)) ref(callSym.topLevelClass.owner).select(callSym.topLevelClass.name).withSpan(pos.span)
308-
else Ident(callSym.topLevelClass.typeRef).withSpan(pos.span)
307+
val topLevelCls = callSym.topLevelClass
308+
if (callSym.is(Macro)) ref(topLevelCls.owner).select(topLevelCls.name)(using ctx.withOwner(topLevelCls.owner)).withSpan(pos.span)
309+
else Ident(topLevelCls.typeRef).withSpan(pos.span)
309310
}
310311

311312
object Intrinsics {

compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,10 +1375,12 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
13751375
def tpe: TypeBounds = self.tpe.asInstanceOf[Types.TypeBounds]
13761376
def low: TypeTree = self match
13771377
case self: tpd.TypeBoundsTree => self.lo
1378-
case self: tpd.TypeTree => tpd.TypeTree(self.tpe.asInstanceOf[Types.TypeBounds].lo).withSpan(self.span)
1378+
case self: tpd.TypeTree => makeTypeDef(self.tpe.asInstanceOf[Types.TypeBounds].lo)
13791379
def hi: TypeTree = self match
13801380
case self: tpd.TypeBoundsTree => self.hi
1381-
case self: tpd.TypeTree => tpd.TypeTree(self.tpe.asInstanceOf[Types.TypeBounds].hi).withSpan(self.span)
1381+
case self: tpd.TypeTree => makeTypeDef(self.tpe.asInstanceOf[Types.TypeBounds].hi)
1382+
private def makeTypeDef(tpe: Types.Type) =
1383+
tpd.TypeTree(tpe)(using ctx.withSource(self.source)).withSpan(self.span)
13821384
end extension
13831385
end TypeBoundsTreeMethods
13841386

project/Build.scala

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,21 +1326,38 @@ object Build {
13261326
Compile / resourceGenerators += Def.task {
13271327
import _root_.scala.sys.process._
13281328
import _root_.scala.concurrent._
1329+
import _root_.scala.concurrent.duration.Duration
13291330
import ExecutionContext.Implicits.global
1330-
val inkuireVersion = "1.0.0-M2"
1331+
val inkuireVersion = "1.0.0-M3"
13311332
val inkuireLink = s"https://github.com/VirtusLab/Inkuire/releases/download/$inkuireVersion/inkuire.js"
13321333
val inkuireDestinationFile = (Compile / resourceManaged).value / "dotty_res" / "scripts" / "inkuire.js"
13331334
sbt.IO.touch(inkuireDestinationFile)
1334-
val downloadProcess = (new java.net.URL(inkuireLink) #> inkuireDestinationFile).run()
1335-
val result: Future[Int] = Future(blocking(downloadProcess.exitValue()))
1336-
val res = try {
1337-
Await.result(result, duration.Duration(20, "sec"))
1338-
} catch {
1339-
case _: TimeoutException =>
1340-
downloadProcess.destroy()
1341-
throw new MessageOnlyException(s"Failed to fetch inkuire.js from $inkuireLink: Download timeout")
1335+
1336+
def tryFetch(retries: Int, timeout: Duration): Unit = {
1337+
val downloadProcess = (new java.net.URL(inkuireLink) #> inkuireDestinationFile).run()
1338+
val result: Future[Int] = Future(blocking(downloadProcess.exitValue()))
1339+
try {
1340+
Await.result(result, timeout) match {
1341+
case 0 =>
1342+
case res if retries > 0 =>
1343+
println(s"Failed to fetch inkuire.js from $inkuireLink: Error code $res. $retries retries left")
1344+
tryFetch(retries - 1, timeout)
1345+
case res => throw new MessageOnlyException(s"Failed to fetch inkuire.js from $inkuireLink: Error code $res")
1346+
}
1347+
} catch {
1348+
case e: TimeoutException =>
1349+
downloadProcess.destroy()
1350+
if (retries > 0) {
1351+
println(s"Failed to fetch inkuire.js from $inkuireLink: Download timeout. $retries retries left")
1352+
tryFetch(retries - 1, timeout)
1353+
}
1354+
else {
1355+
throw new MessageOnlyException(s"Failed to fetch inkuire.js from $inkuireLink: Download timeout")
1356+
}
1357+
}
13421358
}
1343-
if(res != 0) throw new MessageOnlyException(s"Failed to fetch inkuire.js from $inkuireLink: Error code $res")
1359+
1360+
tryFetch(5, Duration(60, "s"))
13441361
Seq(inkuireDestinationFile)
13451362
}.taskValue,
13461363
libraryDependencies ++= Dependencies.flexmarkDeps ++ Seq(

scaladoc-js/src/searchbar/PageEntry.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ case class InkuireMatch(
2525
functionName: String,
2626
packageLocation: String,
2727
pageLocation: String,
28-
entryType: String
28+
entryType: String,
29+
mq: Int
2930
)
3031

3132
object PageEntry {

scaladoc-js/src/searchbar/SearchbarComponent.scala

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dotty.tools.scaladoc
22

33
import org.scalajs.dom._
4+
import org.scalajs.dom.ext._
45
import org.scalajs.dom.html.Input
56
import scala.scalajs.js.timers._
67
import scala.concurrent.duration._
@@ -40,6 +41,7 @@ class SearchbarComponent(engine: SearchbarEngine, inkuireEngine: InkuireJSSearch
4041
val wrapper = document.createElement("div").asInstanceOf[html.Div]
4142
wrapper.classList.add("scaladoc-searchbar-result")
4243
wrapper.classList.add("monospace")
44+
wrapper.setAttribute("mq", m.mq.toString)
4345

4446
val resultDiv = document.createElement("div").asInstanceOf[html.Div]
4547
resultDiv.classList.add("scaladoc-searchbar-result-row")
@@ -132,7 +134,16 @@ class SearchbarComponent(engine: SearchbarEngine, inkuireEngine: InkuireJSSearch
132134
loading.appendChild(animation)
133135
properResultsDiv.appendChild(loading)
134136
inkuireEngine.query(query) { (m: InkuireMatch) =>
135-
properResultsDiv.appendChild(m.toHTML)
137+
val next = properResultsDiv.children.foldLeft[Option[Element]](None) {
138+
case (acc, child) if !acc.isEmpty => acc
139+
case (_, child) =>
140+
Option.when(child.hasAttribute("mq") && Integer.parseInt(child.getAttribute("mq")) > m.mq)(child)
141+
}
142+
next.fold {
143+
properResultsDiv.appendChild(m.toHTML)
144+
} { next =>
145+
properResultsDiv.insertBefore(m.toHTML, next)
146+
}
136147
} { (s: String) =>
137148
animation.classList.remove("loading")
138149
properResultsDiv.appendChild(s.toHTMLError)

scaladoc-js/src/searchbar/engine/InkuireJSSearchEngine.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ class InkuireJSSearchEngine {
2222
d.functionName.asInstanceOf[String],
2323
d.packageLocation.asInstanceOf[String],
2424
d.pageLocation.asInstanceOf[String],
25-
d.entryType.asInstanceOf[String]
25+
d.entryType.asInstanceOf[String],
26+
d.mq.asInstanceOf[Int]
2627
)
2728
}
2829

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package tests.anImplicitClass
2+
3+
implicit class AddingOps(n: Int):
4+
def inc: Int = n + 1
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package tests.caseClassesWithVars
2+
3+
case class Sheep(var numberOfLegs: Int):
4+
var name: String = "Lawrence"
5+
6+
class Goat(var numberOfLegs: Int)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package tests.justmethods
2+
3+
trait Animal
4+
5+
def whatSoundDoIMake(animal: Animal): String = "IoIo"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package test.syntaxes
2+
3+
class DoingStuffOps[A](a: A):
4+
def doStuff: Unit = ()
5+
6+
trait DoingStuffSyntax:
7+
implicit def toDoingStuffOps[A](a: A): DoingStuffOps[A] = DoingStuffOps(a)
8+
9+
trait AllSyntaxes extends DoingStuffSyntax
10+
11+
object doingstuff extends AllSyntaxes

scaladoc/src/dotty/tools/scaladoc/Inkuire.scala

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@ import dotty.tools.scaladoc.util._
55
object Inkuire {
66

77
var db = InkuireDb(Seq.empty, Map.empty, Seq.empty, Map.empty)
8+
var implicitConversions: Seq[(Option[TypeLike], Type)] = Seq.empty
89

910
def beforeSave(): Unit = {
1011
db = db.copy(
11-
functions = db.functions.sortBy(_.hashCode),
12+
functions = functions.sortBy(_.hashCode),
1213
types = db.types.toSeq.sortBy(_._1.uuid).toMap,
13-
implicitConversions = db.implicitConversions.sortBy(_._1.uuid)
14+
implicitConversions = implicitConversions.collect {
15+
case (Some(from), to) => from -> to
16+
}.sortBy(_._1.hashCode)
1417
)
1518
}
1619

@@ -34,10 +37,59 @@ object Inkuire {
3437
case _ => e
3538
}
3639

40+
def functions: Seq[ExternalSignature] = Inkuire.db.functions.flatMap { func =>
41+
val fromConversions = Inkuire.implicitConversions.filter { ic =>
42+
func.signature.receiver.nonEmpty && matchingICTypes(ic._2, func.signature.receiver.get.typ)
43+
}.map {
44+
case (Some(from), to) =>
45+
func.copy(
46+
signature = func.signature.copy(
47+
receiver = func.signature.receiver.map { rcvr =>
48+
Contravariance(
49+
newReceiver(rcvr.typ, to, from)
50+
)
51+
}
52+
)
53+
)
54+
case (None, to) =>
55+
func.copy(
56+
signature = func.signature.copy(
57+
receiver = None
58+
)
59+
)
60+
}
61+
Seq(func) ++ fromConversions
62+
}
63+
.distinct
64+
65+
def matchingICTypes(a: TypeLike, b: TypeLike): Boolean = (a, b) match {
66+
case (a: Type, b: Type) if a.params.size == 0 && b.params.size == 0 && a.itid == b.itid => true
67+
case (a: Type, b: Type) if a.params.size == 1 && b.params.size == 1 && a.itid == b.itid =>
68+
a.params.head.typ.isInstanceOf[Type] && a.params.head.typ.asInstanceOf[Type].isVariable &&
69+
b.params.head.typ.isInstanceOf[Type] && b.params.head.typ.asInstanceOf[Type].isVariable
70+
case _ => false
71+
}
72+
73+
def newReceiver(old: TypeLike, to: TypeLike, from: TypeLike): TypeLike = (old, to) match {
74+
case (a: Type, b: Type) if a.params.size == 0 && b.params.size == 0 && a.itid == b.itid => from
75+
case (a: Type, b: Type) if a.params.size == 1 && b.params.size == 1 && a.itid == b.itid
76+
&& a.params.head.typ.isInstanceOf[Type] && a.params.head.typ.asInstanceOf[Type].isVariable &&
77+
b.params.head.typ.isInstanceOf[Type] && b.params.head.typ.asInstanceOf[Type].isVariable =>
78+
if from.isInstanceOf[Type] && from.asInstanceOf[Type].params.size == 1 && from.asInstanceOf[Type].params.head.typ.isInstanceOf[Type] && from.asInstanceOf[Type].params.head.typ.asInstanceOf[Type].isVariable then
79+
from.asInstanceOf[Type].copy(
80+
params = Seq(Contravariance(a.params.head.typ.asInstanceOf[Type]))
81+
)
82+
else if from.isInstanceOf[Type] && from.asInstanceOf[Type].isVariable then
83+
a.params.head.typ.asInstanceOf[Type]
84+
else
85+
from
86+
case _ => old
87+
}
88+
3789
case class InkuireDb(
3890
functions: Seq[ExternalSignature],
3991
types: Map[ITID, (Type, Seq[Type])],
40-
implicitConversions: Seq[(ITID, Type)],
92+
implicitConversions: Seq[(TypeLike, Type)],
4193
typeAliases: Map[ITID, TypeLike]
4294
)
4395

@@ -164,7 +216,7 @@ object Inkuire {
164216
)
165217
}
166218

167-
private def serializeConversion(conversion: (ITID, Type)): JSON = {
219+
private def serializeConversion(conversion: (TypeLike, Type)): JSON = {
168220
jsonList(
169221
Seq(
170222
serialize(conversion._1),

0 commit comments

Comments
 (0)