From 32676deb85c74ce9decc4c1eff2ffd49e0013bce Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 28 Jan 2021 20:04:36 +0100 Subject: [PATCH] Improve variable instantiation when member is missing The `couldInstantiateTypeVar` method instantiated only type variables that were the constructor of an applied type. But sometimes we might also need to instantiate the argument(s), since doing so can influence the members of the applied type. This happens in two cases 1. The applied type's rhs is a match type 2. The applied type's result is one of its type parameters i11236.scala tests the first case, the additions to i9567.scala the second. Fixes #11236 --- .../dotty/tools/dotc/typer/Inferencing.scala | 17 +++++++++++++- .../test/dotc/pos-test-pickling.blacklist | 1 + tests/pos/i11236.scala | 22 +++++++++++++++++++ tests/pos/i9567.scala | 12 +++++----- 4 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 tests/pos/i11236.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 1392ed688959..dd806fc19926 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -99,8 +99,23 @@ object Inferencing { && tvar.hasLowerBound => tvar.instantiate(fromBelow = true) true - case AppliedType(tycon, _) => + case AppliedType(tycon, args) => + // The argument in `args` that may potentially appear directly as result + // and thereby influence the members of this type + def argsInResult: List[Type] = tycon match + case tycon: TypeRef => + tycon.info match + case MatchAlias(_) => args + case TypeBounds(_, upper: TypeLambda) => + upper.resultType match + case ref: TypeParamRef if ref.binder == upper => + args.lazyZip(upper.paramRefs).collect { + case (arg, pref) if pref eq ref => arg + }.toList + case _ => Nil + case _ => Nil couldInstantiateTypeVar(tycon) + || argsInResult.exists(couldInstantiateTypeVar) case RefinedType(parent, _, _) => couldInstantiateTypeVar(parent) case tp: AndOrType => diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index 3df8675fbf15..c3630d3bc047 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -32,6 +32,7 @@ i7868.scala i7872.scala 6709.scala 6687.scala +i11236.scala # Opaque type i5720.scala diff --git a/tests/pos/i11236.scala b/tests/pos/i11236.scala new file mode 100644 index 000000000000..e2b9892d4f43 --- /dev/null +++ b/tests/pos/i11236.scala @@ -0,0 +1,22 @@ +class Test { + val tup: Char #: Int #: String #: TupleK = ??? + val x: String #: TupleK = (tup.tail: Int #: String #: TupleK).tail + val a = tup.tail + val b = a.tail + val y: String #: TupleK = tup.tail.tail + val z: Unit = tup.tail.tail +} + +trait TupleK + +object TupleK { + type Tail[X <: NonEmptyTupleK] <: TupleK = X match { + case _ #: xs => xs + } +} + +trait NonEmptyTupleK extends TupleK { + /*inline*/ def tail[This >: this.type <: NonEmptyTupleK]: TupleK.Tail[This] = ??? +} + +abstract class #:[+H, +T <: TupleK] extends NonEmptyTupleK \ No newline at end of file diff --git a/tests/pos/i9567.scala b/tests/pos/i9567.scala index d1af00680388..74b2423a8ba5 100644 --- a/tests/pos/i9567.scala +++ b/tests/pos/i9567.scala @@ -2,8 +2,11 @@ // val x: Int => Int = identity // } -trait Foo[F[_]] { +trait Foo[F[_], I[X] <: X] { + type Id[X] <: X def foo[G[x] >: F[x]]: G[Unit] + def foo2[X >: F[String]]: Id[X] + def foo3[X >: F[String]]: I[X] } trait M[A] { @@ -12,11 +15,10 @@ trait M[A] { } object Test { - def bar(x: Foo[M]): Unit = { - // error: value bla is not a member of G[Unit], where: G is a type variable with constraint >: M and <: [x] =>> Any + def bar(x: Foo[M, [X] =>> X]): Unit = { x.foo.bla - - // error: value bla is not a member of G[Unit], where: G is a type variable with constraint >: M and <: [x] =>> Any x.foo.baz(x => x) + x.foo2.bla + x.foo3.bla } }