From d64490beb66df8cdee3187c761d6ba42cfe4c8f2 Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 14 Jul 2022 12:19:12 +0200 Subject: [PATCH] Survive TypeErrors in isMatchedBy Fixes #15673 --- .../src/dotty/tools/dotc/core/Types.scala | 11 ++--- .../dotty/tools/dotc/typer/ProtoTypes.scala | 41 ++++++++++++------- tests/pos/i15673.scala | 7 ++++ 3 files changed, 40 insertions(+), 19 deletions(-) create mode 100644 tests/pos/i15673.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 5292c0506c36..a0dbe768ba67 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -299,13 +299,13 @@ object Types { * ThisType of `symd`'s owner, or a reference to `symd`'s owner.' */ def isArgPrefixOf(symd: SymDenotation)(using Context): Boolean = - symd.exists && !symd.owner.is(Package) && // Early exit if possible because the next check would force SymbolLoaders - symd.isAllOf(ClassTypeParam) && { - this match { + symd.exists + && !symd.owner.is(Package) // Early exit if possible because the next check would force SymbolLoaders + && symd.isAllOf(ClassTypeParam) + && { this match case tp: ThisType => tp.cls ne symd.owner case tp: TypeRef => tp.symbol ne symd.owner case _ => true - } } /** Is this type a (possibly aliased) singleton type? */ @@ -2337,7 +2337,8 @@ object Types { i"""bad parameter reference $this at ${ctx.phase} |the parameter is ${param.showLocated} but the prefix $prefix |does not define any corresponding arguments. - |idx = $idx, args = $args""") + |idx = $idx, args = $args%, %, + |constraint = ${ctx.typerState.constraint}""") NoDenotation } } diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index d9c23c408dc4..71b500dc04a9 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -189,20 +189,33 @@ object ProtoTypes { case _ => false override def isMatchedBy(tp1: Type, keepConstraint: Boolean)(using Context): Boolean = - name == nme.WILDCARD || hasUnknownMembers(tp1) || - { - val mbr = if (privateOK) tp1.member(name) else tp1.nonPrivateMember(name) - def qualifies(m: SingleDenotation) = - val isAccessible = !m.symbol.exists || m.symbol.isAccessibleFrom(tp1, superAccess = true) - isAccessible - && (memberProto.isRef(defn.UnitClass) - || tp1.isValueType && compat.normalizedCompatible(NamedType(tp1, name, m), memberProto, keepConstraint)) - // Note: can't use `m.info` here because if `m` is a method, `m.info` - // loses knowledge about `m`'s default arguments. - mbr match { // hasAltWith inlined for performance - case mbr: SingleDenotation => mbr.exists && qualifies(mbr) - case _ => mbr hasAltWith qualifies - } + name == nme.WILDCARD + || hasUnknownMembers(tp1) + || { + try + val mbr = if privateOK then tp1.member(name) else tp1.nonPrivateMember(name) + def qualifies(m: SingleDenotation) = + val isAccessible = !m.symbol.exists || m.symbol.isAccessibleFrom(tp1, superAccess = true) + isAccessible + && (memberProto.isRef(defn.UnitClass) + || tp1.isValueType && compat.normalizedCompatible(NamedType(tp1, name, m), memberProto, keepConstraint)) + // Note: can't use `m.info` here because if `m` is a method, `m.info` + // loses knowledge about `m`'s default arguments. + mbr match // hasAltWith inlined for performance + case mbr: SingleDenotation => mbr.exists && qualifies(mbr) + case _ => mbr hasAltWith qualifies + catch case ex: TypeError => + // A scenario where this can happen is in pos/15673.scala: + // We have a type `CC[A]#C` where `CC`'s upper bound is `[X] => Any`, but + // the current constraint stipulates CC <: SeqOps[...], where `SeqOps` defines + // the `C` parameter. We try to resolve this using `argDenot` but `argDenot` + // consults the base type of `CC`, which is not `SeqOps`, so it does not + // find a corresponding argument. In fact, `argDenot` is not allowed to + // consult short-lived things like the current constraint, so it has no other + // choice. The problem will be healed later, when normal selection fails + // and we try to instantiate type variables to compensate. But we have to make + // sure we do not issue a type error before we get there. + false } def underlying(using Context): Type = WildcardType diff --git a/tests/pos/i15673.scala b/tests/pos/i15673.scala new file mode 100644 index 000000000000..1400a1913f06 --- /dev/null +++ b/tests/pos/i15673.scala @@ -0,0 +1,7 @@ +trait SeqOps[+A, +CC[_], +C]: // scala.collection.SeqOps + def reverse: C + +extension[A, CC[B] <: SeqOps[B, CC, CC[B]]](ring: CC[A]) + def startAt(i: Int): CC[A] = ??? + def reflectAt(i: Int): CC[A] = + startAt(i).reverse \ No newline at end of file