Skip to content

Commit 5dc6860

Browse files
authored
Make sure typeParams returns a stable result even in the presence of completions (#19974)
Fixes #19942 Based on #19960 @dwijnand I would have pushed onto the original PR if it had been on staging. Better to always push to staging, not your own repo.
2 parents 397da20 + c65bdb3 commit 5dc6860

File tree

5 files changed

+66
-4
lines changed

5 files changed

+66
-4
lines changed

Diff for: compiler/src/dotty/tools/dotc/core/TypeApplications.scala

+11-1
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,17 @@ class TypeApplications(val self: Type) extends AnyVal {
182182
val tsym = self.symbol
183183
if (tsym.isClass) tsym.typeParams
184184
else tsym.infoOrCompleter match {
185-
case info: LazyType if isTrivial(self.prefix, tsym) => info.completerTypeParams(tsym)
185+
case info: LazyType if isTrivial(self.prefix, tsym) =>
186+
val tparams = info.completerTypeParams(tsym)
187+
if tsym.isCompleted then tsym.info.typeParams
188+
// Completers sometimes represent parameters as symbols where
189+
// the completed type represents them as paramrefs. Make sure we get
190+
// a stable result by calling `typeParams` recursively. Test case
191+
// is pos/i19942.scala, where parameter F0 has initially a Namer#TypeDefCompleter.
192+
// After calling its completerTypeParams, we get a list of parameter symbols
193+
// and as a side effect F0 is completed. Calling typeParams on the completed
194+
// type gives a list of paramrefs.
195+
else tparams
186196
case _ => self.info.typeParams
187197
}
188198
case self: AppliedType =>

Diff for: compiler/src/dotty/tools/dotc/typer/Typer.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -4293,7 +4293,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
42934293

42944294
def adaptType(tp: Type): Tree = {
42954295
val tree1 =
4296-
if ((pt eq AnyTypeConstructorProto) || tp.typeParamSymbols.isEmpty) tree
4296+
if (pt eq AnyTypeConstructorProto) || tp.typeParamSymbols.isEmpty then tree
42974297
else {
42984298
if (ctx.isJava)
42994299
// Cook raw type

Diff for: tests/neg-macros/quote-type-variable-no-inference-3.check

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
-- Warning: tests/neg-macros/quote-type-variable-no-inference-3.scala:5:22 ---------------------------------------------
22
5 | case '{ $_ : F[t, t]; () } => // warn // error
33
| ^
4-
| Ignored bound <: Comparable[U]
4+
| Ignored bound <: Comparable[Any]
55
|
66
| Consider defining bounds explicitly:
7-
| '{ type t <: Comparable[U]; ... }
7+
| '{ type t <: Comparable[Any]; ... }
88
-- Warning: tests/neg-macros/quote-type-variable-no-inference-3.scala:6:49 ---------------------------------------------
99
6 | case '{ type u <: Comparable[`u`]; $_ : F[u, u] } =>
1010
| ^

Diff for: tests/pos/i19942.1.scala

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
trait Alternative[F[_]]
2+
3+
opaque type Derived[A] = A
4+
object Derived:
5+
extension [A](derived: Derived[A]) def instance: A = derived
6+
infix type <<<[F[_], G[_]] = [x] =>> F[G[x]]
7+
8+
import Derived.*
9+
import scala.compiletime.summonInline
10+
11+
type DerivedAlternative[F[_]] = Derived[Alternative[F]]
12+
object DerivedAlternative:
13+
inline def apply[F[_]]: Alternative[F] =
14+
import DerivedAlternative.given
15+
summonInline[DerivedAlternative[F]].instance
16+
given nested[F[_], G[_]]: DerivedAlternative[F <<< G] = ???
17+
18+
object auto:
19+
object alternative:
20+
transparent inline given [F[_]]: Alternative[F] = DerivedAlternative[F]
21+
22+
trait Test:
23+
import Test.*
24+
import auto.alternative.given
25+
val fails = summon[Alternative[OptList]]
26+
27+
// Fails if companion object defined AFTER trait
28+
object Test:
29+
type OptList[A] = Option[List[A]]

Diff for: tests/pos/i19942.scala

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
type LifecycleF = [_] =>> Any
2+
trait Lifecycle[+F[_], +A]
3+
4+
trait LifecycleTag[R]
5+
object LifecycleTag:
6+
implicit def resourceTag[R <: Lifecycle[F0, A0], F0[_], A0]: LifecycleTag[R] = ???
7+
8+
trait MakeDSL[T]:
9+
final def fromResource[R <: Lifecycle[LifecycleF, T]](implicit tag: LifecycleTag[R]): Any = ???
10+
11+
object distage:
12+
// Cannot be defined in the same scope as rest of code
13+
final type Identity[+A] = A
14+
import distage.*
15+
16+
trait Resource
17+
trait DependentResource() extends Lifecycle[Identity, Resource]
18+
19+
@main def Test = {
20+
val dsl: MakeDSL[Resource] = ???
21+
val fails = dsl.fromResource[DependentResource]
22+
val works = dsl.fromResource[DependentResource](using LifecycleTag.resourceTag[DependentResource, Identity, Resource])
23+
}

0 commit comments

Comments
 (0)