diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 4366515b8ce3..f267e6c85e03 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -1076,6 +1076,7 @@ object Denotations { def aggregate[T](f: SingleDenotation => T, g: (T, T) => T): T = f(this) type AsSeenFromResult = SingleDenotation + protected def computeAsSeenFrom(pre: Type)(using Context): SingleDenotation = { val symbol = this.symbol val owner = this match { @@ -1120,19 +1121,31 @@ object Denotations { then this else if symbol.isAllOf(ClassTypeParam) then val arg = symbol.typeRef.argForParam(pre, widenAbstract = true) - if arg.exists then - // take the argument bounds, but intersect with the symbols bounds if - // this forces nothing and gives a non-empty type. - val newBounds = - if symbol.isCompleted && !symbol.info.containsLazyRefs then - val combined @ TypeBounds(lo, hi) = symbol.info.bounds & arg.bounds - if lo frozen_<:< hi then combined - else arg.bounds - else arg.bounds - derivedSingleDenotation(symbol, newBounds, pre) + if arg.exists + then derivedSingleDenotation(symbol, normalizedArgBounds(arg.bounds), pre) else derived(symbol.info) else derived(symbol.info) } + + /** The argument bounds, possibly intersected with the parameter's info TypeBounds, + * if the latter is not F-bounded and does not refer to other type parameters + * of the same class, and the intersection is provably nonempty. + */ + private def normalizedArgBounds(argBounds: TypeBounds)(using Context): TypeBounds = + if symbol.isCompleted && !hasBoundsDependingOnParamsOf(symbol.owner) then + val combined @ TypeBounds(lo, hi) = symbol.info.bounds & argBounds + if (lo frozen_<:< hi) then combined + else argBounds + else argBounds + + private def hasBoundsDependingOnParamsOf(cls: Symbol)(using Context): Boolean = + val acc = new TypeAccumulator[Boolean]: + def apply(x: Boolean, tp: Type): Boolean = tp match + case _: LazyRef => true + case tp: TypeRef + if tp.symbol.isAllOf(ClassTypeParam) && tp.symbol.owner == cls => true + case _ => foldOver(x, tp) + acc(false, symbol.info) } abstract class NonSymSingleDenotation(symbol: Symbol, initInfo: Type, override val prefix: Type) extends SingleDenotation(symbol, initInfo) { diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 483745928f25..f60bba88dde2 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -442,14 +442,6 @@ object Types { final def containsWildcardTypes(using Context) = existsPart(_.isInstanceOf[WildcardType], StopAt.Static, forceLazy = false) - /** Does this type contain LazyRef types? */ - final def containsLazyRefs(using Context) = - val acc = new TypeAccumulator[Boolean]: - def apply(x: Boolean, tp: Type): Boolean = tp match - case _: LazyRef => true - case _ => x || foldOver(x, tp) - acc(false, this) - // ----- Higher-order combinators ----------------------------------- /** Returns true if there is a part of this type that satisfies predicate `p`. diff --git a/tests/pos/i16105.scala b/tests/pos/i16105.scala new file mode 100644 index 000000000000..477e47e98aa7 --- /dev/null +++ b/tests/pos/i16105.scala @@ -0,0 +1,12 @@ +trait SQLSyntaxSupport[A] + +trait ResultNameSQLSyntaxProvider[S <: SQLSyntaxSupport[A], A] +trait QuerySQLSyntaxProvider[S <: SQLSyntaxSupport[A], A]{ + def resultName: ResultNameSQLSyntaxProvider[S, A] = ??? +} + +def include(syntaxProviders: QuerySQLSyntaxProvider[_, _]*) = { + syntax(syntaxProviders.map(_.resultName): _*) +} + +def syntax(resultNames: ResultNameSQLSyntaxProvider[_, _]*) = ??? \ No newline at end of file