From 4f31f017673d99addaccb02668fb19b6c85c8879 Mon Sep 17 00:00:00 2001 From: Emil Ejbyfeldt Date: Thu, 23 May 2024 08:19:30 +0200 Subject: [PATCH 1/7] Remove seen from TypeSizeAccumulator This fixes #15692 and does not seem to break an existing compilation tests. The problem with seen when it comes #15692 is that seen means that type that has repeated types will get a lower size and this incorrectly triggers the divergence check since some of the steps involved less repeated types. The seen logic was introduced in #6329 and the motivation was to deal with F-bounds. Since not tests fail it not clear if this logic is still needed to deal with F-bounds? If it is still needed we can add a test and instead of removing the seen logic we can make it track only types the appear as a bound and could cause infinite recursion instead of tracking all. [Cherry-picked c2ef180d351c7e1e759b9199c6a6df73cdbba584] --- .../src/dotty/tools/dotc/core/Types.scala | 31 ++++++++----------- tests/pos/i15692.scala | 24 ++++++++++++++ 2 files changed, 37 insertions(+), 18 deletions(-) create mode 100644 tests/pos/i15692.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index f0ca272cce36..fc00147c81e7 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -6585,25 +6585,20 @@ object Types extends TypeUtils { } class TypeSizeAccumulator(using Context) extends TypeAccumulator[Int] { - var seen = util.HashSet[Type](initialCapacity = 8) def apply(n: Int, tp: Type): Int = - if seen.contains(tp) then n - else { - seen += tp - tp match { - case tp: AppliedType => - val tpNorm = tp.tryNormalize - if tpNorm.exists then apply(n, tpNorm) - else foldOver(n + 1, tp) - case tp: RefinedType => - foldOver(n + 1, tp) - case tp: TypeRef if tp.info.isTypeAlias => - apply(n, tp.superType) - case tp: TypeParamRef => - apply(n, TypeComparer.bounds(tp)) - case _ => - foldOver(n, tp) - } + tp match { + case tp: AppliedType => + val tpNorm = tp.tryNormalize + if tpNorm.exists then apply(n, tpNorm) + else foldOver(n + 1, tp) + case tp: RefinedType => + foldOver(n + 1, tp) + case tp: TypeRef if tp.info.isTypeAlias => + apply(n, tp.superType) + case tp: TypeParamRef => + apply(n, TypeComparer.bounds(tp)) + case _ => + foldOver(n, tp) } } diff --git a/tests/pos/i15692.scala b/tests/pos/i15692.scala new file mode 100644 index 000000000000..11b69e3bd377 --- /dev/null +++ b/tests/pos/i15692.scala @@ -0,0 +1,24 @@ +sealed trait Nat +sealed trait Succ[Prev <: Nat] extends Nat +sealed trait Zero extends Nat + +class Sum[M <: Nat, N <: Nat] { + type Out <: Nat +} + +object Sum { + type Aux[M <: Nat, N <: Nat, R <: Nat] = Sum[M, N] { type Out = R } + + implicit def sum0[N <: Nat]: Sum.Aux[Zero, N, N] = new Sum[Zero, N] { type Out = N } + implicit def sum1[M <: Nat, N <: Nat, R <: Nat](implicit sum: Sum.Aux[M, Succ[N], R]): Sum.Aux[Succ[M], N, R] = + new Sum[Succ[M], N] { type Out = R } +} + +object Test { + def main(args: Array[String]): Unit = { + type _3 = Succ[Succ[Succ[Zero]]] + type _5 = Succ[Succ[_3]] + + implicitly[Sum[_3, _5]] + } +} From 9d5bb69521bb710bc03a9fedd7265bcfdaa52a73 Mon Sep 17 00:00:00 2001 From: Emil Ejbyfeldt Date: Sat, 5 Jul 2025 21:59:37 +0200 Subject: [PATCH 2/7] Do seen check only for LazyRef Also adds test case that stackoverflows without any seen check. [Cherry-picked 008d58e42a7143af670cb238105148f2ae200236] --- compiler/src/dotty/tools/dotc/core/Types.scala | 4 ++++ tests/neg/i15692.scala | 5 +++++ tests/pos/i15692.scala | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tests/neg/i15692.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index fc00147c81e7..d400335e7f83 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -6585,7 +6585,9 @@ object Types extends TypeUtils { } class TypeSizeAccumulator(using Context) extends TypeAccumulator[Int] { + var seen = util.HashSet[Type](initialCapacity = 8) def apply(n: Int, tp: Type): Int = + seen += tp tp match { case tp: AppliedType => val tpNorm = tp.tryNormalize @@ -6597,6 +6599,8 @@ object Types extends TypeUtils { apply(n, tp.superType) case tp: TypeParamRef => apply(n, TypeComparer.bounds(tp)) + case tp: LazyRef if seen.contains(tp) => + n case _ => foldOver(n, tp) } diff --git a/tests/neg/i15692.scala b/tests/neg/i15692.scala new file mode 100644 index 000000000000..0cb163426691 --- /dev/null +++ b/tests/neg/i15692.scala @@ -0,0 +1,5 @@ +trait TC[X] +object TC { + given [T, S <: TC[S]](using TC[S]): TC[T] = ??? + summon[TC[Int]] // error +} diff --git a/tests/pos/i15692.scala b/tests/pos/i15692.scala index 11b69e3bd377..99eddcd33d71 100644 --- a/tests/pos/i15692.scala +++ b/tests/pos/i15692.scala @@ -9,7 +9,7 @@ class Sum[M <: Nat, N <: Nat] { object Sum { type Aux[M <: Nat, N <: Nat, R <: Nat] = Sum[M, N] { type Out = R } - implicit def sum0[N <: Nat]: Sum.Aux[Zero, N, N] = new Sum[Zero, N] { type Out = N } + implicit def sum0[N <: Nat]: Sum.Aux[Zero, N, N] = new Sum[Zero, N] { type Out = N } implicit def sum1[M <: Nat, N <: Nat, R <: Nat](implicit sum: Sum.Aux[M, Succ[N], R]): Sum.Aux[Succ[M], N, R] = new Sum[Succ[M], N] { type Out = R } } From 39b3db3738bde35d2f8cfc96b92d38c2b59ba60b Mon Sep 17 00:00:00 2001 From: Emil Ejbyfeldt Date: Tue, 16 Sep 2025 15:18:26 +0200 Subject: [PATCH 3/7] Only track types in from LazyRef [Cherry-picked 8a0bbdf1ce04add1d864f52f2a88c001509bd0bd] --- compiler/src/dotty/tools/dotc/core/Types.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index d400335e7f83..abdf7c82bbfd 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -6587,7 +6587,6 @@ object Types extends TypeUtils { class TypeSizeAccumulator(using Context) extends TypeAccumulator[Int] { var seen = util.HashSet[Type](initialCapacity = 8) def apply(n: Int, tp: Type): Int = - seen += tp tp match { case tp: AppliedType => val tpNorm = tp.tryNormalize @@ -6599,8 +6598,11 @@ object Types extends TypeUtils { apply(n, tp.superType) case tp: TypeParamRef => apply(n, TypeComparer.bounds(tp)) - case tp: LazyRef if seen.contains(tp) => - n + case tp: LazyRef => + if seen.contains(tp) then n + else + seen += tp + foldOver(n, tp) case _ => foldOver(n, tp) } From fce6922c447921b4112de8241bc9e750410334ce Mon Sep 17 00:00:00 2001 From: Tomasz Godzik Date: Fri, 26 Sep 2025 19:36:30 +0200 Subject: [PATCH 4/7] bugfix: Fix duplicate option in TyperOps --- compiler/src/dotty/tools/dotc/core/TypeUtils.scala | 5 ----- 1 file changed, 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeUtils.scala b/compiler/src/dotty/tools/dotc/core/TypeUtils.scala index e3b6c0daa3d3..fbdd633ef743 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeUtils.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeUtils.scala @@ -206,10 +206,5 @@ class TypeUtils: self.decl(nme.CONSTRUCTOR).altsWith(isApplicable).map(_.symbol) - /** Strip all outer refinements off this type */ - def stripRefinement: Type = self match - case self: RefinedOrRecType => self.parent.stripRefinement - case seld => self - end TypeUtils From 6c20869336fa10b73d4d76ed0fe696ad9c8f0f48 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 15 Aug 2025 15:35:12 -0700 Subject: [PATCH 5/7] Remove obsolete unused options --- .../tools/dotc/config/ScalaSettings.scala | 6 ----- .../tools/dotc/transform/CheckUnused.scala | 23 +++---------------- tests/warn/i15503j.scala | 22 +++++++++--------- tests/{pos => warn}/i17762.scala | 5 ++-- tests/warn/unused-can-equal.scala | 6 ++--- 5 files changed, 19 insertions(+), 43 deletions(-) rename tests/{pos => warn}/i17762.scala (87%) diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index eb3296ab476c..69121c4f51fa 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -193,12 +193,6 @@ private sealed trait WarningSettings: ChoiceWithHelp("patvars","Warn if a variable bound in a pattern is unused"), //ChoiceWithHelp("inlined", "Apply -Wunused to inlined expansions"), // TODO ChoiceWithHelp("linted", "Enable -Wunused:imports,privates,locals,implicits"), - ChoiceWithHelp( - name = "strict-no-implicit-warn", - description = """Same as -Wunused:imports, only for imports of explicit named members. - |NOTE : This overrides -Wunused:imports and NOT set by -Wunused:all""".stripMargin - ), - ChoiceWithHelp("unsafe-warn-patvars", "Deprecated alias for `patvars`"), ), default = Nil ) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 1ec68d4e2db4..9c2de1348d29 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -639,7 +639,6 @@ object CheckUnused: || m.is(Synthetic) || m.hasAnnotation(dd.UnusedAnnot) // param of unused method || sym.owner.name.isContextFunction // a ubiquitous parameter - || sym.isCanEqual || sym.info.dealias.typeSymbol.match // more ubiquity case dd.DummyImplicitClass | dd.SubTypeClass | dd.SameTypeClass => true case tps => @@ -671,7 +670,6 @@ object CheckUnused: def checkLocal(sym: Symbol, pos: SrcPos) = if ctx.settings.WunusedHas.locals && !sym.is(InlineProxy) - && !sym.isCanEqual then if sym.is(Mutable) && infos.asss(sym) then warnAt(pos)(UnusedSymbol.localVars) @@ -703,7 +701,9 @@ object CheckUnused: import scala.jdk.CollectionConverters.given import Rewrites.ActionPatch type ImpSel = (Import, ImportSelector) - def isUsed(sel: ImportSelector): Boolean = infos.sels.containsKey(sel) + // true if used or might be used, to imply don't warn about it + def isUsable(imp: Import, sel: ImportSelector): Boolean = + sel.isImportExclusion || infos.sels.containsKey(sel) def warnImport(warnable: ImpSel, actions: List[CodeAction] = Nil): Unit = val (imp, sel) = warnable val msg = UnusedSymbol.imports(actions) @@ -978,8 +978,6 @@ object CheckUnused: def isSerializationSupport: Boolean = sym.is(Method) && serializationNames(sym.name.toTermName) && sym.owner.isClass && sym.owner.derivesFrom(defn.JavaSerializableClass) - def isCanEqual: Boolean = - sym.isOneOf(GivenOrImplicit) && sym.info.finalResultType.baseClasses.exists(_.derivesFrom(defn.CanEqualClass)) def isMarkerTrait: Boolean = sym.info.hiBound.resultType.allMembers.forall: d => val m = d.symbol @@ -1013,21 +1011,6 @@ object CheckUnused: def isGeneratedByEnum: Boolean = imp.symbol.exists && imp.symbol.owner.is(Enum, butNot = Case) - /** Under -Wunused:strict-no-implicit-warn, avoid false positives - * if this selector is a wildcard that might import implicits or - * specifically does import an implicit. - * Similarly, import of CanEqual must not warn, as it is always witness. - */ - def isLoose(sel: ImportSelector): Boolean = - if ctx.settings.WunusedHas.strictNoImplicitWarn then - if sel.isWildcard - || imp.expr.tpe.member(sel.name.toTermName).hasAltWith(_.symbol.isOneOf(GivenOrImplicit)) - || imp.expr.tpe.member(sel.name.toTypeName).hasAltWith(_.symbol.isOneOf(GivenOrImplicit)) - then return true - if sel.isWildcard && sel.isGiven - then imp.expr.tpe.allMembers.exists(_.symbol.isCanEqual) - else imp.expr.tpe.member(sel.name.toTermName).hasAltWith(_.symbol.isCanEqual) - extension (pos: SrcPos) def isZeroExtentSynthetic: Boolean = pos.span.isSynthetic && pos.span.isZeroExtent def isSynthetic: Boolean = pos.span.isSynthetic && pos.span.exists diff --git a/tests/warn/i15503j.scala b/tests/warn/i15503j.scala index fa30601d8960..62e3557fc0d3 100644 --- a/tests/warn/i15503j.scala +++ b/tests/warn/i15503j.scala @@ -1,4 +1,4 @@ -//> using options -Wunused:strict-no-implicit-warn +//> using options -Wunused:imports package foo.unused.strict.test: package a: @@ -7,15 +7,15 @@ package foo.unused.strict.test: val z: Int = 2 def f: Int = 3 package b: - import a.given // OK - import a._ // OK - import a.* // OK - import a.x // OK - import a.y // OK + import a.given // warn + import a._ // warn + import a.* // warn + import a.x // warn + import a.y // warn import a.z // warn import a.f // warn package c: - import a.given // OK + import a.given // warn import a.x // OK import a.y // OK import a.z // OK @@ -28,8 +28,8 @@ package foo.implicits.resolution: object A { implicit val x: X = new X } object B { implicit val y: Y = new Y } class C { - import A._ // OK - import B._ // OK + import A.given // warn + import B.given // OK def t = implicitly[X] } @@ -44,7 +44,7 @@ package foo.unused.summon.inlines: given willBeUsed: (A & B) = new A with B {} package use: - import lib.{A, B, C, willBeUnused, willBeUsed} //OK + import lib.{A, B, C, willBeUnused, willBeUsed} // warn import compiletime.summonInline //OK transparent inline given conflictInside: C = @@ -56,4 +56,4 @@ package foo.unused.summon.inlines: ??? val b: B = summon[B] - val c: C = summon[C] \ No newline at end of file + val c: C = summon[C] diff --git a/tests/pos/i17762.scala b/tests/warn/i17762.scala similarity index 87% rename from tests/pos/i17762.scala rename to tests/warn/i17762.scala index 65275c4619db..e3adda821fad 100644 --- a/tests/pos/i17762.scala +++ b/tests/warn/i17762.scala @@ -1,4 +1,4 @@ -//> using options -Xfatal-warnings -Wunused:all +//> using options -Werror -Wunused:all class SomeType @@ -16,6 +16,5 @@ object UsesCanEqual: object UsesCanEqual2: import HasCanEqual.f - def testIt(st1: SomeType, st2: SomeType): Boolean = - st1 == st2 \ No newline at end of file + st1 != st2 diff --git a/tests/warn/unused-can-equal.scala b/tests/warn/unused-can-equal.scala index 6e38591ccef1..7c7f0a61e163 100644 --- a/tests/warn/unused-can-equal.scala +++ b/tests/warn/unused-can-equal.scala @@ -1,5 +1,4 @@ - -//> using options -Werror -Wunused:all +//> using options -Wunused:all import scala.language.strictEquality @@ -7,9 +6,10 @@ class Box[T](x: T) derives CanEqual: def y = x def f[A, B](a: A, b: B)(using CanEqual[A, B]) = a == b // no warn +def z[A, B](a: A, b: B)(using ce: CanEqual[A, B]) = a.toString == b.toString // no warn def g = - import Box.given // no warn + import Box.given // warn "42".length @main def test() = println: From 48de883b28b98467addc2515a7a50411ae5f4f5f Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 15 Aug 2025 16:11:31 -0700 Subject: [PATCH 6/7] Report unused masking import selectors --- compiler/src/dotty/tools/dotc/ast/untpd.scala | 1 + .../tools/dotc/transform/CheckUnused.scala | 30 ++++++++++++++----- tests/warn/i15503a.scala | 4 +-- tests/warn/i23758.scala | 11 +++++++ 4 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 tests/warn/i23758.scala diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 4c8fea17c076..84253fa63b1f 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -138,6 +138,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case Ident(rename: TermName) => rename case _ => name + /** It's a masking import if `!isWildcard`. */ def isUnimport = rename == nme.WILDCARD } diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 9c2de1348d29..00d157e6db6b 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -26,6 +26,7 @@ import dotty.tools.dotc.util.chaining.* import java.util.IdentityHashMap +import scala.annotation.* import scala.collection.mutable, mutable.{ArrayBuilder, ListBuffer, Stack} import CheckUnused.* @@ -288,6 +289,8 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha alt.symbol == sym || nm.isTypeName && alt.symbol.isAliasType && alt.info.dealias.typeSymbol == sym sameSym && alt.symbol.isAccessibleFrom(qtpe) + def hasAltMemberNamed(nm: Name) = qtpe.member(nm).hasAltWith(_.symbol.isAccessibleFrom(qtpe)) + def loop(sels: List[ImportSelector]): ImportSelector | Null = sels match case sel :: sels => val matches = @@ -304,9 +307,17 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha else !sym.is(Given) // Normal wildcard, check that the symbol is not a given (but can be implicit) } + else if sel.isUnimport then + val masksMatchingMember = + name != nme.NO_NAME + && sels.exists(x => x.isWildcard && !x.isGiven) + && !name.exists(_.toTermName != sel.name) // import a.b as _, b must match name + && (hasAltMemberNamed(sel.name) || hasAltMemberNamed(sel.name.toTypeName)) + if masksMatchingMember then + refInfos.sels.put(sel, ()) // imprecise due to precedence but errs on the side of false negative + false else - // if there is an explicit name, it must match - !name.exists(_.toTermName != sel.rename) + !name.exists(_.toTermName != sel.rename) // if there is an explicit name, it must match && (prefix.eq(NoPrefix) || qtpe =:= prefix) && (hasAltMember(sel.name) || hasAltMember(sel.name.toTypeName)) if matches then sel else loop(sels) @@ -697,11 +708,11 @@ object CheckUnused: warnAt(pos)(UnusedSymbol.unsetPrivates) def checkImports() = - // TODO check for unused masking import import scala.jdk.CollectionConverters.given import Rewrites.ActionPatch type ImpSel = (Import, ImportSelector) - // true if used or might be used, to imply don't warn about it + def isUsed(sel: ImportSelector): Boolean = infos.sels.containsKey(sel) + @unused // avoid merge conflict def isUsable(imp: Import, sel: ImportSelector): Boolean = sel.isImportExclusion || infos.sels.containsKey(sel) def warnImport(warnable: ImpSel, actions: List[CodeAction] = Nil): Unit = @@ -712,7 +723,7 @@ object CheckUnused: warnAt(sel.srcPos)(msg, origin) if !actionable then - for imp <- infos.imps.keySet.nn.asScala; sel <- imp.selectors if !isUsable(imp, sel) do + for imp <- infos.imps.keySet.nn.asScala; sel <- imp.selectors if !isUsed(sel) do warnImport(imp -> sel) else // If the rest of the line is blank, include it in the final edit position. (Delete trailing whitespace.) @@ -767,7 +778,7 @@ object CheckUnused: while index < sortedImps.length do val nextImport = sortedImps.indexSatisfying(from = index + 1)(_.isPrimaryClause) // next import statement if sortedImps.indexSatisfying(from = index, until = nextImport): imp => - imp.selectors.exists(!isUsable(imp, _)) // check if any selector in statement was unused + imp.selectors.exists(!isUsed(_)) // check if any selector in statement was unused < nextImport then // if no usable selectors in the import statement, delete it entirely. // if there is exactly one usable selector, then replace with just that selector (i.e., format it). @@ -776,7 +787,7 @@ object CheckUnused: // Reminder that first clause span includes the keyword, so delete point-to-start instead. val existing = sortedImps.slice(index, nextImport) val (keeping, deleting) = existing.iterator.flatMap(imp => imp.selectors.map(imp -> _)).toList - .partition(isUsable(_, _)) + .partition((imp, sel) => isUsed(sel)) if keeping.isEmpty then val editPos = existing.head.srcPos.sourcePos.withSpan: Span(start = existing.head.srcPos.span.start, end = existing.last.srcPos.span.end) @@ -1001,6 +1012,11 @@ object CheckUnused: def boundTpe: Type = sel.bound match case untpd.TypedSplice(tree) => tree.tpe case _ => NoType + /** Is a "masking" import of the form import `qual.member as _`. + * Both conditions must be checked. + */ + @unused // matchingSelector checks isWildcard first + def isImportExclusion: Boolean = sel.isUnimport && !sel.isWildcard extension (imp: Import)(using Context) /** Is it the first import clause in a statement? `a.x` in `import a.x, b.{y, z}` */ diff --git a/tests/warn/i15503a.scala b/tests/warn/i15503a.scala index 8fc97888b584..439799ee8e3d 100644 --- a/tests/warn/i15503a.scala +++ b/tests/warn/i15503a.scala @@ -85,12 +85,12 @@ object InnerMostCheck: val a = Set(1) object IgnoreExclusion: - import collection.mutable.{Set => _} // OK - import collection.mutable.{Map => _} // OK + import collection.mutable.{Map => _, Set => _, *} // OK?? import collection.mutable.{ListBuffer} // warn def check = val a = Set(1) val b = Map(1 -> 2) + def c = Seq(42) /** * Some given values for the test */ diff --git a/tests/warn/i23758.scala b/tests/warn/i23758.scala new file mode 100644 index 000000000000..08acb8aa588c --- /dev/null +++ b/tests/warn/i23758.scala @@ -0,0 +1,11 @@ +//> using options -Wunused:imports + +import scala.util.Try as _ // warn + +class Promise(greeting: String): + override def toString = greeting + +@main def test = println: + import scala.concurrent.{Promise as _, *}, ExecutionContext.Implicits.given + val promise = new Promise("world") + Future(s"hello, $promise") From 98a0ccb1a68aba00a0b1f9830ba26e2d861ce261 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 15 Aug 2025 15:35:12 -0700 Subject: [PATCH 7/7] Remove obsolete unused options --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 3 --- 1 file changed, 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 00d157e6db6b..03d0f0dd1e8d 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -712,9 +712,6 @@ object CheckUnused: import Rewrites.ActionPatch type ImpSel = (Import, ImportSelector) def isUsed(sel: ImportSelector): Boolean = infos.sels.containsKey(sel) - @unused // avoid merge conflict - def isUsable(imp: Import, sel: ImportSelector): Boolean = - sel.isImportExclusion || infos.sels.containsKey(sel) def warnImport(warnable: ImpSel, actions: List[CodeAction] = Nil): Unit = val (imp, sel) = warnable val msg = UnusedSymbol.imports(actions)