Skip to content

Commit

Permalink
Make sure typeParams returns a stable result even in the presence of …
Browse files Browse the repository at this point in the history
…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.
  • Loading branch information
odersky authored Mar 18, 2024
2 parents 397da20 + c65bdb3 commit 5dc6860
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 4 deletions.
12 changes: 11 additions & 1 deletion compiler/src/dotty/tools/dotc/core/TypeApplications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,17 @@ class TypeApplications(val self: Type) extends AnyVal {
val tsym = self.symbol
if (tsym.isClass) tsym.typeParams
else tsym.infoOrCompleter match {
case info: LazyType if isTrivial(self.prefix, tsym) => info.completerTypeParams(tsym)
case info: LazyType if isTrivial(self.prefix, tsym) =>
val tparams = info.completerTypeParams(tsym)
if tsym.isCompleted then tsym.info.typeParams
// Completers sometimes represent parameters as symbols where
// the completed type represents them as paramrefs. Make sure we get
// a stable result by calling `typeParams` recursively. Test case
// is pos/i19942.scala, where parameter F0 has initially a Namer#TypeDefCompleter.
// After calling its completerTypeParams, we get a list of parameter symbols
// and as a side effect F0 is completed. Calling typeParams on the completed
// type gives a list of paramrefs.
else tparams
case _ => self.info.typeParams
}
case self: AppliedType =>
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4293,7 +4293,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer

def adaptType(tp: Type): Tree = {
val tree1 =
if ((pt eq AnyTypeConstructorProto) || tp.typeParamSymbols.isEmpty) tree
if (pt eq AnyTypeConstructorProto) || tp.typeParamSymbols.isEmpty then tree
else {
if (ctx.isJava)
// Cook raw type
Expand Down
4 changes: 2 additions & 2 deletions tests/neg-macros/quote-type-variable-no-inference-3.check
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
-- Warning: tests/neg-macros/quote-type-variable-no-inference-3.scala:5:22 ---------------------------------------------
5 | case '{ $_ : F[t, t]; () } => // warn // error
| ^
| Ignored bound <: Comparable[U]
| Ignored bound <: Comparable[Any]
|
| Consider defining bounds explicitly:
| '{ type t <: Comparable[U]; ... }
| '{ type t <: Comparable[Any]; ... }
-- Warning: tests/neg-macros/quote-type-variable-no-inference-3.scala:6:49 ---------------------------------------------
6 | case '{ type u <: Comparable[`u`]; $_ : F[u, u] } =>
| ^
Expand Down
29 changes: 29 additions & 0 deletions tests/pos/i19942.1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
trait Alternative[F[_]]

opaque type Derived[A] = A
object Derived:
extension [A](derived: Derived[A]) def instance: A = derived
infix type <<<[F[_], G[_]] = [x] =>> F[G[x]]

import Derived.*
import scala.compiletime.summonInline

type DerivedAlternative[F[_]] = Derived[Alternative[F]]
object DerivedAlternative:
inline def apply[F[_]]: Alternative[F] =
import DerivedAlternative.given
summonInline[DerivedAlternative[F]].instance
given nested[F[_], G[_]]: DerivedAlternative[F <<< G] = ???

object auto:
object alternative:
transparent inline given [F[_]]: Alternative[F] = DerivedAlternative[F]

trait Test:
import Test.*
import auto.alternative.given
val fails = summon[Alternative[OptList]]

// Fails if companion object defined AFTER trait
object Test:
type OptList[A] = Option[List[A]]
23 changes: 23 additions & 0 deletions tests/pos/i19942.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
type LifecycleF = [_] =>> Any
trait Lifecycle[+F[_], +A]

trait LifecycleTag[R]
object LifecycleTag:
implicit def resourceTag[R <: Lifecycle[F0, A0], F0[_], A0]: LifecycleTag[R] = ???

trait MakeDSL[T]:
final def fromResource[R <: Lifecycle[LifecycleF, T]](implicit tag: LifecycleTag[R]): Any = ???

object distage:
// Cannot be defined in the same scope as rest of code
final type Identity[+A] = A
import distage.*

trait Resource
trait DependentResource() extends Lifecycle[Identity, Resource]

@main def Test = {
val dsl: MakeDSL[Resource] = ???
val fails = dsl.fromResource[DependentResource]
val works = dsl.fromResource[DependentResource](using LifecycleTag.resourceTag[DependentResource, Identity, Resource])
}

0 comments on commit 5dc6860

Please sign in to comment.