From dfdecf1fe5d9ea896d84e6204a9f503426bbee9e Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 13 Jul 2022 17:08:52 +0200 Subject: [PATCH] Better infos for dependent class parameter references We sometimes create a dependent parameter reference p.X where `p` is a path with a type that has a wildcard argument, e.g. `P[X >: L <: H].` So far the denotation of `p.X` had as info the bounds with which `X` was declared in `P`. Now it gets the actual parameter bounds instead. Fixes #15652 #15652 started failing when tuple unpackings started to use `val`s instead of `def`s in #14816. That caused dependent class parameter references to be created and uncovered the problem. --- .../src/dotty/tools/dotc/core/Denotations.scala | 4 ++++ compiler/src/dotty/tools/dotc/core/TypeOps.scala | 3 +-- compiler/src/dotty/tools/dotc/core/Types.scala | 14 ++++++++------ tests/pos/i15652.scala | 12 ++++++++++++ 4 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 tests/pos/i15652.scala diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index f183cb35d9cc..61dd71603f64 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -1118,6 +1118,10 @@ object Denotations { if !owner.membersNeedAsSeenFrom(pre) && (!ownerIsPrefix || hasOriginalInfo) || symbol.is(NonMember) then this + else if symbol.isAllOf(ClassTypeParam) then + val arg = symbol.typeRef.argForParam(pre, widenAbstract = true) + if arg.exists then derivedSingleDenotation(symbol, arg.bounds, pre) + else derived(symbol.info) else derived(symbol.info) } } diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index fc18c9758947..46a8351fef2c 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -426,8 +426,7 @@ object TypeOps: override def apply(tp: Type): Type = try tp match - case tp: TermRef - if toAvoid(tp) => + case tp: TermRef if toAvoid(tp) => tp.info.widenExpr.dealias match { case info: SingletonType => apply(info) case info => range(defn.NothingType, apply(info)) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 241621631c41..a2ac3a2964dd 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2453,10 +2453,12 @@ object Types { ctx.base.underlyingRecursions -= 1 /** The argument corresponding to class type parameter `tparam` as seen from - * prefix `pre`. Can produce a TypeBounds type in case prefix is an & or | type - * and parameter is non-variant. + * prefix `pre`. Can produce a TypeBounds type if `widenAbstract` is true, + * or prefix is an & or | type and parameter is non-variant. + * Otherwise, a typebounds argument is dropped and the original type parameter + * reference is returned. */ - def argForParam(pre: Type)(using Context): Type = { + def argForParam(pre: Type, widenAbstract: Boolean = false)(using Context): Type = { val tparam = symbol val cls = tparam.owner val base = pre.baseType(cls) @@ -2468,7 +2470,7 @@ object Types { while (tparams.nonEmpty && args.nonEmpty) { if (tparams.head.eq(tparam)) return args.head match { - case _: TypeBounds => TypeRef(pre, tparam) + case _: TypeBounds if !widenAbstract => TypeRef(pre, tparam) case arg => arg } tparams = tparams.tail @@ -5803,10 +5805,10 @@ object Types { // if H#T = U, then for any x in L..H, x.T =:= U, // hence we can replace with U under all variances reapply(alias.rewrapAnnots(tp1)) - case tp: TypeBounds => + case bounds: TypeBounds => // If H#T = ? >: S <: U, then for any x in L..H, S <: x.T <: U, // hence we can replace with S..U under all variances - expandBounds(tp) + expandBounds(bounds) case info: SingletonType => // if H#x: y.type, then for any x in L..H, x.type =:= y.type, // hence we can replace with y.type under all variances diff --git a/tests/pos/i15652.scala b/tests/pos/i15652.scala new file mode 100644 index 000000000000..57d8fa54c0c4 --- /dev/null +++ b/tests/pos/i15652.scala @@ -0,0 +1,12 @@ +trait Node +type NodeParser[T] = Node => T + +def child(key: String): Option[Node] = ??? + +def optionalOneOf[T](in: Map[String, NodeParser[? <: T]]): Option[T] = + val mappings = in flatMap { (key, parser) => + child(key) map { node => + key -> (() => parser(node)) + } + } + mappings.headOption map (_._2())