From 95145a24cb9a37536677a38ff639b790939b8326 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 14 Jan 2022 16:07:59 +0000 Subject: [PATCH 1/4] Avoid cyclic error in checkNoModuleClash --- compiler/src/dotty/tools/dotc/typer/Checking.scala | 6 +++--- tests/pos/i14152.min.scala | 6 ++++++ tests/pos/i14152.scala | 6 ++++++ 3 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 tests/pos/i14152.min.scala create mode 100644 tests/pos/i14152.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 85eba072f7a8..ceb504bc5087 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -886,11 +886,11 @@ trait Checking { */ def checkNoModuleClash(sym: Symbol)(using Context): Unit = if sym.effectiveOwner.is(Package) - && sym.owner.info.member(sym.name.moduleClassName).symbol.isAbsent() + && sym.effectiveOwner.info.member(sym.name.moduleClassName).symbol.isAbsent() then - val conflicting = sym.owner.info.member(sym.name.toTypeName).symbol + val conflicting = sym.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, sym.effectiveOwner, conflicting), sym.srcPos) /** Check that `tp` is a class type. * Also, if `traitReq` is true, check that `tp` is a trait. diff --git a/tests/pos/i14152.min.scala b/tests/pos/i14152.min.scala new file mode 100644 index 000000000000..c2525843caa0 --- /dev/null +++ b/tests/pos/i14152.min.scala @@ -0,0 +1,6 @@ +val aa1 = { + object O1 extends AnyRef + Array(O1) +} + +val aa2: Array[_ <: AnyRef] = aa1 diff --git a/tests/pos/i14152.scala b/tests/pos/i14152.scala new file mode 100644 index 000000000000..364b11d9cbae --- /dev/null +++ b/tests/pos/i14152.scala @@ -0,0 +1,6 @@ +val aa1 = { + object O1 extends AnyRef + Array(Array(O1)) +} + +val aa2: Array[_ <: Array[_ <: AnyRef]] = aa1 From a71210b1139f10dc93e6169c522be6ebe5b62a39 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 17 Jan 2022 17:03:04 +0000 Subject: [PATCH 2/4] Extract effectiveOwner --- compiler/src/dotty/tools/dotc/typer/Checking.scala | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index ceb504bc5087..be38221ef167 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -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.effectiveOwner.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.effectiveOwner.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.effectiveOwner, 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. From 5f51f5f6f68172484f5a7057ddbe78ac23c72bbe Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 17 Jan 2022 17:01:35 +0000 Subject: [PATCH 3/4] Fix type avoidance in nested Array case --- .../src/dotty/tools/dotc/core/Types.scala | 11 +++++--- tests/pos/i14152.min.scala | 6 ----- tests/pos/i14152.scala | 26 ++++++++++++++++++- 3 files changed, 32 insertions(+), 11 deletions(-) delete mode 100644 tests/pos/i14152.min.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 2d02546a6d5e..9e567f6ca57b 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -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 @@ -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 { @@ -5886,8 +5886,11 @@ 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 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) } diff --git a/tests/pos/i14152.min.scala b/tests/pos/i14152.min.scala deleted file mode 100644 index c2525843caa0..000000000000 --- a/tests/pos/i14152.min.scala +++ /dev/null @@ -1,6 +0,0 @@ -val aa1 = { - object O1 extends AnyRef - Array(O1) -} - -val aa2: Array[_ <: AnyRef] = aa1 diff --git a/tests/pos/i14152.scala b/tests/pos/i14152.scala index 364b11d9cbae..2377d5ffeae3 100644 --- a/tests/pos/i14152.scala +++ b/tests/pos/i14152.scala @@ -1,6 +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]] From 3c9a2ffd78e972e17b8022ca5e431dffbba3bd96 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 17 Jan 2022 21:21:57 +0000 Subject: [PATCH 4/4] Avoid weird Ranges of TypeBounds?? --- compiler/src/dotty/tools/dotc/core/Types.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 9e567f6ca57b..ca0b3b45f072 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -5886,7 +5886,7 @@ object Types { if (distributeArgs(args, tp.tyconTypeParams)) range(tp.derivedAppliedType(tycon, loBuf.toList), tp.derivedAppliedType(tycon, hiBuf.toList)) - else if tycon.isLambdaSub then + else if tycon.isLambdaSub || args.exists(isRangeOfNonTermTypes) then range(defn.NothingType, defn.AnyType) else // See lampepfl/dotty#14152 @@ -5894,6 +5894,10 @@ object Types { 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)