From 63cf4b6aaba14c778e26415b76cfa78de03a3919 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 20 Nov 2018 22:04:43 +0100 Subject: [PATCH 1/2] Fix #5481: Follow opaque aliases when determining prototypes of functions If the prototype of a function value is a synthetic opaque type alias, assume the alias type. If the alias type is a function type, this allows one to infer the parameter types of the function value. --- compiler/src/dotty/tools/dotc/core/Types.scala | 8 ++++++++ .../src/dotty/tools/dotc/typer/Typer.scala | 7 +++---- tests/pos/i5481.scala | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 tests/pos/i5481.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index b101ada545cb..07723994dabe 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1083,6 +1083,14 @@ object Types { /** Like `dealiasKeepAnnots`, but keeps only refining annotations */ final def dealiasKeepRefiningAnnots(implicit ctx: Context): Type = dealias1(keepIfRefining) + /** If this is a synthetic opaque type, its opaque alias, otherwise the type itself */ + final def followSyntheticOpaque(implicit ctx: Context): Type = this match { + case tp: TypeProxy if tp.typeSymbol.is(SyntheticOpaque) => + val AndType(alias, _) = tp.superType + alias + case _ => this + } + /** The result of normalization using `tryNormalize`, or the type itself if * tryNormlize yields NoType */ diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index cea957340d90..8176a28fc309 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -728,13 +728,12 @@ class Typer extends Namer case _: WildcardType => untpd.TypeTree() case _ => untpd.TypeTree(tp) } - pt.stripTypeVar match { - case _ if defn.isNonDepFunctionType(pt) => + pt.stripTypeVar.dealias.followSyntheticOpaque match { + case pt1 if defn.isNonDepFunctionType(pt1) => // if expected parameter type(s) are wildcards, approximate from below. // if expected result type is a wildcard, approximate from above. // this can type the greatest set of admissible closures. - val funType = pt.dealias - (funType.argTypesLo.init, typeTree(funType.argTypesHi.last)) + (pt1.argTypesLo.init, typeTree(pt1.argTypesHi.last)) case SAMType(sam @ MethodTpe(_, formals, restpe)) => (formals, if (sam.isResultDependent) diff --git a/tests/pos/i5481.scala b/tests/pos/i5481.scala new file mode 100644 index 000000000000..d43cd988541f --- /dev/null +++ b/tests/pos/i5481.scala @@ -0,0 +1,18 @@ +object TypeAlias { + + type Set[A] = A => Boolean + + object Set { + def singleton[A](a: A): Set[A] = _ == a // Works + } +} + +object OpaqueType { + + opaque type Set[A] = A => Boolean + + object Set { + def singleton[A](a: A): Set[A] = _ == a // Does not compile + def singleton0[A](a: A): Set[A] = (_: A) == a // Works + } +} \ No newline at end of file From 9e4d43398f0e4f30202a778b51949da4307b31f7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 20 Nov 2018 23:00:31 +0100 Subject: [PATCH 2/2] More explanations and tests --- .../src/dotty/tools/dotc/core/Types.scala | 10 +++++++--- tests/neg/i5481.scala | 20 +++++++++++++++++++ tests/pos/i5481.scala | 7 ++++--- 3 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 tests/neg/i5481.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 07723994dabe..576e69e790cc 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1083,11 +1083,15 @@ object Types { /** Like `dealiasKeepAnnots`, but keeps only refining annotations */ final def dealiasKeepRefiningAnnots(implicit ctx: Context): Type = dealias1(keepIfRefining) - /** If this is a synthetic opaque type, its opaque alias, otherwise the type itself */ + /** If this is a synthetic opaque type seen from inside the opaque companion object, + * its opaque alias, otherwise the type itself. + */ final def followSyntheticOpaque(implicit ctx: Context): Type = this match { case tp: TypeProxy if tp.typeSymbol.is(SyntheticOpaque) => - val AndType(alias, _) = tp.superType - alias + tp.superType match { + case AndType(alias, _) => alias // in this case we are inside the companion object + case _ => this + } case _ => this } diff --git a/tests/neg/i5481.scala b/tests/neg/i5481.scala new file mode 100644 index 000000000000..274575689384 --- /dev/null +++ b/tests/neg/i5481.scala @@ -0,0 +1,20 @@ +object TypeAlias { + + type Set[A] = A => Boolean + + object Set { + def singleton[A](a: A): Set[A] = _ == a + } +} + +object OpaqueType { + + opaque type Set[A] = A => Boolean + + object Set { + def singleton[A](a: A): Set[A] = _ == a + def singleton0[A](a: A): Set[A] = (_: A) == a + } + + def singleton[A](a: A): Set[A] = _ == a // error: missing parameter type +} \ No newline at end of file diff --git a/tests/pos/i5481.scala b/tests/pos/i5481.scala index d43cd988541f..102121b9ede5 100644 --- a/tests/pos/i5481.scala +++ b/tests/pos/i5481.scala @@ -3,7 +3,7 @@ object TypeAlias { type Set[A] = A => Boolean object Set { - def singleton[A](a: A): Set[A] = _ == a // Works + def singleton[A](a: A): Set[A] = _ == a } } @@ -12,7 +12,8 @@ object OpaqueType { opaque type Set[A] = A => Boolean object Set { - def singleton[A](a: A): Set[A] = _ == a // Does not compile - def singleton0[A](a: A): Set[A] = (_: A) == a // Works + def singleton[A](a: A): Set[A] = _ == a + def singleton0[A](a: A): Set[A] = (_: A) == a } + } \ No newline at end of file