Skip to content

Fix checkNoModuleClash & avoid types with less precision loss #14274

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5856,7 +5856,7 @@ object Types {
tp.derivedAppliedType(tycon, args.map(rangeToBounds)) match
case tp1: AppliedType if tp1.isUnreducibleWild =>
// don't infer a type that would trigger an error later in
// Checling.checkAppliedType; fall through to default handling instead
// Checking.checkAppliedType; fall through to default handling instead
case tp1 =>
return tp1
end if
Expand All @@ -5865,7 +5865,7 @@ object Types {
// non-range arguments L1, ..., Ln and H1, ..., Hn such that
// C[L1, ..., Ln] <: C[H1, ..., Hn] by taking the right limits of
// ranges that appear in as co- or contravariant arguments.
// Fail for non-variant argument ranges.
// Fail for non-variant argument ranges (see use-site else branch below).
// If successful, the L-arguments are in loBut, the H-arguments in hiBuf.
// @return operation succeeded for all arguments.
def distributeArgs(args: List[Type], tparams: List[ParamInfo]): Boolean = args match {
Expand All @@ -5886,11 +5886,18 @@ object Types {
if (distributeArgs(args, tp.tyconTypeParams))
range(tp.derivedAppliedType(tycon, loBuf.toList),
tp.derivedAppliedType(tycon, hiBuf.toList))
else range(defn.NothingType, defn.AnyType)
// TODO: can we give a better bound than `topType`?
else if tycon.isLambdaSub || args.exists(isRangeOfNonTermTypes) then
range(defn.NothingType, defn.AnyType)
else
// See lampepfl/dotty#14152
range(defn.NothingType, tp.derivedAppliedType(tycon, args.map(rangeToBounds)))
else tp.derivedAppliedType(tycon, args)
}

private def isRangeOfNonTermTypes(tp: Type): Boolean = tp match
case Range(lo, hi) => !lo.isInstanceOf[TermType] || !hi.isInstanceOf[TermType]
case _ => false

override protected def derivedAndType(tp: AndType, tp1: Type, tp2: Type): Type =
if (isRange(tp1) || isRange(tp2)) range(lower(tp1) & lower(tp2), upper(tp1) & upper(tp2))
else tp.derivedAndType(tp1, tp2)
Expand Down
9 changes: 5 additions & 4 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -885,12 +885,13 @@ trait Checking {
* that is concurrently compiled in another source file.
*/
def checkNoModuleClash(sym: Symbol)(using Context): Unit =
if sym.effectiveOwner.is(Package)
&& sym.owner.info.member(sym.name.moduleClassName).symbol.isAbsent()
val effectiveOwner = sym.effectiveOwner
if effectiveOwner.is(Package)
&& effectiveOwner.info.member(sym.name.moduleClassName).symbol.isAbsent()
then
val conflicting = sym.owner.info.member(sym.name.toTypeName).symbol
val conflicting = effectiveOwner.info.member(sym.name.toTypeName).symbol
if conflicting.exists then
report.error(AlreadyDefined(sym.name, sym.owner, conflicting), sym.srcPos)
report.error(AlreadyDefined(sym.name, effectiveOwner, conflicting), sym.srcPos)

/** Check that `tp` is a class type.
* Also, if `traitReq` is true, check that `tp` is a trait.
Expand Down
30 changes: 30 additions & 0 deletions tests/pos/i14152.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
val a1 = {
object O1 extends AnyRef
Array(O1)
}
val a2: Array[_ <: AnyRef] = aa1

val aa1 = {
object O1 extends AnyRef
Array(Array(O1))
}
val aa2: Array[_ <: Array[_ <: AnyRef]] = aa1

val aaa1 = {
object O1 extends AnyRef
Array(Array(Array(O1)))
}
val aaa2: Array[_ <: Array[_ <: Array[_ <: AnyRef]]] = aaa1


// Let's make sure avoidance still does the right thing given abstract type constructors

class Inv[T](x: T)

def foo[F[_]](fn: [A] => Inv[A] => F[A]) =
object O1 extends AnyRef
val res0 = fn(new Inv(fn(new Inv[O1.type](O1))))
val res1: F[F[O1.type]] = res0
res1 // checked with -Xprint:typer that this widens to Any
// instead of the original F[F[O1.type]]
// or the incorrectly avoided F[? <: F[? <: Object]]