diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index 223d3bb11515..eeb18eaa9cc7 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -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 => diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index ade395f835ca..78637a0292ed 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -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 diff --git a/tests/neg-macros/quote-type-variable-no-inference-3.check b/tests/neg-macros/quote-type-variable-no-inference-3.check index 91476728654d..e98900069740 100644 --- a/tests/neg-macros/quote-type-variable-no-inference-3.check +++ b/tests/neg-macros/quote-type-variable-no-inference-3.check @@ -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] } => | ^ diff --git a/tests/pos/i19942.1.scala b/tests/pos/i19942.1.scala new file mode 100644 index 000000000000..20f923886089 --- /dev/null +++ b/tests/pos/i19942.1.scala @@ -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]] diff --git a/tests/pos/i19942.scala b/tests/pos/i19942.scala new file mode 100644 index 000000000000..9be97f635df4 --- /dev/null +++ b/tests/pos/i19942.scala @@ -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]) +}