From 1f9a61ef6711176de5fea09671699a9a2f74a6b5 Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 26 May 2024 21:57:20 +0200 Subject: [PATCH] Add special handling for comparisons with Singleton types Fixes #15030 --- .../src/dotty/tools/dotc/core/TypeComparer.scala | 13 ++++++++----- compiler/src/dotty/tools/dotc/core/Types.scala | 10 ++++++++++ tests/pos/i15030.scala | 16 ++++++++++++++++ 3 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 tests/pos/i15030.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index c2c502a984c4..e0ab30907314 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -970,12 +970,15 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling compareAppliedType1(tp1, tycon1, args1) case tp1: SingletonType => def comparePaths = tp2 match - case tp2: TermRef => + case tp2: (TermRef | ThisType) => compareAtoms(tp1, tp2, knownSingletons = true).getOrElse(false) - || { // needed to make from-tasty work. test cases: pos/i1753.scala, pos/t839.scala - tp2.info.widenExpr.dealias match - case tp2i: SingletonType => recur(tp1, tp2i) - case _ => false + || { + // If tp2's underlying type tp2super is also effectively a singleton, compare + // against that. The idea is that if tp1 <: tp2super and tp2 <: tp2super and + // tp2super is also singleton, then tp1 and tp2 must be the same singleton. + // Needed to make from-tasty work. test cases: pos/i1753.scala, pos/t839.scala + val tp2super = tp2.superType.widenExpr + tp2super.isEffectivelySingleton && recur(tp1, tp2super) } case _ => false diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index eeffc41d4159..cad06e973741 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -328,6 +328,16 @@ object Types extends TypeUtils { /** Is this type a (possibly aliased) singleton type? */ def isSingleton(using Context): Boolean = dealias.isInstanceOf[SingletonType] + /** Is this type a (possibly aliased) singleton type or a type proxy + * or Or/And type known to be a singleton type? + */ + def isEffectivelySingleton(using Context): Boolean = dealias match + case tp: SingletonType => true + case tp: TypeProxy => tp.superType.isEffectivelySingleton + case AndType(tpL, tpR) => tpL.isEffectivelySingleton || tpR.isEffectivelySingleton + case OrType(tpL, tpR) => tpL.isEffectivelySingleton && tpR.isEffectivelySingleton + case _ => false + /** Is this upper-bounded by a (possibly aliased) singleton type? * Overridden in TypeVar */ diff --git a/tests/pos/i15030.scala b/tests/pos/i15030.scala new file mode 100644 index 000000000000..f0983140120c --- /dev/null +++ b/tests/pos/i15030.scala @@ -0,0 +1,16 @@ +sealed trait Schema[A] + +object Schema extends RecordInstances: + case class Field[A]() + +sealed trait RecordInstances: + self: Schema.type => + + case class Record[A](field: Field[A]) extends Schema[A] + +import Schema._ + +val field: Field[Int] = Field() + +// Uh oh Found Playground.Schema.Field[Int] but Requried RecordInstances.this.Field[Int] +val record = Record[Int](field) \ No newline at end of file